diff --git a/.clang-format b/.clang-format index 153f0e4a33..74975cc3c0 100644 --- a/.clang-format +++ b/.clang-format @@ -84,6 +84,7 @@ PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left +QualifierAlignment: Left ReflowComments: true SortIncludes: true SortUsingDeclarations: true diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000000..f2f118c0d6 --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,3 @@ +commit: $Format:%H$ +commit-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..82bf71c1c5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.git_archival.txt export-subst \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..99ba77a6fe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Report a bug that is preventing proper operation +title: '' +labels: Bugs +assignees: '' + +--- + + + +## Bug Description + + + +## Steps to Reproduce + + + +## Environment + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..40e40338b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Troubleshooting and User Support + url: https://openmc.discourse.group/ + about: For user support and troubleshooting, please use our Discourse forum diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000000..04ecb8cf9c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,10 @@ +--- +name: Documentation improvement +about: Found something incomplete or incorrect in our documentation? +title: '' +labels: Documentation +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..3b13abdbcb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature or enhancement request +about: Suggest a new feature or enhancement to existing capabilities +title: '' +labels: '' +assignees: '' + +--- + +## Description + + + +## Alternatives + + + +## Compatibility + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..68a6e692a4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,24 @@ + + +# Description + +Please include a summary of the change and which issue is fixed if applicable. Please also include relevant motivation and context. + +Fixes # (issue) + +# Checklist + +- [ ] I have performed a self-review of my own code +- [ ] I have run [clang-format](https://docs.openmc.org/en/latest/devguide/styleguide.html#automatic-formatting) (version 15) on any C++ source files (if applicable) +- [ ] I have followed the [style guidelines](https://docs.openmc.org/en/latest/devguide/styleguide.html#python) for Python source files (if applicable) +- [ ] I have made corresponding changes to the documentation (if applicable) +- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e65e630b15..d75a64d662 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,10 @@ env: jobs: main: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: - python-version: [3.8] + python-version: ["3.11"] mpi: [n, y] omp: [n, y] dagmc: [n] @@ -34,30 +34,30 @@ jobs: vectfit: [n] include: - - python-version: 3.6 + - python-version: "3.12" omp: n mpi: n - - python-version: 3.7 + - python-version: "3.13" omp: n mpi: n - dagmc: y - python-version: 3.8 + python-version: "3.11" mpi: y omp: y - libmesh: y - python-version: 3.8 + python-version: "3.11" mpi: y omp: y - libmesh: y - python-version: 3.8 + python-version: "3.11" mpi: n omp: y - event: y - python-version: 3.8 + python-version: "3.11" omp: y mpi: n - vectfit: y - python-version: 3.8 + python-version: "3.11" omp: n mpi: y name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }}, @@ -73,62 +73,143 @@ jobs: EVENT: ${{ matrix.event }} VECTFIT: ${{ matrix.vectfit }} LIBMESH: ${{ matrix.libmesh }} + NPY_DISABLE_CPU_FEATURES: "AVX512F AVX512_SKX" + OPENBLAS_NUM_THREADS: 1 + PYTEST_ADDOPTS: --cov=openmc --cov-report=lcov:coverage-python.lcov + # libfabric complains about fork() as a result of using Python multiprocessing. + # We can work around it with RDMAV_FORK_SAFE=1 in libfabric < 1.13 and with + # FI_EFA_FORK_SAFE=1 in more recent versions. + RDMAV_FORK_SAFE: 1 steps: - - uses: actions/checkout@v2 + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.31' + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Environment Variables run: | - echo "DAGMC_ROOT=$HOME/DAGMC" echo "OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml" >> $GITHUB_ENV echo "OPENMC_ENDF_DATA=$HOME/endf-b-vii.1" >> $GITHUB_ENV + # get the sha of the last branch commit + # for push and workflow_dispatch events, use the current reference head + BRANCH_SHA=HEAD + # for a pull_request event, use the last reference of the parents of the merge commit + if [ "${{ github.event_name }}" == "pull_request" ]; then + BRANCH_SHA=$(git rev-list --parents -n 1 HEAD | rev | cut -d" " -f 1 | rev) + fi + COMMIT_MESSAGE=$(git log $BRANCH_SHA -1 --pretty=%B | tr '\n' ' ') + echo ${COMMIT_MESSAGE} + echo "COMMIT_MESSAGE=${COMMIT_MESSAGE}" >> $GITHUB_ENV - name: Apt dependencies shell: bash run: | sudo apt -y update sudo apt install -y libpng-dev \ - libmpich-dev \ libnetcdf-dev \ libpnetcdf-dev \ libhdf5-serial-dev \ - libhdf5-mpich-dev \ libeigen3-dev + + - name: Optional apt dependencies for MPI + shell: bash + if: ${{ matrix.mpi == 'y' }} + run: | + sudo apt install -y libhdf5-mpich-dev \ + libmpich-dev sudo update-alternatives --set mpi /usr/bin/mpicc.mpich sudo update-alternatives --set mpirun /usr/bin/mpirun.mpich sudo update-alternatives --set mpi-x86_64-linux-gnu /usr/include/x86_64-linux-gnu/mpich + - name: Optional apt dependencies for vectfit + shell: bash + if: ${{ matrix.vectfit == 'y' }} + run: sudo apt install -y libblas-dev liblapack-dev + - name: install shell: bash run: | echo "$HOME/NJOY2016/build" >> $GITHUB_PATH $GITHUB_WORKSPACE/tools/ci/gha-install.sh + - name: display-config + shell: bash + run: | + openmc -v + + - name: cache-xs + uses: actions/cache@v4 + with: + path: | + ~/nndc_hdf5 + ~/endf-b-vii.1 + key: ${{ runner.os }}-build-xs-cache + - name: before shell: bash run: $GITHUB_WORKSPACE/tools/ci/gha-before-script.sh - name: test shell: bash - run: $GITHUB_WORKSPACE/tools/ci/gha-script.sh + run: | + CTEST_OUTPUT_ON_FAILURE=1 make test -C $GITHUB_WORKSPACE/build/ + $GITHUB_WORKSPACE/tools/ci/gha-script.sh - - name: after_success + - name: Setup tmate debug session + continue-on-error: true + if: ${{ contains(env.COMMIT_MESSAGE, '[gha-debug]') }} + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 10 + + - name: Generate C++ coverage (gcovr) shell: bash run: | - cpp-coveralls -i src -i include --exclude-pattern "/usr/*" --dump cpp_cov.json - coveralls --merge=cpp_cov.json --service=github + # Produce LCOV directly from gcov data in the build tree + gcovr \ + --root "$GITHUB_WORKSPACE" \ + --object-directory "$GITHUB_WORKSPACE/build" \ + --filter "$GITHUB_WORKSPACE/src" \ + --filter "$GITHUB_WORKSPACE/include" \ + --exclude "$GITHUB_WORKSPACE/src/external/.*" \ + --exclude "$GITHUB_WORKSPACE/src/include/openmc/external/.*" \ + --gcov-ignore-errors source_not_found \ + --gcov-ignore-errors output_error \ + --gcov-ignore-parse-errors suspicious_hits.warn \ + --print-summary \ + --lcov -o coverage-cpp.lcov || true + + - name: Merge C++ and Python coverage + shell: bash + run: | + # Merge C++ and Python LCOV into a single file for upload + cat coverage-cpp.lcov coverage-python.lcov > coverage.lcov + + - name: Upload coverage to Coveralls + if: ${{ hashFiles('coverage.lcov') != '' }} + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel: true + flag-name: C++ and Python + path-to-lcov: coverage.lcov finish: needs: main runs-on: ubuntu-latest steps: - name: Coveralls Finished - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: - github-token: ${{ secrets.github_token }} + github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.github/workflows/dockerhub-publish-dagmc-libmesh.yml b/.github/workflows/dockerhub-publish-dagmc-libmesh.yml index 3336c653eb..813596953b 100644 --- a/.github/workflows/dockerhub-publish-dagmc-libmesh.yml +++ b/.github/workflows/dockerhub-publish-dagmc-libmesh.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:latest-dagmc-libmesh diff --git a/.github/workflows/dockerhub-publish-dagmc.yml b/.github/workflows/dockerhub-publish-dagmc.yml index 663e1fd17d..6757f77271 100644 --- a/.github/workflows/dockerhub-publish-dagmc.yml +++ b/.github/workflows/dockerhub-publish-dagmc.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:latest-dagmc diff --git a/.github/workflows/dockerhub-publish-dev.yml b/.github/workflows/dockerhub-publish-dev.yml index a08d13e5ae..7a81363a78 100644 --- a/.github/workflows/dockerhub-publish-dev.yml +++ b/.github/workflows/dockerhub-publish-dev.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:develop diff --git a/.github/workflows/dockerhub-publish-develop-dagmc-libmesh.yml b/.github/workflows/dockerhub-publish-develop-dagmc-libmesh.yml index 0c90d87962..a219f2a91d 100644 --- a/.github/workflows/dockerhub-publish-develop-dagmc-libmesh.yml +++ b/.github/workflows/dockerhub-publish-develop-dagmc-libmesh.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:develop-dagmc-libmesh diff --git a/.github/workflows/dockerhub-publish-develop-dagmc.yml b/.github/workflows/dockerhub-publish-develop-dagmc.yml index b98b3dff6a..a901b8d3f0 100644 --- a/.github/workflows/dockerhub-publish-develop-dagmc.yml +++ b/.github/workflows/dockerhub-publish-develop-dagmc.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:develop-dagmc diff --git a/.github/workflows/dockerhub-publish-develop-libmesh.yml b/.github/workflows/dockerhub-publish-develop-libmesh.yml index e42ba0b271..22e9aa68fb 100644 --- a/.github/workflows/dockerhub-publish-develop-libmesh.yml +++ b/.github/workflows/dockerhub-publish-develop-libmesh.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:develop-libmesh diff --git a/.github/workflows/dockerhub-publish-libmesh.yml b/.github/workflows/dockerhub-publish-libmesh.yml index a861c5c9cd..843ce0f6fd 100644 --- a/.github/workflows/dockerhub-publish-libmesh.yml +++ b/.github/workflows/dockerhub-publish-libmesh.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:latest-libmesh diff --git a/.github/workflows/dockerhub-publish-release-dagmc-libmesh.yml b/.github/workflows/dockerhub-publish-release-dagmc-libmesh.yml index b8a152aef8..db62bb53e6 100644 --- a/.github/workflows/dockerhub-publish-release-dagmc-libmesh.yml +++ b/.github/workflows/dockerhub-publish-release-dagmc-libmesh.yml @@ -8,25 +8,25 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:${{ env.RELEASE_VERSION }}-dagmc-libmesh diff --git a/.github/workflows/dockerhub-publish-release-dagmc.yml b/.github/workflows/dockerhub-publish-release-dagmc.yml index 927e91212a..de95937828 100644 --- a/.github/workflows/dockerhub-publish-release-dagmc.yml +++ b/.github/workflows/dockerhub-publish-release-dagmc.yml @@ -8,25 +8,25 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:${{ env.RELEASE_VERSION }}-dagmc diff --git a/.github/workflows/dockerhub-publish-release-libmesh.yml b/.github/workflows/dockerhub-publish-release-libmesh.yml index 1907eef46e..e8ea98aebd 100644 --- a/.github/workflows/dockerhub-publish-release-libmesh.yml +++ b/.github/workflows/dockerhub-publish-release-libmesh.yml @@ -8,25 +8,25 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:${{ env.RELEASE_VERSION }}-libmesh diff --git a/.github/workflows/dockerhub-publish-release.yml b/.github/workflows/dockerhub-publish-release.yml index c94a50c0da..fab030192b 100644 --- a/.github/workflows/dockerhub-publish-release.yml +++ b/.github/workflows/dockerhub-publish-release.yml @@ -8,25 +8,25 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:${{ env.RELEASE_VERSION }} diff --git a/.github/workflows/dockerhub-publish.yml b/.github/workflows/dockerhub-publish.yml index 6bd0c4dc69..fd51a9fa73 100644 --- a/.github/workflows/dockerhub-publish.yml +++ b/.github/workflows/dockerhub-publish.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: push: true tags: openmc/openmc:latest diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml new file mode 100644 index 0000000000..cef14ca2c8 --- /dev/null +++ b/.github/workflows/format-check.yml @@ -0,0 +1,32 @@ +name: C++ Format Check + +on: + # allow workflow to be run manually + workflow_dispatch: + + pull_request: + branches: + - develop + - master + +jobs: + cpp-linter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cpp-linter/cpp-linter-action@v2 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + style: file + files-changed-only: true + tidy-checks: '-*' + version: '15' # clang-format version + file-annotations: true + step-summary: true + extensions: 'cpp,h' + + - name: Failure Check + if: steps.linter.outputs.checks-failed > 0 + run: echo "Some files failed the formatting check! See job summary and file annotations for more info" && exit 1 diff --git a/.gitignore b/.gitignore index 88e59895d5..3b2a24a4db 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.o *.log *.out +*.pkl # Compiler python objects *.pyc diff --git a/.gitmodules b/.gitmodules index ff91200103..f84d09bb1f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "vendor/pugixml"] path = vendor/pugixml url = https://github.com/zeux/pugixml.git -[submodule "vendor/gsl-lite"] - path = vendor/gsl-lite - url = https://github.com/martinmoene/gsl-lite.git [submodule "vendor/xtensor"] path = vendor/xtensor url = https://github.com/xtensor-stack/xtensor.git @@ -13,3 +10,6 @@ [submodule "vendor/fmt"] path = vendor/fmt url = https://github.com/fmtlib/fmt.git +[submodule "vendor/Catch2"] + path = vendor/Catch2 + url = https://github.com/catchorg/Catch2.git diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 98ca8b581d..3578144b25 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,13 +1,18 @@ version: 2 build: - os: "ubuntu-20.04" + os: "ubuntu-24.04" tools: - python: "3.9" - + python: "3.12" + jobs: + post_checkout: + - git fetch --unshallow || true sphinx: configuration: docs/source/conf.py python: install: - - requirements: docs/requirements-rtd.txt + - method: pip + path: . + extra_requirements: + - docs diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000000..19b4213a15 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,34 @@ +preferred-citation: + authors: + - family-names: Romano + given-names: Paul K. + orcid: "https://orcid.org/0000-0002-1147-045X" + - final-names: Horelik + given-names: Nicholas E. + - family-names: Herman + given-names: Bryan R. + - family-names: Nelson + given-names: Adam G. + orcid: "https://orcid.org/0000-0002-3614-0676" + - family-names: Forget + given-names: Benoit + orcid: "https://orcid.org/0000-0003-1459-7672" + - family-names: Smith + given-names: Kord + contact: + - family-names: Romano + given-names: Paul K. + orcid: "https://orcid.org/0000-0002-1147-045X" + doi: 10.1016/j.anucene.2014.07.048 + issn: 0306-4549 + volume: 82 + journal: Annals of Nuclear Energy + publisher: + name: Elsevier + start: 90 + end: 97 + year: 2015 + month: 8 + title: "OpenMC: A state-of-the-art Monte Carlo code for research and development" + type: article + url: "https://doi.org/10.1016/j.anucene.2014.07.048" diff --git a/CMakeLists.txt b/CMakeLists.txt index cfffbdccc1..474451c8ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,18 @@ -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) project(openmc C CXX) -# Set version numbers -set(OPENMC_VERSION_MAJOR 0) -set(OPENMC_VERSION_MINOR 13) -set(OPENMC_VERSION_RELEASE 0) -set(OPENMC_VERSION ${OPENMC_VERSION_MAJOR}.${OPENMC_VERSION_MINOR}.${OPENMC_VERSION_RELEASE}) +# Set module path +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) + +include(GetVersionFromGit) + +# Output version information +message(STATUS "OpenMC version: ${OPENMC_VERSION}") +message(STATUS "OpenMC dev state: ${OPENMC_DEV_STATE}") +message(STATUS "OpenMC commit hash: ${OPENMC_COMMIT_HASH}") +message(STATUS "OpenMC commit count: ${OPENMC_COMMIT_COUNT}") + +# Generate version.h configure_file(include/openmc/version.h.in "${CMAKE_BINARY_DIR}/include/openmc/version.h" @ONLY) # Setup output directories @@ -13,75 +20,134 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -# Set module path -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) - -# Allow user to specify _ROOT variables -if (NOT (CMAKE_VERSION VERSION_LESS 3.12)) - cmake_policy(SET CMP0074 NEW) +# Enable correct usage of CXX_EXTENSIONS +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.22) + cmake_policy(SET CMP0128 NEW) endif() #=============================================================================== # Command line options #=============================================================================== -option(openmp "Enable shared-memory parallelism with OpenMP" ON) -option(profile "Compile with profiling flags" OFF) -option(debug "Compile with debug flags" OFF) -option(optimize "Turn on all compiler optimization flags" OFF) -option(coverage "Compile with coverage analysis flags" OFF) -option(dagmc "Enable support for DAGMC (CAD) geometry" OFF) -option(libmesh "Enable support for libMesh unstructured mesh tallies" OFF) +option(OPENMC_USE_OPENMP "Enable shared-memory parallelism with OpenMP" ON) +option(OPENMC_BUILD_TESTS "Build tests" ON) +option(OPENMC_ENABLE_PROFILE "Compile with profiling flags" OFF) +option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags" OFF) +option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF) +option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) +option(OPENMC_USE_MPI "Enable MPI" OFF) +option(OPENMC_USE_UWUW "Enable UWUW" OFF) +option(OPENMC_FORCE_VENDORED_LIBS "Explicitly use submodules defined in 'vendor'" OFF) + +message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}") +message(STATUS "OPENMC_BUILD_TESTS ${OPENMC_BUILD_TESTS}") +message(STATUS "OPENMC_ENABLE_PROFILE ${OPENMC_ENABLE_PROFILE}") +message(STATUS "OPENMC_ENABLE_COVERAGE ${OPENMC_ENABLE_COVERAGE}") +message(STATUS "OPENMC_USE_DAGMC ${OPENMC_USE_DAGMC}") +message(STATUS "OPENMC_USE_LIBMESH ${OPENMC_USE_LIBMESH}") +message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}") +message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}") +message(STATUS "OPENMC_FORCE_VENDORED_LIBS ${OPENMC_FORCE_VENDORED_LIBS}") + +# Warnings for deprecated options +foreach(OLD_OPT IN ITEMS "openmp" "profile" "coverage" "dagmc" "libmesh") + if(DEFINED ${OLD_OPT}) + string(TOUPPER ${OLD_OPT} OPT_UPPER) + if ("${OLD_OPT}" STREQUAL "profile" OR "${OLD_OPT}" STREQUAL "coverage") + set(NEW_OPT_PREFIX "OPENMC_ENABLE") + else() + set(NEW_OPT_PREFIX "OPENMC_USE") + endif() + message(WARNING "The OpenMC CMake option '${OLD_OPT}' has been deprecated. " + "Its value will be ignored. " + "Please use '-D${NEW_OPT_PREFIX}_${OPT_UPPER}=${${OLD_OPT}}' instead.") + unset(${OLD_OPT} CACHE) + endif() +endforeach() + +foreach(OLD_BLD in ITEMS "debug" "optimize") + if(DEFINED ${OLD_BLD}) + if("${OLD_BLD}" STREQUAL "debug") + set(BLD_VAR "Debug") + else() + set(BLD_VAR "Release") + endif() + message(WARNING "The OpenMC CMake option '${OLD_BLD}' has been deprecated. " + "Its value will be ignored. " + "OpenMC now uses the CMAKE_BUILD_TYPE variable to set the build mode. " + "Please use '-DCMAKE_BUILD_TYPE=${BLD_VAR}' instead.") + unset(${OLD_BLD} CACHE) + endif() +endforeach() + +#=============================================================================== +# Set a default build configuration if not explicitly specified +#=============================================================================== + +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, defaulting to RelWithDebInfo") + set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build" FORCE) +endif() + +#=============================================================================== +# OpenMP for shared-memory parallelism (and GPU support some day!) +#=============================================================================== + +if(OPENMC_USE_OPENMP) + find_package(OpenMP REQUIRED) +endif() #=============================================================================== # MPI for distributed-memory parallelism #=============================================================================== -set(MPI_ENABLED FALSE) -if(${CMAKE_CXX_COMPILER} MATCHES "(mpi[^/]*|CC)$") - message(STATUS "Detected MPI wrapper: ${CMAKE_CXX_COMPILER}") - set(MPI_ENABLED TRUE) +if(OPENMC_USE_MPI) + find_package(MPI REQUIRED) endif() +#=============================================================================== +# Helper macro for finding a dependency +#=============================================================================== + +macro(find_package_write_status pkg) + find_package(${pkg} QUIET NO_SYSTEM_ENVIRONMENT_PATH) + if(${pkg}_FOUND) + message(STATUS "Found ${pkg}: ${${pkg}_DIR} (version ${${pkg}_VERSION})") + else() + message(STATUS "Did not find ${pkg}, will use submodule instead") + endif() +endmacro() + #=============================================================================== # DAGMC Geometry Support - need DAGMC/MOAB #=============================================================================== -if(dagmc) + +if(OPENMC_USE_DAGMC) find_package(DAGMC REQUIRED PATH_SUFFIXES lib/cmake) if (${DAGMC_VERSION} VERSION_LESS 3.2.0) - message(FATAL_ERROR "Discovered DAGMC Version: ${DAGMC_VERSION}. \ - Please update DAGMC to version 3.2.0 or greater.") + message(FATAL_ERROR "Discovered DAGMC Version: ${DAGMC_VERSION}." + "Please update DAGMC to version 3.2.0 or greater.") endif() -endif() + message(STATUS "Found DAGMC: ${DAGMC_DIR} (version ${DAGMC_VERSION})") -#=============================================================================== -# Check for submodules perhaps already on system -#=============================================================================== - -# If not found, we just pull appropriate versions from github and build them. -find_package(fmt QUIET NO_SYSTEM_ENVIRONMENT_PATH) -if(fmt_FOUND) - message(STATUS "Found fmt: ${fmt_DIR} (version ${fmt_VERSION})") -else() - message(STATUS "Did not find fmt, will use submodule instead") -endif() -find_package(pugixml QUIET NO_SYSTEM_ENVIRONMENT_PATH) -if(pugixml_FOUND) - message(STATUS "Found pugixml: ${pugixml_DIR}") -else() - message(STATUS "Did not find pugixml, will use submodule instead") + # Check if UWUW is needed and available + if(OPENMC_USE_UWUW AND NOT DAGMC_BUILD_UWUW) + message(FATAL_ERROR "UWUW is enabled but DAGMC was not configured with UWUW.") + endif() endif() #=============================================================================== # libMesh Unstructured Mesh Support #=============================================================================== -if(libmesh) + +if(OPENMC_USE_LIBMESH) find_package(LIBMESH REQUIRED) endif() #=============================================================================== # libpng #=============================================================================== + find_package(PNG) #=============================================================================== @@ -102,11 +168,16 @@ if(NOT DEFINED HDF5_PREFER_PARALLEL) endif() find_package(HDF5 REQUIRED COMPONENTS C HL) + +# Remove HDF5 transitive dependencies that are system libraries +list(FILTER HDF5_LIBRARIES EXCLUDE REGEX ".*lib(pthread|dl|m).*") +message(STATUS "HDF5 Libraries: ${HDF5_LIBRARIES}") + if(HDF5_IS_PARALLEL) - if(NOT MPI_ENABLED) - message(FATAL_ERROR "Parallel HDF5 was detected, but the detected compiler,\ - ${CMAKE_CXX_COMPILER}, does not support MPI. An MPI-capable compiler must \ - be used with parallel HDF5.") + if(NOT OPENMC_USE_MPI) + message(FATAL_ERROR "Parallel HDF5 was detected, but MPI was not enabled.\ + To use parallel HDF5, OpenMC needs to be built with MPI support by passing\ + -DOPENMC_USE_MPI=ON when calling cmake.") endif() message(STATUS "Using parallel HDF5") endif() @@ -114,7 +185,7 @@ endif() # Version 1.12 of HDF5 deprecates the H5Oget_info_by_idx() interface. # Thus, we give these flags to allow usage of the old interface in newer # versions of HDF5. -if(NOT (${HDF5_VERSION} VERSION_LESS 1.12.0)) +if(${HDF5_VERSION} VERSION_GREATER_EQUAL 1.12.0) list(APPEND cxxflags -DH5Oget_info_by_idx_vers=1 -DH5O_info_t_vers=1) endif() @@ -125,30 +196,13 @@ endif() # Skip for Visual Studio which has its own configurations through GUI if(NOT MSVC) -if(openmp) - # Requires CMake 3.1+ - find_package(OpenMP) - if(OPENMP_FOUND) - list(APPEND cxxflags ${OpenMP_CXX_FLAGS}) - list(APPEND ldflags ${OpenMP_CXX_FLAGS}) - endif() -endif() - set(CMAKE_POSITION_INDEPENDENT_CODE ON) -list(APPEND cxxflags -O2) -if(debug) - list(REMOVE_ITEM cxxflags -O2) - list(APPEND cxxflags -g -O0) -endif() -if(profile) +if(OPENMC_ENABLE_PROFILE) list(APPEND cxxflags -g -fno-omit-frame-pointer) endif() -if(optimize) - list(REMOVE_ITEM cxxflags -O2) - list(APPEND cxxflags -O3) -endif() -if(coverage) + +if(OPENMC_ENABLE_COVERAGE) list(APPEND cxxflags --coverage) list(APPEND ldflags --coverage) endif() @@ -162,8 +216,6 @@ endif() #=============================================================================== # Update git submodules as needed #=============================================================================== - -find_package(Git) if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") option(GIT_SUBMODULE "Check submodules during build" ON) if(GIT_SUBMODULE) @@ -188,42 +240,63 @@ endif() # pugixml library #=============================================================================== -if (NOT pugixml_FOUND) +if(OPENMC_FORCE_VENDORED_LIBS) add_subdirectory(vendor/pugixml) set_target_properties(pugixml PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS OFF) +else() + find_package_write_status(pugixml) + if (NOT pugixml_FOUND) + add_subdirectory(vendor/pugixml) + set_target_properties(pugixml PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS OFF) + endif() endif() #=============================================================================== # {fmt} library #=============================================================================== -if (NOT fmt_FOUND) +if(OPENMC_FORCE_VENDORED_LIBS) set(FMT_INSTALL ON CACHE BOOL "Generate the install target.") add_subdirectory(vendor/fmt) +else() + find_package_write_status(fmt) + if (NOT fmt_FOUND) + set(FMT_INSTALL ON CACHE BOOL "Generate the install target.") + add_subdirectory(vendor/fmt) + endif() endif() #=============================================================================== # xtensor header-only library #=============================================================================== -# CMake 3.13+ will complain about policy CMP0079 unless it is set explicitly -if (NOT (CMAKE_VERSION VERSION_LESS 3.13)) - cmake_policy(SET CMP0079 NEW) +if(OPENMC_FORCE_VENDORED_LIBS) + add_subdirectory(vendor/xtl) + set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl) + add_subdirectory(vendor/xtensor) +else() + find_package_write_status(xtensor) + if (NOT xtensor_FOUND) + add_subdirectory(vendor/xtl) + set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl) + add_subdirectory(vendor/xtensor) + endif() endif() -add_subdirectory(vendor/xtl) -set(xtl_DIR ${CMAKE_CURRENT_BINARY_DIR}/vendor/xtl) -add_subdirectory(vendor/xtensor) - #=============================================================================== -# GSL header-only library +# Catch2 library #=============================================================================== -add_subdirectory(vendor/gsl-lite) - -# Make sure contract violations throw exceptions -target_compile_definitions(gsl-lite-v1 INTERFACE GSL_THROW_ON_CONTRACT_VIOLATION) -target_compile_definitions(gsl-lite-v1 INTERFACE gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON=1) +if(OPENMC_BUILD_TESTS) + if (OPENMC_FORCE_VENDORED_LIBS) + add_subdirectory(vendor/Catch2) + else() + find_package_write_status(Catch2) + if (NOT Catch2_FOUND) + add_subdirectory(vendor/Catch2) + endif() + endif() +endif() #=============================================================================== # RPATH information @@ -254,18 +327,6 @@ if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") endif() -#=============================================================================== -# faddeeva library -#=============================================================================== - -add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) -target_include_directories(faddeeva - PUBLIC - $ - $ -) -target_compile_options(faddeeva PRIVATE ${cxxflags}) - #=============================================================================== # libopenmc #=============================================================================== @@ -274,10 +335,11 @@ list(APPEND libopenmc_SOURCES src/bank.cpp src/boundary_condition.cpp src/bremsstrahlung.cpp - src/dagmc.cpp src/cell.cpp + src/chain.cpp src/cmfd_solver.cpp src/cross_sections.cpp + src/dagmc.cpp src/distribution.cpp src/distribution_angle.cpp src/distribution_energy.cpp @@ -287,18 +349,23 @@ list(APPEND libopenmc_SOURCES src/endf.cpp src/error.cpp src/event.cpp - src/initialize.cpp + src/file_utils.cpp src/finalize.cpp src/geometry.cpp src/geometry_aux.cpp src/hdf5_interface.cpp + src/ifp.cpp + src/initialize.cpp src/lattice.cpp src/material.cpp src/math_functions.cpp + src/mcpl_interface.cpp src/mesh.cpp src/message_passing.cpp src/mgxs.cpp src/mgxs_interface.cpp + src/ncrystal_interface.cpp + src/ncrystal_load.cpp src/nuclide.cpp src/output.cpp src/particle.cpp @@ -313,6 +380,12 @@ list(APPEND libopenmc_SOURCES src/progress_bar.cpp src/random_dist.cpp src/random_lcg.cpp + src/random_ray/random_ray_simulation.cpp + src/random_ray/random_ray.cpp + src/random_ray/flat_source_domain.cpp + src/random_ray/linear_source_domain.cpp + src/random_ray/moment_matrix.cpp + src/random_ray/source_region.cpp src/reaction.cpp src/reaction_product.cpp src/scattdata.cpp @@ -331,20 +404,25 @@ list(APPEND libopenmc_SOURCES src/tallies/derivative.cpp src/tallies/filter.cpp src/tallies/filter_azimuthal.cpp - src/tallies/filter_cellborn.cpp - src/tallies/filter_cellfrom.cpp src/tallies/filter_cell.cpp src/tallies/filter_cell_instance.cpp + src/tallies/filter_cellborn.cpp + src/tallies/filter_cellfrom.cpp + src/tallies/filter_collision.cpp src/tallies/filter_delayedgroup.cpp src/tallies/filter_distribcell.cpp - src/tallies/filter_energyfunc.cpp src/tallies/filter_energy.cpp - src/tallies/filter_collision.cpp + src/tallies/filter_energyfunc.cpp src/tallies/filter_legendre.cpp src/tallies/filter_material.cpp + src/tallies/filter_materialfrom.cpp src/tallies/filter_mesh.cpp + src/tallies/filter_meshborn.cpp + src/tallies/filter_meshmaterial.cpp src/tallies/filter_meshsurface.cpp src/tallies/filter_mu.cpp + src/tallies/filter_musurface.cpp + src/tallies/filter_parent_nuclide.cpp src/tallies/filter_particle.cpp src/tallies/filter_polar.cpp src/tallies/filter_sph_harm.cpp @@ -352,13 +430,15 @@ list(APPEND libopenmc_SOURCES src/tallies/filter_surface.cpp src/tallies/filter_time.cpp src/tallies/filter_universe.cpp + src/tallies/filter_weight.cpp src/tallies/filter_zernike.cpp src/tallies/tally.cpp src/tallies/tally_scoring.cpp src/tallies/trigger.cpp - src/timer.cpp src/thermal.cpp + src/timer.cpp src/track_output.cpp + src/universe.cpp src/urr.cpp src/volume_calc.cpp src/weight_windows.cpp @@ -368,7 +448,8 @@ list(APPEND libopenmc_SOURCES # Add bundled external dependencies list(APPEND libopenmc_SOURCES - src/external/quartic_solver.cpp) + src/external/quartic_solver.cpp + src/external/Faddeeva.cc) # For Visual Studio compilers if(MSVC) @@ -382,6 +463,8 @@ else() add_library(libopenmc SHARED ${libopenmc_SOURCES}) endif() +add_library(OpenMC::libopenmc ALIAS libopenmc) + # Avoid vs error lnk1149 :output filename matches input filename if(NOT MSVC) set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) @@ -398,39 +481,42 @@ target_include_directories(libopenmc target_compile_options(libopenmc PRIVATE ${cxxflags}) # Add include directory for configured version file -target_include_directories(libopenmc PRIVATE ${CMAKE_BINARY_DIR}/include) +target_include_directories(libopenmc + PUBLIC $) if (HDF5_IS_PARALLEL) target_compile_definitions(libopenmc PRIVATE -DPHDF5) endif() -if (MPI_ENABLED) +if (OPENMC_USE_MPI) target_compile_definitions(libopenmc PUBLIC -DOPENMC_MPI) endif() -# Set git SHA1 hash as a compile definition -if(GIT_FOUND) - execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SHA1_SUCCESS - OUTPUT_VARIABLE GIT_SHA1 - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(GIT_SHA1_SUCCESS EQUAL 0) - target_compile_definitions(libopenmc PRIVATE -DGIT_SHA1="${GIT_SHA1}") - endif() -endif() - # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} - pugixml faddeeva xtensor gsl-lite-v1 fmt::fmt) + xtensor fmt::fmt ${CMAKE_DL_LIBS}) -if(dagmc) - target_compile_definitions(libopenmc PRIVATE DAGMC) - target_link_libraries(libopenmc dagmc-shared uwuw-shared) +if(TARGET pugixml::pugixml) + target_link_libraries(libopenmc pugixml::pugixml) +else() + target_link_libraries(libopenmc pugixml) endif() -if(libmesh) - target_compile_definitions(libopenmc PRIVATE LIBMESH) +if(OPENMC_USE_DAGMC) + target_compile_definitions(libopenmc PRIVATE OPENMC_DAGMC_ENABLED) + target_link_libraries(libopenmc dagmc-shared) + + if(OPENMC_USE_UWUW) + target_compile_definitions(libopenmc PRIVATE OPENMC_UWUW_ENABLED) + target_link_libraries(libopenmc uwuw-shared) + endif() +elseif(OPENMC_USE_UWUW) + set(OPENMC_USE_UWUW OFF) + message(FATAL_ERROR "DAGMC must be enabled when UWUW is enabled.") +endif() + +if(OPENMC_USE_LIBMESH) + target_compile_definitions(libopenmc PRIVATE OPENMC_LIBMESH_ENABLED) target_link_libraries(libopenmc PkgConfig::LIBMESH) endif() @@ -439,19 +525,46 @@ if (PNG_FOUND) target_link_libraries(libopenmc PNG::PNG) endif() +if (OPENMC_USE_OPENMP) + target_link_libraries(libopenmc OpenMP::OpenMP_CXX) +endif() + +if (OPENMC_USE_MPI) + target_link_libraries(libopenmc MPI::MPI_CXX) +endif() + +if (OPENMC_BUILD_TESTS) + # Add cpp tests directory + include(CTest) + add_subdirectory(tests/cpp_unit_tests) +endif() + +#=============================================================================== +# Log build info that this executable can report later +#=============================================================================== +target_compile_definitions(libopenmc PRIVATE BUILD_TYPE=${CMAKE_BUILD_TYPE}) +target_compile_definitions(libopenmc PRIVATE COMPILER_ID=${CMAKE_CXX_COMPILER_ID}) +target_compile_definitions(libopenmc PRIVATE COMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION}) +if (OPENMC_ENABLE_PROFILE) + target_compile_definitions(libopenmc PRIVATE PROFILINGBUILD) +endif() +if (OPENMC_ENABLE_COVERAGE) + target_compile_definitions(libopenmc PRIVATE COVERAGEBUILD) +endif() + #=============================================================================== # openmc executable #=============================================================================== add_executable(openmc src/main.cpp) +add_executable(OpenMC::openmc ALIAS openmc) target_compile_options(openmc PRIVATE ${cxxflags}) target_include_directories(openmc PRIVATE ${CMAKE_BINARY_DIR}/include) target_link_libraries(openmc libopenmc) -# Ensure C++14 standard is used. Starting with CMake 3.8, another way this could -# be done is using the cxx_std_14 compiler feature. -set_target_properties( - openmc libopenmc faddeeva - PROPERTIES CXX_STANDARD 14 CXX_EXTENSIONS OFF) +# Ensure C++17 standard is used and turn off GNU extensions +target_compile_features(openmc PUBLIC cxx_std_17) +target_compile_features(libopenmc PUBLIC cxx_std_17) +set_target_properties(openmc libopenmc PROPERTIES CXX_EXTENSIONS OFF) #=============================================================================== # Python package @@ -471,7 +584,7 @@ configure_file(cmake/OpenMCConfig.cmake.in "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIR configure_file(cmake/OpenMCConfigVersion.cmake.in "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfigVersion.cmake" @ONLY) set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) -install(TARGETS openmc libopenmc faddeeva +install(TARGETS openmc libopenmc EXPORT openmc-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -482,7 +595,6 @@ install(EXPORT openmc-targets NAMESPACE OpenMC:: DESTINATION ${INSTALL_CONFIGDIR}) -install(DIRECTORY src/relaxng DESTINATION ${CMAKE_INSTALL_DATADIR}/openmc) install(FILES "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfig.cmake" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMCConfigVersion.cmake" @@ -491,7 +603,3 @@ install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES "${CMAKE_BINARY_DIR}/include/openmc/version.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openmc) - -# Copy headers for vendored dependencies (note that all except faddeeva are handled -# separately since they are managed by CMake) -install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/CODEOWNERS b/CODEOWNERS index a061b7b6ea..c77366de78 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,9 +5,9 @@ openmc/data/ @paulromano openmc/lib/ @paulromano # Depletion -openmc/deplete/ @drewejohnson -tests/regression_tests/deplete/ @drewejohnson -tests/unit_tests/test_deplete_*.py @drewejohnson +openmc/deplete/ @paulromano +tests/regression_tests/deplete/ @paulromano +tests/unit_tests/test_deplete_*.py @paulromano # MG-related functionality openmc/mgxs_library.py @nelsonag @@ -26,6 +26,12 @@ src/dagmc.cpp @pshriwise tests/regression_tests/dagmc/ @pshriwise tests/unit_tests/dagmc/ @pshriwise +# Weight windows +openmc/weight_windows.py @pshriwise +openmc/lib/weight_windows.py @pshriwise +src/weight_windows.py @pshriwise +tests/unit_tests/weightwindows/ @pshriwise + # Photon transport openmc/data/BREMX.DAT @amandalund openmc/data/compton_profiles.h5 @amandalund @@ -46,3 +52,16 @@ openmc/lib/plot.py @pshriwise # Resonance covariance openmc/data/resonance_covariance.py @icmeyer + +# Docker +Dockerfile @shimwell + +# Random ray +src/random_ray/ @jtramm + +# NCrystal interface +src/ncrystal_interface.cpp @marquezj @tkittel +src/ncrystal_load.cpp @marquezj @tkittel + +# MCPL interface +src/mcpl_interface.cpp @ebknudsen diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 378ca346a8..184c522d43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ openmc@anl.gov. ## Resources - [GitHub Repository](https://github.com/openmc-dev/openmc) -- [Documentation](http://docs.openmc.org/en/latest) +- [Documentation](https://docs.openmc.org/en/latest) - [Discussion Forum](https://openmc.discourse.group) - [Slack Community](https://openmc.slack.com/signup) (If you don't see your domain listed, contact openmc@anl.gov) diff --git a/Dockerfile b/Dockerfile index 7399988645..a163a28102 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,50 +15,53 @@ # sudo docker run image_name:tag_name or ID with no tag sudo docker run ID number -FROM debian:bullseye-slim + +# global ARG as these ARGS are used in multiple stages +# By default one core is used to compile +ARG compile_cores=1 # By default this Dockerfile builds OpenMC without DAGMC and LIBMESH support ARG build_dagmc=off ARG build_libmesh=off -# By default one core is used to compile -ARG compile_cores=1 +FROM ubuntu:24.04 AS dependencies + +ARG compile_cores +ARG build_dagmc +ARG build_libmesh # Set default value of HOME to /root ENV HOME=/root -# OpenMC variables -ARG openmc_branch=master -ENV OPENMC_REPO='https://github.com/openmc-dev/openmc' - # Embree variables -ENV EMBREE_TAG='v3.12.2' +ENV EMBREE_TAG='v4.3.1' ENV EMBREE_REPO='https://github.com/embree/embree' ENV EMBREE_INSTALL_DIR=$HOME/EMBREE/ # MOAB variables -ENV MOAB_TAG='5.3.0' +ENV MOAB_TAG='5.5.1' ENV MOAB_REPO='https://bitbucket.org/fathomteam/moab/' # Double-Down variables -ENV DD_TAG='v1.0.0' +ENV DD_TAG='v1.1.0' ENV DD_REPO='https://github.com/pshriwise/double-down' ENV DD_INSTALL_DIR=$HOME/Double_down # DAGMC variables -ENV DAGMC_BRANCH='develop' +ENV DAGMC_BRANCH='v3.2.4' ENV DAGMC_REPO='https://github.com/svalinn/DAGMC' ENV DAGMC_INSTALL_DIR=$HOME/DAGMC/ # LIBMESH variables -ENV LIBMESH_TAG='v1.6.0' +ENV LIBMESH_TAG='v1.7.1' ENV LIBMESH_REPO='https://github.com/libMesh/libmesh' ENV LIBMESH_INSTALL_DIR=$HOME/LIBMESH +# NJOY variables +ENV NJOY_REPO='https://github.com/njoy/NJOY2016' + # Setup environment variables for Docker image -ENV CC=/usr/bin/mpicc CXX=/usr/bin/mpicxx \ - LD_LIBRARY_PATH=${DAGMC_INSTALL_DIR}/lib:$LD_LIBRARY_PATH \ - OPENMC_CROSS_SECTIONS=/root/nndc_hdf5/cross_sections.xml \ +ENV LD_LIBRARY_PATH=${DAGMC_INSTALL_DIR}/lib:$LD_LIBRARY_PATH \ OPENMC_ENDF_DATA=/root/endf-b-vii.1 \ DEBIAN_FRONTEND=noninteractive @@ -68,23 +71,32 @@ RUN apt-get update -y && \ apt-get install -y \ python3-pip python-is-python3 wget git build-essential cmake \ mpich libmpich-dev libhdf5-serial-dev libhdf5-mpich-dev \ - libpng-dev && \ + libpng-dev python3-venv && \ apt-get autoremove +# create virtual enviroment to avoid externally managed environment error +RUN python3 -m venv openmc_venv +ENV PATH=/openmc_venv/bin:$PATH + # Update system-provided pip RUN pip install --upgrade pip # Clone and install NJOY2016 -RUN cd $HOME && git clone --depth 1 https://github.com/njoy/NJOY2016.git && \ - cd NJOY2016 && mkdir build && cd build && \ - cmake -Dstatic=on .. && make 2>/dev/null -j${compile_cores} install && \ - rm -rf $HOME/NJOY2016 +RUN cd $HOME \ + && git clone --single-branch --depth 1 ${NJOY_REPO} \ + && cd NJOY2016 \ + && mkdir build \ + && cd build \ + && cmake -Dstatic=on .. \ + && make 2>/dev/null -j${compile_cores} install \ + && rm -rf $HOME/NJOY2016 RUN if [ "$build_dagmc" = "on" ]; then \ # Install addition packages required for DAGMC apt-get -y install libeigen3-dev libnetcdf-dev libtbb-dev libglfw3-dev \ - && pip install --upgrade numpy cython \ + && pip install --upgrade numpy \ + && pip install --no-cache-dir setuptools cython \ # Clone and install EMBREE && mkdir -p $HOME/EMBREE && cd $HOME/EMBREE \ && git clone --single-branch -b ${EMBREE_TAG} --depth 1 ${EMBREE_REPO} \ @@ -100,7 +112,8 @@ RUN if [ "$build_dagmc" = "on" ]; then \ mkdir -p $HOME/MOAB && cd $HOME/MOAB \ && git clone --single-branch -b ${MOAB_TAG} --depth 1 ${MOAB_REPO} \ && mkdir build && cd build \ - && cmake ../moab -DENABLE_HDF5=ON \ + && cmake ../moab -DCMAKE_BUILD_TYPE=Release \ + -DENABLE_HDF5=ON \ -DENABLE_NETCDF=ON \ -DBUILD_SHARED_LIBS=OFF \ -DENABLE_FORTRAN=OFF \ @@ -112,6 +125,7 @@ RUN if [ "$build_dagmc" = "on" ]; then \ && make 2>/dev/null -j${compile_cores} install \ && cd pymoab && bash install.sh \ && python setup.py install \ + && python -c "import pymoab" \ && rm -rf $HOME/MOAB ; \ # Clone and install Double-Down mkdir -p $HOME/Double_down && cd $HOME/Double_down \ @@ -165,43 +179,63 @@ RUN if [ "$build_libmesh" = "on" ]; then \ && rm -rf ${LIBMESH_INSTALL_DIR}/build ${LIBMESH_INSTALL_DIR}/libmesh ; \ fi +FROM dependencies AS build + +ENV HOME=/root + +ARG openmc_branch=master +ENV OPENMC_REPO='https://github.com/openmc-dev/openmc' + +ARG compile_cores +ARG build_dagmc +ARG build_libmesh + +ENV DAGMC_INSTALL_DIR=$HOME/DAGMC/ +ENV LIBMESH_INSTALL_DIR=$HOME/LIBMESH + # clone and install openmc RUN mkdir -p ${HOME}/OpenMC && cd ${HOME}/OpenMC \ - && git clone --shallow-submodules --recurse-submodules -b ${openmc_branch} --depth=1 ${OPENMC_REPO} \ + && git clone --shallow-submodules --recurse-submodules --single-branch -b ${openmc_branch} ${OPENMC_REPO} \ && mkdir build && cd build ; \ if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "on" ]; then \ cmake ../openmc \ - -Doptimize=off \ - -Ddebug=off \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ - -Ddagmc=on \ - -Dlibmesh=on \ + -DOPENMC_USE_DAGMC=on \ + -DOPENMC_USE_LIBMESH=on \ -DCMAKE_PREFIX_PATH="${DAGMC_INSTALL_DIR};${LIBMESH_INSTALL_DIR}" ; \ fi ; \ if [ ${build_dagmc} = "on" ] && [ ${build_libmesh} = "off" ]; then \ cmake ../openmc \ - -Doptimize=off \ - -Ddebug=off \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ - -Ddagmc=ON \ + -DOPENMC_USE_DAGMC=ON \ -DCMAKE_PREFIX_PATH=${DAGMC_INSTALL_DIR} ; \ fi ; \ if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "on" ]; then \ cmake ../openmc \ - -Doptimize=off \ - -Ddebug=off \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on \ - -Dlibmesh=on \ + -DOPENMC_USE_LIBMESH=on \ -DCMAKE_PREFIX_PATH=${LIBMESH_INSTALL_DIR} ; \ fi ; \ if [ ${build_dagmc} = "off" ] && [ ${build_libmesh} = "off" ]; then \ cmake ../openmc \ - -Doptimize=off \ - -Ddebug=off \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DOPENMC_USE_MPI=on \ -DHDF5_PREFER_PARALLEL=on ; \ fi ; \ make 2>/dev/null -j${compile_cores} install \ - && cd ../openmc && pip install -e .[test,depletion-mpi] + && cd ../openmc && pip install .[test,depletion-mpi] \ + && python -c "import openmc" + +FROM build AS release + +ENV HOME=/root +ENV OPENMC_CROSS_SECTIONS=/root/nndc_hdf5/cross_sections.xml # Download cross sections (NNDC and WMP) and ENDF data needed by test suite RUN ${HOME}/OpenMC/openmc/tools/ci/download-xs.sh diff --git a/LICENSE b/LICENSE index 7fb6e3f27b..8a60b66bf9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2022 Massachusetts Institute of Technology, UChicago Argonne +Copyright (c) 2011-2025 Massachusetts Institute of Technology, UChicago Argonne LLC, and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/MANIFEST.in b/MANIFEST.in index e660a1161d..cdc7e2abcf 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,7 +8,6 @@ include schemas.xml include pyproject.toml include pytest.ini include docs/source/_templates/layout.html -include docs/sphinxext/LICENSE global-include *.cmake global-include *.cmake.in global-include *.rst @@ -20,25 +19,20 @@ recursive-include docs *.svg recursive-include docs *.tex recursive-include docs *.txt recursive-include docs Makefile -recursive-include examples *.h5 -recursive-include examples *.png recursive-include examples *.cpp recursive-include examples *.py recursive-include examples *.xml recursive-include include *.h +recursive-include include *.h.in +recursive-include include *.hh recursive-include man *.1 -recursive-inlcude openmc *.pyx -recursive-include openmc *.c -recursive-include src *.c recursive-include src *.cc recursive-include src *.cpp -recursive-include src *.h -recursive-include src *.hpp recursive-include src *.rnc recursive-include src *.rng recursive-include tests *.dat recursive-include tests *.h5 -recursive-inlcude tests *.h5m +recursive-include tests *.h5m recursive-include tests *.py recursive-include tests *.xml recursive-include vendor CMakeLists.txt @@ -49,6 +43,5 @@ recursive-include vendor *.hh recursive-include vendor *.hpp recursive-include vendor *.pc.in recursive-include vendor *.natvis -include vendor/gsl-lite/include/gsl/gsl prune docs/build prune docs/source/pythonapi/generated/ diff --git a/README.md b/README.md index 186c1e5e03..6539ec3c7c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # OpenMC Monte Carlo Particle Transport Code [![License](https://img.shields.io/badge/license-MIT-green)](https://docs.openmc.org/en/latest/license.html) -[![GitHub Actions build status (Linux)](https://github.com/openmc-dev/openmc/workflows/CI/badge.svg?branch=develop)](https://github.com/openmc-dev/openmc/actions?query=workflow%3ACI) +[![GitHub Actions build status (Linux)](https://github.com/openmc-dev/openmc/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/openmc-dev/openmc/actions/workflows/ci.yml) [![Code Coverage](https://coveralls.io/repos/github/openmc-dev/openmc/badge.svg?branch=develop)](https://coveralls.io/github/openmc-dev/openmc?branch=develop) [![dockerhub-publish-develop-dagmc](https://github.com/openmc-dev/openmc/workflows/dockerhub-publish-develop-dagmc/badge.svg)](https://github.com/openmc-dev/openmc/actions?query=workflow%3Adockerhub-publish-develop-dagmc) [![dockerhub-publish-develop](https://github.com/openmc-dev/openmc/workflows/dockerhub-publish-develop/badge.svg)](https://github.com/openmc-dev/openmc/actions?query=workflow%3Adockerhub-publish-develop) +[![conda-pacakge](https://anaconda.org/conda-forge/openmc/badges/version.svg)](https://anaconda.org/conda-forge/openmc) The OpenMC project aims to provide a fully-featured Monte Carlo particle transport code based on modern methods. It is a constructive solid geometry, @@ -14,7 +15,8 @@ project started under the Computational Reactor Physics Group at MIT. Complete documentation on the usage of OpenMC is hosted on Read the Docs (both for the [latest release](https://docs.openmc.org/en/stable/) and [developmental](https://docs.openmc.org/en/latest/) version). If you are -interested in the project, or would like to help and contribute, please get in touch on the OpenMC [discussion forum](https://openmc.discourse.group/). +interested in the project, or would like to help and contribute, please get in +touch on the OpenMC [discussion forum](https://openmc.discourse.group/). ## Installation @@ -35,20 +37,21 @@ citing the following publication: ## Troubleshooting If you run into problems compiling, installing, or running OpenMC, first check -the [Troubleshooting section](https://docs.openmc.org/en/stable/usersguide/troubleshoot.html) in -the User's Guide. If you are not able to find a solution to your problem there, +the [Troubleshooting +section](https://docs.openmc.org/en/stable/usersguide/troubleshoot.html) in the +User's Guide. If you are not able to find a solution to your problem there, please post to the [discussion forum](https://openmc.discourse.group/). ## Reporting Bugs OpenMC is hosted on GitHub and all bugs are reported and tracked through the -[Issues](https://github.com/openmc-dev/openmc/issues) feature on GitHub. However, -GitHub Issues should not be used for common troubleshooting purposes. If you are -having trouble installing the code or getting your model to run properly, you -should first send a message to the User's Group mailing list. If it turns out -your issue really is a bug in the code, an issue will then be created on -GitHub. If you want to request that a feature be added to the code, you may -create an Issue on github. +[Issues](https://github.com/openmc-dev/openmc/issues) feature on GitHub. +However, GitHub Issues should not be used for common troubleshooting purposes. +If you are having trouble installing the code or getting your model to run +properly, you should first send a message to the [discussion +forum](https://openmc.discourse.group/). If it turns out your issue really is a +bug in the code, an issue will then be created on GitHub. If you want to request +that a feature be added to the code, you may create an Issue on github. ## License diff --git a/cmake/Modules/FindLIBMESH.cmake b/cmake/Modules/FindLIBMESH.cmake index 82d806343a..df9208c18b 100644 --- a/cmake/Modules/FindLIBMESH.cmake +++ b/cmake/Modules/FindLIBMESH.cmake @@ -14,8 +14,8 @@ if(DEFINED ENV{METHOD}) message(STATUS "Using environment variable METHOD to determine libMesh build: ${LIBMESH_PC_FILE}") endif() -include(FindPkgConfig) -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${LIBMESH_PC}") -set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH True) -pkg_check_modules(LIBMESH REQUIRED ${LIBMESH_PC_FILE}>=1.6.0 IMPORTED_TARGET) -pkg_get_variable(LIBMESH_PREFIX ${LIBMESH_PC_FILE} prefix) \ No newline at end of file +find_package(PkgConfig REQUIRED) + +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) +pkg_check_modules(LIBMESH REQUIRED ${LIBMESH_PC_FILE}>=1.7.0 IMPORTED_TARGET) +pkg_get_variable(LIBMESH_PREFIX ${LIBMESH_PC_FILE} prefix) diff --git a/cmake/Modules/GetVersionFromGit.cmake b/cmake/Modules/GetVersionFromGit.cmake new file mode 100644 index 0000000000..3736955ff4 --- /dev/null +++ b/cmake/Modules/GetVersionFromGit.cmake @@ -0,0 +1,120 @@ +# GetVersionFromGit.cmake +# Standalone script to retrieve versioning information from Git or .git_archival.txt. +# Customizable for any project by setting variables before including this file. + +# Configurable variables: +# - VERSION_PREFIX: Prefix for version tags (default: "v"). +# - VERSION_SUFFIX: Suffix for version tags (default: "[~+-]([a-zA-Z0-9]+)"). +# - VERSION_REGEX: Regex to extract version (default: "(?[0-9]+\\.[0-9]+\\.[0-9]+)"). +# - ARCHIVAL_FILE: Path to .git_archival.txt (default: "${CMAKE_SOURCE_DIR}/.git_archival.txt"). +# - DESCRIBE_NAME_KEY: Key for describe name in .git_archival.txt (default: "describe-name: "). +# - COMMIT_HASH_KEY: Key for commit hash in .git_archival.txt (default: "commit: "). + +# Default Format Example: +# 1.2.3 v1.2.3 v1.2.3-rc1 + +set(VERSION_PREFIX "v" CACHE STRING "Prefix used in version tags") +set(VERSION_SUFFIX "[~+-]([a-zA-Z0-9]+)" CACHE STRING "Suffix used in version tags") +set(VERSION_REGEX "?([0-9]+\\.[0-9]+\\.[0-9]+)" CACHE STRING "Regex for extracting version") +set(ARCHIVAL_FILE "${CMAKE_SOURCE_DIR}/.git_archival.txt" CACHE STRING "Path to .git_archival.txt") +set(DESCRIBE_NAME_KEY "describe-name: " CACHE STRING "Key for describe name in .git_archival.txt") +set(COMMIT_HASH_KEY "commit: " CACHE STRING "Key for commit hash in .git_archival.txt") + + +# Combine prefix and regex +set(VERSION_REGEX_WITH_PREFIX "^${VERSION_PREFIX}${VERSION_REGEX}") + +# Find Git +find_package(Git) + +# Attempt to retrieve version from Git +if(EXISTS "${CMAKE_SOURCE_DIR}/.git" AND GIT_FOUND) + message(STATUS "Using git describe for versioning") + + # Extract the version string + execute_process( + COMMAND git describe --tags --dirty + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + + # If no tags are found, set version to 0 and show a warning + if(VERSION_STRING STREQUAL "") + set(VERSION_STRING "0.0.0") + message(WARNING + "No git tags found. Version set to 0.0.0.\n" + "Run 'git fetch --tags' to ensure proper versioning.\n" + "For more information, see OpenMC developer documentation." + ) + endif() + + # Extract the commit hash + execute_process( + COMMAND git rev-parse HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + message(STATUS "Using archival file for versioning: ${ARCHIVAL_FILE}") + if(EXISTS "${ARCHIVAL_FILE}") + file(READ "${ARCHIVAL_FILE}" ARCHIVAL_CONTENT) + + # Extract the describe-name line + string(REGEX MATCH "${DESCRIBE_NAME_KEY}([^\\n]+)" VERSION_STRING "${ARCHIVAL_CONTENT}") + if(VERSION_STRING MATCHES "${DESCRIBE_NAME_KEY}(.*)") + set(VERSION_STRING "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Could not extract version from ${ARCHIVAL_FILE}") + endif() + + # Extract the commit hash + string(REGEX MATCH "${COMMIT_HASH_KEY}([a-f0-9]+)" COMMIT_HASH "${ARCHIVAL_CONTENT}") + if(COMMIT_HASH MATCHES "${COMMIT_HASH_KEY}([a-f0-9]+)") + set(COMMIT_HASH "${CMAKE_MATCH_1}") + else() + message(FATAL_ERROR "Could not extract commit hash from ${ARCHIVAL_FILE}") + endif() + else() + message(FATAL_ERROR "Neither git describe nor ${ARCHIVAL_FILE} is available for versioning.") + endif() +endif() + +# Ensure version string format +if(VERSION_STRING MATCHES "${VERSION_REGEX_WITH_PREFIX}") + set(VERSION_NO_SUFFIX "${CMAKE_MATCH_1}") +else() + message(FATAL_ERROR "Invalid version format: Missing base version in ${VERSION_STRING}") +endif() + +# Check for development state +if(VERSION_STRING MATCHES "-([0-9]+)-g([0-9a-f]+)") + set(DEV_STATE "true") + set(COMMIT_COUNT "${CMAKE_MATCH_1}") + string(REGEX REPLACE "-([0-9]+)-g([0-9a-f]+)" "" VERSION_WITHOUT_META "${VERSION_STRING}") +else() + set(DEV_STATE "false") + set(VERSION_WITHOUT_META "${VERSION_STRING}") +endif() + +# Split and set version components +string(REPLACE "." ";" VERSION_LIST "${VERSION_NO_SUFFIX}") +list(GET VERSION_LIST 0 VERSION_MAJOR) +list(GET VERSION_LIST 1 VERSION_MINOR) +list(GET VERSION_LIST 2 VERSION_PATCH) + +# Increment patch number for dev versions +if(DEV_STATE) + math(EXPR VERSION_PATCH "${VERSION_PATCH} + 1") +endif() + +# Export variables +set(OPENMC_VERSION_MAJOR "${VERSION_MAJOR}") +set(OPENMC_VERSION_MINOR "${VERSION_MINOR}") +set(OPENMC_VERSION_PATCH "${VERSION_PATCH}") +set(OPENMC_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +set(OPENMC_COMMIT_HASH "${COMMIT_HASH}") +set(OPENMC_DEV_STATE "${DEV_STATE}") +set(OPENMC_COMMIT_COUNT "${COMMIT_COUNT}") diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index b12e95f23e..837a39c783 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -1,19 +1,21 @@ get_filename_component(OpenMC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) -find_package(fmt REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../fmt) -find_package(gsl-lite REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../gsl-lite) -find_package(pugixml REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../pugixml) -find_package(xtl REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtl) -find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) -if(@dagmc@) +# Compute the install prefix from this file's location +get_filename_component(_OPENMC_PREFIX "${OpenMC_CMAKE_DIR}/../../.." ABSOLUTE) + +find_package(fmt CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) +find_package(pugixml CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) +find_package(xtl CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) +find_package(xtensor CONFIG REQUIRED HINTS ${_OPENMC_PREFIX}) +if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() -if(@libmesh@) +if(@OPENMC_USE_LIBMESH@) include(FindPkgConfig) list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@) set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH True) - pkg_check_modules(LIBMESH REQUIRED @LIBMESH_PC_FILE@>=1.6.0 IMPORTED_TARGET) + pkg_check_modules(LIBMESH REQUIRED @LIBMESH_PC_FILE@>=1.7.0 IMPORTED_TARGET) endif() find_package(PNG) @@ -21,3 +23,15 @@ find_package(PNG) if(NOT TARGET OpenMC::libopenmc) include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake") endif() + +if(@OPENMC_USE_MPI@) + find_package(MPI REQUIRED) +endif() + +if(@OPENMC_USE_OPENMP@) + find_package(OpenMP REQUIRED) +endif() + +if(@OPENMC_USE_UWUW@ AND NOT ${DAGMC_BUILD_UWUW}) + message(FATAL_ERROR "UWUW is enabled in OpenMC but the DAGMC installation discovered was not configured with UWUW.") +endif() diff --git a/docs/requirements-rtd.txt b/docs/requirements-rtd.txt deleted file mode 100644 index c3d1c0372b..0000000000 --- a/docs/requirements-rtd.txt +++ /dev/null @@ -1,11 +0,0 @@ -sphinx==4.3.0 -sphinx-numfig -jupyter -sphinxcontrib-katex -sphinxcontrib-svg2pdfconverter -numpy -scipy -h5py -pandas -uncertainties -matplotlib diff --git a/docs/source/_images/2x2_fsrs.jpeg b/docs/source/_images/2x2_fsrs.jpeg new file mode 100644 index 0000000000..1c8e474d0e Binary files /dev/null and b/docs/source/_images/2x2_fsrs.jpeg differ diff --git a/docs/source/_images/2x2_materials.jpeg b/docs/source/_images/2x2_materials.jpeg new file mode 100644 index 0000000000..b76607eca6 Binary files /dev/null and b/docs/source/_images/2x2_materials.jpeg differ diff --git a/docs/source/_images/2x2_sr_mesh.png b/docs/source/_images/2x2_sr_mesh.png new file mode 100644 index 0000000000..5cdc684d3b Binary files /dev/null and b/docs/source/_images/2x2_sr_mesh.png differ diff --git a/docs/source/_images/hexlat_anim.gif b/docs/source/_images/hexlat_anim.gif new file mode 100644 index 0000000000..fceedc0104 Binary files /dev/null and b/docs/source/_images/hexlat_anim.gif differ diff --git a/docs/source/_images/phong_triso.png b/docs/source/_images/phong_triso.png new file mode 100644 index 0000000000..7f1ac61cab Binary files /dev/null and b/docs/source/_images/phong_triso.png differ diff --git a/docs/source/_images/plotmeshtally.png b/docs/source/_images/plotmeshtally.png deleted file mode 100644 index d874b4906a..0000000000 Binary files a/docs/source/_images/plotmeshtally.png and /dev/null differ diff --git a/docs/source/_images/sphere-mesh-vtk.png b/docs/source/_images/sphere-mesh-vtk.png new file mode 100644 index 0000000000..73b412bdd3 Binary files /dev/null and b/docs/source/_images/sphere-mesh-vtk.png differ diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index eb6493e0e9..2583d51dff 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -84,6 +84,17 @@ Functions :return: Return status (negative if an error occurred) :rtype: int +.. c:function:: int openmc_cell_get_density(int32_t index, const int32_t* instance, double* density) + + Get the density of a cell + + :param int32_t index: Index in the cells array + :param int32_t* instance: Which instance of the cell. If a null pointer is passed, the density + multiplier of the first instance is returned. + :param double* density: Density of the cell in [g/cm3] + :return: Return status (negative if an error occurred) + :rtype: int + .. c:function:: int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices) Set the fill for a cell @@ -113,8 +124,22 @@ Functions :param double T: Temperature in Kelvin :param instance: Which instance of the cell. To set the temperature for all instances, pass a null pointer. - :param set_contained: If the cell is not filled by a material, whether to set the temperatures - of all filled cells + :param bool set_contained: If the cell is not filled by a material, whether + to set the temperatures of all filled cells + :type instance: const int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_cell_set_density(index index, double density, const int32_t* instance, bool set_contained) + + Set the density of a cell. + + :param int32_t index: Index in the cells array + :param double density: Density of the cell in [g/cm3] + :param instance: Which instance of the cell. To set the density multiplier for all + instances, pass a null pointer. + :param bool set_contained: If the cell is not filled by a material, whether + to set the density multiplier of all filled cells :type instance: const int32_t* :return: Return status (negative if an error occurred) :rtype: int @@ -355,7 +380,7 @@ Functions Get density of a material. :param int32_t index: Index in the materials array - :param double* denity: Pointer to a density + :param double* density: Pointer to a density :return: Return status (negative if an error occurs) :rtype: int @@ -420,6 +445,16 @@ Functions :return: Return status (negative if an error occurred) :rtype: int +.. c:function:: int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh) + + Get the mesh for a mesh filter + + :param int32_t index: Index in the filters array + :param index_mesh: Index in the meshes array + :type index_mesh: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + .. c:function:: int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh) Set the mesh for a mesh filter @@ -429,6 +464,98 @@ Functions :return: Return status (negative if an error occurred) :rtype: int +.. c:function:: int openmc_mesh_filter_get_translation(int32_t index, double translation[3]) + + Get the 3-D translation coordinates for a mesh filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_mesh_filter_set_translation(int32_t index, double translation[3]) + + Set the 3-D translation coordinates for a mesh filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshborn_filter_get_mesh(int32_t index, int32_t* index_mesh) + + Get the mesh for a meshborn filter + + :param int32_t index: Index in the filters array + :param index_mesh: Index in the meshes array + :type index_mesh: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshborn_filter_set_mesh(int32_t index, int32_t index_mesh) + + Set the mesh for a meshborn filter + + :param int32_t index: Index in the filters array + :param int32_t index_mesh: Index in the meshes array + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshborn_filter_get_translation(int32_t index, double translation[3]) + + Get the 3-D translation coordinates for a meshborn filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshborn_filter_set_translation(int32_t index, double translation[3]) + + Set the 3-D translation coordinates for a meshborn filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh) + + Get the mesh for a mesh surface filter + + :param int32_t index: Index in the filters array + :param index_mesh: Index in the meshes array + :type index_mesh: int32_t* + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh) + + Set the mesh for a mesh surface filter + + :param int32_t index: Index in the filters array + :param int32_t index_mesh: Index in the meshes array + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshsurface_filter_get_translation(int32_t index, double translation[3]) + + Get the 3-D translation coordinates for a mesh surface filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + +.. c:function:: int openmc_meshsurface_filter_set_translation(int32_t index, double translation[3]) + + Set the 3-D translation coordinates for a mesh surface filter + + :param int32_t index: Index in the filters array + :param double[3] translation: 3-D translation coordinates + :return: Return status (negative if an error occurred) + :rtype: int + .. c:function:: int openmc_next_batch() Simulate next batch of particles. Must be called after openmc_simulation_init(). @@ -460,6 +587,13 @@ Functions :return: Return status (negative if an error occurs) :rtype: int +.. c:function:: int openmc_remove_tally(int32_t index); + + Given an index of a tally, remove it from the tallies array + :param int index: Index in tallies array + :return: Return status (negative if an error occurs) + :rtype: int + .. c:function:: int openmc_run() Run a simulation diff --git a/docs/source/conf.py b/docs/source/conf.py index fb12a4f56c..826c20022a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -52,7 +52,7 @@ if not on_rtd: templates_path = ['_templates'] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = {'.rst': 'restructuredtext'} # The encoding of source files. #source_encoding = 'utf-8' @@ -62,16 +62,17 @@ master_doc = 'index' # General information about the project. project = 'OpenMC' -copyright = '2011-2022, Massachusetts Institute of Technology, UChicago Argonne LLC, and OpenMC contributors' +copyright = '2011-2025, Massachusetts Institute of Technology, UChicago Argonne LLC, and OpenMC contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = "0.13" + +import openmc + # The full version, including alpha/beta/rc tags. -release = "0.13.0" +version = release = openmc.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -120,10 +121,8 @@ pygments_style = 'tango' # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages -if not on_rtd: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +html_theme = 'sphinx_rtd_theme' +html_baseurl = "https://docs.openmc.org/en/stable/" html_logo = '_images/openmc_logo.png' @@ -215,6 +214,7 @@ latex_elements = { \setcounter{tocdepth}{2} \numberwithin{equation}{section} \DeclareUnicodeCharacter{03B1}{$\alpha$} +\DeclareUnicodeCharacter{03C0}{$\pi$} """, 'printindex': r"" } @@ -246,7 +246,7 @@ napoleon_use_ivar = True intersphinx_mapping = { 'python': ('https://docs.python.org/3', None), 'numpy': ('https://numpy.org/doc/stable/', None), - 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), + 'scipy': ('https://docs.scipy.org/doc/scipy/', None), 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None), - 'matplotlib': ('https://matplotlib.org/', None) + 'matplotlib': ('https://matplotlib.org/stable/', None) } diff --git a/docs/source/devguide/contributing.rst b/docs/source/devguide/contributing.rst index c08b1a362f..cda0313892 100644 --- a/docs/source/devguide/contributing.rst +++ b/docs/source/devguide/contributing.rst @@ -109,7 +109,7 @@ Leadership Team The TC consists of the following individuals: - `Paul Romano `_ -- `Sterling Harper `_ +- `Patrick Shriwise `_ - `Adam Nelson `_ - `Benoit Forget `_ diff --git a/docs/source/devguide/docbuild.rst b/docs/source/devguide/docbuild.rst index 38ef628df5..f723db06ea 100644 --- a/docs/source/devguide/docbuild.rst +++ b/docs/source/devguide/docbuild.rst @@ -5,21 +5,14 @@ Building Sphinx Documentation ============================= In order to build the documentation in the ``docs`` directory, you will need to -have the `Sphinx `_ third-party Python -package. The easiest way to install Sphinx is via pip: +have the several third-party Python packages installed, including `Sphinx +`_. To install the necessary +prerequisites, provide the optional "docs" dependencies when installing OpenMC's +Python API. That is, from the root directory of the OpenMC repository: .. code-block:: sh - pip install sphinx - -Additionally, you will need several Sphinx extensions that can be installed -directly with pip: - -.. code-block:: sh - - pip install sphinx-numfig - pip install sphinxcontrib-katex - pip install sphinxcontrib-svg2pdfconverter + python -m pip install ".[docs]" ----------------------------------- Building Documentation as a Webpage diff --git a/docs/source/devguide/docker.rst b/docs/source/devguide/docker.rst index 0b21911686..50ff29bd22 100644 --- a/docs/source/devguide/docker.rst +++ b/docs/source/devguide/docker.rst @@ -45,12 +45,11 @@ Now you can run the following to create a `Docker container`_ called This command will open an interactive shell running from within the Docker container where you have access to use OpenMC. -.. note:: The ``docker run`` command supports many - `options `_ +.. note:: The ``docker run`` command supports many options_ for spawning containers -- including `mounting volumes`_ from the host filesystem -- which many users will find useful. -.. _Docker image: https://docs.docker.com/engine/reference/commandline/images/ +.. _Docker image: https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/ .. _Docker container: https://www.docker.com/resources/what-container -.. _options: https://docs.docker.com/engine/reference/commandline/run/ -.. _mounting volumes: https://docs.docker.com/storage/volumes/ +.. _options: https://docs.docker.com/reference/cli/docker/container/run/ +.. _mounting volumes: https://docs.docker.com/engine/storage/volumes/ diff --git a/docs/source/devguide/index.rst b/docs/source/devguide/index.rst index d100fdcdfd..2e131e0949 100644 --- a/docs/source/devguide/index.rst +++ b/docs/source/devguide/index.rst @@ -15,6 +15,7 @@ other related topics. contributing workflow styleguide + policies tests user-input docbuild diff --git a/docs/source/devguide/policies.rst b/docs/source/devguide/policies.rst new file mode 100644 index 0000000000..2cf3199876 --- /dev/null +++ b/docs/source/devguide/policies.rst @@ -0,0 +1,35 @@ +.. _devguide_policies: + +======== +Policies +======== + +--------------------- +Python Version Policy +--------------------- + +OpenMC follows the Scientific Python Ecosystem Coordination guidelines `SPEC 0 +`_ on minimum supported +versions, which recommends that support for Python versions be dropped 3 years +after their initial release. + +------------------- +C++ Standard Policy +------------------- + +C++ code in OpenMC must conform to the most recent C++ standard that is fully +supported in the `version of the gcc compiler +`_ that is distributed with the +oldest version of Ubuntu that is still within its `standard support period +`_. Ubuntu 20.04 LTS will be supported +through April 2025 and is distributed with gcc 9.3.0, which fully supports the +C++17 standard. + +-------------------- +CMake Version Policy +-------------------- + +Similar to the C++ standard policy, the minimum supported version of CMake +corresponds to whatever version is distributed with the oldest version of Ubuntu +still within its standard support period. Ubuntu 20.04 LTS is distributed with +CMake 3.16. diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index 44d80915e0..2c882b0341 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -29,6 +29,10 @@ whenever a file is saved. For example, `Visual Studio Code `_ includes support for running clang-format. +.. note:: + OpenMC's CI uses `clang-format` version 15. A different version of `clang-format` + may produce different line changes and as a result fail the CI test. + Miscellaneous ------------- @@ -36,14 +40,15 @@ Follow the `C++ Core Guidelines`_ except when they conflict with another guideline listed here. For convenience, many important guidelines from that list are repeated here. -Conform to the C++14 standard. +Conform to the C++17 standard. Always use C++-style comments (``//``) as opposed to C-style (``/**/``). (It is more difficult to comment out a large section of code that uses C-style comments.) Do not use C-style casting. Always use the C++-style casts ``static_cast``, -``const_cast``, or ``reinterpret_cast``. (See `ES.49 `_) +``const_cast``, or ``reinterpret_cast``. (See `ES.49 +`_) Source Files ------------ @@ -51,7 +56,7 @@ Source Files Use a ``.cpp`` suffix for code files and ``.h`` for header files. Header files should always use include guards with the following style (See -`SF.8 `_): +`SF.8 `_): .. code-block:: C++ @@ -142,7 +147,7 @@ Style for Python code should follow PEP8_. Docstrings for functions and methods should follow numpydoc_ style. -Python code should work with Python 3.6+. +Python code should work with Python 3.8+. Use of third-party Python packages should be limited to numpy_, scipy_, matplotlib_, pandas_, and h5py_. Use of other third-party packages must be @@ -152,11 +157,11 @@ Prefer pathlib_ when working with filesystem paths over functions in the os_ module or other standard-library modules. Functions that accept arguments that represent a filesystem path should work with both strings and Path_ objects. -.. _C++ Core Guidelines: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines -.. _PEP8: https://www.python.org/dev/peps/pep-0008/ +.. _C++ Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines +.. _PEP8: https://peps.python.org/pep-0008/ .. _numpydoc: https://numpydoc.readthedocs.io/en/latest/format.html .. _numpy: https://numpy.org/ -.. _scipy: https://www.scipy.org/ +.. _scipy: https://scipy.org/ .. _matplotlib: https://matplotlib.org/ .. _pandas: https://pandas.pydata.org/ .. _h5py: https://www.h5py.org/ diff --git a/docs/source/devguide/tests.rst b/docs/source/devguide/tests.rst index d557130624..f2e39441a8 100644 --- a/docs/source/devguide/tests.rst +++ b/docs/source/devguide/tests.rst @@ -23,17 +23,17 @@ Prerequisites OpenMC in development/editable mode. With setuptools, this is accomplished by running:: - python setup.py develop - - or using pip (recommended):: - - pip install -e .[test] + python -m pip install -e .[test] - The test suite requires a specific set of cross section data in order for tests to pass. A download URL for the data that OpenMC expects can be found - within ``tools/ci/download-xs.sh``. + within ``tools/ci/download-xs.sh``. Once the tarball is downloaded and + unpacked, set the :envvar:`OPENMC_CROSS_SECTIONS` environment variable to the + path of the ``cross_sections.xml`` file within the unpacked data. - In addition to the HDF5 data, some tests rely on ENDF files. A download URL - for those can also be found in ``tools/ci/download-xs.sh``. + for those can also be found in ``tools/ci/download-xs.sh``. Once the tarball + is downloaded and unpacked, set the :envvar:`OPENMC_ENDF_DATA` environment + variable to the top-level directory of the unpacked tarball. - Some tests require `NJOY `_ to preprocess cross section data. The test suite assumes that you have an ``njoy`` executable available on your :envvar:`PATH`. @@ -41,7 +41,7 @@ Prerequisites Running Tests ------------- -To execute the test suite, go to the ``tests/`` directory and run:: +To execute the Python test suite, go to the ``tests/`` directory and run:: pytest @@ -51,6 +51,63 @@ installed and run:: pytest --cov=../openmc --cov-report=html +To execute the C++ test suite, go to your build directory and run:: + + ctest + +If you want to view testing output on failure run:: + + ctest --output-on-failure + +Possible Reasons for Test Failures +---------------------------------- + +You may find that when you run the test suite, not everything passes. First, +make sure you have satisfied all the prerequisites above. After you have done +that, consider the following: + +- When building OpenMC, make sure you run CMake with + ``-DCMAKE_BUILD_TYPE=Debug``. Building with a release build will result in + some test failures due to differences in which compiler optimizations are + used. +- Because tallies involve the sum of many floating point numbers, the + non-associativity of floating point numbers can result in different answers + especially when the number of threads is high (different order of operations). + Thus, if you are running on a CPU with many cores, you may need to limit the + number of OpenMP threads used. It is recommended to set the + :envvar:`OMP_NUM_THREADS` environment variable to 2. +- Recent versions of NumPy use instruction dispatch that may generate different + results depending the particular ISA that you are running on. To avoid issues, + you may need to disable AVX512 instructions. This can be done by setting the + :envvar:`NPY_DISABLE_CPU_FEATURES` environment variable to "AVX512F + AVX512_SKX". When NumPy/SciPy are built against OpenBLAS, you may also need to + limit the number of threads that OpenBLAS uses internally; this can be done by + setting the :envvar:`OPENBLAS_NUM_THREADS` environment variable to 1. + +Debugging Tests in CI +--------------------- + +Tests can be debugged in CI using a feature called +`tmate `_. +CI debugging can be +enabled by including "[gha-debug]" in the commit message. When the test fails, a +link similar to the one shown below will be provided in the GitHub Actions +output after failure occurs. Logging into the provided link will allow you to +debug the test in the CI environment. The following is an example of the output +shown in the CI log that provides the link to the tmate session: + +.. code-block:: text + :linenos: + + Created new session successfully + ssh 2VcykjU7vNdvAzEjQcc839GM2@nyc1.tmate.io + https://tmate.io/t/2VcykjU7vNdvAzEjQcc839GM2 + Entering main loop + Web shell: https://tmate.io/t/2VcykjU7vNdvAzEjQcc839GM2 + SSH: ssh 2VcykjU7vNdvAzEjQcc839GM2@nyc1.tmate.io + ... + + Generating XML Inputs --------------------- @@ -62,6 +119,23 @@ run:: pytest --build-inputs +Adding C++ Unit Tests +--------------------- + +The C++ test suite uses Catch2 integrated with CTest. Each header file should +have a corresponding test file in ``tests/cpp_unit_tests/``. If the test file +does not exist run:: + + touch test_.cpp + +The file must be added to the CMake build system in +``tests/cpp_unit_tests/CMakeLists.txt``. ``test_`` should +be added to ``TEST_NAMES``. + +To add a test case to ``test_.cpp`` ensure +``catch2/catch_test_macros.hpp`` is included. A unit test can then be added +using the ``TEST_CASE`` macro and the ``REQUIRE`` assertion from Catch2. + Adding Tests to the Regression Suite ------------------------------------ @@ -84,6 +158,12 @@ following files to your new test directory: compiler options during openmc configuration and build (e.g., no MPI, no debug/optimization). +For tests using the Python API, both the **inputs_true.dat** and +**results_true.dat** files can be generated automatically in the correct format +via:: + + pytest --update + In addition to this description, please see the various types of tests that are already included in the test suite to see how to create them. If all is implemented correctly, the new test will automatically be discovered by pytest. diff --git a/docs/source/devguide/user-input.rst b/docs/source/devguide/user-input.rst index 0bde0fb058..bbae3b7158 100644 --- a/docs/source/devguide/user-input.rst +++ b/docs/source/devguide/user-input.rst @@ -49,23 +49,12 @@ following steps should be followed to make changes to user input: written out to the statepoint or summary files and that the :class:`openmc.StatePoint` and :class:`openmc.Summary` classes read them in. -7. Finally, a set of `RELAX NG`_ schemas exists that enables validation of input - files. You should modify the RELAX NG schema for the file you changed. The - easiest way to do this is to change the `compact syntax`_ file - (e.g. ``src/relaxng/geometry.rnc``) and then convert it to regular XML syntax - using trang_:: - - trang geometry.rnc geometry.rng - For most user input additions and changes, it is simple enough to follow a "monkey see, monkey do" approach. When in doubt, contact your nearest OpenMC developer or send a message to the `developers mailing list`_. .. _property attribute: https://docs.python.org/3.6/library/functions.html#property -.. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/ -.. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean -.. _RELAX NG: https://relaxng.org/ -.. _compact syntax: https://relaxng.org/compact-tutorial-20030326.html -.. _trang: https://relaxng.org/jclark/trang.html +.. _XML Schema Part 2: https://www.w3.org/TR/xmlschema-2/ +.. _boolean: https://www.w3.org/TR/xmlschema-2/#boolean .. _developers mailing list: https://groups.google.com/forum/?fromgroups=#!forum/openmc-dev diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 57f2b1c68f..c49326a209 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -4,7 +4,7 @@ Development Workflow ==================== -Anyone wishing to make contributions to OpenMC should be fully acquianted and +Anyone wishing to make contributions to OpenMC should be fully acquainted and comfortable working with git_ and GitHub_. We assume here that you have git installed on your system, have a GitHub account, and have setup SSH keys to be able to create/push to repositories on GitHub. @@ -81,7 +81,7 @@ features and bug fixes. The general steps for contributing are as follows: openmc-dev/openmc as the target. At a minimum, you should describe what the changes you've made are and why - you are making them. If the changes are related to an oustanding issue, make + you are making them. If the changes are related to an outstanding issue, make sure it is cross-referenced. 5. A committer will review your pull request based on the criteria @@ -91,6 +91,30 @@ features and bug fixes. The general steps for contributing are as follows: 6. After the pull request has been thoroughly vetted, it is merged back into the *develop* branch of openmc-dev/openmc. +Setting Up Upstream Tracking (Required for Versioning) +------------------------------------------------------ + +By default, your fork **does not** include tags from the upstream OpenMC repository. +OpenMC relies on `git describe --tags` for versioning in source builds, and missing tags can lead +to incorrect version detection (i.e., ``0.0.0``). To ensure proper versioning, follow these steps: + +1. **Add the Upstream Repository** + This allows you to fetch updates from the main OpenMC repository. + + .. code-block:: sh + + git remote add upstream https://github.com/openmc-dev/openmc.git + +2. **Fetch and Push Tags** + Retrieve tags from the upstream repository and update your fork: + + .. code-block:: sh + + git fetch --tags upstream + git push --tags origin + +This ensures that both your **local** and **remote** fork have the correct versioning information. + Private Development ------------------- @@ -116,20 +140,20 @@ pip_. From the root directory of the OpenMC repository, run: .. code-block:: sh - pip install -e .[test] + python -m pip install -e .[test] This installs the OpenMC Python package in `"editable" mode -`_ so -that 1) it can be imported from a Python interpreter and 2) any changes made are +`_ so that 1) +it can be imported from a Python interpreter and 2) any changes made are immediately reflected in the installed version (that is, you don't need to keep reinstalling it). While the same effect can be achieved using the :envvar:`PYTHONPATH` environment variable, this is generally discouraged as it can interfere with virtual environments. -.. _git: http://git-scm.com/ +.. _git: https://git-scm.com/ .. _GitHub: https://github.com/ .. _git flow: https://nvie.com/git-model -.. _valgrind: https://www.valgrind.org/ +.. _valgrind: https://valgrind.org/ .. _style guide: https://docs.openmc.org/en/latest/devguide/styleguide.html .. _pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests .. _openmc-dev/openmc: https://github.com/openmc-dev/openmc diff --git a/docs/source/index.rst b/docs/source/index.rst index a01055374b..97666e7c3c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,7 +12,7 @@ files produced by NJOY. Parallelism is enabled via a hybrid MPI and OpenMP programming model. OpenMC was originally developed by members of the `Computational Reactor Physics -Group `_ at the `Massachusetts Institute of Technology +Group `_ at the `Massachusetts Institute of Technology `_ starting in 2011. Various universities, laboratories, and other organizations now contribute to the development of OpenMC. For more information on OpenMC, feel free to post a message on the `OpenMC Discourse diff --git a/docs/source/io_formats/depletion_chain.rst b/docs/source/io_formats/depletion_chain.rst index 89c76525f8..74413e7b61 100644 --- a/docs/source/io_formats/depletion_chain.rst +++ b/docs/source/io_formats/depletion_chain.rst @@ -56,6 +56,27 @@ attributes: .. _io_chain_reaction: +-------------------- +```` Element +-------------------- + +The ```` element represents photon and electron sources associated with +the decay of a nuclide and contains information to construct an +:class:`openmc.stats.Univariate` object that represents this emission as an +energy distribution. This element has the following attributes: + + :type: + The type of :class:`openmc.stats.Univariate` source term. + + :particle: + The type of particle emitted, e.g., 'photon' or 'electron' + + :parameters: + The parameters of the source term, e.g., for a + :class:`openmc.stats.Discrete` source, the energies (in [eV]) at which the + particles are emitted and their relative intensities in [Bq/atom] (in other + words, decay constants). + ---------------------- ```` Element ---------------------- diff --git a/docs/source/io_formats/depletion_results.rst b/docs/source/io_formats/depletion_results.rst index 172c17ae63..7035fc9c9e 100644 --- a/docs/source/io_formats/depletion_results.rst +++ b/docs/source/io_formats/depletion_results.rst @@ -47,10 +47,3 @@ The current version of the depletion results file format is 1.1. **/reactions//** :Attributes: - **index** (*int*) -- Index user in results for this reaction - -.. note:: - - The reaction rates for some isotopes not originally present may - be non-zero, but should be negligible compared to other atoms. - This can be controlled by changing the - :class:`openmc.deplete.Operator` ``dilute_initial`` attribute. diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index ff2f6eb74e..2947502196 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -48,7 +48,7 @@ Each ```` element can have the following attributes or sub-elements: :periodic_surface_id: If a periodic boundary condition is applied, this attribute identifies the - ``id`` of the corresponding periodic sufrace. + ``id`` of the corresponding periodic surface. The following quadratic surfaces can be modeled: @@ -219,6 +219,30 @@ Each ```` element can have the following attributes or sub-elements: *Default*: None + :triso_particle: + If the cell is filled with a TRISO particle, use this element to mark it. + + .. note:: Only cells with spherical area can be marked. + + *Default*: false + + :virtual_lattice: + If the cell is filled with a matrix containing the TRISO particle, use this + element to mark it. This can accelerate the search speed of neutrons in the + region containing a large number of TRISO particles. + + *Default*: false + + :shape: + If the virtual_lattice is True. This element specifies the shape of the + lattice. + + .. note:: The shape of the lattice must be specified if the virtual_lattice + is True. Related methods can be referred to Liang, J., Li, R., Liu, Z., + 2024. Virtual lattice method for efficient Monte Carlo transport simulation + of dispersion nuclear fuels. Computer Physics Communications 295, 108985. + https://doi.org/10.1016/j.cpc.2023.108985 + --------------------- ```` Element @@ -407,13 +431,33 @@ Each ```` element can have the following attributes or sub-eleme *Default*: None + :material_overrides: + This element contains information on material overrides to be applied to the + DAGMC universe. It has the following attributes and sub-elements: - .. note:: A geometry.xml file containing only a DAGMC model for a file named `dagmc.h5m` (no CSG) - looks as follows + :cell: + Material override information for a single cell. It contains the following + attributes and sub-elements: - .. code-block:: xml + :id: + The cell ID in the DAGMC geometry for which the material override will + apply. - - - - + :materials: + A list of material IDs that will apply to instances of the cell. If the + list contains only one ID, it will replace the original material + assignment of all instances of the DAGMC cell. If the list contains more + than one material, each material ID of the list will be assigned to the + various instances of the DAGMC cell. + + *Default*: None + +.. note:: A geometry.xml file containing only a DAGMC model for a file named + `dagmc.h5m` (no CSG) looks as follows: + + .. code-block:: xml + + + + + diff --git a/docs/source/io_formats/index.rst b/docs/source/io_formats/index.rst index 8c89bd2a1d..4bbaa961a6 100644 --- a/docs/source/io_formats/index.rst +++ b/docs/source/io_formats/index.rst @@ -51,3 +51,4 @@ Output Files track voxel volume + weight_windows diff --git a/docs/source/io_formats/nuclear_data.rst b/docs/source/io_formats/nuclear_data.rst index ef61604cb4..8174108e1a 100644 --- a/docs/source/io_formats/nuclear_data.rst +++ b/docs/source/io_formats/nuclear_data.rst @@ -339,6 +339,16 @@ Incoherent elastic scattering [eV\ :math:`^{-1}`]. :Attributes: - **type** (*char[]*) -- 'IncoherentElastic' +Sum of functions +---------------- + +:Object type: Group +:Attributes: - **type** (*char[]*) -- "Sum" + - **n** (*int*) -- Number of functions +:Datasets: + - ***func_** (:ref:`function <1d_functions>`) -- Dataset for the + i-th function (indexing starts at 1) + .. _angle_energy: -------------------------- @@ -501,6 +511,19 @@ equiprobable bins. - **skewed** (*int8_t*) -- Whether discrete angles are equi-probable (0) or have a skewed distribution (1). +Mixed Elastic +------------- + +This angle-energy distribution is used when an evaluation specifies both +coherent and incoherent elastic thermal neutron scattering. + +:Object type: Group +:Attributes: - **type** (*char[]*) -- "mixed_elastic" +:Groups: - **coherent** -- Distribution for coherent elastic scattering. The + format is given in :ref:`angle_energy`. + - **incoherent** -- Distribution for incoherent elastic scattering. + The format is given in :ref:`angle_energy`. + .. _energy_distribution: -------------------- diff --git a/docs/source/io_formats/particle_restart.rst b/docs/source/io_formats/particle_restart.rst index 53f86735f3..2734f0470e 100644 --- a/docs/source/io_formats/particle_restart.rst +++ b/docs/source/io_formats/particle_restart.rst @@ -26,6 +26,8 @@ The current version of the particle restart file format is 2.0. - **run_mode** (*char[]*) -- Run mode used, either 'fixed source', 'eigenvalue', or 'particle restart'. - **id** (*int8_t*) -- Unique identifier of the particle. + - **type** (*int*) -- Particle type (0=neutron, 1=photon, 2=electron, + 3=positron) - **weight** (*double*) -- Weight of the particle. - **energy** (*double*) -- Energy of the particle in eV for continuous-energy mode, or the energy group of the particle for diff --git a/docs/source/io_formats/plots.rst b/docs/source/io_formats/plots.rst index e6b75eafcb..1a42a4281d 100644 --- a/docs/source/io_formats/plots.rst +++ b/docs/source/io_formats/plots.rst @@ -7,13 +7,18 @@ Geometry Plotting Specification -- plots.xml Basic plotting capabilities are available in OpenMC by creating a plots.xml file and subsequently running with the ``--plot`` command-line flag. The root element of the plots.xml is simply ```` and any number output plots can be -defined with ```` sub-elements. Two plot types are currently implemented +defined with ```` sub-elements. Four plot types are currently implemented in openMC: * ``slice`` 2D pixel plot along one of the major axes. Produces a PNG image file. * ``voxel`` 3D voxel data dump. Produces an HDF5 file containing voxel xyz position and cell or material id. +* ``wireframe_raytrace`` 2D pixel plot of a three-dimensional view of a + geometry using wireframes around cells or materials and coloring by depth + through each material. +* ``solid_raytrace`` 2D pixel plot of a three-dimensional view of a geometry + with solid colored surfaces of a set of cells or materials. ------------------ @@ -66,21 +71,22 @@ sub-elements: *Default*: None - Required entry :type: - Keyword for type of plot to be produced. Currently only "slice" and "voxel" - plots are implemented. The "slice" plot type creates 2D pixel maps saved in - the PNG file format. The "voxel" plot type produces a binary datafile - containing voxel grid positioning and the cell or material (specified by the - ``color`` tag) at the center of each voxel. Voxel plot files can be - processed into VTK files using the :ref:`scripts_voxel` script provided with - OpenMC and subsequently viewed with a 3D viewer such as VISIT or Paraview. - See the :ref:`io_voxel` for information about the datafile structure. + Keyword for type of plot to be produced. Currently "slice", "voxel", + "wireframe_raytrace", and "solid_raytrace" plots are implemented. The + "slice" plot type creates 2D pixel maps saved in the PNG file format. The + "voxel" plot type produces a binary datafile containing voxel grid + positioning and the cell or material (specified by the ``color`` tag) at the + center of each voxel. Voxel plot files can be processed into VTK files using + the :func:`openmc.voxel_to_vtk` function and subsequently viewed with a 3D + viewer such as VISIT or Paraview. See :ref:`io_voxel` for information about + the datafile structure. .. note:: High-resolution voxel files produced by OpenMC can be quite large, but the equivalent VTK files will be significantly smaller. *Default*: "slice" -```` elements of ``type`` "slice" and "voxel" must contain the ``pixels`` +All ```` elements must contain the ``pixels`` attribute or sub-element: :pixels: @@ -96,7 +102,7 @@ attribute or sub-element: ``width``/``pixels`` along that basis direction may not appear in the plot. - *Default*: None - Required entry for "slice" and "voxel" plots + *Default*: None - Required entry for all plots ```` elements of ``type`` "slice" can also contain the following attributes or sub-elements. These are not used in "voxel" plots: @@ -125,6 +131,11 @@ attributes or sub-elements. These are not used in "voxel" plots: Specifies the custom color for the cell or material. Should be 3 integers separated by spaces. + :xs: + The attenuation coefficient for volume rendering of color in units of + inverse centimeters. Zero corresponds to transparency. Only for plot type + "wireframe_raytrace". + As an example, if your plot is colored by material and you want material 23 to be blue, the corresponding ``color`` element would look like: @@ -151,6 +162,18 @@ attributes or sub-elements. These are not used in "voxel" plots: *Default*: 255 255 255 (white) + :show_overlaps: + Indicates whether overlapping regions of different cells are shown. + + *Default*: None + + :overlap_color: + Specifies the RGB color of overlapping regions of different cells. Does not + do anything if ``show_overlaps`` is "false" or not specified. Should be 3 + integers separated by spaces. + + *Default*: 255 0 0 (red) + :meshlines: The ``meshlines`` sub-element allows for plotting the boundaries of a regular mesh on top of a plot. Only one ``meshlines`` element is allowed per @@ -179,3 +202,80 @@ attributes or sub-elements. These are not used in "voxel" plots: *Default*: 0 0 0 (black) *Default*: None + +```` elements of ``type`` "wireframe_raytrace" or "solid_raytrace" can contain the +following attributes or sub-elements. + + :camera_position: + Location in 3D Cartesian space the camera is at. + + + *Default*: None - Required for all ``wireframe_raytrace`` or + ``solid_raytrace`` plots + + :look_at: + Location in 3D Cartesian space the camera is looking at. + + + *Default*: None - Required for all ``wireframe_raytrace`` or + ``solid_raytrace`` plots + + :field_of_view: + The horizontal field of view in degrees. Defaults to roughly the same value + as for the human eye. + + *Default*: 70 + + :orthographic_width: + If set to a nonzero value, an orthographic rather than perspective + projection for the camera is employed. An orthographic projection puts out + parallel rays from the camera of a width prescribed here in the horizontal + direction, with the width in the vertical direction decided by the pixel + aspect ratio. + + *Default*: 0 + +```` elements of ``type`` "solid_raytrace" can contain the following attributes or +sub-elements. + + :opaque_ids: + List of integer IDs of cells or materials to be treated as visible in the + plot. Whether the integers are interpreted as cell or material IDs depends + on ``color_by``. + + *Default*: None - Required for all phong plots + + :light_position: + Location in 3D Cartesian space of the light. + + + *Default*: Same location as ``camera_position`` + + :diffuse_fraction: + Fraction of light originating from non-directional sources. If set to one, + the coloring is not influenced by surface curvature, and no shadows appear. + If set to zero, only regions illuminated by the light are not black. + + + *Default*: 0.1 + +```` elements of ``type`` "wireframe_raytrace" can contain the following +attributes or sub-elements. + + :wireframe_color: + RGB value of the wireframe's color + + *Default*: 0, 0, 0 (black) + + :wireframe_thickness: + Integer number of pixels that the wireframe takes up. The value is a radius + of the wireframe. Setting to zero removes any wireframing. + + *Default*: 0 + + :wireframe_ids: + Integer IDs of cells or materials of regions to draw wireframes around. + Whether the integers are interpreted as cell or material IDs depends on + ``color_by``. + + *Default*: None diff --git a/docs/source/io_formats/properties.rst b/docs/source/io_formats/properties.rst index 5030e78f35..4cc5da379b 100644 --- a/docs/source/io_formats/properties.rst +++ b/docs/source/io_formats/properties.rst @@ -4,7 +4,7 @@ Properties File Format ====================== -The current version of the properties file format is 1.0. +The current version of the properties file format is 1.1. **/** @@ -25,6 +25,7 @@ The current version of the properties file format is 1.0. **/geometry/cells/cell /** :Datasets: - **temperature** (*double[]*) -- Temperature of the cell in [K]. + - **density** (*double[]*) -- Density of the cell in [g/cm3]. **/materials/** diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index d1d9e69fac..720846c851 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -32,6 +32,17 @@ standard deviation. *Default*: false +------------------------------------- +```` Element +------------------------------------- + +The ```` element indicates whether delayed neutrons +are created in fission. If this element is set to "true", delayed neutrons +will be created in fission events; otherwise only prompt neutrons will be +created. + + *Default*: true + ------------------------------------- ```` Element ------------------------------------- @@ -49,13 +60,15 @@ fission. ```` Element -------------------- -The ```` element indicates two kinds of cutoffs. The first is the weight -cutoff used below which particles undergo Russian roulette. Surviving particles -are assigned a user-determined weight. Note that weight cutoffs and Russian -rouletting are not turned on by default. The second is the energy cutoff which -is used to kill particles under certain energy. The energy cutoff should not be -used unless you know particles under the energy are of no importance to results -you care. This element has the following attributes/sub-elements: +The ```` element indicates three kinds of cutoffs. The first is the +weight cutoff used below which particles undergo Russian roulette. Surviving +particles are assigned a user-determined weight. Note that weight cutoffs and +Russian rouletting are not turned on by default. The second is the energy cutoff +which is used to kill particles under certain energy. The energy cutoff should +not be used unless you know particles under the energy are of no importance to +results you care. The third is the time cutoff used to kill particles whose time +exceeds a specific cutoff. Particles will be killed exactly at the specified +time. :weight: The weight below which particles undergo Russian roulette. @@ -68,6 +81,13 @@ you care. This element has the following attributes/sub-elements: *Default*: 1.0 + :survival_normalization: + If this element is set to "true", this will enable the use of survival + biasing source normalization, whereby the weight parameters, weight and + weight_avg, are multiplied per history by the start weight of said history. + + *Default*: false + :energy_neutron: The energy under which neutrons will be killed. @@ -88,6 +108,26 @@ you care. This element has the following attributes/sub-elements: *Default*: 0.0 + :time_neutron + The time above which neutrons will be killed. + + *Default*: Infinity + + :time_photon + The time above which photons will be killed. + + *Default*: Infinity + + :time_electron + The time above which electrons will be killed. + + *Default*: Infinity + + :time_positron + The time above which positorns will be killed. + + *Default*: Infinity + ---------------------------- ```` ---------------------------- @@ -138,6 +178,16 @@ history-based parallelism. *Default*: false +-------------------------------- +```` Element +-------------------------------- + +The ```` element specifies the energy multiplier, expressed +in units of :math:`kT`, that determines when the free gas scattering approach is +used for elastic scattering. Values must be positive. + + *Default*: 400.0 + ----------------------------------- ```` Element ----------------------------------- @@ -205,7 +255,7 @@ based on the recommended value in LA-UR-14-24530_. .. note:: This element is not used in the multi-group :ref:`energy_mode`. -.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf +.. _LA-UR-14-24530: https://mcnp.lanl.gov/pdf_files/TechReport_2014_LANL_LA-UR-14-24530_Brown.pdf --------------------------- ```` @@ -219,19 +269,45 @@ to false. *Default*: true ----------------------------------------- -```` Element ----------------------------------------- +-------------------------------- +```` Element +-------------------------------- -This element indicates the number of neutrons to run in flight concurrently +This element indicates the maximum number of lost particles. + + *Default*: 10 + +------------------------------------ +```` Element +------------------------------------ + + +This element indicates the maximum number of lost particles, relative to the +total number of particles. + + *Default*: 1.0e-6 + +------------------------------------- +```` Element +------------------------------------- + +This element indicates the number of particles to run in flight concurrently when using event-based parallelism. A higher value uses more memory, but may be more efficient computationally. *Default*: 100000 ---------------------------- +--------------------------------- +```` Element +--------------------------------- + +This element indicates the maximum number of events a particle can undergo. + + *Default*: 1000000 + +----------------------- ```` Element ---------------------------- +----------------------- The ```` element allows the user to set a maximum scattering order to apply to every nuclide/material in the problem. That is, if the data @@ -243,6 +319,23 @@ then, OpenMC will only use up to the :math:`P_1` data. .. note:: This element is not used in the continuous-energy :ref:`energy_mode`. +-------------------------------- +```` Element +-------------------------------- + +The ```` element indicates the number of times a particle can split during a history. + + *Default*: 1000 + +-------------------------------------- +```` Element +-------------------------------------- + +This ```` element indicates the maximum number of +particle restart files (per MPI process) to write for lost particles. + + *Default*: None + .. _mesh_element: ------------------ @@ -334,6 +427,15 @@ either "false" or "true". *Default*: false +----------------------- +```` Element +----------------------- + +The ```` element is used to set the seed for the pseudorandom number +generator during generation of colors in plots. + + *Default*: 1 + --------------------- ```` Element --------------------- @@ -346,6 +448,64 @@ or sub-elements and can be set to either "false" or "true". .. note:: This element is not used in the multi-group :ref:`energy_mode`. +------------------------ +```` Element +------------------------ + +The ```` element enables random ray mode and contains a number of +settings relevant to the solver. Tips for selecting these parameters can be +found in the :ref:`random ray user guide `. + + :distance_inactive: + The inactive ray length (dead zone length) in [cm]. + + *Default*: None + + :distance_active: + The active ray length in [cm]. + + *Default*: None + + :source: + Specifies the starting ray distribution, and follows the format for + :ref:`source_element`. It must be uniform in space and angle and cover the + full domain. It does not represent a physical neutron or photon source -- it + is only used to sample integrating ray starting locations and directions. + + *Default*: None + + :sample_method: + Specifies the method for sampling the starting ray distribution. This + element can be set to "prng" or "halton". + + *Default*: prng + + :source_region_meshes: + Relates meshes to spatial domains for subdividing source regions with each domain. + + :mesh: + Contains an ``id`` attribute and one or more ```` sub-elements. + + :id: + The unique identifier for the mesh. + + :domain: + Each domain element has an ``id`` attribute and a ``type`` attribute. + + :id: + The unique identifier for the domain. + + :type: + The type of the domain. Can be ``material``, ``cell``, or ``universe``. + + :diagonal_stabilization_rho: + The rho factor for use with diagonal stabilization. This technique is + applied when negative diagonal (in-group) elements are detected in + the scattering matrix of input MGXS data, which is a common feature + of transport corrected MGXS data. + + *Default*: 1.0 + ---------------------------------- ```` Element ---------------------------------- @@ -421,6 +581,17 @@ pseudo-random number generator. *Default*: 1 +-------------------- +```` Element +-------------------- + +The ``stride`` element is used to specify how many random numbers are allocated +for each source particle history. + + *Default*: 152,917 + +.. _source_element: + -------------------- ```` Element -------------------- @@ -438,24 +609,29 @@ attributes/sub-elements: *Default*: 1.0 + :type: + Indicator of source type. One of ``independent``, ``file``, ``compiled``, or + ``mesh``. The type of the source will be determined by this attribute if it + is present. + :particle: The source particle type, either ``neutron`` or ``photon``. *Default*: neutron :file: - If this attribute is given, it indicates that the source is to be read from - a binary source file whose path is given by the value of this element. Note, - the number of source sites needs to be the same as the number of particles - simulated in a fission source generation. + If this attribute is given, it indicates that the source type is ``file``, + meaning particles are to be read from a binary source file whose path is + given by the value of this element. *Default*: None :library: - If this attribute is given, it indicates that the source is to be - instantiated from an externally compiled source function. This source can be - as complex as is required to define the source for your problem. The library - has a few basic requirements: + If this attribute is given, it indicates that the source type is + ``compiled``, meaning that particles are instantiated from an externally + compiled source function. This source can be completely customized as needed + to define the source for your problem. The library has a few basic + requirements: * It must contain a class that inherits from ``openmc::Source``; * The class must implement a function called ``sample()``; @@ -463,16 +639,15 @@ attributes/sub-elements: as a unique pointer. This function can be used to pass parameters through to the source from the XML, if needed. - More documentation on how to build sources can be found in :ref:`custom_source`. - - *Default*: None + More documentation on how to build sources can be found in + :ref:`compiled_source`. :parameters: - If this attribute is given, it provides the parameters to pass through to the - class generated using the ``library`` parameter . More documentation on how to - build parametrized sources can be found in :ref:`parameterized_custom_source`. - - *Default*: None + If this attribute is given, it indicated that the source type is + ``compiled``. Its value provides the parameters to pass through to the class + generated using the ``library`` parameter. More documentation on how to + build parametrized sources can be found in + :ref:`parameterized_compiled_source`. :space: An element specifying the spatial distribution of source sites. This element @@ -480,19 +655,37 @@ attributes/sub-elements: :type: The type of spatial distribution. Valid options are "box", "fission", - "point", "cartesian", "cylindrical", and "spherical". A "box" spatial - distribution has coordinates sampled uniformly in a parallelepiped. A - "fission" spatial distribution samples locations from a "box" + "point", "cartesian", "cylindrical", "spherical", "mesh", and "cloud". + + A "box" spatial distribution has coordinates sampled uniformly in a + parallelepiped. + + A "fission" spatial distribution samples locations from a "box" distribution but only locations in fissionable materials are accepted. + A "point" spatial distribution has coordinates specified by a triplet. + A "cartesian" spatial distribution specifies independent distributions of - x-, y-, and z-coordinates. A "cylindrical" spatial distribution specifies - independent distributions of r-, phi-, and z-coordinates where phi is the - azimuthal angle and the origin for the cylindrical coordinate system is - specified by origin. A "spherical" spatial distribution specifies - independent distributions of r-, theta-, and phi-coordinates where theta - is the angle with respect to the z-axis, phi is the azimuthal angle, and - the sphere is centered on the coordinate (x0,y0,z0). + x-, y-, and z-coordinates. + + A "cylindrical" spatial distribution specifies independent distributions + of r-, phi-, and z-coordinates where phi is the azimuthal angle and the + origin for the cylindrical coordinate system is specified by origin. + + A "spherical" spatial distribution specifies independent distributions of + r-, cos_theta-, and phi-coordinates where cos_theta is the cosine of the + angle with respect to the z-axis, phi is the azimuthal angle, and the + sphere is centered on the coordinate (x0,y0,z0). + + A "mesh" spatial distribution samples source sites from a mesh element + based on the relative strengths provided in the node. Source locations + within an element are sampled isotropically. If no strengths are provided, + the space within the mesh is uniformly sampled. + + A "cloud" spatial distribution samples source sites from a list of spatial + positions provided in the node, based on the relative strengths provided + in the node. If no strengths are provided, the positions are uniformly + sampled. *Default*: None @@ -559,6 +752,26 @@ attributes/sub-elements: For "cylindrical and "spherical" distributions, this element specifies the coordinates for the origin of the coordinate system. + :mesh_id: + For "mesh" spatial distributions, this element specifies which mesh ID to + use for the geometric description of the mesh. + + :coords: + For "cloud" distributions, this element specifies a list of coordinates + for each of the points in the cloud. + + :strengths: + For "mesh" and "cloud" spatial distributions, this element specifies the + relative source strength of each mesh element or each point in the cloud. + + :volume_normalized: + For "mesh" spatial distrubtions, this optional boolean element specifies + whether the vector of relative strengths should be multiplied by the mesh + element volume. This is most common if the strengths represent a source + per unit volume. + + *Default*: false + :angle: An element specifying the angular distribution of source sites. This element has the following attributes: @@ -606,6 +819,47 @@ attributes/sub-elements: *Default*: false + :mesh: + For mesh sources, this indicates the ID of the corresponding mesh. + + :source: + For mesh sources, this sub-element specifies the source for an individual + mesh element and follows the format for :ref:`source_element`. The number of + ```` sub-elements should correspond to the number of mesh elements. + + :constraints: + This sub-element indicates the presence of constraints on sampled source + sites (see :ref:`usersguide_source_constraints` for details). It may have + the following sub-elements: + + :domain_ids: + The unique IDs of domains for which source sites must be within. + + *Default*: None + + :domain_type: + The type of each domain for source rejection ("cell", "material", or + "universe"). + + *Default*: None + + :fissionable: + A boolean indicating whether source sites must be sampled within a + material that is fissionable in order to be accepted. + + :time_bounds: + A pair of times in [s] indicating the lower and upper bound for a time + interval that source particles must be within. + + :energy_bounds: + A pair of energies in [eV] indicating the lower and upper bound for an + energy interval that source particles must be within. + + :rejection_strategy: + Either "resample", indicating that source sites should be resampled when + one is rejected, or "kill", indicating that a rejected source site is + assigned zero weight. + .. _univariate: Univariate Probability Distributions @@ -673,6 +927,16 @@ variable and whose sub-elements/attributes are as follows: :dist: This sub-element of a ``pair`` element provides information on the corresponding univariate distribution. +--------------------------------------- +```` Element +--------------------------------------- + +The ```` element specifies the minimum fraction of +external source sites that must be accepted when applying rejection sampling +based on constraints. + + *Default*: 0.05 + ------------------------- ```` Element ------------------------- @@ -731,27 +995,39 @@ attributes/sub-elements: *Default*: false ---------------------------- -```` Element ---------------------------- + :mcpl: + If this element is set to "true", the source point file containing the + source bank will be written as an MCPL_ file name ``source.mcpl`` instead of + an HDF5 file. This option is only applicable if the ```` element + is set to true. -The ```` element specifies a surface source file for OpenMC to -read source bank for initializing histories. -This element has the following attributes/sub-elements: + *Default*: false + +------------------------------ +```` Element +------------------------------ + +The ```` element specifies a surface source file for OpenMC to +read source bank for initializing histories. This element has the following +attributes/sub-elements: :path: Absolute or relative path to a surface source file to read in source bank. *Default*: ``surface_source.h5`` in current working directory ----------------------------- -```` Element ----------------------------- +------------------------------- +```` Element +------------------------------- -The ```` element triggers OpenMC to bank particles crossing +The ```` element triggers OpenMC to bank particles crossing certain surfaces and write out the source bank in a separate file called -``surface_source.h5``. -This element has the following attributes/sub-elements: +``surface_source.h5``. One or multiple surface IDs and one cell ID can be used +to select the surfaces of interest. If no surface IDs are declared, every surface +of the model is eligible to bank particles. In that case, a cell ID (using +either the ``cell``, ``cellfrom`` or ``cellto`` attributes) can be used to select +every surface of a specific cell. This element has the following +attributes/sub-elements: :surface_ids: A list of integers separated by spaces indicating the unique IDs of surfaces @@ -767,6 +1043,53 @@ This element has the following attributes/sub-elements: *Default*: None + :max_source_files: + An integer value indicating the number of surface source files to be written + containing the maximum number of particles each. The surface source bank + will be cleared in simulation memory each time a surface source file is + written. By default a ``surface_source.h5`` file will be created when the + maximum number of saved particles is reached. + + *Default*: 1 + + :mcpl: + An optional boolean which indicates if the banked particles should be + written to a file in the MCPL_-format instead of the native HDF5-based + format. If activated the output file name is changed to + ``surface_source.mcpl``. + + *Default*: false + + .. _MCPL: https://mctools.github.io/mcpl/mcpl.pdf + + :cell: + An integer representing the cell ID used to determine if particles crossing + identified surfaces are to be banked. Particles coming from or going to this + declared cell will be banked if they cross the identified surfaces. + + *Default*: None + + :cellfrom: + An integer representing the cell ID used to determine if particles crossing + identified surfaces are to be banked. Particles coming from this declared + cell will be banked if they cross the identified surfaces. + + *Default*: None + + :cellto: + An integer representing the cell ID used to determine if particles crossing + identified surfaces are to be banked. Particles going to this declared cell + will be banked if they cross the identified surfaces. + + *Default*: None + +.. note:: The ``cell``, ``cellfrom`` and ``cellto`` attributes cannot be + used simultaneously. + +.. note:: Surfaces with boundary conditions that are not "transmission" or "vacuum" + are not eligible to store any particles when using ``cell``, ``cellfrom`` + or ``cellto`` attributes. It is recommended to use surface IDs instead. + ------------------------------ ```` Element ------------------------------ @@ -832,7 +1155,9 @@ cell, the nearest temperature at which cross sections are given is to be applied, within a given tolerance (see :ref:`temperature_tolerance`). A value of "interpolation" indicates that cross sections are to be linear-linear interpolated between temperatures at which nuclear data are present (see -:ref:`temperature_treatment`). +:ref:`temperature_treatment`). With the "interpolation" method, temperatures +outside of the bounds of the nuclear data may be accepted, provided they still +fall within the tolerance (see :ref:`temperature_tolerance`). *Default*: "nearest" @@ -871,7 +1196,12 @@ The ```` element specifies a tolerance in Kelvin that is to be applied when the "nearest" temperature method is used. For example, if a cell temperature is 340 K and the tolerance is 15 K, then the closest temperature in the range of 325 K to 355 K will be used to evaluate cross -sections. +sections. If the ```` is "interpolation", the tolerance +specified applies to cell temperatures outside of the data bounds. For example, +if a cell is specified at 695K, a tolerance of 15K and data is only available +at 700K and 1000K, the cell's cross sections will be evaluated at 700K, since +the desired temperature of 695K is within the tolerance of the actual data +despite not being bounded on both sides. *Default*: 10 K @@ -986,8 +1316,14 @@ The ```` element indicates that a stochastic volume calculation should be run at the beginning of the simulation. This element has the following sub-elements/attributes: - :cells: - The unique IDs of cells for which the volume should be estimated. + :domain_type: + The type of each domain for the volume calculation ("cell", "material", or + "universe"). + + *Default*: None + + :domain_ids: + The unique IDs of domains for which the volume should be estimated. *Default*: None @@ -997,16 +1333,41 @@ sub-elements/attributes: *Default*: None :lower_left: - The lower-left Cartesian coordinates of a bounding box that is used to - sample points within. + The lower-left Cartesian coordinates of a bounding box that is used to + sample points within. - *Default*: None + *Default*: None :upper_right: - The upper-right Cartesian coordinates of a bounding box that is used to - sample points within. + The upper-right Cartesian coordinates of a bounding box that is used to + sample points within. - *Default*: None + *Default*: None + + :threshold: + Presence of a ```` sub-element indicates that the volume + calculation will be halted based on a threshold on the error. It has the + following sub-elements/attributes: + + :type: + The type of the trigger. Accepted options are "variance", "std_dev", + and "rel_err". + + :variance: + Variance of the mean, :math:`\sigma^2` + + :std_dev: + Standard deviation of the mean, :math:`\sigma` + + :rel_err: + Relative error of the mean, :math:`\frac{\sigma}{\mu}` + + *Default*: None + + :threshold: + The trigger's convergence criterion for the given type. + + *Default*: None ---------------------------- ```` Element @@ -1027,7 +1388,7 @@ sub-elements/attributes: :particle_type: The particle that the weight windows will apply to (e.g., 'neutron') - *Default*: None + *Default*: 'neutron' :energy_bins: Monotonically increasing list of bounding energies in [eV] to be used for @@ -1065,3 +1426,92 @@ sub-elements/attributes: Threshold below which particles will be terminated *Default*: :math:`10^{-38}` + +-------------------------------------- +```` Element +-------------------------------------- + +The ```` element provides information for creating a set of +mesh-based weight windows. + + :mesh: + ID of a mesh that is to be used for the weight windows spatial bins + + *Default*: None + + :energy_bounds: + The weight window energy bounds. If not present, the max/min energy of the + cross section data is applied as a single energy bin. + + *Default*: None + + :particle_type: + The particle that the weight windows will apply to (e.g., 'neutron') + + *Default*: neutron + + :max_realizations: + The number of tally realizations after which the weight windows will stop updating. + + *Default*: 1 + + :update_interval: + The number of tally realizations between weight window updates. + + *Default*: 1 + + :on_the_fly: + Controls whether or not the tally results are reset after a weight window update. + + *Default*: true + + :method: + Method used to update weight window values (one of 'magic' or 'fw_cadis') + + *Default*: magic + + :update_parameters: + Method-specific update parameters used when generating/updating weight windows. + + For MAGIC: + + :value: + The type of tally value to use when creating weight windows (one of 'mean' or 'rel_err') + + *Default*: 'mean' + + :threshold: + The relative error threshold above which tally results will be ignored. + + *Default*: 1.0 + + :ratio: + The ratio of the lower to upper weight window bounds. + + *Default*: 5.0 + +--------------------------------------- +```` Element +--------------------------------------- + +The ```` element indicates the checkpoints for weight +window split/roulette (surface, collision or both). This element has the +following sub-elements/attributes: + + :surface: + If set to "true", weight window checks will be performed at surface + crossings. + + *Default*: False + + :collision: + If set to "true", weight window checks will be performed at collisions. + + *Default*: True + +-------------------------------------- +```` Element +-------------------------------------- + + The ``weight_windows_file`` element has no attributes and contains the path to + a weight windows HDF5 file to load during simulation initialization. diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 591dc27777..3b10317696 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -4,7 +4,7 @@ State Point File Format ======================= -The current version of the statepoint file format is 17.0. +The current version of the statepoint file format is 18.1. **/** @@ -23,6 +23,7 @@ The current version of the statepoint file format is 17.0. bank is present (1) or not (0). :Datasets: - **seed** (*int8_t*) -- Pseudo-random number generator seed. + - **stride** (*uint64_t*) -- Pseudo-random number generator stride. - **energy_mode** (*char[]*) -- Energy mode of the run, either 'continuous-energy' or 'multi-group'. - **run_mode** (*char[]*) -- Run mode used, either 'eigenvalue' or @@ -68,20 +69,44 @@ The current version of the statepoint file format is 17.0. :Attributes: - **n_meshes** (*int*) -- Number of meshes in the problem. - **ids** (*int[]*) -- User-defined unique ID of each mesh. +.. _mesh-spec-hdf5: + **/tallies/meshes/mesh /** -:Datasets: - **type** (*char[]*) -- Type of mesh. +:Attributes: - **id** (*int*) -- ID of the mesh + +:Datasets: - **name** (*char[]*) -- Name of the mesh. + - **type** (*char[]*) -- Type of mesh. - **dimension** (*int*) -- Number of mesh cells in each dimension. - - **lower_left** (*double[]*) -- Coordinates of lower-left corner of - mesh. - - **upper_right** (*double[]*) -- Coordinates of upper-right corner - of mesh. - - **width** (*double[]*) -- Width of each mesh cell in each - dimension. + - **Regular Mesh Only:** + - **lower_left** (*double[]*) -- Coordinates of lower-left corner of + mesh. + - **upper_right** (*double[]*) -- Coordinates of upper-right corner + of mesh. + - **width** (*double[]*) -- Width of each mesh cell in each + dimension. + - **Rectilinear Mesh Only:** + - **x_grid** (*double[]*) -- Mesh divisions along the x-axis. + - **y_grid** (*double[]*) -- Mesh divisions along the y-axis. + - **z_grid** (*double[]*) -- Mesh divisions along the z-axis. + - **Cylindrical & Spherical Mesh Only:** + - **r_grid** (*double[]*) -- The mesh divisions along the r-axis. + - **phi_grid** (*double[]*) -- The mesh divisions along the phi-axis. + - **origin** (*double[]*) -- The origin in cartesian coordinates. + - **Spherical Mesh Only:** + - **theta_grid** (*double[]*) -- The mesh divisions along the theta-axis. - **Unstructured Mesh Only:** + - **filename** (*char[]*) -- Name of the mesh file. + - **library** (*char[]*) -- Mesh library used to represent the + mesh ("moab" or "libmesh"). + - **length_multiplier** (*double*) Scaling factor applied to the mesh. + - **options** (*char[]*) -- Special options that control spatial + search data structures used. - **volumes** (*double[]*) -- Volume of each mesh cell. - - **centroids** (*double[]*) -- Location of the mesh cell - centroids. + - **vertices** (*double[]*) -- x, y, z values of the mesh vertices. + - **connectivity** (*int[]*) -- Connectivity array for the mesh + cells. + - **element_types** (*int[]*) -- Mesh element types. **/tallies/filters/** @@ -103,6 +128,10 @@ The current version of the statepoint file format is 17.0. - **y** (*double[]*) -- Interpolant values for energyfunction interpolation. Only used for 'energyfunction' filters. + :Attributes: + - **interpolation** (*int*) -- Interpolation type. Only used for + 'energyfunction' filters. + **/tallies/derivatives/derivative /** :Datasets: - **independent variable** (*char[]*) -- Independent variable of @@ -118,6 +147,8 @@ The current version of the statepoint file format is 17.0. - **internal** (*int*) -- Flag indicating the presence of tally data (0) or absence of tally data (1). All user defined tallies will have a value of 0 unless otherwise instructed. + - **multiply_density** (*int*) -- Flag indicating whether reaction + rates should be multiplied by atom density (1) or not (0). :Datasets: - **n_realizations** (*int*) -- Number of realizations. - **n_filters** (*int*) -- Number of filters used. @@ -149,7 +180,7 @@ All values are given in seconds and are measured on the master process. finalization. - **transport** (*double*) -- Time spent transporting particles. - **inactive batches** (*double*) -- Time spent in the inactive - batches (including non-transport activities like communcating + batches (including non-transport activities like communicating sites). - **active batches** (*double*) -- Time spent in the active batches (including non-transport activities like communicating sites). diff --git a/docs/source/io_formats/summary.rst b/docs/source/io_formats/summary.rst index c140d98ae3..64ca68b9c3 100644 --- a/docs/source/io_formats/summary.rst +++ b/docs/source/io_formats/summary.rst @@ -4,7 +4,7 @@ Summary File Format =================== -The current version of the summary file format is 6.0. +The current version of the summary file format is 6.1. **/** @@ -38,6 +38,7 @@ The current version of the summary file format is 6.0. is an array if the cell uses distributed materials, otherwise it is a scalar. - **temperature** (*double[]*) -- Temperature of the cell in Kelvin. + - **density** (*double[]*) -- Density of the cell in [g/cm3]. - **translation** (*double[3]*) -- Translation applied to the fill universe. This dataset is present only if fill_type is set to 'universe'. @@ -60,9 +61,11 @@ The current version of the summary file format is 6.0. - **coefficients** (*double[]*) -- Array of coefficients that define the surface. See :ref:`surface_element` for what coefficients are defined for each surface type. - - **boundary_condition** (*char[]*) -- Boundary condition applied to - the surface. Can be 'transmission', 'vacuum', 'reflective', or - 'periodic'. + - **boundary_type** (*char[]*) -- Boundary condition applied to + the surface. Can be 'transmission', 'vacuum', 'reflective', + 'periodic', or 'white'. + - **albedo** (*double*) -- Boundary albedo as a positive multiplier + of particle weight. If absent, it is assumed to be 1.0. - **geom_type** (*char[]*) -- Type of geometry used to create the cell. Either 'csg' or 'dagmc'. diff --git a/docs/source/io_formats/tallies.rst b/docs/source/io_formats/tallies.rst index 3cde6fc854..9f29a949ac 100644 --- a/docs/source/io_formats/tallies.rst +++ b/docs/source/io_formats/tallies.rst @@ -40,15 +40,15 @@ The ```` element accepts the following sub-elements: :nuclides: If specified, the scores listed will be for particular nuclides, not the - summation of reactions from all nuclides. The format for nuclides should be - [Atomic symbol]-[Mass number], e.g. "U-235". The reaction rate for all + summation of reactions from all nuclides. Nuclides are expressed using the + GNDS naming convention, e.g. "U235" or "Am242_m1". The reaction rate for all nuclides can be obtained with "total". For example, to obtain the reaction - rates for U-235, Pu-239, and all nuclides in a material, this element should + rates for U235, Pu239, and all nuclides in a material, this element should be: .. code-block:: xml - U-235 Pu-239 total + U235 Pu239 total *Default*: total @@ -69,6 +69,12 @@ The ```` element accepts the following sub-elements: list of valid scores can be found in the :ref:`user's guide `. + :multiply_density: + A boolean that indicates whether reaction rate scores should be computed by + multiplying by the atom density of a nuclide present in a material. + + *Default*: true + :trigger: Precision trigger applied to all filter bins and nuclides for this tally. It must specify the trigger's type, threshold and scores to which it will @@ -94,6 +100,18 @@ The ```` element accepts the following sub-elements: *Default*: None + :ignore_zeros: + Whether to allow zero tally bins to be ignored when assessing the + convergece of the precision trigger. If True, only nonzero tally scores + will be compared to the trigger's threshold. + + .. note:: The ``ignore_zeros`` option can cause the tally trigger to fire + prematurely if there are no hits in any bins at the first + evalulation. It is the user's responsibility to specify enough + particles per batch to get a nonzero score in at least one bin. + + *Default*: False + :scores: The score(s) in this tally to which the trigger should be applied. @@ -311,6 +329,11 @@ If a mesh is desired as a filter for a tally, it must be specified in a separate element with the tag name ````. This element has the following attributes/sub-elements: + :name: + An optional string name to identify the mesh in output files. + + *Default*: "" + :type: The type of mesh. This can be either "regular", "rectilinear", "cylindrical", "spherical", or "unstructured". @@ -351,10 +374,17 @@ attributes/sub-elements: :theta_grid: The mesh divisions along the theta-axis. (For spherical mesh only.) + :origin: + The origin in cartesian coordinates. (For cylindrical and spherical meshes only.) + :library: The mesh library used to represent an unstructured mesh. This can be either "moab" or "libmesh". (For unstructured mesh only.) + :options: + Special options that control spatial search data structures used. (For + unstructured mesh using MOAB only) + :filename: The name of the mesh file to be loaded at runtime. (For unstructured mesh only.) diff --git a/docs/source/io_formats/track.rst b/docs/source/io_formats/track.rst index c617cd73e3..a97d75e58e 100644 --- a/docs/source/io_formats/track.rst +++ b/docs/source/io_formats/track.rst @@ -4,18 +4,34 @@ Track File Format ================= -The current revision of the particle track file format is 2.0. +The current revision of the particle track file format is 3.0. **/** :Attributes: - **filetype** (*char[]*) -- String indicating the type of file. - **version** (*int[2]*) -- Major and minor version of the track file format. - - **n_particles** (*int*) -- Number of particles for which tracks - are recorded. - - **n_coords** (*int[]*) -- Number of coordinates for each - particle. :Datasets: - - **coordinates_** (*double[][3]*) -- (x,y,z) coordinates for the - *i*-th particle. + - **track___

** (Compound type) -- Particle track information + for source particle in batch *b*, generation *g*, and particle + number *p*. particle. The compound type has fields ``r``, ``u``, + ``E``, ``time``, ``wgt``, ``cell_id``, ``cell_instance``, and + ``material_id``, which represent the position (each coordinate in + [cm]), direction, energy in [eV], time in [s], weight, cell ID, + cell instance, and material ID, respectively. When the particle is + present in a cell with no material assigned, the material ID is + given as -1. Note that this array contains information for one or + more primary/secondary particles originating. The starting index + for each primary/secondary particle is given by the ``offsets`` + attribute. + + :Attributes: - **n_particles** (*int*) -- Number of + primary/secondary particles for the source history. + - **offsets** (*int[]*) Offset (starting index) into + the array for each primary/secondary particle. The + last offset should match the total size of the + array. + - **particles** (*int[]*) -- Particle type for each + primary/secondary particle (0=neutron, 1=photon, + 2=electron, 3=positron). diff --git a/docs/source/io_formats/weight_windows.rst b/docs/source/io_formats/weight_windows.rst new file mode 100644 index 0000000000..302e893187 --- /dev/null +++ b/docs/source/io_formats/weight_windows.rst @@ -0,0 +1,39 @@ +.. _io_weight_windows: + +==================== +Weight Window Format +==================== + +The current revision of the weight window file format is 1.0. + +**/** + +:Attributes: - **filetype** (*char[]*) -- String indicating the type of file. + - **version** (*int[2]*) -- Major and minor version of the weight + window file format. + +**/weight_windows/** + +:Attributes: - **n_weight_windows** (*int*) -- Number of weight window objects in the file. + - **ids** (*int[]*) -- Unique IDs of weight window objects in the file. + +**/weight_windows/weight_windows_/** + +:Datasets: - **mesh** (*int*) -- ID of the mesh associated with the weight window object. + - **particle_type** (*char[]*) -- Particle type to which the weight windows apply. + - **energy_bounds** (*double[]*) -- Energy bounds of the weight windows in [eV] + - **lower_ww_bounds** (*double[]*) -- Weight window lower bounds. + - **upper_ww_bounds** (*double[]*) -- Weight window upper bounds. + - **survival_ratio** (*double*) -- Weight window survival ratio. + - **max_lower_bound_ratio** (*double*) -- Maximum particle weight to lower weight window bound ratio. + - **max_split** (*int*) -- Maximum number of splits per weight window check. + - **weight_cutoff** (*double*) -- Particle weight cutoff. + +**/meshes/** + +:Attributes: - **n_meshes** (*int*) -- Number of meshes in the file. + - **ids** (*int[]*) -- User-defined unique ID of each mesh. + +**/meshes/mesh /** + +Please see the section on **/tallies/meshes/** in the :doc:`statepoint`. diff --git a/docs/source/license.rst b/docs/source/license.rst index 9d2638bfb3..0a90a74413 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -4,7 +4,7 @@ License Agreement ================= -Copyright © 2011-2022 Massachusetts Institute of Technology, UChicago Argonne +Copyright © 2011-2025 Massachusetts Institute of Technology, UChicago Argonne LLC, and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/docs/source/methods/charged_particles_physics.rst b/docs/source/methods/charged_particles_physics.rst new file mode 100644 index 0000000000..5d763074fd --- /dev/null +++ b/docs/source/methods/charged_particles_physics.rst @@ -0,0 +1,362 @@ +.. _methods_charged_particle_physics: + +======================== +Charged Particle Physics +======================== + +OpenMC neglects the spatial transport of charged particles (electrons and +positrons), assuming they deposit all their energy locally and produce +bremsstrahlung photons at their birth location. This approximation, called +thick-target bremsstrahlung (TTB) approximation is justified by the fact that +charged particles have much shorter stopping ranges compared to neutrons and +photons, especially in high-density materials. + +----------------------------- +Charged Particle Interactions +----------------------------- + +Bremsstrahlung +-------------- + +When a charged particle is decelerated in the field of an atom, some of its +kinetic energy is converted into electromagnetic radiation known as +bremsstrahlung, or 'braking radiation'. In each event, an electron or positron +with kinetic energy :math:`T` generates a photon with an energy :math:`E` +between :math:`0` and :math:`T`. Bremsstrahlung is described by a cross section +that is differential in photon energy, in the direction of the emitted photon, +and in the final direction of the charged particle. However, in Monte Carlo +simulations it is typical to integrate over the angular variables to obtain a +single differential cross section with respect to photon energy, which is often +expressed in the form + +.. math:: + :label: bremsstrahlung-dcs + + \frac{d\sigma_{\text{br}}}{dE} = \frac{Z^2}{\beta^2} \frac{1}{E} + \chi(Z, T, \kappa), + +where :math:`\kappa = E/T` is the reduced photon energy and :math:`\chi(Z, T, +\kappa)` is the scaled bremsstrahlung cross section, which is experimentally +measured. + +Because electrons are attracted to atomic nuclei whereas positrons are +repulsed, the cross section for positrons is smaller, though it approaches that +of electrons in the high energy limit. To obtain the positron cross section, we +multiply :eq:`bremsstrahlung-dcs` by the :math:`\kappa`-independent factor used +in Salvat_, + +.. math:: + :label: positron-factor + + \begin{aligned} + F_{\text{p}}(Z,T) = + & 1 - \text{exp}(-1.2359\times 10^{-1}t + 6.1274\times 10^{-2}t^2 - 3.1516\times 10^{-2}t^3 \\ + & + 7.7446\times 10^{-3}t^4 - 1.0595\times 10^{-3}t^5 + 7.0568\times 10^{-5}t^6 \\ + & - 1.8080\times 10^{-6}t^7), + \end{aligned} + +where + +.. math:: + :label: positron-factor-t + + t = \ln\left(1 + \frac{10^6}{Z^2}\frac{T}{\text{m}_\text{e}c^2} \right). + +:math:`F_{\text{p}}(Z,T)` is the ratio of the radiative stopping powers for +positrons and electrons. Stopping power describes the average energy loss per +unit path length of a charged particle as it passes through matter: + +.. math:: + :label: stopping-power + + -\frac{dT}{ds} = n \int E \frac{d\sigma}{dE} dE \equiv S(T), + +where :math:`n` is the number density of the material and :math:`d\sigma/dE` is +the cross section differential in energy loss. The total stopping power +:math:`S(T)` can be separated into two components: the radiative stopping +power :math:`S_{\text{rad}}(T)`, which refers to energy loss due to +bremsstrahlung, and the collision stopping power :math:`S_{\text{col}}(T)`, +which refers to the energy loss due to inelastic collisions with bound +electrons in the material that result in ionization and excitation. The +radiative stopping power for electrons is given by + +.. math:: + :label: radiative-stopping-power + + S_{\text{rad}}(T) = n \frac{Z^2}{\beta^2} T \int_0^1 \chi(Z,T,\kappa) + d\kappa. + + +To obtain the radiative stopping power for positrons, +:eq:`radiative-stopping-power` is multiplied by :eq:`positron-factor`. + +While the models for photon interactions with matter described above can safely +assume interactions occur with free atoms, sampling the target atom based on +the macroscopic cross sections, molecular effects cannot necessarily be +disregarded for charged particle treatment. For compounds and mixtures, the +bremsstrahlung cross section is calculated using Bragg's additivity rule as + +.. math:: + :label: material-bremsstrahlung-dcs + + \frac{d\sigma_{\text{br}}}{dE} = \frac{1}{\beta^2 E} \sum_i \gamma_i Z^2_i + \chi(Z_i, T, \kappa), + +where the sum is over the constituent elements and :math:`\gamma_i` is the +atomic fraction of the :math:`i`-th element. Similarly, the radiative stopping +power is calculated using Bragg's additivity rule as + +.. math:: + :label: material-radiative-stopping-power + + S_{\text{rad}}(T) = \sum_i w_i S_{\text{rad},i}(T), + +where :math:`w_i` is the mass fraction of the :math:`i`-th element and +:math:`S_{\text{rad},i}(T)` is found for element :math:`i` using +:eq:`radiative-stopping-power`. The collision stopping power, however, is a +function of certain quantities such as the mean excitation energy :math:`I` and +the density effect correction :math:`\delta_F` that depend on molecular +properties. These quantities cannot simply be summed over constituent elements +in a compound, but should instead be calculated for the material. The Bethe +formula can be used to find the collision stopping power of the material: + +.. math:: + :label: material-collision-stopping-power + + S_{\text{col}}(T) = \frac{2 \pi r_e^2 m_e c^2}{\beta^2} N_A \frac{Z}{A_M} + [\ln(T^2/I^2) + \ln(1 + \tau/2) + F(\tau) - \delta_F(T)], + +where :math:`N_A` is Avogadro's number, :math:`A_M` is the molar mass, +:math:`\tau = T/m_e`, and :math:`F(\tau)` depends on the particle type. For +electrons, + +.. math:: + :label: F-electron + + F_{-}(\tau) = (1 - \beta^2)[1 + \tau^2/8 - (2\tau + 1) \ln2], + +while for positrons + +.. math:: + :label: F-positron + + F_{+}(\tau) = 2\ln2 - (\beta^2/12)[23 + 14/(\tau + 2) + 10/(\tau + 2)^2 + + 4/(\tau + 2)^3]. + +The density effect correction :math:`\delta_F` takes into account the reduction +of the collision stopping power due to the polarization of the material the +charged particle is passing through by the electric field of the particle. +It can be evaluated using the method described by Sternheimer_, where the +equation for :math:`\delta_F` is + +.. math:: + :label: density-effect-correction + + \delta_F(\beta) = \sum_{i=1}^n f_i \ln[(l_i^2 + l^2)/l_i^2] - + l^2(1-\beta^2). + +Here, :math:`f_i` is the oscillator strength of the :math:`i`-th transition, +given by :math:`f_i = n_i/Z`, where :math:`n_i` is the number of electrons in +the :math:`i`-th subshell. The frequency :math:`l` is the solution of the +equation + +.. math:: + :label: density-effect-l + + \frac{1}{\beta^2} - 1 = \sum_{i=1}^{n} \frac{f_i}{\bar{\nu}_i^2 + l^2}, + +where :math:`\bar{v}_i` is defined as + +.. math:: + :label: density-effect-nubar + + \bar{\nu}_i = h\nu_i \rho / h\nu_p. + +The plasma energy :math:`h\nu_p` of the medium is given by + +.. math:: + :label: plasma-frequency + + h\nu_p = \sqrt{\frac{(hc)^2 r_e \rho_m N_A Z}{\pi A}}, + +where :math:`A` is the atomic weight and :math:`\rho_m` is the density of the +material. In :eq:`density-effect-nubar`, :math:`h\nu_i` is the oscillator +energy, and :math:`\rho` is an adjustment factor introduced to give agreement +between the experimental values of the oscillator energies and the mean +excitation energy. The :math:`l_i` in :eq:`density-effect-correction` are +defined as + +.. math:: + :label: density-effect-li + + \begin{aligned} + l_i &= (\bar{\nu}_i^2 + 2/3f_i)^{1/2} ~~~~&\text{for}~~ \bar{\nu}_i > 0 \\ + l_n &= f_n^{1/2} ~~~~&\text{for}~~ \bar{\nu}_n = 0, + \end{aligned} + +where the second case applies to conduction electrons. For a conductor, +:math:`f_n` is given by :math:`n_c/Z`, where :math:`n_c` is the effective +number of conduction electrons, and :math:`v_n = 0`. The adjustment factor +:math:`\rho` is determined using the equation for the mean excitation energy: + +.. math:: + :label: mean-excitation-energy + + \ln I = \sum_{i=1}^{n-1} f_i \ln[(h\nu_i\rho)^2 + 2/3f_i(h\nu_p)^2]^{1/2} + + f_n \ln (h\nu_pf_n^{1/2}). + +.. _ttb: + + +Thick-Target Bremsstrahlung Approximation ++++++++++++++++++++++++++++++++++++++++++ + +Since charged particles lose their energy on a much shorter distance scale than +neutral particles, not much error should be introduced by neglecting to +transport electrons. However, the bremsstrahlung emitted from high energy +electrons and positrons can travel far from the interaction site. Thus, even +without a full electron transport mode it is necessary to model bremsstrahlung. +We use a thick-target bremsstrahlung (TTB) approximation based on the models in +Salvat_ and Kaltiaisenaho_ for generating bremsstrahlung photons, which assumes +the charged particle loses all its energy in a single homogeneous material +region. + +To model bremsstrahlung using the TTB approximation, we need to know the number +of photons emitted by the charged particle and the energy distribution of the +photons. These quantities can be calculated using the continuous slowing down +approximation (CSDA). The CSDA assumes charged particles lose energy +continuously along their trajectory with a rate of energy loss equal to the +total stopping power, ignoring fluctuations in the energy loss. The +approximation is useful for expressing average quantities that describe how +charged particles slow down in matter. For example, the CSDA range approximates +the average path length a charged particle travels as it slows to rest: + +.. math:: + :label: csda-range + + R(T) = \int^T_0 \frac{dT'}{S(T')}. + +Actual path lengths will fluctuate around :math:`R(T)`. The average number of +photons emitted per unit path length is given by the inverse bremsstrahlung +mean free path: + +.. math:: + :label: inverse-bremsstrahlung-mfp + + \lambda_{\text{br}}^{-1}(T,E_{\text{cut}}) + = n\int_{E_{\text{cut}}}^T\frac{d\sigma_{\text{br}}}{dE}dE + = n\frac{Z^2}{\beta^2}\int_{\kappa_{\text{cut}}}^1\frac{1}{\kappa} + \chi(Z,T,\kappa)d\kappa. + +The lower limit of the integral in :eq:`inverse-bremsstrahlung-mfp` is non-zero +because the bremsstrahlung differential cross section diverges for small photon +energies but is finite for photon energies above some cutoff energy +:math:`E_{\text{cut}}`. The mean free path +:math:`\lambda_{\text{br}}^{-1}(T,E_{\text{cut}})` is used to calculate the +photon number yield, defined as the average number of photons emitted with +energy greater than :math:`E_{\text{cut}}` as the charged particle slows down +from energy :math:`T` to :math:`E_{\text{cut}}`. The photon number yield is +given by + +.. math:: + :label: photon-number-yield + + Y(T,E_{\text{cut}}) = \int^{R(T)}_{R(E_{\text{cut}})} + \lambda_{\text{br}}^{-1}(T',E_{\text{cut}})ds = \int_{E_{\text{cut}}}^T + \frac{\lambda_{\text{br}}^{-1}(T',E_{\text{cut}})}{S(T')}dT'. + +:math:`Y(T,E_{\text{cut}})` can be used to construct the energy spectrum of +bremsstrahlung photons: the number of photons created with energy between +:math:`E_1` and :math:`E_2` by a charged particle with initial kinetic energy +:math:`T` as it comes to rest is given by :math:`Y(T,E_1) - Y(T,E_2)`. + +To simulate the emission of bremsstrahlung photons, the total stopping power +and bremsstrahlung differential cross section for positrons and electrons must +be calculated for a given material using :eq:`material-bremsstrahlung-dcs` and +:eq:`material-radiative-stopping-power`. These quantities are used to build the +tabulated bremsstrahlung energy PDF and CDF for that material for each incident +energy :math:`T_k` on the energy grid. The following algorithm is then applied +to sample the photon energies: + +1. For an incident charged particle with energy :math:`T`, sample the number of + emitted photons as + + .. math:: + + N = \lfloor Y(T,E_{\text{cut}}) + \xi_1 \rfloor. + +2. Rather than interpolate the PDF between indices :math:`k` and :math:`k+1` + for which :math:`T_k < T < T_{k+1}`, which is computationally expensive, use + the composition method and sample from the PDF at either :math:`k` or + :math:`k+1`. Using linear interpolation on a logarithmic scale, the PDF can + be expressed as + + .. math:: + + p_{\text{br}}(T,E) = \pi_k p_{\text{br}}(T_k,E) + \pi_{k+1} + p_{\text{br}}(T_{k+1},E), + + where the interpolation weights are + + .. math:: + + \pi_k = \frac{\ln T_{k+1} - \ln T}{\ln T_{k+1} - \ln T_k},~~~ + \pi_{k+1} = \frac{\ln T - \ln T_k}{\ln T_{k+1} - \ln T_k}. + + Sample either the index :math:`i = k` or :math:`i = k+1` according to the + point probabilities :math:`\pi_{k}` and :math:`\pi_{k+1}`. + +3. Determine the maximum value of the CDF :math:`P_{\text{br,max}}`. + +3. Sample the photon energies using the inverse transform method with the + tabulated CDF :math:`P_{\text{br}}(T_i, E)` i.e., + + .. math:: + + E = E_j \left[ (1 + a_j) \frac{\xi_2 P_{\text{br,max}} - + P_{\text{br}}(T_i, E_j)} {E_j p_{\text{br}}(T_i, E_j)} + 1 + \right]^{\frac{1}{1 + a_j}} + + where the interpolation factor :math:`a_j` is given by + + .. math:: + + a_j = \frac{\ln p_{\text{br}}(T_i,E_{j+1}) - \ln p_{\text{br}}(T_i,E_j)} + {\ln E_{j+1} - \ln E_j} + + and :math:`P_{\text{br}}(T_i, E_j) \le \xi_2 P_{\text{br,max}} \le + P_{\text{br}}(T_i, E_{j+1})`. + +We ignore the range of the electron or positron, i.e., the bremsstrahlung +photons are produced in the same location that the charged particle was +created. The direction of the photons is assumed to be the same as the +direction of the incident charged particle, which is a reasonable approximation +at higher energies when the bremsstrahlung radiation is emitted at small +angles. + + +Electron-Positron Annihilation +------------------------------ + +When a positron collides with an electron, both particles are annihilated and +generally two photons with equal energy are created. If the kinetic energy of +the positron is high enough, the two photons can have different energies, and +the higher-energy photon is emitted preferentially in the direction of flight +of the positron. It is also possible to produce a single photon if the +interaction occurs with a bound electron, and in some cases three (or, rarely, +even more) photons can be emitted. However, the annihilation cross section is +largest for low-energy positrons, and as the positron energy decreases, the +angular distribution of the emitted photons becomes isotropic. + +In OpenMC, we assume the most likely case in which a low-energy positron (which +has already lost most of its energy to bremsstrahlung radiation) interacts with +an electron which is free and at rest. Two photons with energy equal to the +electron rest mass energy :math:`m_e c^2 = 0.511` MeV are emitted isotropically +in opposite directions. + + +.. _Kaltiaisenaho: https://aaltodoc.aalto.fi/bitstream/handle/123456789/21004/master_Kaltiaisenaho_Toni_2016.pdf + +.. _Salvat: https://doi.org/10.1787/32da5043-en + +.. _Sternheimer: https://doi.org/10.1103/PhysRevB.26.6067 diff --git a/docs/source/methods/cmfd.rst b/docs/source/methods/cmfd.rst index 344a4cc1a6..66d545ac18 100644 --- a/docs/source/methods/cmfd.rst +++ b/docs/source/methods/cmfd.rst @@ -266,11 +266,11 @@ and eq. :eq:`eq_cell_bound` can be written in this generic form, The parameter :math:`\widetilde{D}_{l,m,n}^{u,g}` represents the linear coupling term between current and flux. These current relationships can be -sustituted into eq. :eq:`eq_neut_bal` to produce a linear system of multigroup +substituted into eq. :eq:`eq_neut_bal` to produce a linear system of multigroup diffusion equations for each spatial cell and energy group. However, a solution to these equations is not consistent with a higher order transport solution unless equivalence factors are present. This is because both the diffusion -approximation, governed by Fick's Law, and spatial trunction error will produce +approximation, governed by Fick's Law, and spatial truncation error will produce differences. Therefore, a nonlinear parameter, :math:`\widehat{D}_{l,m,n}^{u,g}`, is added to eqs. :eq:`eq_cell_cell` and :eq:`eq_cell_bound`. These equations are, respectively, diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index c6d1f44f44..a66abb3ed4 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -9,7 +9,7 @@ Continuous-Energy Data ---------------------- In OpenMC, the data governing the interaction of neutrons with various nuclei -for continous-energy problems are represented using an HDF5 format that can be +for continuous-energy problems are represented using an HDF5 format that can be produced by converting files in the ACE format, which is used by MCNP_ and Serpent_. ACE-format data can be generated with the NJOY_ nuclear data processing system, which converts raw `ENDF/B data`_ into linearly-interpolable @@ -178,6 +178,27 @@ been selected. There are three methods available: section data is loaded for a single temperature and is used in the unresolved resonance and fast energy ranges. +------------------ +NCrystal materials +------------------ + +As an alternative of the standard thermal scattering treatment using +:math:`S(\alpha,\beta)` tables, OpenMC allows to create materials using +NCrystal_. In addition to the regular thermal elastic, and thermal inelastic +processes, NCrystal allows the generation of models for materials that cannot +currently included in ACE files such as oriented single crystals (see the +`NCrystal paper`_), and further extend the physics `using plugins`_. Thermal +scattering kernels are generated on the fly from dynamic and structural data, or +loaded from :math:`S(\alpha,\beta)` tables converted from ENDF6 evaluations. +These kernels are sampled in a direct way using a fast `rejection algorithm`_ +that does not require previous processing. A `large library`_ of materials is +already included in the NCrystal distribution, and new materials can be easily +defined from scratch in the `NCMAT format`_ or `combining existing files`_. + +The compositions of the materials defined in NCrystal are passed on to OpenMC +all other reactions except for thermal neutron scattering are handled by +continuous energy ACE libraries. + ---------------- Multi-Group Data ---------------- @@ -187,9 +208,10 @@ are represented using a multi-group library format specific to the OpenMC code. The format is described in the :ref:`mgxs_lib_spec`. The data itself can be prepared via traditional paths or directly from a continuous-energy OpenMC calculation by use of the Python API as is shown in an `example notebook -<../examples/mg-mode-part-i.ipynb>`_. This multi-group library consists of -meta-data (such as the energy group structure) and multiple `xsdata` objects -which contains the required microscopic or macroscopic multi-group data. +`_. +This multi-group library consists of meta-data (such as the energy group +structure) and multiple `xsdata` objects which contains the required microscopic +or macroscopic multi-group data. At a minimum, the library must contain the absorption cross section (:math:`\sigma_{a,g}`) and a scattering matrix. If the problem is an eigenvalue @@ -268,13 +290,20 @@ scattering information in the water while the fuel can be simulated with linear or even isotropic scattering. .. _logarithmic mapping technique: - https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf + https://mcnp.lanl.gov/pdf_files/TechReport_2014_LANL_LA-UR-14-24530_Brown.pdf .. _Hwang: https://doi.org/10.13182/NSE87-A16381 .. _Josey: https://doi.org/10.1016/j.jcp.2015.08.013 .. _WMP Library: https://github.com/mit-crpg/WMP_Library .. _MCNP: https://mcnp.lanl.gov -.. _Serpent: http://montecarlo.vtt.fi -.. _NJOY: https://www.njoy21.io/NJOY21/ -.. _ENDF/B data: https://www.nndc.bnl.gov/endf/b8.0/ +.. _Serpent: https://serpent.vtt.fi +.. _NJOY: https://www.njoy21.io/ +.. _ENDF/B data: https://www.nndc.bnl.gov/endf-b8.0/ .. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019 -.. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package +.. _algorithms: http://ab-initio.mit.edu/faddeeva/ +.. _NCrystal: https://github.com/mctools/ncrystal +.. _NCrystal paper: https://doi.org/10.1016/j.cpc.2019.07.015 +.. _using plugins: https://doi.org/10.1016/j.cpc.2021.108082 +.. _rejection algorithm: https://doi.org/10.1016/j.jcp.2018.11.043 +.. _large library: https://github.com/mctools/ncrystal/wiki/Data-library +.. _NCMAT format: https://github.com/mctools/ncrystal/wiki/NCMAT-format +.. _combining existing files: https://github.com/mctools/ncrystal/wiki/Announcement-Release3.0.0#2-multiphase-materials diff --git a/docs/source/methods/depletion.rst b/docs/source/methods/depletion.rst index dce1f2503e..edcf2c3f53 100644 --- a/docs/source/methods/depletion.rst +++ b/docs/source/methods/depletion.rst @@ -103,17 +103,18 @@ integrate over the entire timestep. Our aim here is not to exhaustively describe all integration methods but rather to give a few examples that elucidate the main considerations one must take into account when choosing a method. Generally, there is a tradeoff between the -accuracy of the method and its computational expense. The expense is driven -almost entirely by the time to compute a transport solution, i.e., to evaluate -:math:`\mathbf{A}` for a given :math:`\mathbf{n}`. Thus, the cost of a method -scales with the number of :math:`\mathbf{A}` evaluations that are performed per -timestep. On the other hand, methods that require more evaluations generally -achieve higher accuracy. The predictor method only requires one evaluation and -its error converges as :math:`\mathcal{O}(h)`. The CE/CM method requires two -evaluations and is thus twice as expensive as the predictor method, but achieves -an error of :math:`\mathcal{O}(h^2)`. An exhaustive description of time -integration methods and their merits can be found in the `thesis of Colin Josey -`_. +accuracy of the method and its computational expense. In the case of +transport-coupled depletion, the expense is driven almost entirely by the time +to compute a transport solution, i.e., to evaluate :math:`\mathbf{A}` for a +given :math:`\mathbf{n}`. Thus, the cost of a method scales with the number of +:math:`\mathbf{A}` evaluations that are performed per timestep. On the other +hand, methods that require more evaluations generally achieve higher accuracy. +The predictor method only requires one evaluation and its error converges as +:math:`\mathcal{O}(h)`. The CE/CM method requires two evaluations and is thus +twice as expensive as the predictor method, but achieves an error of +:math:`\mathcal{O}(h^2)`. An exhaustive description of time integration methods +and their merits can be found in the `thesis of Colin Josey +`_. OpenMC does not rely on a single time integration method but rather has several classes that implement different algorithms. For example, the @@ -169,12 +170,14 @@ Data Considerations In principle, solving Eq. :eq:`depletion-matrix` using CRAM is fairly simple: just construct the burnup matrix at various times and solve a set of sparse -linear systems. However, constructing the burnup matrix itself involves not only -solving the transport equation to estimate transmutation reaction rates but also -a series of choices about what data to include. In OpenMC, the burnup matrix is -constructed based on data inside of a *depletion chain* file, which includes -fundamental data gathered from ENDF incident neutron, decay, and fission product -yield sublibraries. For each nuclide, this file includes: +linear systems. However, constructing the burnup matrix itself involves not +only solving the transport equation to estimate transmutation reaction rates +(in the case of transport-coupled depletion) or to obtain microscopic cross +sections (in the case of transport-independent depletion), but also a series of +choices about what data to include. In OpenMC, the burnup matrix is constructed +based on data inside of a *depletion chain* file, which includes fundamental +data gathered from ENDF incident neutron, decay, and fission product yield +sublibraries. For each nuclide, this file includes: - What transmutation reactions are possible, their Q values, and their products; - If a nuclide is not stable, what decay modes are possible, their branching @@ -185,9 +188,12 @@ yield sublibraries. For each nuclide, this file includes: Transmutation Reactions ----------------------- -OpenMC will setup tallies in a problem based on what transmutation reactions are -available in a depletion chain file, so any arbitrary number of transmutation -reactions can be tracked. The pregenerated chain files that are available on +In transport-coupled depletion, OpenMC will setup tallies in a problem based on +what transmutation reactions are available in a depletion chain file, so any +arbitrary number of transmutation reactions can be tracked. In +transport-independent depletion, OpenMC will calculate reaction rates for every +reaction that is present in both the available cross sections and the depletion +chain file. The pregenerated chain files that are available on https://openmc.org include the following transmutation reactions: fission, (n,\ :math:`\gamma`\ ), (n,2n), (n,3n), (n,4n), (n,p), and (n,\ :math:`\alpha`\ ). @@ -202,11 +208,12 @@ accurately model the branching of the capture reaction in Am241. This is complicated by the fact that the branching ratio may depend on the incident neutron energy causing capture. -OpenMC does not currently allow energy-dependent capture branching ratios. -However, the depletion chain file does allow a transmutation reaction to be -listed multiple times with different branching ratios resulting in different -products. Spectrum-averaged capture branching ratios have been computed in LWR -and SFR spectra and are available at https://openmc.org/depletion-chains. +OpenMC's transport solver does not currently allow energy-dependent capture +branching ratios. However, the depletion chain file does allow a transmutation +reaction to be listed multiple times with different branching ratios resulting +in different products. Spectrum-averaged capture branching ratios have been +computed in LWR and SFR spectra and are available at +https://openmc.org/depletion-chains. Fission Product Yields ---------------------- @@ -217,26 +224,31 @@ energies. It is an open question as to what the best way to handle this energy dependence is. OpenMC includes three methods for treating the energy dependence of FPY: -1. Use FPY data corresponding to a specified energy. +1. Use FPY data corresponding to a specified energy. This is used by default in + both transport-coupled and transport-independent depletion. 2. Tally fission rates above and below a specified cutoff energy. Assume that all fissions below the cutoff energy correspond to thermal FPY data and all - fission above the cutoff energy correspond to fast FPY data. + fission above the cutoff energy correspond to fast FPY data. Only applicable + to transport-coupled depletion. 3. Compute the average energy at which fission events occur and use an effective FPY by linearly interpolating between FPY provided at neighboring energies. + Only applicable to transport-coupled depletion. -The method can be selected through the ``fission_yield_mode`` argument to the -:class:`openmc.deplete.Operator` constructor. +The method for transport-coupled depletion can be selected through the +``fission_yield_mode`` argument to the :class:`openmc.deplete.CoupledOperator` +constructor. Power Normalization ------------------- -The reaction rates provided OpenMC are given in units of reactions per source -particle. For depletion, it is necessary to compute an absolute reaction rate in -reactions per second. To do so, the reaction rates are normalized based on a -specified power. A complete description of how this normalization can be -performed is described in :ref:`usersguide_tally_normalization`. Here, we simply -note that the main depletion class, :class:`openmc.deplete.Operator`, allows the -user to choose one of two methods for estimating the heating rate, including: +In transport-coupled depletion, the reaction rates provided OpenMC are given in +units of reactions per source particle. For depletion, it is necessary to +compute an absolute reaction rate in reactions per second. To do so, the +reaction rates are normalized based on a specified power. A complete +description of how this normalization can be performed is described in +:ref:`usersguide_tally_normalization`. Here, we simply note that the main +depletion class, :class:`openmc.deplete.CoupledOperator`, allows the user to +choose one of two methods for estimating the heating rate, including: 1. Using fixed Q values from a depletion chain file (useful for comparisons to other codes that use fixed Q values), or @@ -244,4 +256,86 @@ user to choose one of two methods for estimating the heating rate, including: energy-dependent estimate of the true heating rate. The method for normalization can be chosen through the ``normalization_mode`` -argument to the :class:`openmc.deplete.Operator` class. +argument to the :class:`openmc.deplete.CoupledOperator` class. + +-------------- +Transfer Rates +-------------- + +OpenMC allows continuous removal or feed of nuclides by adding an +extra transfer rate term to the depletion matrix. An application of this feature +is the chemical processing of Molten Salt Reactors (MSRs), where one can +model the removal of fission products or feeding fresh fuel into the system. + +A transfer rate as defined here is the rate at which nuclides are +continuously removed/fed from/to a material. + +.. note:: + + A transfer rate can be positive or negative, indicating removal or feed + respectively. + +Mathematically, it can be thought of as an additional term :math:`\mathbf{T}` +in the depletion equation that is proportional to the nuclide density, which can be written as: + +.. math:: + + \begin{aligned}\frac{dN_i(t)}{dt} = &\underbrace{\sum\limits_j f_{j\rightarrow i} + \int_0^\infty dE \; \sigma_j (E,t) \phi(E,t) N_j(t) - \int_0^\infty dE \; \sigma_i(E,t) + \phi(E,t) N_i(t)}_\textbf{R} \\ + &+ \underbrace{\sum_j \left [ \lambda_{j\rightarrow i} N_j(t) - \lambda_{i\rightarrow j} N_i(t) \right ]}_\textbf{D} \\ + &- \underbrace{t_i N_i(t)}_\textbf{T} \end{aligned} + +where the reaction term :math:`\mathbf{R}`, the decay term :math:`\mathbf{D}` +and the new transfer term :math:`\mathbf{T}` have been grouped together so that +:math:`\mathbf{A} = \mathbf{R}+\mathbf{D}-\mathbf{T}`. +The transfer rate coefficient :math:`t_i` defines the continuous transfer of the +nuclide :math:`i`, which behaves similar to radioactive decay. +:math:`t_i` can also be defined as the reciprocal of a cycle time +:math:`T_{cyc}`, intended as the time needed to process the whole inventory. + +Note that this formulation assumes homogeneous distribution of nuclide +:math:`i` throughout the material. + +A more rigorous description of removal rate and its implementation can be found +in the paper by `Hombourger +`_. + +The resulting burnup matrix can be solved with the same integration algorithms +that are used in the absence of the transfer term. + +.. note:: + + If no ``destination_material`` is specified, nuclides that are removed + or fed will not be tracked afterwards. + +Coupling materials +------------------ + +To keep track of removed nuclides or to feed nuclides from one depletable material +to another, the respective depletion equations have to be coupled. This can be +achieved by defining one block matrix, with diagonal blocks corresponding to +depletion matrices :math:`\mathbf{A_{ii}}`, where the index :math:`i` indicates +the depletable material id, and off-diagonal blocks corresponding to inter-material +coupling matrices :math:`\mathbf{T_{ij}}`, positioned so that that the indices :math:`i` and +:math:`j` indicate the nuclides receiving and losing materials, respectively. +The nuclide vectors are assembled together in one single vector and the resulting +system is solved with the same integration algorithms seen before. + +As an example, consider the case of two depletable materials and one +transfer defined from material 1 to material 2. The final system will look like: + +.. math:: + + \begin{aligned}\frac{d}{dt}\begin{pmatrix}\vec{N_1}\\ \vec{N_2}\end{pmatrix} &= + \begin{pmatrix}\mathbf{A_{11}} & \mathbf{0}\\ \mathbf{T_{21}} & \mathbf{A_{22 }} + \end{pmatrix} \begin{pmatrix}\vec{N_1}\\ \vec{N_2}\end{pmatrix} \end{aligned} + +where: + +:math:`\mathbf{A_{11}} = \mathbf{R_{11}}+\mathbf{D_{11}}-\mathbf{T_{21}}`, and + +:math:`\mathbf{A_{22}} = \mathbf{R_{22}}+\mathbf{D_{22}}`. + +Note that mass conservation is guaranteed by transferring the number +of atoms directly. diff --git a/docs/source/methods/eigenvalue.rst b/docs/source/methods/eigenvalue.rst index 41bf865492..8abcc09574 100644 --- a/docs/source/methods/eigenvalue.rst +++ b/docs/source/methods/eigenvalue.rst @@ -55,15 +55,17 @@ in :ref:`fission-bank-algorithms`. Source Convergence Issues ------------------------- +.. _methods-shannon-entropy: + Diagnosing Convergence with Shannon Entropy ------------------------------------------- As discussed earlier, it is necessary to converge both :math:`k_{eff}` and the source distribution before any tallies can begin. Moreover, the convergence rate -of the source distribution is in general slower than that of -:math:`k_{eff}`. One should thus examine not only the convergence of -:math:`k_{eff}` but also the convergence of the source distribution in order to -make decisions on when to start active batches. +of the source distribution is in general slower than that of :math:`k_{eff}`. +One should thus examine not only the convergence of :math:`k_{eff}` but also the +convergence of the source distribution in order to make decisions on when to +start active batches. However, the representation of the source distribution makes it a bit more difficult to analyze its convergence. Since :math:`k_{eff}` is a scalar @@ -108,6 +110,13 @@ at plots of :math:`k_{eff}` and the Shannon entropy. A number of methods have been proposed (see e.g. [Romano]_, [Ueki]_), but each of these is not without problems. +Shannon entropy is calculated differently for the random ray solver, as +described :ref:`in the random ray theory section +`. Additionally, as the Shannon entropy only +serves as a diagnostic tool for convergence of the fission source distribution, +there is currently no diagnostic to determine if the scattering source +distribution in random ray is converged. + --------------------------- Uniform Fission Site Method --------------------------- @@ -142,7 +151,7 @@ than unity. By ensuring that the expected number of fission sites in each mesh cell is constant, the collision density across all cells, and hence the variance of tallies, is more uniform than it would be otherwise. -.. _Shannon entropy: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-06-3737.pdf +.. _Shannon entropy: https://mcnp.lanl.gov/pdf_files/TechReport_2006_LANL_LA-UR-06-3737_Brown.pdf .. [Lieberoth] J. Lieberoth, "A Monte Carlo Technique to Solve the Static Eigenvalue Problem of the Boltzmann Transport Equation," *Nukleonik*, **11**, diff --git a/docs/source/methods/energy_deposition.rst b/docs/source/methods/energy_deposition.rst index fbcccc26b2..c43ee64ac4 100644 --- a/docs/source/methods/energy_deposition.rst +++ b/docs/source/methods/energy_deposition.rst @@ -7,7 +7,7 @@ Heating and Energy Deposition As particles traverse a problem, some portion of their energy is deposited at collision sites. This energy is deposited when charged particles, including electrons and recoil nuclei, undergo electromagnetic interactions with -surrounding electons and ions. The information describing how much energy +surrounding electrons and ions. The information describing how much energy is deposited for a specific reaction is referred to as "heating numbers" and can be computed using a program like NJOY with the ``heatr`` module. @@ -25,19 +25,38 @@ KERMA (Kinetic Energy Release in Materials) [Mack97]_ coefficients for reaction :math:`\times` cross-section (e.g., eV-barn) and can be used much like a reaction cross section for the purpose of tallying energy deposition. -KERMA coefficients can be computed using the energy-balance method with -a nuclear data processing code like NJOY, which performs the following -iteration over all reactions :math:`r` for all isotopes :math:`i` -requested +KERMA coefficients can be computed using the energy-balance method with a +nuclear data processing code like NJOY, which estimates the KERMA coefficients +using the following equation: .. math:: - k_{i, r}(E) = \left(E + Q_{i, r} - \bar{E}_{i, r, n} + k_{i, r}(E) = \left(E + Q_{i, r} - \sum\limits_x \bar{E}_{i, r, x} + \right)\sigma_{i, r}(E), + +where the summation is over each secondary particle type :math:`x`. This +equation states that the energy deposited is equal to the energy of the incident +particle plus the reaction :math:`Q` value less the energy of secondary +particles that are transported away from the reaction site. For neutron +interactions, the energy-balance KERMA coefficient is + +.. math:: + + k_{i, r}(E) = \left(E + Q_{i, r} - \sum\limits_x \bar{E}_{i, r, n} - \bar{E}_{i, r, \gamma}\right)\sigma_{i, r}(E), -removing the energy of neutral particles (neutrons and photons) that are -transported away from the reaction site :math:`\bar{E}`, and the reaction -:math:`Q` value. +where :math:`\bar{E}_{i, r, n}` is the average energy of secondary neutrons and +:math:`\bar{E}_{i, r, \gamma}` is the average energy of secondary photons. For +photon and charged particle interactions the KERMA coefficient is + +.. math:: + :label: energy-balance-photon + + k_{i, r}(E) = \left(E + Q_{i, r} - \sum\limits_x \bar{E}_{i, r, x} + \right)\sigma_{i, r}(E). + +where the :math:`Q` value is zero for all interactions except for pair +production and positron annihilation. ------- Fission @@ -120,7 +139,7 @@ run with :math:`N918` reflecting fission heating computed from NJOY. This modified heating data is stored as the MT=901 reaction and will be scored if ``heating-local`` is included in :attr:`openmc.Tally.scores`. -Coupled neutron-photon transport +Coupled Neutron-Photon Transport -------------------------------- Here, OpenMC instructs ``heatr`` to assume that energy from photons is not @@ -138,6 +157,50 @@ Let :math:`N301` represent the total heating number returned from this This modified heating data is stored as the MT=301 reaction and will be scored if ``heating`` is included in :attr:`openmc.Tally.scores`. +Photons and Charged Particles +----------------------------- + +In OpenMC, energy deposition from photons or charged particles is scored using +the energy balance method based on Equation :eq:`energy-balance-photon`. Special +consideration is given to electrons and positrons as described below. + ++++++++++++++++++ +Charged Particles ++++++++++++++++++ + +OpenMC tracks photons interaction by interaction so the energy deposited in each +collision is easily attributed back to the nuclide and reaction for which the +photon interacted with. Charged particles (electrons and photons) aren't tracked +in the same way. For charged particles, OpenMC assumes that all their energy +(less the energy of bremsstrahlung radiation) is deposited in the material in +which they were born. In this way it is harder to trace how much energy should +be attributed in each nuclide. + +According to the CSDA approximation (see :ref:`ttb`) the energy deposited by a +charged particle with kinetic energy :math:`T` in the :math:`i`-th element can +be calculated as: + +.. math:: + + E_{i} = \int_{0}^{R(T)} w_{i}S_{\text{col,i}} dx + +where :math:`R(T)` is the CSDA range of the charged particle, +:math:`S_{\text{col},i}` is the collision stopping power of the charged particle +in the :math:`i`-th element and :math:`w_i` is the mass fraction of the +:math:`i`-th element. According to the Bethe formula the collision stopping +power of the :math:`i`-th element is proportional to :math:`Z_i/A_i`, so the +fractional collision stopping power from the :math:`i`-th element is: + +.. math:: + + \frac{w_{i}S_{\text{col},i}(T)}{S_{\text{col}}(T)} = + \frac{\frac{w_{i}Z_{i}}{A_{i}}}{\sum_{i}\frac{w_{i}Z_{i}}{A_{i}}} = + \frac{\gamma_i Z_{i}}{\sum_{i}\gamma_i Z_{i}}. + +where :math:`\gamma_i` is the atomic fraction of the :math:`i`-th element. +Therefore, the energy deposited by charged particles should be attributed to +a given element according to its fractional charge density. + ---------- References ---------- diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index b282ffdee3..05cda4b642 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -1066,5 +1066,5 @@ surface is known as in :ref:`reflection`. .. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry .. _surfaces: https://en.wikipedia.org/wiki/Surface .. _MCNP: https://mcnp.lanl.gov -.. _Serpent: http://montecarlo.vtt.fi +.. _Serpent: https://serpent.vtt.fi .. _Monte Carlo Performance benchmark: https://github.com/mit-crpg/benchmarks/tree/master/mc-performance/openmc diff --git a/docs/source/methods/index.rst b/docs/source/methods/index.rst index 59892ac273..121d04b1de 100644 --- a/docs/source/methods/index.rst +++ b/docs/source/methods/index.rst @@ -14,9 +14,12 @@ Theory and Methodology random_numbers neutron_physics photon_physics + charged_particles_physics tallies eigenvalue depletion energy_deposition parallelization cmfd + variance_reduction + random_ray diff --git a/docs/source/methods/introduction.rst b/docs/source/methods/introduction.rst index 924d4858de..eacdd70f52 100644 --- a/docs/source/methods/introduction.rst +++ b/docs/source/methods/introduction.rst @@ -45,7 +45,7 @@ following steps: - Initialize the pseudorandom number generator. - - Read the contiuous-energy or multi-group cross section data specified in + - Read the continuous-energy or multi-group cross section data specified in the problem. - If using a special energy grid treatment such as a union energy grid or diff --git a/docs/source/methods/neutron_physics.rst b/docs/source/methods/neutron_physics.rst index 7c1490647f..2b797e3dbc 100644 --- a/docs/source/methods/neutron_physics.rst +++ b/docs/source/methods/neutron_physics.rst @@ -91,7 +91,7 @@ inelastic scattering reactions. The specific multi-group scattering implementation is discussed in the :ref:`multi-group-scatter` section. Elastic scattering refers to the process by which a neutron scatters off a -nucleus and does not leave it in an excited. It is referred to as "elastic" +nucleus and does not leave it in an excited state. It is referred to as "elastic" because in the center-of-mass system, the neutron does not actually lose energy. However, in lab coordinates, the neutron does indeed lose energy. Elastic scattering can be treated exactly in a Monte Carlo code thanks @@ -182,7 +182,7 @@ Inelastic Scattering -------------------- Note that the multi-group mode makes no distinction between elastic or -inelastic scattering reactions. The spceific multi-group scattering +inelastic scattering reactions. The specific multi-group scattering implementation is discussed in the :ref:`multi-group-scatter` section. The major algorithms for inelastic scattering were described in previous @@ -290,7 +290,10 @@ create and store fission sites for the following generation. First, the average number of prompt and delayed neutrons must be determined to decide whether the secondary neutrons will be prompt or delayed. This is important because delayed neutrons have a markedly different spectrum from prompt neutrons, one that has a -lower average energy of emission. The total number of neutrons emitted +lower average energy of emission. Furthermore, in simulations where tracking +time of neutrons is important, we need to consider the emission time delay of +the secondary neutrons, which is dependent on the decay constant of the +delayed neutron precursor. The total number of neutrons emitted :math:`\nu_t` is given as a function of incident energy in the ENDF format. Two representations exist for :math:`\nu_t`. The first is a polynomial of order :math:`N` with coefficients :math:`c_0,c_1,\dots,c_N`. If :math:`\nu_t` has this @@ -306,8 +309,8 @@ interpolation law. The number of prompt neutrons released per fission event :math:`\nu_p` is also given as a function of incident energy and can be specified in a polynomial or tabular format. The number of delayed neutrons released per fission event :math:`\nu_d` can only be specified in a tabular -format. In practice, we only need to determine :math:`nu_t` and -:math:`nu_d`. Once these have been determined, we can calculated the delayed +format. In practice, we only need to determine :math:`\nu_t` and +:math:`\nu_d`. Once these have been determined, we can calculate the delayed neutron fraction .. math:: @@ -335,8 +338,14 @@ neutrons. Otherwise, we produce :math:`\lfloor \nu \rfloor + 1` neutrons. Then, for each fission site produced, we sample the outgoing angle and energy according to the algorithms given in :ref:`sample-angle` and :ref:`sample-energy` respectively. If the neutron is to be born delayed, then -there is an extra step of sampling a delayed neutron precursor group since they -each have an associated secondary energy distribution. +there is an extra step of sampling a delayed neutron precursor group to get the +associated secondary energy distribution and the decay constant +:math:`\lambda`, which is needed to sample the emission delay time :math:`t_d`: + +.. math:: + :label: sample-delay-time + + t_d = -\frac{\ln \xi}{\lambda}. The sampled outgoing angle and energy of fission neutrons along with the position of the collision site are stored in an array called the fission @@ -359,7 +368,7 @@ secondary energy and angle sampling. For a reaction with secondary products, it is necessary to determine the outgoing angle and energy of the products. For any reaction other than elastic and level inelastic scattering, the outgoing energy must be determined based on -tabulated or parameterized data. The `ENDF-6 Format `_ specifies a +tabulated or parameterized data. The `ENDF-6 Format`_ specifies a variety of ways that the secondary energy distribution can be represented. ENDF File 5 contains uncorrelated energy distribution whereas ENDF File 6 contains correlated energy-angle distributions. The ACE format specifies its own @@ -1403,7 +1412,7 @@ given analytically by .. math:: :label: coherent-elastic-angle - \mu = 1 - \frac{E_i}{E} + \mu = 1 - \frac{2E_i}{E} where :math:`E_i` is the energy of the Bragg edge that scattered the neutron. @@ -1416,8 +1425,7 @@ For incoherent elastic scattering, OpenMC has two methods for calculating the cosine of the angle of scattering. The first method uses the Debye-Waller integral, :math:`W'`, and the characteristic bound cross section as given directly in an ENDF-6 formatted file. In this case, the cosine of the angle of -scattering can be sampled by inverting equation 7.4 from the `ENDF-6 Format -Manual `_: +scattering can be sampled by inverting equation 7.4 from the `ENDF-6 Format`_: .. math:: :label: incoherent-elastic-mu-exact @@ -1744,19 +1752,19 @@ types. .. _Watt fission spectrum: https://doi.org/10.1103/PhysRev.87.1037 -.. _Foderaro: http://hdl.handle.net/1721.1/1716 +.. _Foderaro: https://dspace.mit.edu/handle/1721.1/1716 .. _OECD: https://www.oecd-nea.org/tools/abstract/detail/NEA-1792 .. _NJOY: https://www.njoy21.io/NJOY2016/ -.. _PREPRO: https://www-nds.iaea.org/ndspub/endf/prepro/ +.. _PREPRO: https://www-nds.iaea.org/public/endf/prepro/ -.. _endf102: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf +.. _ENDF-6 Format: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf -.. _Monte Carlo Sampler: https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-09721-MS +.. _Monte Carlo Sampler: https://mcnp.lanl.gov/pdf_files/TechReport_1983_LANL_LA-9721-MS_EverettCashwell.pdf -.. _LA-UR-14-27694: https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-14-27694 +.. _LA-UR-14-27694: https://www.osti.gov/biblio/1159204 .. _MC21: https://www.osti.gov/biblio/903083 @@ -1764,6 +1772,4 @@ types. .. _Sutton and Brown: https://www.osti.gov/biblio/307911 -.. _lectures: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-05-4983.pdf - -.. _MCNP Manual: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-03-1987.pdf +.. _lectures: https://mcnp.lanl.gov/pdf_files/TechReport_2005_LANL_LA-UR-05-4983_Brown.pdf diff --git a/docs/source/methods/parallelization.rst b/docs/source/methods/parallelization.rst index 29748807ab..87ac485903 100644 --- a/docs/source/methods/parallelization.rst +++ b/docs/source/methods/parallelization.rst @@ -293,7 +293,7 @@ Cost of Nearest Neighbor Algorithm ---------------------------------- With the communication cost of the traditional fission bank algorithm -quantified, we now proceed to discuss the communicatin cost of the proposed +quantified, we now proceed to discuss the communication cost of the proposed algorithm. Comparing the cost of communication of this algorithm with the traditional algorithm is not trivial due to fact that the cost will be a function of how many fission sites are sampled on each node. If each node @@ -398,7 +398,7 @@ equation :eq:`k-to-source`, we can relate the stochastic eigenvalue to the integral of the noise component of the source distribution as .. math:: - :label: noise-integeral + :label: noise-integral N\hat{k} = Nk + \sqrt{N} \int \hat{\epsilon}(\mathbf{r}) \: d\mathbf{r}. @@ -609,17 +609,17 @@ is actually independent of the number of nodes: .. _first paper: https://doi.org/10.2307/2280232 -.. _work of Forrest Brown: http://hdl.handle.net/2027.42/24996 +.. _work of Forrest Brown: https://deepblue.lib.umich.edu/handle/2027.42/24996 .. _Brissenden and Garlick: https://doi.org/10.1016/0306-4549(86)90095-2 -.. _MPICH: http://www.mpich.org +.. _MPICH: https://www.mpich.org .. _binomial tree: https://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf .. _Geary: https://doi.org/10.2307/2342070 -.. _Barnett: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.7772 +.. _Barnett: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.7772 .. _single-instruction multiple-data: https://en.wikipedia.org/wiki/SIMD diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index bc912b943d..d2bd3ac760 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -667,342 +667,6 @@ and Auger electrons: 5. Repeat from step 1 for vacancy left by the transition electron. -Electron-Positron Annihilation ------------------------------- - -When a positron collides with an electron, both particles are annihilated and -generally two photons with equal energy are created. If the kinetic energy of -the positron is high enough, the two photons can have different energies, and -the higher-energy photon is emitted preferentially in the direction of flight -of the positron. It is also possible to produce a single photon if the -interaction occurs with a bound electron, and in some cases three (or, rarely, -even more) photons can be emitted. However, the annihilation cross section is -largest for low-energy positrons, and as the positron energy decreases, the -angular distribution of the emitted photons becomes isotropic. - -In OpenMC, we assume the most likely case in which a low-energy positron (which -has already lost most of its energy to bremsstrahlung radiation) interacts with -an electron which is free and at rest. Two photons with energy equal to the -electron rest mass energy :math:`m_e c^2 = 0.511` MeV are emitted isotropically -in opposite directions. - -Bremsstrahlung --------------- - -When a charged particle is decelerated in the field of an atom, some of its -kinetic energy is converted into electromagnetic radiation known as -bremsstrahlung, or 'braking radiation'. In each event, an electron or positron -with kinetic energy :math:`T` generates a photon with an energy :math:`E` -between :math:`0` and :math:`T`. Bremsstrahlung is described by a cross section -that is differential in photon energy, in the direction of the emitted photon, -and in the final direction of the charged particle. However, in Monte Carlo -simulations it is typical to integrate over the angular variables to obtain a -single differential cross section with respect to photon energy, which is often -expressed in the form - -.. math:: - :label: bremsstrahlung-dcs - - \frac{d\sigma_{\text{br}}}{dE} = \frac{Z^2}{\beta^2} \frac{1}{E} - \chi(Z, T, \kappa), - -where :math:`\kappa = E/T` is the reduced photon energy and :math:`\chi(Z, T, -\kappa)` is the scaled bremsstrahlung cross section, which is experimentally -measured. - -Because electrons are attracted to atomic nuclei whereas positrons are -repulsed, the cross section for positrons is smaller, though it approaches that -of electrons in the high energy limit. To obtain the positron cross section, we -multiply :eq:`bremsstrahlung-dcs` by the :math:`\kappa`-independent factor used -in Salvat_, - -.. math:: - :label: positron-factor - - \begin{aligned} - F_{\text{p}}(Z,T) = - & 1 - \text{exp}(-1.2359\times 10^{-1}t + 6.1274\times 10^{-2}t^2 - 3.1516\times 10^{-2}t^3 \\ - & + 7.7446\times 10^{-3}t^4 - 1.0595\times 10^{-3}t^5 + 7.0568\times 10^{-5}t^6 \\ - & - 1.8080\times 10^{-6}t^7), - \end{aligned} - -where - -.. math:: - :label: positron-factor-t - - t = \ln\left(1 + \frac{10^6}{Z^2}\frac{T}{\text{m}_\text{e}c^2} \right). - -:math:`F_{\text{p}}(Z,T)` is the ratio of the radiative stopping powers for -positrons and electrons. Stopping power describes the average energy loss per -unit path length of a charged particle as it passes through matter: - -.. math:: - :label: stopping-power - - -\frac{dT}{ds} = n \int E \frac{d\sigma}{dE} dE \equiv S(T), - -where :math:`n` is the number density of the material and :math:`d\sigma/dE` is -the cross section differential in energy loss. The total stopping power -:math:`S(T)` can be separated into two components: the radiative stopping -power :math:`S_{\text{rad}}(T)`, which refers to energy loss due to -bremsstrahlung, and the collision stopping power :math:`S_{\text{col}}(T)`, -which refers to the energy loss due to inelastic collisions with bound -electrons in the material that result in ionization and excitation. The -radiative stopping power for electrons is given by - -.. math:: - :label: radiative-stopping-power - - S_{\text{rad}}(T) = n \frac{Z^2}{\beta^2} T \int_0^1 \chi(Z,T,\kappa) - d\kappa. - - -To obtain the radiative stopping power for positrons, -:eq:`radiative-stopping-power` is multiplied by :eq:`positron-factor`. - -While the models for photon interactions with matter described above can safely -assume interactions occur with free atoms, sampling the target atom based on -the macroscopic cross sections, molecular effects cannot necessarily be -disregarded for charged particle treatment. For compounds and mixtures, the -bremsstrahlung cross section is calculated using Bragg's additivity rule as - -.. math:: - :label: material-bremsstrahlung-dcs - - \frac{d\sigma_{\text{br}}}{dE} = \frac{1}{\beta^2 E} \sum_i \gamma_i Z^2_i - \chi(Z_i, T, \kappa), - -where the sum is over the constituent elements and :math:`\gamma_i` is the -atomic fraction of the :math:`i`-th element. Similarly, the radiative stopping -power is calculated using Bragg's additivity rule as - -.. math:: - :label: material-radiative-stopping-power - - S_{\text{rad}}(T) = \sum_i w_i S_{\text{rad},i}(T), - -where :math:`w_i` is the mass fraction of the :math:`i`-th element and -:math:`S_{\text{rad},i}(T)` is found for element :math:`i` using -:eq:`radiative-stopping-power`. The collision stopping power, however, is a -function of certain quantities such as the mean excitation energy :math:`I` and -the density effect correction :math:`\delta_F` that depend on molecular -properties. These quantities cannot simply be summed over constituent elements -in a compound, but should instead be calculated for the material. The Bethe -formula can be used to find the collision stopping power of the material: - -.. math:: - :label: material-collision-stopping-power - - S_{\text{col}}(T) = \frac{2 \pi r_e^2 m_e c^2}{\beta^2} N_A \frac{Z}{A_M} - [\ln(T^2/I^2) + \ln(1 + \tau/2) + F(\tau) - \delta_F(T)], - -where :math:`N_A` is Avogadro's number, :math:`A_M` is the molar mass, -:math:`\tau = T/m_e`, and :math:`F(\tau)` depends on the particle type. For -electrons, - -.. math:: - :label: F-electron - - F_{-}(\tau) = (1 - \beta^2)[1 + \tau^2/8 - (2\tau + 1) \ln2], - -while for positrons - -.. math:: - :label: F-positron - - F_{+}(\tau) = 2\ln2 - (\beta^2/12)[23 + 14/(\tau + 2) + 10/(\tau + 2)^2 + - 4/(\tau + 2)^3]. - -The density effect correction :math:`\delta_F` takes into account the reduction -of the collision stopping power due to the polarization of the material the -charged particle is passing through by the electric field of the particle. -It can be evaluated using the method described by Sternheimer_, where the -equation for :math:`\delta_F` is - -.. math:: - :label: density-effect-correction - - \delta_F(\beta) = \sum_{i=1}^n f_i \ln[(l_i^2 + l^2)/l_i^2] - - l^2(1-\beta^2). - -Here, :math:`f_i` is the oscillator strength of the :math:`i`-th transition, -given by :math:`f_i = n_i/Z`, where :math:`n_i` is the number of electrons in -the :math:`i`-th subshell. The frequency :math:`l` is the solution of the -equation - -.. math:: - :label: density-effect-l - - \frac{1}{\beta^2} - 1 = \sum_{i=1}^{n} \frac{f_i}{\bar{\nu}_i^2 + l^2}, - -where :math:`\bar{v}_i` is defined as - -.. math:: - :label: density-effect-nubar - - \bar{\nu}_i = h\nu_i \rho / h\nu_p. - -The plasma energy :math:`h\nu_p` of the medium is given by - -.. math:: - :label: plasma-frequency - - h\nu_p = \sqrt{\frac{(hc)^2 r_e \rho_m N_A Z}{\pi A}}, - -where :math:`A` is the atomic weight and :math:`\rho_m` is the density of the -material. In :eq:`density-effect-nubar`, :math:`h\nu_i` is the oscillator -energy, and :math:`\rho` is an adjustment factor introduced to give agreement -between the experimental values of the oscillator energies and the mean -excitation energy. The :math:`l_i` in :eq:`density-effect-correction` are -defined as - -.. math:: - :label: density-effect-li - - \begin{aligned} - l_i &= (\bar{\nu}_i^2 + 2/3f_i)^{1/2} ~~~~&\text{for}~~ \bar{\nu}_i > 0 \\ - l_n &= f_n^{1/2} ~~~~&\text{for}~~ \bar{\nu}_n = 0, - \end{aligned} - -where the second case applies to conduction electrons. For a conductor, -:math:`f_n` is given by :math:`n_c/Z`, where :math:`n_c` is the effective -number of conduction electrons, and :math:`v_n = 0`. The adjustment factor -:math:`\rho` is determined using the equation for the mean excitation energy: - -.. math:: - :label: mean-excitation-energy - - \ln I = \sum_{i=1}^{n-1} f_i \ln[(h\nu_i\rho)^2 + 2/3f_i(h\nu_p)^2]^{1/2} + - f_n \ln (h\nu_pf_n^{1/2}). - -.. _ttb: - -Thick-Target Bremsstrahlung Approximation -+++++++++++++++++++++++++++++++++++++++++ - -Since charged particles lose their energy on a much shorter distance scale than -neutral particles, not much error should be introduced by neglecting to -transport electrons. However, the bremsstrahlung emitted from high energy -electrons and positrons can travel far from the interaction site. Thus, even -without a full electron transport mode it is necessary to model bremsstrahlung. -We use a thick-target bremsstrahlung (TTB) approximation based on the models in -Salvat_ and Kaltiaisenaho_ for generating bremsstrahlung photons, which assumes -the charged particle loses all its energy in a single homogeneous material -region. - -To model bremsstrahlung using the TTB approximation, we need to know the number -of photons emitted by the charged particle and the energy distribution of the -photons. These quantities can be calculated using the continuous slowing down -approximation (CSDA). The CSDA assumes charged particles lose energy -continuously along their trajectory with a rate of energy loss equal to the -total stopping power, ignoring fluctuations in the energy loss. The -approximation is useful for expressing average quantities that describe how -charged particles slow down in matter. For example, the CSDA range approximates -the average path length a charged particle travels as it slows to rest: - -.. math:: - :label: csda-range - - R(T) = \int^T_0 \frac{dT'}{S(T')}. - -Actual path lengths will fluctuate around :math:`R(T)`. The average number of -photons emitted per unit path length is given by the inverse bremsstrahlung -mean free path: - -.. math:: - :label: inverse-bremsstrahlung-mfp - - \lambda_{\text{br}}^{-1}(T,E_{\text{cut}}) - = n\int_{E_{\text{cut}}}^T\frac{d\sigma_{\text{br}}}{dE}dE - = n\frac{Z^2}{\beta^2}\int_{\kappa_{\text{cut}}}^1\frac{1}{\kappa} - \chi(Z,T,\kappa)d\kappa. - -The lower limit of the integral in :eq:`inverse-bremsstrahlung-mfp` is non-zero -because the bremsstrahlung differential cross section diverges for small photon -energies but is finite for photon energies above some cutoff energy -:math:`E_{\text{cut}}`. The mean free path -:math:`\lambda_{\text{br}}^{-1}(T,E_{\text{cut}})` is used to calculate the -photon number yield, defined as the average number of photons emitted with -energy greater than :math:`E_{\text{cut}}` as the charged particle slows down -from energy :math:`T` to :math:`E_{\text{cut}}`. The photon number yield is -given by - -.. math:: - :label: photon-number-yield - - Y(T,E_{\text{cut}}) = \int^{R(T)}_{R(E_{\text{cut}})} - \lambda_{\text{br}}^{-1}(T',E_{\text{cut}})ds = \int_{E_{\text{cut}}}^T - \frac{\lambda_{\text{br}}^{-1}(T',E_{\text{cut}})}{S(T')}dT'. - -:math:`Y(T,E_{\text{cut}})` can be used to construct the energy spectrum of -bremsstrahlung photons: the number of photons created with energy between -:math:`E_1` and :math:`E_2` by a charged particle with initial kinetic energy -:math:`T` as it comes to rest is given by :math:`Y(T,E_1) - Y(T,E_2)`. - -To simulate the emission of bremsstrahlung photons, the total stopping power -and bremsstrahlung differential cross section for positrons and electrons must -be calculated for a given material using :eq:`material-bremsstrahlung-dcs` and -:eq:`material-radiative-stopping-power`. These quantities are used to build the -tabulated bremsstrahlung energy PDF and CDF for that material for each incident -energy :math:`T_k` on the energy grid. The following algorithm is then applied -to sample the photon energies: - -1. For an incident charged particle with energy :math:`T`, sample the number of - emitted photons as - - .. math:: - - N = \lfloor Y(T,E_{\text{cut}}) + \xi_1 \rfloor. - -2. Rather than interpolate the PDF between indices :math:`k` and :math:`k+1` - for which :math:`T_k < T < T_{k+1}`, which is computationally expensive, use - the composition method and sample from the PDF at either :math:`k` or - :math:`k+1`. Using linear interpolation on a logarithmic scale, the PDF can - be expressed as - - .. math:: - - p_{\text{br}}(T,E) = \pi_k p_{\text{br}}(T_k,E) + \pi_{k+1} - p_{\text{br}}(T_{k+1},E), - - where the interpolation weights are - - .. math:: - - \pi_k = \frac{\ln T_{k+1} - \ln T}{\ln T_{k+1} - \ln T_k},~~~ - \pi_{k+1} = \frac{\ln T - \ln T_k}{\ln T_{k+1} - \ln T_k}. - - Sample either the index :math:`i = k` or :math:`i = k+1` according to the - point probabilities :math:`\pi_{k}` and :math:`\pi_{k+1}`. - -3. Determine the maximum value of the CDF :math:`P_{\text{br,max}}`. - -3. Sample the photon energies using the inverse transform method with the - tabulated CDF :math:`P_{\text{br}}(T_i, E)` i.e., - - .. math:: - - E = E_j \left[ (1 + a_j) \frac{\xi_2 P_{\text{br,max}} - - P_{\text{br}}(T_i, E_j)} {E_j p_{\text{br}}(T_i, E_j)} + 1 - \right]^{\frac{1}{1 + a_j}} - - where the interpolation factor :math:`a_j` is given by - - .. math:: - - a_j = \frac{\ln p_{\text{br}}(T_i,E_{j+1}) - \ln p_{\text{br}}(T_i,E_j)} - {\ln E_{j+1} - \ln E_j} - - and :math:`P_{\text{br}}(T_i, E_j) \le \xi_2 P_{\text{br,max}} \le - P_{\text{br}}(T_i, E_{j+1})`. - -We ignore the range of the electron or positron, i.e., the bremsstrahlung -photons are produced in the same location that the charged particle was -created. The direction of the photons is assumed to be the same as the -direction of the incident charged particle, which is a reasonable approximation -at higher energies when the bremsstrahlung radiation is emitted at small -angles. .. _photon_production: @@ -1059,16 +723,14 @@ emitted photon. .. _anomalous scattering: http://pd.chem.ucl.ac.uk/pdnn/diff1/anomscat.htm -.. _Kahn's rejection method: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/aecu-3259_kahn.pdf +.. _Kahn's rejection method: https://doi.org/10.2172/4353680 .. _Klein-Nishina: https://en.wikipedia.org/wiki/Klein%E2%80%93Nishina_formula -.. _LA-UR-04-0487: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-04-0487.pdf +.. _LA-UR-04-0487: https://mcnp.lanl.gov/pdf_files/TechReport_2004_LANL_LA-UR-04-0487_Sood.pdf -.. _LA-UR-04-0488: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-04-0488.pdf +.. _LA-UR-04-0488: https://mcnp.lanl.gov/pdf_files/TechReport_2004_LANL_LA-UR-04-0488_SoodWhite.pdf .. _Kaltiaisenaho: https://aaltodoc.aalto.fi/bitstream/handle/123456789/21004/master_Kaltiaisenaho_Toni_2016.pdf -.. _Salvat: https://www.oecd-nea.org/globalsearch/download.php?doc=77434 - -.. _Sternheimer: https://doi.org/10.1103/PhysRevB.26.6067 +.. _Salvat: https://doi.org/10.1787/32da5043-en diff --git a/docs/source/methods/random_numbers.rst b/docs/source/methods/random_numbers.rst index 0cefc9156b..4376bbdb0c 100644 --- a/docs/source/methods/random_numbers.rst +++ b/docs/source/methods/random_numbers.rst @@ -7,7 +7,7 @@ Random Number Generation In order to sample probability distributions, one must be able to produce random numbers. The standard technique to do this is to generate numbers on the interval :math:`[0,1)` from a deterministic sequence that has properties that -make it appear to be random, e.g. being uniformly distributed and not exhibiting +make it appear to be random, e.g., being uniformly distributed and not exhibiting correlation between successive terms. Since the numbers produced this way are not truly "random" in a strict sense, they are typically referred to as pseudorandom numbers, and the techniques used to generate them are pseudorandom @@ -15,6 +15,11 @@ number generators (PRNGs). Numbers sampled on the unit interval can then be transformed for the purpose of sampling other continuous or discrete probability distributions. +There are many different algorithms for pseudorandom number generation. OpenMC +currently uses `permuted congruential generator`_ (PCG), which builds on top of +the simpler linear congruential generator (LCG). Both algorithms are described +below. + ------------------------------ Linear Congruential Generators ------------------------------ @@ -37,8 +42,8 @@ be generated with a method chosen at random. Some theory should be used." Typically, :math:`M` is chosen to be a power of two as this enables :math:`x \mod M` to be performed using the bitwise AND operator with a bit mask. The constants for the linear congruential generator used by default in OpenMC are -:math:`g = 2806196910506780709`, :math:`c = 1`, and :math:`M = 2^{63}` (see -`L'Ecuyer`_). +:math:`g = 2806196910506780709`, :math:`c = 1`, and :math:`M = 2^{63}` (from +`L'Ecuyer `_). Skip-ahead Capability --------------------- @@ -50,7 +55,8 @@ want to skip ahead :math:`N` random numbers and :math:`N` is large, the cost of sampling :math:`N` random numbers to get to that position may be prohibitively expensive. Fortunately, algorithms have been developed that allow us to skip ahead in :math:`O(\log_2 N)` operations instead of :math:`O(N)`. One algorithm -to do so is described in a paper by Brown_. This algorithm relies on the following +to do so is described in a `paper by Brown +`_. This algorithm relies on the following relationship: .. math:: @@ -58,15 +64,26 @@ relationship: \xi_{i+k} = g^k \xi_i + c \frac{g^k - 1}{g - 1} \mod M -Note that equation :eq:`lcg-skipahead` has the same general form as equation :eq:`lcg`, so -the idea is to determine the new multiplicative and additive constants in -:math:`O(\log_2 N)` operations. - -.. only:: html - - .. rubric:: References +Note that equation :eq:`lcg-skipahead` has the same general form as equation +:eq:`lcg`, so the idea is to determine the new multiplicative and additive +constants in :math:`O(\log_2 N)` operations. -.. _L'Ecuyer: https://doi.org/10.1090/S0025-5718-99-00996-5 -.. _Brown: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/anl-rn-arb-stride.pdf +-------------------------------- +Permuted Congruential Generators +-------------------------------- + +The `permuted congruential generator`_ (PCG) algorithm aims to improve upon the +LCG algorithm by permuting the output. The algorithm works on the basic +principle of first advancing the generator state using the LCG algorithm and +then applying a permutation function on the LCG state to obtain the output. This +results in increased statistical quality as measured by common statistical tests +while exhibiting a very small performance overhead relative to the LCG algorithm +and an equivalent memory footprint. For further details, see the original +technical report by `O'Neill +`_. OpenMC uses the +PCG-RXS-M-XS variant with a 64-bit state and 64-bit output. + .. _linear congruential generator: https://en.wikipedia.org/wiki/Linear_congruential_generator + +.. _permuted congruential generator: https://en.wikipedia.org/wiki/Permuted_congruential_generator diff --git a/docs/source/methods/random_ray.rst b/docs/source/methods/random_ray.rst new file mode 100644 index 0000000000..5e17316aa1 --- /dev/null +++ b/docs/source/methods/random_ray.rst @@ -0,0 +1,1168 @@ +.. _methods_random_ray: + +========== +Random Ray +========== + +.. _methods_random_ray_intro: + +------------------- +What is Random Ray? +------------------- + +`Random ray `_ is a stochastic transport method, closely related to +the deterministic Method of Characteristics (MOC) [Askew-1972]_. Rather than +each ray representing a single neutron as in Monte Carlo, it represents a +characteristic line through the simulation geometry upon which the transport +equation can be written as an ordinary differential equation that can be solved +analytically (although with discretization required in energy, making it a +multigroup method). The behavior of the governing transport equation can be +approximated by solving along many characteristic tracks (rays) through the +system. Unlike particles in Monte Carlo, rays in random ray or MOC are not +affected by the material characteristics of the simulated problem---rays are +selected so as to explore the full simulation problem with a statistically equal +distribution in space and angle. + +.. raw:: html + + + +The above animation is an example of the random ray integration process at work, +showing a series of random rays being sampled and transported through the +geometry. In the following sections, we will discuss how the random ray solver +works. + +---------------------------------------------- +Why is a Random Ray Solver Included in OpenMC? +---------------------------------------------- + +* One area that Monte Carlo struggles with is maintaining numerical efficiency + in regions of low physical particle flux. Random ray, on the other hand, has + approximately even variance throughout the entire global simulation domain, + such that areas with low neutron flux are no less well known that areas of + high neutron flux. Absent weight windows in MC, random ray can be several + orders of magnitude faster than multigroup Monte Carlo in classes of problems + where areas with low physical neutron flux need to be resolved. While MC + uncertainty can be greatly improved with variance reduction techniques, they + add some user complexity, and weight windows can often be expensive to + generate via MC transport alone (e.g., via the `MAGIC method + `_). The random ray solver + may be used in future versions of OpenMC as a fast way to generate weight + windows for subsequent usage by the MC solver in OpenMC. + +* In practical implementation terms, random ray is mechanically very similar to + how Monte Carlo works, in terms of the process of ray tracing on constructive + solid geometry (CSG) and handling stochastic convergence, etc. In the original + 1972 paper by Askew that introduces MOC (which random ray is a variant of), he + stated: + + .. epigraph:: + + "One of the features of the method proposed [MoC] is that ... the + tracking process needed to perform this operation is common to the + proposed method ... and to Monte Carlo methods. Thus a single tracking + routine capable of recognizing a geometric arrangement could be utilized + to service all types of solution, choice being made depending which was + more appropriate to the problem size and required accuracy." + + -- Askew [Askew-1972]_ + + This prediction holds up---the additional requirements needed in OpenMC to + handle random ray transport turned out to be fairly small. + +* It amortizes the code complexity in OpenMC for representing multigroup cross + sections. There is a significant amount of interface code, documentation, and + complexity in allowing OpenMC to generate and use multigroup XS data in its + MGMC mode. Random ray allows the same multigroup data to be used, making full + reuse of these existing capabilities. + +------------------------------- +Random Ray Numerical Derivation +------------------------------- + +In this section, we will derive the numerical basis for the random ray solver +mode in OpenMC. The derivation of random ray is also discussed in several papers +(`1 `_, `2 `_, `3 `_), and some of those +derivations are reproduced here verbatim. Several extensions are also made to +add clarity, particularly on the topic of OpenMC's treatment of cell volumes in +the random ray solver. + +~~~~~~~~~~~~~~~~~~~~~~~~~ +Method of Characteristics +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Boltzmann neutron transport equation is a partial differential equation +(PDE) that describes the angular flux within a system. It is a balance equation, +with the streaming and absorption terms typically appearing on the left hand +side, which are balanced by the scattering source, fission, and fixed source +terms on the right hand side. + +.. math:: + :label: transport + + \begin{aligned} + \mathbf{\Omega} \cdot \mathbf{\nabla} \psi(\mathbf{r},\mathbf{\Omega},E) & + \Sigma_t(\mathbf{r},E) \psi(\mathbf{r},\mathbf{\Omega},E) = \\ + & \int_0^\infty d E^\prime \int_{4\pi} d \Omega^{\prime} \Sigma_s(\mathbf{r},\mathbf{\Omega}^\prime \rightarrow \mathbf{\Omega}, E^\prime \rightarrow E) \psi(\mathbf{r},\mathbf{\Omega}^\prime, E^\prime) \\ + & + \frac{\chi(\mathbf{r}, E)}{4\pi k_{eff}} \int_0^\infty dE^\prime \nu \Sigma_f(\mathbf{r},E^\prime) \int_{4\pi}d \Omega^\prime \psi(\mathbf{r},\mathbf{\Omega}^\prime,E^\prime) + \end{aligned} + +In Equation :eq:`transport`, :math:`\psi` is the angular neutron flux. This +parameter represents the total distance traveled by all neutrons in a particular +direction inside of a control volume per second, and is often given in units of +:math:`1/(\text{cm}^{2} \text{s})`. The angular direction unit vector, +:math:`\mathbf{\Omega}`, represents the direction of travel for the neutron. The +spatial position vector, :math:`\mathbf{r}`, represents the location within the +simulation. The neutron energy, :math:`E`, or speed in continuous space, is +often given in units of electron volts. The total macroscopic neutron cross +section is :math:`\Sigma_t`. This value represents the total probability of +interaction between a neutron traveling at a certain speed (i.e., neutron energy +:math:`E`) and a target nucleus (i.e., the material through which the neutron is +traveling) per unit path length, typically given in units of +:math:`1/\text{cm}`. Macroscopic cross section data is a combination of +empirical data and quantum mechanical modeling employed in order to generate an +evaluation represented either in pointwise form or resonance parameters for each +target isotope of interest in a material, as well as the density of the +material, and is provided as input to a simulation. The scattering neutron cross +section, :math:`\Sigma_s`, is similar to the total cross section but only +measures scattering interactions between the neutron and the target nucleus, and +depends on the change in angle and energy the neutron experiences as a result of +the interaction. Several additional reactions like (n,2n) and (n,3n) are +included in the scattering transfer cross section. The fission neutron cross +section, :math:`\Sigma_f`, is also similar to the total cross section but only +measures the fission interaction between a neutron and a target nucleus. The +energy spectrum for neutrons born from fission, :math:`\chi`, represents a known +distribution of outgoing neutron energies based on the material that fissioned, +which is taken as input data to a computation. The average number of neutrons +born per fission is :math:`\nu`. The eigenvalue of the equation, +:math:`k_{eff}`, represents the effective neutron multiplication factor. If the +right hand side of Equation :eq:`transport` is condensed into a single term, +represented by the total neutron source term :math:`Q(\mathbf{r}, \mathbf{\Omega},E)`, +the form given in Equation :eq:`transport_simple` is reached. + +.. math:: + :label: transport_simple + + \overbrace{\mathbf{\Omega} \cdot \mathbf{\nabla} \psi(\mathbf{r},\mathbf{\Omega},E)}^{\text{streaming term}} + \overbrace{\Sigma_t(\mathbf{r},E) \psi(\mathbf{r},\mathbf{\Omega},E)}^{\text{absorption term}} = \overbrace{Q(\mathbf{r}, \mathbf{\Omega},E)}^{\text{total neutron source term}} + +Fundamentally, MOC works by solving Equation :eq:`transport_simple` along a +single characteristic line, thus altering the full spatial and angular scope of +the transport equation into something that holds true only for a particular +linear path (or track) through the reactor. These tracks are linear for neutral +particles that are not subject to field effects. With our transport equation in +hand, we will now derive the solution along a track. To accomplish this, we +parameterize :math:`\mathbf{r}` with respect to some reference location +:math:`\mathbf{r}_0` such that :math:`\mathbf{r} = \mathbf{r}_0 + s\mathbf{\Omega}`. In this +manner, Equation :eq:`transport_simple` can be rewritten for a specific segment +length :math:`s` at a specific angle :math:`\mathbf{\Omega}` through a constant +cross section region of the reactor geometry as in Equation :eq:`char_long`. + +.. math:: + :label: char_long + + \mathbf{\Omega} \cdot \mathbf{\nabla} \psi(\mathbf{r}_0 + s\mathbf{\Omega},\mathbf{\Omega},E) + \Sigma_t(\mathbf{r}_0 + s\mathbf{\Omega},E) \psi(\mathbf{r}_0 + s\mathbf{\Omega},\mathbf{\Omega},E) = Q(\mathbf{r}_0 + s\mathbf{\Omega}, \mathbf{\Omega},E) + +As this equation holds along a one dimensional path, we can assume the +dependence of :math:`s` on :math:`\mathbf{r}_0` and :math:`\mathbf{\Omega}` such that +:math:`\mathbf{r}_0 + s\mathbf{\Omega}` simplifies to :math:`s`. When the differential +operator is also applied to the angular flux :math:`\psi`, we arrive at the +characteristic form of the Boltzmann Neutron Transport Equation given in +Equation :eq:`char`. + +.. math:: + :label: char + + \frac{d}{ds} \psi(s,\mathbf{\Omega},E) + \Sigma_t(s,E) \psi(s,\mathbf{\Omega},E) = Q(s, \mathbf{\Omega},E) + +An analytical solution to this characteristic equation can be achieved with the +use of an integrating factor: + +.. math:: + :label: int_factor + + e^{ \int_0^s ds' \Sigma_t (s', E)} + +to arrive at the final form of the characteristic equation shown in Equation +:eq:`full_char`. + +.. math:: + :label: full_char + + \psi(s,\mathbf{\Omega},E) = \psi(\mathbf{r}_0,\mathbf{\Omega},E) e^{-\int_0^s ds^\prime \Sigma_t(s^\prime,E)} + \int_0^s ds^{\prime\prime} Q(s^{\prime\prime},\mathbf{\Omega}, E) e^{-\int_{s^{\prime\prime}}^s ds^\prime \Sigma_t(s^\prime,E)} + +With this characteristic form of the transport equation, we now have an +analytical solution along a linear path through any constant cross section +region of a system. While the solution only holds along a linear track, no +discretizations have yet been made. + +Similar to many other solution approaches to the Boltzmann neutron transport +equation, the MOC approach also uses a "multigroup" approximation in order to +discretize the continuous energy spectrum of neutrons traveling through the +system into fixed set of energy groups :math:`G`, where each group :math:`g \in +G` has its own specific cross section parameters. This makes the difficult +non-linear continuous energy dependence much more manageable as group wise cross +section data can be precomputed and fed into a simulation as input data. The +computation of multigroup cross section data is not a trivial task and can +introduce errors in the simulation. However, this is an active field of research +common to all multigroup methods, and there are numerous generation methods +available that are capable of reducing the biases introduced by the multigroup +approximation. Commonly used methods include the subgroup self-shielding method +and use of fast (unconverged) Monte Carlo simulations to produce cross section +estimates. It is important to note that Monte Carlo methods are capable of +treating the energy variable of the neutron continuously, meaning that they do +not need to make this approximation and are therefore not subject to any +multigroup errors. + +Following the multigroup discretization, another assumption made is that a large +and complex problem can be broken up into small constant cross section regions, +and that these regions have group dependent, flat, isotropic sources (fission +and scattering), :math:`Q_g`. Anisotropic as well as higher order sources are +also possible with MOC-based methods. With these key assumptions, the multigroup +MOC form of the neutron transport equation can be written as in Equation +:eq:`moc_final`. + +.. math:: + :label: moc_final + + \psi_g(s, \mathbf{\Omega}) = \psi_g(\mathbf{r_0}, \mathbf{\Omega}) e^{-\int_0^s ds^\prime \Sigma_{t_g}(s^\prime)} + \int_0^s ds^{\prime\prime} Q_g(s^{\prime\prime},\mathbf{\Omega}) e^{-\int_{s^{\prime\prime}}^s ds^\prime \Sigma_{t_g}(s^\prime)} + +The CSG definition of the system is used to create spatially defined source +regions (each region being denoted as :math:`i`). These neutron source regions +are often approximated as being constant +(flat) in source intensity but can also be defined using a higher order source +(linear, quadratic, etc.) that allows for fewer source regions to be required to +achieve a specified solution fidelity. In OpenMC, the approximation of a +spatially constant isotropic fission and scattering source :math:`Q_{i,g}` in +cell :math:`i` leads +to simple exponential attenuation along an individual characteristic of length +:math:`s` given by Equation :eq:`fsr_attenuation`. + +.. math:: + :label: fsr_attenuation + + \psi_g(s) = \psi_g(0) e^{-\Sigma_{t,i,g} s} + \frac{Q_{i,g}}{\Sigma_{t,i,g}} \left( 1 - e^{-\Sigma_{t,i,g} s} \right) + +For convenience, we can also write this equation in terms of the incoming and +outgoing angular flux (:math:`\psi_g^{in}` and :math:`\psi_g^{out}`), and +consider a specific tracklength for a particular ray :math:`r` crossing cell +:math:`i` as :math:`\ell_r`, as in: + +.. math:: + :label: fsr_attenuation_in_out + + \psi_g^{out} = \psi_g^{in} e^{-\Sigma_{t,i,g} \ell_r} + \frac{Q_{i,g}}{\Sigma_{t,i,g}} \left( 1 - e^{-\Sigma_{t,i,g} \ell_r} \right) . + +We can then define the average angular flux of a single ray passing through the +cell as: + +.. math:: + :label: average + + \overline{\psi}_{r,i,g} = \frac{1}{\ell_r} \int_0^{\ell_r} \psi_{g}(s)ds . + +We can then substitute in Equation :eq:`fsr_attenuation` and solve, resulting +in: + +.. math:: + :label: average_solved + + \overline{\psi}_{r,i,g} = \frac{Q_{i,g}}{\Sigma_{t,i,g}} - \frac{\psi_{r,g}^{out} - \psi_{r,g}^{in}}{\ell_r \Sigma_{t,i,g}} . + +By rearranging Equation :eq:`fsr_attenuation_in_out`, we can then define +:math:`\Delta \psi_{r,g}` as the change in angular flux for ray :math:`r` +passing through region :math:`i` as: + +.. math:: + :label: delta_psi + + \Delta \psi_{r,g} = \psi_{r,g}^{in} - \psi_{r,g}^{out} = \left(\psi_{r,g}^{in} - \frac{Q_{i,g}}{\Sigma_{t,i,g}} \right) \left( 1 - e^{-\Sigma_{t,i,g} \ell_r} \right) . + +Equation :eq:`delta_psi` is a useful expression as it is easily computed with +the known inputs for a ray crossing through the region. + +By substituting :eq:`delta_psi` into :eq:`average_solved`, we can arrive at a +final expression for the average angular flux for a ray crossing a region as: + +.. math:: + :label: average_psi_final + + \overline{\psi}_{r,i,g} = \frac{Q_{i,g}}{\Sigma_{t,i,g}} + \frac{\Delta \psi_{r,g}}{\ell_r \Sigma_{t,i,g}}. + +~~~~~~~~~~~ +Random Rays +~~~~~~~~~~~ + +In the previous subsection, the governing characteristic equation along a 1D +line through the system was written, such that an analytical solution for the +ODE can be computed. If enough characteristic tracks (ODEs) are solved, then the +behavior of the governing PDE can be numerically approximated. In traditional +deterministic MOC, the selection of tracks is chosen deterministically, where +azimuthal and polar quadratures are defined along with even track spacing in +three dimensions. This is the point at which random ray diverges from +deterministic MOC numerically. In the random ray method, rays are randomly +sampled from a uniform distribution in space and angle and tracked along a +predefined distance through the geometry before terminating. **Importantly, +different rays are sampled each power iteration, leading to a fully stochastic +convergence process.** This results in a need to utilize both inactive and +active batches as in the Monte Carlo method. + +While Monte Carlo implicitly converges the scattering source fully within each +iteration, random ray (and MOC) solvers are not typically written to fully +converge the scattering source within a single iteration. Rather, both the +fission and scattering sources are updated each power iteration, thus requiring +enough outer iterations to reach a stationary distribution in both the fission +source and scattering source. So, even in a low dominance ratio problem like a +2D pincell, several hundred inactive batches may still be required with random +ray to allow the scattering source to fully develop, as neutrons undergoing +hundreds of scatters may constitute a non-trivial contribution to the fission +source. We note that use of a two-level second iteration scheme is sometimes +used by some MOC or random ray solvers so as to fully converge the scattering +source with many inner iterations before updating the fission source in the +outer iteration. It is typically more efficient to use the single level +iteration scheme, as there is little reason to spend so much work converging the +scattering source if the fission source is not yet converged. + +Overall, the difference in how random ray and Monte Carlo converge the +scattering source means that in practice, random ray typically requires more +inactive iterations than are required in Monte Carlo. While a Monte Carlo +simulation may need 100 inactive iterations to reach a stationary source +distribution for many problems, a random ray solve will likely require 1,000 +iterations or more. Source convergence metrics (e.g., Shannon entropy) are thus +recommended when performing random ray simulations to ascertain when the source +has fully developed. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Converting Angular Flux to Scalar Flux +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Thus far in our derivation, we have been able to write analytical equations that +solve for the change in angular flux of a ray crossing a flat source region +(Equation :eq:`delta_psi`) as well as the ray's average angular flux through +that region (Equation :eq:`average_psi_final`). To determine the source for the +next power iteration, we need to assemble our estimates of angular fluxes from +all the sampled rays into scalar fluxes within each FSR. + +We can define the scalar flux in region :math:`i` as: + +.. math:: + :label: integral + + \phi_i = \frac{\int_{V_i} \int_{4\pi} \psi(r, \Omega) d\Omega d\mathbf{r}}{\int_{V_i} d\mathbf{r}} . + +The integral in the numerator: + +.. math:: + :label: numerator + + \int_{V_i} \int_{4\pi} \psi(r, \Omega) d\Omega d\mathbf{r} . + +is not known analytically, but with random ray, we are going the numerically +approximate it by discretizing over a finite number of tracks (with a finite +number of locations and angles) crossing the domain. We can then use the +characteristic method to determine the total angular flux along that line. + +Conceptually, this can be thought of as taking a volume-weighted sum of angular +fluxes for all :math:`N_i` rays that happen to pass through cell :math:`i` that +iteration. When written in discretized form (with the discretization happening +in terms of individual ray segments :math:`r` that pass through region +:math:`i`), we arrive at: + +.. math:: + :label: discretized + + \phi_{i,g} = \frac{\int_{V_i} \int_{4\pi} \psi(r, \Omega) d\Omega d\mathbf{r}}{\int_{V_i} d\mathbf{r}} = \overline{\overline{\psi}}_{i,g} \approx \frac{\sum\limits_{r=1}^{N_i} \ell_r w_r \overline{\psi}_{r,i,g}}{\sum\limits_{r=1}^{N_i} \ell_r w_r} . + +Here we introduce the term :math:`w_r`, which represents the "weight" of the ray +(its 2D area), such that the volume that a ray is responsible for can be +determined by multiplying its length :math:`\ell` by its weight :math:`w`. As +the scalar flux vector is a shape function only, we are actually free to +multiply all ray weights :math:`w` by any constant such that the overall shape +is still maintained, even if the magnitude of the shape function changes. Thus, +we can simply set :math:`w_r` to be unity for all rays, such that: + +.. math:: + :label: weights + + \text{Volume of cell } i = V_i \approx \sum\limits_{r=1}^{N_i} \ell_r w_r = \sum\limits_{r=1}^{N_i} \ell_r . + +We can then rewrite our discretized equation as: + +.. math:: + :label: discretized_2 + + \phi_{i,g} \approx \frac{\sum\limits_{r=1}^{N_i} \ell_r w_r \overline{\psi}_{r,i,g}}{\sum\limits_{r=1}^{N_i} \ell_r w_r} = \frac{\sum\limits_{r=1}^{N_i} \ell_r \overline{\psi}_{r,i,g}}{\sum\limits_{r=1}^{N_i} \ell_r} . + +Thus, the scalar flux can be inferred if we know the volume weighted sum of the +average angular fluxes that pass through the cell. Substituting +:eq:`average_psi_final` into :eq:`discretized_2`, we arrive at: + +.. math:: + :label: scalar_full + + \phi_{i,g} = \frac{\int_{V_i} \int_{4\pi} \psi(r, \Omega) d\Omega d\mathbf{r}}{\int_{V_i} d\mathbf{r}} = \overline{\overline{\psi}}_{i,g} = \frac{\sum\limits_{r=1}^{N_i} \ell_r \overline{\psi}_{r,i,g}}{\sum\limits_{r=1}^{N_i} \ell_r} = \frac{\sum\limits_{r=1}^{N_i} \ell_r \frac{Q_{i,g}}{\Sigma_{t,i,g}} + \frac{\Delta \psi_{r,g}}{\ell_r \Sigma_{t,i,g}}}{\sum\limits_{r=1}^{N_i} \ell_r}, + +which when partially simplified becomes: + +.. math:: + :label: scalar_four_vols + + \phi = \frac{Q_{i,g} \sum\limits_{r=1}^{N_i} \ell_r}{\Sigma_{t,i,g} \sum\limits_{r=1}^{N_i} \ell_r} + \frac{\sum\limits_{r=1}^{N_i} \ell_r \frac{\Delta \psi_i}{\ell_r}}{\Sigma_{t,i,g} \sum\limits_{r=1}^{N_i} \ell_r} . + +Note that there are now four (seemingly identical) volume terms in this equation. + +.. _methods_random_ray_vol: + +~~~~~~~~~~~~~~ +Volume Dilemma +~~~~~~~~~~~~~~ + +At first glance, Equation :eq:`scalar_four_vols` appears ripe for cancellation +of terms. Mathematically, such cancellation allows us to arrive at the following +"naive" estimator for the scalar flux: + +.. math:: + :label: phi_naive + + \phi_{i,g}^{naive} = \frac{Q_{i,g} }{\Sigma_{t,i,g}} + \frac{\sum\limits_{r=1}^{N_i} \Delta \psi_{r,g}}{\Sigma_{t,i,g} \sum\limits_{r=1}^{N_i} \ell_r} . + +This derivation appears mathematically sound at first glance but unfortunately +raises a serious issue as discussed in more depth by `Tramm et al. +`_ and `Cosgrove and Tramm `_. Namely, the second +term: + +.. math:: + :label: ratio_estimator + + \frac{\sum\limits_{r=1}^{N_i} \Delta \psi_{r,g}}{\Sigma_{t,i,g} \sum\limits_{r=1}^{N_i} \ell_r} + +features stochastic variables (the sums over random ray lengths and angular +fluxes) in both the numerator and denominator, making it a stochastic ratio +estimator, which is inherently biased. In practice, usage of the naive estimator +does result in a biased, but "consistent" estimator (i.e., it is biased, but +the bias tends towards zero as the sample size increases). Empirically, this +bias tends to effect eigenvalue calculations much more significantly than in +fixed source simulations. Experimentally, the right answer can be obtained with +this estimator, though for eigenvalue simulations a very fine ray density is +required to eliminate the bias. + +How might we solve the biased ratio estimator problem? While there is no obvious +way to alter the numerator term (which arises from the characteristic +integration approach itself), there is potentially more flexibility in how we +treat the stochastic term in the denominator, :math:`\sum\limits_{r=1}^{N_i} +\ell_r` . From Equation :eq:`weights` we know that this term can be directly +inferred from the volume of the problem, which does not actually change between +iterations. Thus, an alternative treatment for this "volume" term in the +denominator is to replace the actual stochastically sampled total track length +with the expected value of the total track length. For instance, if the true +volume of the FSR is known (as is the total volume of the full simulation domain +and the total tracklength used for integration that iteration), then we know the +true expected value of the tracklength in that FSR. That is, if a FSR accounts +for 2% of the overall volume of a simulation domain, then we know that the +expected value of tracklength in that FSR will be 2% of the total tracklength +for all rays that iteration. This is a key insight, as it allows us to the +replace the actual tracklength that was accumulated inside that FSR each +iteration with the expected value. + +If we know the analytical volumes, then those can be used to directly compute +the expected value of the tracklength in each cell, :math:`L_{avg}`. However, as +the analytical volumes are not typically known in OpenMC due to the usage of +user-defined constructive solid geometry, we need to source this quantity from +elsewhere. An obvious choice is to simply accumulate the total tracklength +through each FSR across all iterations (batches) and to use that sum to compute +the expected average length per iteration, as: + +.. math:: + :label: L_avg + + \sum\limits^{}_{i} \ell_i \approx L_{avg} = \frac{\sum\limits^{B}_{b}\sum\limits^{N_i}_{r=1} \ell_{b,r} }{B} + +where :math:`b` is a single batch in :math:`B` total batches simulated so far. + +In this manner, the expected value of the tracklength will become more refined +as iterations continue, until after many iterations the variance of the +denominator term becomes trivial compared to the numerator term, essentially +eliminating the presence of the stochastic ratio estimator. A "simulation +averaged" estimator is therefore: + +.. math:: + :label: phi_sim + + \phi_{i,g}^{simulation} = \frac{Q_{i,g} }{\Sigma_{t,i,g}} + \frac{\sum\limits_{r=1}^{N_i} \Delta \psi_{r,g}}{\Sigma_{t,i,g} L_{avg}} + +In practical terms, the "simulation averaged" estimator is virtually +indistinguishable numerically from use of the true analytical volume to estimate +this term. Note also that the term "simulation averaged" refers only to the +volume/length treatment, the scalar flux estimate itself is computed fully again +each iteration. + +There are some drawbacks to this method. Recall, this denominator volume term +originally stemmed from taking a volume weighted integral of the angular flux, +in which case the denominator served as a normalization term for the numerator +integral in Equation :eq:`integral`. Essentially, we have now used a different +term for the volume in the numerator as compared to the normalizing volume in +the denominator. The inevitable mismatch (due to noise) between these two +quantities results in a significant increase in variance, and can even result in +the generation of negative fluxes. Notably, the same problem occurs if using a +tracklength estimate based on the analytical volume, as again the numerator +integral and the normalizing denominator integral no longer match on a +per-iteration basis. + +In practice, the simulation averaged method does completely remove the bias seen +when using the naive estimator, though at the cost of a notable increase in +variance. Empirical testing reveals that on most eigenvalue problems, the +simulation averaged estimator does win out overall in numerical performance, as +a much coarser quadrature can be used resulting in faster runtimes overall. +Thus, OpenMC uses the simulation averaged estimator as default in its random ray +mode for eigenvalue solves. + +OpenMC also features a "hybrid" volume estimator that uses the naive estimator +for all regions containing an external (fixed) source term. For all other +source regions, the "simulation averaged" estimator is used. This typically achieves +a best of both worlds result, with the benefits of the low bias simulation averaged +estimator in most regions, while preventing instability and/or large biases in regions +with external source terms via use of the naive estimator. In general, it is +recommended to use the "hybrid" estimator, which is the default method used +in OpenMC. If instability is encountered despite high ray densities, then +the naive estimator may be preferable. + +A table that summarizes the pros and cons, as well as recommendations for +different use cases, is given in the :ref:`volume +estimators` section of the user guide. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +What Happens When a Source Region is Missed? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given the stochastic nature of random ray, when low ray densities are used it is +common for small source regions to occasionally not be hit by any rays in a +particular power iteration :math:`n`. This naturally collapses the flux estimate +in that cell for the iteration from Equation :eq:`phi_naive` to: + +.. math:: + :label: phi_missed_one + + \phi_{i,g,n}^{missed} = \frac{Q_{i,g,n} }{\Sigma_{t,i,g}} + +as the streaming operator has gone to zero. While this is obviously innacurate +as it ignores transport, for most problems where the region is only occasionally +missed this estimator does not tend to introduce any significant bias. + +However, in cases where the total cross section in the region is very small +(e.g., a void-like material) and where a strong external fixed source has been +placed, then this treatment causes major issues. In this pathological case, the +lack of transport forces the entirety of the fixed source to effectively be +contained and collided within the cell, which for a low cross section region is +highly unphysical. The net effect is that a very high estimate of the flux +(often orders of magnitude higher than is expected) is generated that iteration, +which cannot be washed out even with hundreds or thousands of iterations. Thus, +huge biases are often seen in spatial tallies containing void-like regions with +external sources unless a high enough ray density is used such that all source +regions are always hit each iteration. This is particularly problematic as +external sources placed in void-like regions are very common in many types of +fixed source analysis. + +For regions where external sources are present, to eliminate this bias it is +therefore preferable to simply use the previous iteration's estimate of the flux +in that cell, as: + +.. math:: + :label: phi_missed_two + + \phi_{i,g,n}^{missed} = \phi_{i,g,n-1} . + +When linear sources are present, the flux moments from the previous iteration +are used in the same manner. While this introduces some small degree of +correlation to the simulation, for miss rates on the order of a few percent the +correlations are trivial and the bias is eliminated. Thus, in OpenMC the +previous iteration's scalar flux estimate is applied to cells that are missed +where there is an external source term present within the cell. + +~~~~~~~~~~~~~~~ +Power Iteration +~~~~~~~~~~~~~~~ + +Given a starting source term, we now have a way of computing an estimate of the +scalar flux in each cell by way of transporting rays randomly through the +domain, recording the change in angular flux for the rays into each cell as they +make their traversals, and summing these contributions up as in Equation +:eq:`phi_sim`. How then do we turn this into an iterative process such that we +improve the estimate of the source and scalar flux over many iterations, given +that our initial starting source will just be a guess? + +In an eigenvalue simulation, the source :math:`Q^{n}` for iteration :math:`n` +can be inferred from the scalar flux from the previous iteration :math:`n-1` as: + +.. math:: + :label: source_update + + Q^{n}(i, g) = \frac{\chi}{k^{n-1}_{eff}} \nu \Sigma_f(i, g) \phi^{n-1}(g) + \sum\limits^{G}_{g'} \Sigma_{s}(i,g,g') \phi^{n-1}(g') + +where :math:`Q^{n}(i, g)` is the total source (fission + scattering) in region +:math:`i` and energy group :math:`g`. Notably, the in-scattering source in group +:math:`g` must be computed by summing over the contributions from all groups +:math:`g' \in G`. + +The eigenvalue for iteration :math:`n` can be computed as: + +.. math:: + :label: eigenvalue_update + + k^{n}_{eff} = k^{n-1}_{eff} \frac{F^n}{F^{n-1}}, + +where the total spatial- and energy-integrated fission rate :math:`F^n` in +iteration :math:`n` can be computed as: + +.. math:: + :label: fission_source + + F^n = \sum\limits^{M}_{i} \left( V_i \sum\limits^{G}_{g} \nu \Sigma_f(i, g) \phi^{n}(g) \right) + +where :math:`M` is the total number of FSRs in the simulation. Similarly, the +total spatial- and energy-integrated fission rate :math:`F^{n-1}` in iteration +:math:`n-1` can be computed as: + +.. math:: + :label: fission_source_prev + + F^{n-1} = \sum\limits^{M}_{i} \left( V_i \sum\limits^{G}_{g} \nu \Sigma_f(i, g) \phi^{n-1}(g) \right) + +Notably, the volume term :math:`V_i` appears in the eigenvalue update equation. +The same logic applies to the treatment of this term as was discussed earlier. +In OpenMC, we use the "simulation averaged" volume (Equation :eq:`L_avg`) +derived from summing over all ray tracklength contributions to a FSR over all +iterations and dividing by the total integration tracklength to date. Thus, +Equation :eq:`fission_source` becomes: + +.. math:: + :label: fission_source_volumed + + F^n = \sum\limits^{M}_{i} \left( L_{avg} \sum\limits^{G}_{g} \nu \Sigma_f(i, g) \phi^{n}(g) \right) + +and a similar substitution can be made to update Equation +:eq:`fission_source_prev` . In OpenMC, the most up-to-date version of the volume +estimate is used, such that the total fission source from the previous iteration +(:math:`n-1`) is also recomputed each iteration. + +In a fixed source simulation, the fission source is replaced by a user specified +fixed source term :math:`Q_\text{fixed}(i,E)`, which is defined for each FSR and +energy group. This additional source term is applied at this stage for +generating the next iteration's source estimate as: + +.. math:: + :label: fixed_source_update + + Q^{n}(i, g) = Q_\text{fixed}(i,g) + \sum\limits^{G}_{g'} \Sigma_{s}(i,g,g') \phi^{n-1}(g') + +and no eigenvalue is computed. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Ray Starting Conditions and Inactive Length +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Another key area of divergence between deterministic MOC and random ray is the +starting conditions for rays. In deterministic MOC, the angular flux spectrum +for rays are stored at any reflective or periodic boundaries so as to provide a +starting condition for the next iteration. As there are many tracks, storage of +angular fluxes can become costly in terms of memory consumption unless there are +only vacuum boundaries present. + +In random ray, as the starting locations of rays are sampled anew each +iteration, the initial angular flux spectrum for the ray is unknown. While a +guess can be made by taking the isotropic source from the FSR the ray was +sampled in, direct usage of this quantity would result in significant bias and +error being imparted on the simulation. + +Thus, an `on-the-fly approximation method `_ was developed (known +as the "dead zone"), where the first several mean free paths of a ray are +considered to be "inactive" or "read only". In this sense, the angular flux is +solved for using the MOC equation, but the ray does not "tally" any scalar flux +back to the FSRs that it travels through. After several mean free paths have +been traversed, the ray's angular flux spectrum typically becomes dominated by +the accumulated source terms from the cells it has traveled through, while the +(incorrect) starting conditions have been attenuated away. In the animation in +the :ref:`introductory section on this page `, the +yellow portion of the ray lengths is the dead zone. As can be seen in this +animation, the tallied :math:`\sum\limits_{r=1}^{N_i} \Delta \psi_{r,g}` term +that is plotted is not affected by the ray when the ray is within its inactive +length. Only when the ray enters its active mode does the ray contribute to the +:math:`\sum\limits_{r=1}^{N_i} \Delta \psi_{r,g}` sum for the iteration. + +~~~~~~~~~~~~~~~~~~~~~ +Ray Ending Conditions +~~~~~~~~~~~~~~~~~~~~~ + +To ensure that a uniform density of rays is integrated in space and angle +throughout the simulation domain, after exiting the initial inactive "dead zone" +portion of the ray, the rays are run for a user-specified distance. Typically, a +choice of at least several times the length of the inactive "dead zone" is made +so as to amortize the cost of the dead zone. For example, if a dead zone of 30 +cm is selected, then an active length of 300 cm might be selected so that the +cost of the dead zone is at most 10% of the overall runtime. + +-------------------- +Simplified Algorithm +-------------------- + +A simplified set of functions that execute a single random ray power iteration +are given below. Not all global variables are defined in this illustrative +example, but the high level components of the algorithm are shown. A number of +significant simplifications are made for clarity---for example, no inactive +"dead zone" length is shown, geometry operations are abstracted, no parallelism +(or thread safety) is expressed, a naive exponential treatment is used, and rays +are not halted at their exact termination distances, among other subtleties. +Nonetheless, the below algorithms may be useful for gaining intuition on the +basic components of the random ray process. Rather than expressing the algorithm +in abstract pseudocode, C++ is used to make the control flow easier to +understand. + +The first block below shows the logic for a single power iteration (batch): + +.. code-block:: C++ + + double power_iteration(double k_eff) { + + // Update source term (scattering + fission) + update_neutron_source(k_eff); + + // Reset scalar fluxes to zero + fill(global::scalar_flux_new, 0.0f); + + // Transport sweep over all random rays for the iteration + for (int i = 0; i < nrays; i++) { + RandomRay ray; + initialize_ray(ray); + transport_single_ray(ray); + } + + // Normalize scalar flux and update volumes + normalize_scalar_flux_and_volumes(); + + // Add source to scalar flux, compute number of FSR hits + add_source_to_scalar_flux(); + + // Compute k-eff using updated scalar flux + k_eff = compute_k_eff(k_eff); + + // Set phi_old = phi_new + global::scalar_flux_old.swap(global::scalar_flux_new); + + return k_eff; + } + +The second function shows the logic for transporting a single ray within the +transport loop: + +.. code-block:: C++ + + void transport_single_ray(RandomRay& ray) { + + // Reset distance to zero + double distance = 0.0; + + // Continue transport of ray until active length is reached + while (distance < user_setting::active_length) { + // Ray trace to find distance to next surface (i.e., segment length) + double s = distance_to_nearest_boundary(ray); + + // Attenuate flux (and accumulate source/attenuate) on segment + attenuate_flux(ray, s); + + // Advance particle to next surface + ray.location = ray.location + s * ray.direction; + + // Move ray across the surface + cross_surface(ray); + + // Add segment length "s" to total distance traveled + distance += s; + } + } + +The final function below shows the logic for solving for the characteristic MOC +equation (and accumulating the scalar flux contribution of the ray into the +scalar flux value for the FSR). + +.. code-block:: C++ + + void attenuate_flux(RandomRay& ray, double s) { + + // Determine which flat source region (FSR) the ray is currently in + int fsr = get_fsr_id(ray.location); + + // Determine material type + int material = get_material_type(fsr); + + // MOC incoming flux attenuation + source contribution/attenuation equation + for (int e = 0; e < global::n_energy_groups; e++) { + float sigma_t = global::macro_xs[material].total; + float tau = sigma_t * s; + float delta_psi = (ray.angular_flux[e] - global::source[fsr][e] / sigma_t) * (1 - exp(-tau)); + ray.angular_flux_[e] -= delta_psi; + global::scalar_flux_new[fsr][e] += delta_psi; + } + + // Record total tracklength in this FSR (to compute volume) + global::volume[fsr] += s; + } + +.. _methods_random_tallies: + +------------------------ +How are Tallies Handled? +------------------------ + +Most tallies, filters, and scores that you would expect to work with a +multigroup solver like random ray should work. For example, you can define 3D +mesh tallies with energy filters and flux, fission, and nu-fission scores, etc. + +There are some restrictions though. For starters, it is assumed that all filter +mesh boundaries will conform to physical surface boundaries (or lattice +boundaries) in the simulation geometry. It is acceptable for multiple cells +(FSRs) to be contained within a filter mesh cell (e.g., pincell-level or +assembly-level tallies should work), but it is currently left as undefined +behavior if a single simulation cell is able to score to multiple filter mesh +cells. In the future, the capability to fully support mesh tallies may be added +to OpenMC, but for now this restriction needs to be respected. + +Flux tallies are handled slightly differently than in Monte Carlo. By default, +in MC, flux tallies are reported in units of tracklength (cm), so must be +manually normalized by volume by the user to produce an estimate of flux in +units of cm\ :sup:`-2`\. Alternatively, MC flux tallies can be normalized via a +separated volume calculation process as discussed in the :ref:`Volume +Calculation Section`. In random ray, as the volumes are +computed on-the-fly as part of the transport process, the flux tallies can +easily be reported either in units of flux (cm\ :sup:`-2`\) or tracklength (cm). +By default, the unnormalized flux values (units of cm) will be reported. If the +user wishes to received volume normalized flux tallies, then an option for this +is available, as described in the :ref:`User Guide`. + +-------------- +Linear Sources +-------------- + +Instead of making a flat source approximation, as in the previous section, a +Linear Source (LS) approximation can be used. Different LS approximations have +been developed; the OpenMC implementation follows the MOC LS scheme described by +`Ferrer `_. The LS source along a characteristic is given by: + +.. math:: + :label: linear_source + + Q_{i,g}(s) = \bar{Q}_{r,i,g} + \hat{Q}_{r,i,g}(s-\ell_{r}/2), + +where the source, :math:`Q_{i,g}(s)`, varies linearly along the track and +:math:`\bar{Q}_{r,i,g}` and :math:`\hat{Q}_{r,i,g}` are track specific source +terms to define shortly. Integrating the source, as done in Equation +:eq:`moc_final`, leads to + +.. math:: + :label: lsr_attenuation + + \psi^{out}_{r,g}=\psi^{in}_{r,g} + \left(\frac{\bar{Q}_{r, i, g}}{\Sigma_{\mathrm{t}, i, g}}-\psi^{in}_{r,g}\right) + F_{1}\left(\tau_{i,g}\right)+\frac{\hat{Q}_{r, i, g}^{g}}{2\left(\Sigma_{\mathrm{t}, i,g}\right)^{2}} F_{2}\left(\tau_{i,g}\right), + +where for simplicity the term :math:`\tau_{i,g}` and the expoentials :math:`F_1` +and :math:`F_2` are introduced, given by: + +.. math:: + :label: tau + + \tau_{i,g} = \Sigma_{\mathrm{t,i,g}} \ell_{r} + +.. math:: + :label: f1 + + F_1(\tau) = 1 - e^{-\tau}, + +and + +.. math:: + :label: f2 + + F_{2}\left(\tau\right) = 2\left[\tau-F_{1}\left(\tau\right)\right]-\tau F_{1}\left(\tau\right). + + +To solve for the track specific source terms in Equation :eq:`linear_source` we +first define a local reference frame. If we now refer to :math:`\mathbf{r}` as +the global coordinate and introduce the source region specific coordinate +:math:`\mathbf{u}` such that, + +.. math:: + :label: local_coord + + \mathbf{u}_{r} = \mathbf{r}-\mathbf{r}_{\mathrm{c}}, + +where :math:`\mathbf{r}_{\mathrm{c}}` is the centroid of the source region of +interest. In turn :math:`\mathbf{u}_{r,\mathrm{c}}` and :math:`\mathbf{u}_{r,0}` +are the local centroid and entry positions of a ray. The computation of the +local and global centroids are described further by `Gunow `_. + +Using the local position, the source in a source region is given by: + +.. math:: + :label: region_source + + \tilde{Q}(\boldsymbol{x}) ={Q}_{i,g}+ \boldsymbol{\vec{Q}}_{i,g} \cdot \mathbf{u}_{r}\;\mathrm{,} + +This definition allows us to solve for our characteric source terms resulting in: + +.. math:: + :label: source_term_1 + + \bar{Q}_{r, i, g} = Q_{i,g} + \left[\mathbf{u}_{r,\mathrm{c}} \cdot \boldsymbol{\vec{Q}}_{i,g}\right], + +.. math:: + :label: source_term_2 + + \hat{Q}_{r, i, g} = \left[\boldsymbol{\Omega} \cdot \boldsymbol{\vec{Q}}_{i,g}\right]\;\mathrm{,} + +:math:`\boldsymbol{\Omega}` being the direction vector of the ray. The next step +is to solve for the LS source vector :math:`\boldsymbol{\vec{Q}}_{i,g}`. A +relationship between the LS source vector and the source moments, +:math:`\boldsymbol{\vec{q}}_{i,g}` can be derived, as in `Ferrer +`_ and `Gunow `_: + +.. math:: + :label: m_equation + + \mathbf{M}_{i} \boldsymbol{\vec{Q}}_{i,g} = \boldsymbol{\vec{q}}_{i,g} \;\mathrm{.} + +The spatial moments matrix :math:`M_i` in region :math:`i` represents the +spatial distribution of the 3D object composing the `source region +`_. This matrix is independent of the material of the source +region, fluxes, and any transport effects -- it is a purely geometric quantity. +It is a symmetric :math:`3\times3` matrix. While :math:`M_i` is not known +apriori to the simulation, similar to the source region volume, it can be +computed "on-the-fly" as a byproduct of the random ray integration process. Each +time a ray randomly crosses the region within its active length, an estimate of +the spatial moments matrix can be computed by using the midpoint of the ray as +an estimate of the centroid, and the distance and direction of the ray can be +used to inform the other spatial moments within the matrix. As this information +is purely geometric, the stochastic estimate of the centroid and spatial moments +matrix can be accumulated and improved over the entire duration of the +simulation, converging towards their true quantities. + +With an estimate of the spatial moments matrix :math:`M_i` resulting from the +ray tracing process naturally, the LS source vector +:math:`\boldsymbol{\vec{Q}}_{i,g}` can be obtained via a linear solve of +:eq:`m_equation`, or by the direct inversion of :math:`M_i`. However, to +accomplish this, we must first know the source moments +:math:`\boldsymbol{\vec{q}}_{i,g}`. Fortunately, the source moments are also +defined by the definition of the source: + +.. math:: + :label: source_moments + + q_{v, i, g}= \frac{\chi_{i,g}}{k_{eff}} \sum_{g^{\prime}=1}^{G} \nu + \Sigma_{\mathrm{f},i, g^{\prime}} \hat{\phi}_{v, i, g^{\prime}} + \sum_{g^{\prime}=1}^{G} + \Sigma_{\mathrm{s}, i, g^{\prime}\rightarrow g} \hat{\phi}_{v, i, g^{\prime}}\quad \forall v \in(x, y, z)\;\mathrm{,} + +where :math:`v` indicates the direction vector component, and we have introduced +the scalar flux moments :math:`\hat{\phi}`. The scalar flux moments can be +solved for by taking the `integral definition `_ of a spatial +moment, allowing us to derive a "simulation averaged" estimator for the scalar +moment, as in Equation :eq:`phi_sim`, + +.. math:: + :label: scalar_moments_sim + + \hat{\phi}_{v,i,g}^{simulation} = \frac{\sum\limits_{r=1}^{N_i} + \ell_{r} \left[\Omega_{v} \hat{\psi}_{r,i,g} + u_{r,v,0} \bar{\psi}_{r,i,g}\right]} + {\Sigma_{t,i,g} \frac{\sum\limits^{B}_{b}\sum\limits^{N_i}_{r} \ell_{b,r} }{B}} + \quad \forall v \in(x, y, z)\;\mathrm{,} + + +where the average angular flux is given by Equation :eq:`average_psi_final`, and +the angular flux spatial moments :math:`\hat{\psi}_{r,i,g}` by: + +.. math:: + :label: angular_moments + + \hat{\psi}_{r, i, g} = \frac{\ell_{r}\psi^{in}_{r,g}}{2} + + \left(\frac{\bar{Q}_{r,i, g}}{\Sigma_{\mathrm{t}, i, g}}-\psi^{in}_{r,g}\right) + \frac{G_{1}\left(\tau_{i,g}\right)}{\Sigma_{\mathrm{t}, i, g}} + \frac{\ell_{r}\hat{Q}_{r,i,g}} + {2\left(\Sigma_{\mathrm{t}, i, g}\right)^{2}}G_{2}\left(\tau_{i,g}\right)\;\mathrm{.} + + +The new exponentials introduced, again for simplicity, are simply: + +.. math:: + :label: G1 + + G_{1}(\tau) = 1+\frac{\tau}{2}-\left(1+\frac{1}{\tau}\right) F_{1}(\tau), + +.. math:: + :label: G2 + + G_{2}(\tau) = \frac{2}{3} \tau-\left(1+\frac{2}{\tau}\right) G_{1}(\tau) + +The contents of this section, alongside the equations for the flat source and +scalar flux, Equations :eq:`source_update` and :eq:`phi_sim` respectively, +completes the set of equations for LS. + +.. _methods-shannon-entropy-random-ray: + +----------------------------- +Shannon Entropy in Random Ray +----------------------------- + +As :math:`k_{eff}` is updated at each generation, the fission source at each FSR +is used to compute the Shannon entropy. This follows the :ref:`same procedure +for computing Shannon entropy in continuous-energy or multigroup Monte Carlo +simulations `, except that fission sources at FSRs are +considered, rather than fission sites of user-defined regular meshes. Thus, the +volume-weighted fission rate is considered instead, and the fraction of fission +sources is adjusted such that: + +.. math:: + :label: fraction-source-random-ray + + S_i = \frac{\text{Fission source in FSR $i \times$ Volume of FSR + $i$}}{\text{Total fission source}} = \frac{Q_{i} V_{i}}{\sum_{i=1}^{i=N} + Q_{i} V_{i}} + +The Shannon entropy is then computed normally as + +.. math:: + :label: shannon-entropy-random-ray + + H = - \sum_{i=1}^N S_i \log_2 S_i + +where :math:`N` is the number of FSRs. FSRs with no fission source (or, +occassionally, negative fission source, :ref:`due to the volume estimator +problem `) are skipped to avoid taking an undefined +logarithm in :eq:`shannon-entropy-random-ray`. + +.. _usersguide_fixed_source_methods: + +------------ +Fixed Source +------------ + +The random ray solver in OpenMC can be used for both eigenvalue and fixed source +problems. There are a few key differences between fixed source transport with +random ray and Monte Carlo, however. + +- **Source definition:** In Monte Carlo, it is relatively easy to define various + source distributions, including point sources, surface sources, volume + sources, and even custom user sources -- all with varying angular and spatial + statistical distributions. In random ray, the natural way to include a fixed + source term is by adding a fixed (flat) contribution to specific flat source + regions. Thus, in the OpenMC implementation of random ray, particle sources + are restricted to being volumetric and isotropic, although different energy + spectrums are supported. Fixed sources can be applied to specific materials, + cells, or universes. Point sources are "smeared" to fill the volume of the + source region that contains the point source coordinate. + +- **Inactive batches:** In Monte Carlo, use of a fixed source implies that all + batches are active batches, as there is no longer a need to develop a fission + source distribution. However, in random ray mode, there is still a need to + develop the scattering source by way of inactive batches before beginning + active batches. + +.. _adjoint: + +------------------------ +Adjoint Flux Solver Mode +------------------------ + +The random ray solver in OpenMC can also be used to solve for the adjoint flux, +:math:`\psi^{\dagger}`. In combination with the regular (forward) flux solution, +the adjoint flux is useful for perturbation methods as well as for computing +weight windows for subsequent Monte Carlo simulations. The adjoint flux can be +thought of as the "backwards" flux, representing the flux where a particle is +born at an absoprtion point (and typical absorption energy), and then undergoes +transport with a transposed scattering matrix. That is, instead of sampling a +particle and seeing where it might go as in a standard forward solve, we will +sample an absorption location and see where the particle that was absorbed there +might have come from. Notably, for typical neutron absorption at low energy +levels, this means that adjoint flux particles are typically sampled at a low +energy and then upscatter (via a transposed scattering matrix) over their +lifetimes. + +In OpenMC, the random ray adjoint solver is implemented simply by transposing +the scattering matrix, swapping :math:`\nu\Sigma_f` and :math:`\chi`, and then +running a normal transport solve. When no external fixed source is present, no +additional changes are needed in the transport process. However, if an external +fixed forward source is present in the simulation problem, then an additional +step is taken to compute the accompanying fixed adjoint source. In OpenMC, the +adjoint flux does *not* represent a response function for a particular detector +region. Rather, the adjoint flux is the global response, making it appropriate +for use with weight window generation schemes for global variance reduction. +Thus, if using a fixed source, the external source for the adjoint mode is +simply computed as being :math:`1 / \phi`, where :math:`\phi` is the forward +scalar flux that results from a normal forward solve (which OpenMC will run +first automatically when in adjoint mode). The adjoint external source will be +computed for each source region in the simulation mesh, independent of any +tallies. The adjoint external source is always flat, even when a linear +scattering and fission source shape is used. When in adjoint mode, all reported +results (e.g., tallies, eigenvalues, etc.) are derived from the adjoint flux, +even when the physical meaning is not necessarily obvious. These values are +still reported, though we emphasize that the primary use case for adjoint mode +is for producing adjoint flux tallies to support subsequent perturbation studies +and weight window generation. + +Note that the adjoint :math:`k_{eff}` is statistically the same as the forward +:math:`k_{eff}`, despite the flux distributions taking different shapes. + +--------------------------- +Fundamental Sources of Bias +--------------------------- + +Compared to continuous energy Monte Carlo simulations, the known sources of bias +in random ray particle transport are: + + - **Multigroup Energy Discretization:** The multigroup treatment of flux and + cross sections incurs a significant bias, as a reaction rate (:math:`R_g = + V \phi_g \Sigma_g`) for an energy group :math:`g` can only be conserved + for a given choice of multigroup cross section :math:`\Sigma_g` if the + flux (:math:`\phi_g`) is known a priori. If the flux was already known, + then there would be no point to the simulation, resulting in a fundamental + need for approximating this quantity. There are numerous methods for + generating relatively accurate multigroup cross section libraries that can + each be applied to a narrow design area reliably, although there are + always limitations and/or complexities that arise with a multigroup energy + treatment. This is by far the most significant source of simulation bias + between Monte Carlo and random ray for most problems. While the other + areas typically have solutions that are highly effective at mitigating + bias, error stemming from multigroup energy discretization is much harder + to remedy. + - **Source Approximation:**. In OpenMC, a "flat" (0th order) source + approximation is often made, wherein the scattering and fission sources within a + cell are assumed to be spatially uniform. As the source in reality is a + continuous function, this leads to bias, although the bias can be reduced + to acceptable levels if the flat source regions are sufficiently small. + The bias can also be mitigated by assuming a higher-order source such as the + linear source approximation currently implemented into OpenMC. + In practical terms, this source of bias can become very large if cells are + large (with dimensions beyond that of a typical particle mean free path), + but the subdivision of cells can often reduce this bias to trivial levels. + - **Anisotropic Source Approximation:** In OpenMC, the source is not only + assumed to be flat but also isotropic, leading to bias. It is possible for + MOC (and likely random ray) to treat anisotropy explicitly, but this is + not currently supported in OpenMC. This source of bias is not significant + for some problems, but becomes more problematic for others. Even in the + absence of explicit treatment of anistropy, use of transport-corrected + multigroup cross sections can often mitigate this bias, particularly for + light water reactor simulation problems. + - **Angular Flux Initial Conditions:** Each time a ray is sampled, its + starting angular flux is unknown, so a guess must be made (typically the + source term for the cell it starts in). Usage of an adequate inactive ray + length (dead zone) mitigates this error. As the starting guess is + attenuated at a rate of :math:`\exp(-\Sigma_t \ell)`, this bias can driven + below machine precision in a low cost manner on many problems. + +.. _Tramm-2017a: https://doi.org/10.1016/j.jcp.2017.04.038 +.. _Tramm-2017b: https://doi.org/10.1016/j.anucene.2017.10.015 +.. _Tramm-2018: https://dspace.mit.edu/handle/1721.1/119038 +.. _Tramm-2020: https://doi.org/10.1051/EPJCONF/202124703021 +.. _Cosgrove-2023: https://doi.org/10.1080/00295639.2023.2270618 +.. _Ferrer-2016: https://doi.org/10.13182/NSE15-6 +.. _Gunow-2018: https://dspace.mit.edu/handle/1721.1/119030 + +.. only:: html + + .. rubric:: References + +.. [Askew-1972] Askew, “A Characteristics Formulation of the Neutron Transport + Equation in Complicated Geometries.” Technical Report AAEW-M 1108, UK Atomic + Energy Establishment (1972). diff --git a/docs/source/methods/tallies.rst b/docs/source/methods/tallies.rst index dc3a63decf..79a63fbdd8 100644 --- a/docs/source/methods/tallies.rst +++ b/docs/source/methods/tallies.rst @@ -4,9 +4,9 @@ Tallies ======= -Note that the methods discussed in this section are written specifically for -continuous-energy mode but equivalent apply to the multi-group mode if the -particle's energy is replaced with the particle's group +The methods discussed in this section are written specifically for continuous- +energy mode. However, they can also apply to the multi-group mode if the +particle's energy is instead interpreted as the particle's group. ------------------ Filters and Scores @@ -179,12 +179,12 @@ n(\mathbf{r}, \mathbf{\hat{\Omega}}, E, t)` and :math:`d\ell = v \, dt` where Equation :eq:`track-length-integral` indicates that we can use the length of a particle's trajectory as an estimate for the flux, i.e. the track-length -estimator of the flux would be +estimator of the volume-integrated flux would be .. math:: :label: track-length-flux - \phi = \frac{1}{W} \sum_{i \in T} w_i \ell_i + V \phi = \frac{1}{W} \sum_{i \in T} w_i \ell_i where :math:`T` is the set of all the particle's trajectories within the desired volume and :math:`\ell_i` is the length of the :math:`i`-th trajectory. In the @@ -207,6 +207,8 @@ the change-in-angle), we must use an analog estimator. .. TODO: Add description of surface current tallies +.. _tallies_statistics: + ---------- Statistics ---------- @@ -268,6 +270,14 @@ normal, log-normal, Weibull, etc. The central limit theorem states that as Estimating Statistics of a Random Variable ------------------------------------------ +After running OpenMC, each tallied quantity has a reported mean and standard +deviation. The below sections explain how these quantities are computed. Note +that OpenMC uses **batch statistics**, meaning that each observation for a tally +random variable corresponds to the aggregation of tally contributions from +multiple source particles that are grouped together into a single batch. See +:ref:`usersguide_particles` for more information on how the number of source +particles and statistical batches are specified. + Mean ++++ @@ -377,6 +387,33 @@ of this is that the longer you run a simulation, the better you know your results. Therefore, by running a simulation long enough, it is possible to reduce the stochastic uncertainty to arbitrarily low levels. +Figure of Merit ++++++++++++++++ + +The figure of merit (FOM) is an indicator that accounts for both the statistical +uncertainty and the execution time and represents how much information is +obtained per unit time in the simulation. The FOM is defined as + +.. math:: + :label: figure_of_merit + + FOM = \frac{1}{r^2 t}, + +where :math:`t` is the total execution time and :math:`r` is the relative error +defined as + +.. math:: + :label: relative_error + + r = \frac{s_\bar{X}}{\bar{x}}. + +Based on this definition, one can see that a higher FOM is desirable. The FOM is +useful as a comparative tool. For example, if a variance reduction technique is +being applied to a simulation, the FOM with variance reduction can be compared +to the FOM without variance reduction to ascertain whether the reduction in +variance outweighs the potential increase in execution time (e.g., due to +particle splitting). + Confidence Intervals ++++++++++++++++++++ @@ -510,6 +547,6 @@ improve the estimate of the percentile. .. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution -.. _unpublished rational approximation: https://web.archive.org/web/20150926021742/http://home.online.no/~pjacklam/notes/invnorm/ +.. _unpublished rational approximation: https://stackedboxes.org/2017/05/01/acklams-normal-quantile-function/ -.. _MC21: http://www.osti.gov/bridge/servlets/purl/903083-HT5p1o/903083.pdf +.. _MC21: https://www.osti.gov/servlets/purl/903083 diff --git a/docs/source/methods/variance_reduction.rst b/docs/source/methods/variance_reduction.rst new file mode 100644 index 0000000000..353ae5077e --- /dev/null +++ b/docs/source/methods/variance_reduction.rst @@ -0,0 +1,134 @@ +.. _methods_variance_reduction: + +================== +Variance Reduction +================== + +.. _methods_variance_reduction_intro: + +------------ +Introduction +------------ + +Transport problems can sometimes involve a significant degree of attenuation +between the source and a detector (tally) region, which can result in a flux +differential of ten orders of magnitude (or more) throughout the simulation +domain. As Monte Carlo uncertainties tend to be inversely proportional to the +physical flux density, it can be extremely difficult to accurately resolve +tallies in locations that are optically far from the source. This issue is +particularly common in fixed source simulations, where some tally locations may +not experience a single scoring event, even after billions of analog histories. + +Variance reduction techniques aim to either flatten the global uncertainty +distribution, such that all regions of phase space have a fairly similar +uncertainty, or to reduce the uncertainty in specific locations (such as a +detector). There are two strategies available in OpenMC for variance reduction: +the Monte Carlo MAGIC method and the FW-CADIS method. Both strategies work by +developing a weight window mesh that can be utilized by subsequent Monte Carlo +solves to split particles heading towards areas of lower flux densities while +terminating particles in higher flux regions---all while maintaining a fair +game. + +------------ +MAGIC Method +------------ + +The Method of Automatic Generation of Importances by Calculation, or `MAGIC +method `_, is an iterative +technique that uses spatial flux information :math:`\phi(r)` obtained from a +normal Monte Carlo solve to produce weight windows :math:`w(r)` that can be +utilized by a subsequent iteration of Monte Carlo. While the first generation of +weight windows produced may only help to reduce variance slightly, use of these +weights to generate another set of weight windows results in a progressively +improving iterative scheme. + +Equation :eq:`magic` defines how the lower bound of weight windows +:math:`w_{\ell}(r)` are generated with MAGIC using forward flux information. +Here, we can see that the flux at location :math:`r` is normalized by the +maximum flux in any group at that location. We can also see that the weights are +divided by a factor of two, which accounts for the typical :math:`5\times` +factor separating the lower and upper weight window bounds in OpenMC. + +.. math:: + :label: magic + + w_{\ell}(r) = \frac{\phi(r)}{2\,\text{max}(\phi(r))} + +A major advantage of this technique is that it does not require any special +transport machinery; it simply uses multiple Monte Carlo simulations to +iteratively improve a set of weight windows (which are typically defined on a +mesh covering the simulation domain). The downside to this method is that as the +flux differential increases between areas near and far from the source, it +requires more outer Monte Carlo iterations, each of which can be expensive in +itself. Additionally, computation of weight windows based on regular (forward) +neutron flux tally information does not produce the most numerically effective +set of weight windows. Nonetheless, MAGIC remains a simple and effective +technique for generating weight windows. + +-------- +FW-CADIS +-------- + +As discussed in the previous section, computation of weight windows based on +regular (forward) neutron flux tally information does not produce the most +numerically efficient set of weight windows. It is highly preferable to generate +weight windows based on spatial adjoint flux :math:`\phi^{\dag}(r)` +information. The adjoint flux is essentially the "reverse" simulation problem, +where we sample a random point and assume this is where a particle was absorbed, +and then trace it backwards (upscattering in energy), until we sample the point +where it was born from. + +The Forward-Weighted Consistent Adjoint Driven Importance Sampling method, or +`FW-CADIS method `_, produces weight windows +for global variance reduction given adjoint flux information throughout the +entire domain. The weight window lower bound is defined in Equation +:eq:`fw_cadis`, and also involves a normalization step not shown here. + +.. math:: + :label: fw_cadis + + w_{\ell}(r) = \frac{1}{2\phi^{\dag}(r)} + +While the algorithm itself is quite simple, it requires estimates of the global +adjoint flux distribution, which is difficult to generate directly with Monte +Carlo transport. Thus, FW-CADIS typically uses an alternative solver (often +deterministic) that can be more readily adapted for generating adjoint flux +information, and which is often much cheaper than Monte Carlo given that a rough +solution is often sufficient for weight window generation. + +The FW-CADIS implementation in OpenMC utilizes its own internal random ray +multigroup transport solver to generate the adjoint source distribution. No +coupling to any external transport is solver is necessary. The random ray solver +operates on the same geometry as the Monte Carlo solver, so no redefinition of +the simulation geometry is required. More details on how the adjoint flux is +computed are given in the :ref:`adjoint methods section `. + +More information on the workflow is available in the :ref:`user guide +`, but generally production of weight windows with FW-CADIS +involves several stages (some of which are highly automated). These tasks +include generation of approximate multigroup cross section data for use by the +random ray solver, running of the random ray solver in normal (forward flux) +mode to generate a source for the adjoint solver, running of the random ray +solver in adjoint mode to generate adjoint flux tallies, and finally the +production of weight windows via the FW-CADIS method. As is discussed in the +user guide, most of these steps are automated together, making the additional +burden on the user fairly small. + +The major advantage of this technique is that it typically produces much more +numerically efficient weight windows as compared to those generated with MAGIC, +sometimes with an order-of-magnitude improvement in the figure of merit +(Equation :eq:`variance_fom`), which accounts for both the variance and the +execution time. Another major advantage is that the cost of the random ray +solver is typically negligible compared to the cost of the subsequent Monte +Carlo solve itself, making it a very cheap method to deploy. The downside to +this method is that it introduces a second transport method into the mix (random +ray), such that there are more free input parameters for the user to know about +and adjust, potentially making the method more complex to use. However, as many +of the parameters have natural choices, much of this parameterization can be +handled automatically behind the scenes without the need for the user to be +aware of this. + +.. math:: + :label: variance_fom + + \text{FOM} = \frac{1}{\text{Time} \times \sigma^2} diff --git a/docs/source/publications.rst b/docs/source/publications.rst index 88d0a97b97..a2d60d5af1 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -138,8 +138,8 @@ Geometry and Visualization *Trans. Am. Nucl. Soc.*, **114**, 391-394 (2016). - Derek M. Lax, "`Memory efficient indexing algorithm for physical properties in - OpenMC `_," S. M. Thesis, Massachusetts - Institute of Technology (2015). + OpenMC `_," S. M. Thesis, + Massachusetts Institute of Technology (2015). - Derek Lax, William Boyd, Nicholas Horelik, Benoit Forget, and Kord Smith, "A memory efficient algorithm for classifying unique regions in constructive @@ -399,7 +399,8 @@ Doppler Broadening - Jonathan A. Walsh, Benoit Forget, Kord S. Smith, Brian C. Kiedrowski, and Forrest B. Brown, "`Direct, on-the-fly calculation of unresolved resonance region cross sections in Monte Carlo simulations - `_," *Proc. Joint Int. Conf. M&C+SNA+MC*, + `_," *Proc. Joint Int. Conf. + M&C+SNA+MC*, Nashville, Tennessee, Apr. 19--23 (2015). - Colin Josey, Benoit Forget, and Kord Smith, "`Windowed multipole sensitivity @@ -596,7 +597,8 @@ Depletion - Matthew S. Ellis, Colin Josey, Benoit Forget, and Kord Smith, "`Spatially Continuous Depletion Algorithm for Monte Carlo Simulations - `_," *Trans. Am. Nucl. Soc.*, **115**, + `_," *Trans. Am. Nucl. Soc.*, + **115**, 1221-1224 (2016). - Anas Gul, K. S. Chaudri, R. Khan, and M. Azeen, "`Development and verification diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 721f3d113a..2a9d0876cd 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -21,19 +21,21 @@ Simulation Settings :nosignatures: :template: myclass.rst - openmc.Source + openmc.SourceBase + openmc.IndependentSource + openmc.FileSource + openmc.CompiledSource + openmc.MeshSource openmc.SourceParticle openmc.VolumeCalculation - openmc.WeightWindows openmc.Settings -The following function can be used for generating a source file: - .. autosummary:: :toctree: generated :nosignatures: :template: myfunction.rst + openmc.read_source_file openmc.write_source_file Material Specification @@ -44,9 +46,6 @@ Material Specification :nosignatures: :template: myclass.rst - openmc.Nuclide - openmc.Element - openmc.Macroscopic openmc.Material openmc.Materials @@ -88,6 +87,7 @@ Building geometry openmc.Intersection openmc.Union openmc.Complement + openmc.BoundingBox openmc.Cell openmc.Universe openmc.DAGMCUniverse @@ -119,17 +119,21 @@ Constructing Tallies openmc.Filter openmc.UniverseFilter openmc.MaterialFilter + openmc.MaterialFromFilter openmc.CellFilter openmc.CellFromFilter - openmc.CellbornFilter + openmc.CellBornFilter openmc.CellInstanceFilter openmc.CollisionFilter openmc.SurfaceFilter openmc.MeshFilter + openmc.MeshBornFilter + openmc.MeshMaterialFilter openmc.MeshSurfaceFilter openmc.EnergyFilter openmc.EnergyoutFilter openmc.MuFilter + openmc.MuSurfaceFilter openmc.PolarFilter openmc.AzimuthalFilter openmc.DistribcellFilter @@ -139,18 +143,30 @@ Constructing Tallies openmc.SpatialLegendreFilter openmc.SphericalHarmonicsFilter openmc.TimeFilter + openmc.WeightFilter openmc.ZernikeFilter openmc.ZernikeRadialFilter + openmc.ParentNuclideFilter openmc.ParticleFilter + openmc.MeshMaterialVolumes + openmc.Trigger + openmc.TallyDerivative + openmc.Tally + openmc.Tallies + +Meshes +------ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclassinherit.rst + openmc.RegularMesh openmc.RectilinearMesh openmc.CylindricalMesh openmc.SphericalMesh openmc.UnstructuredMesh - openmc.Trigger - openmc.TallyDerivative - openmc.Tally - openmc.Tallies Geometry Plotting ----------------- @@ -161,6 +177,8 @@ Geometry Plotting :template: myclass.rst openmc.Plot + openmc.WireframeRayTracePlot + openmc.SolidRayTracePlot openmc.Plots Running OpenMC @@ -186,8 +204,19 @@ Post-processing :template: myclass.rst openmc.Particle + openmc.ParticleList + openmc.ParticleTrack openmc.StatePoint openmc.Summary + openmc.Track + openmc.Tracks + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.voxel_to_vtk The following classes and functions are used for functional expansion reconstruction. @@ -220,6 +249,27 @@ Various classes may be created when performing tally slicing and/or arithmetic: openmc.arithmetic.AggregateNuclide openmc.arithmetic.AggregateFilter +Variance Reduction +------------------ + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass + + openmc.WeightWindows + openmc.WeightWindowsList + openmc.WeightWindowGenerator + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.hdf5_to_wws + openmc.wwinp_to_wws + + Coarse Mesh Finite Difference Acceleration ------------------------------------------ diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index 03be25abf4..67eca00947 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -13,25 +13,38 @@ Functions :template: myfunction.rst calculate_volumes + current_batch export_properties + export_weight_windows finalize find_cell find_material + dagmc_universe_cell_ids + global_bounding_box + global_tallies hard_reset + id_map import_properties + import_weight_windows init + is_statepoint_batch iter_batches keff load_nuclide + master next_batch num_realizations plot_geometry + property_map reset + reset_timers run run_in_memory - simulation_init + sample_external_source simulation_finalize + simulation_init source_bank + statepoint_load statepoint_write Classes @@ -42,12 +55,89 @@ Classes :nosignatures: :template: myclass.rst + AzimuthalFilter Cell + CellFilter + CellInstanceFilter + CellbornFilter + CellfromFilter + CollisionFilter + CylindricalMesh + DelayedGroupFilter + DistribcellFilter EnergyFilter - MaterialFilter + EnergyFunctionFilter + EnergyoutFilter + Filter + LegendreFilter Material + MaterialFilter + MaterialFromFilter + Mesh MeshFilter + MeshBornFilter MeshSurfaceFilter + MuFilter Nuclide + ParentNuclideFilter + ParticleFilter + PolarFilter + RectilinearMesh RegularMesh + SpatialLegendreFilter + SphericalHarmonicsFilter + SphericalMesh + SurfaceFilter Tally + TemporarySession + UniverseFilter + UnstructuredMesh + WeightFilter + WeightWindows + ZernikeFilter + ZernikeRadialFilter + +Data +---- + +.. data:: cells + + Mapping of cell ID to :class:`openmc.lib.Cell` instances. + + :type: dict + +.. data:: filters + + Mapping of filter ID to :class:`openmc.lib.Filter` instances. + + :type: dict + +.. data:: materials + + Mapping of material ID to :class:`openmc.lib.Material` instances. + + :type: dict + +.. data:: meshes + + Mapping of mesh ID to :class:`openmc.lib.Mesh` instances. + + :type: dict + +.. data:: nuclides + + Mapping of nuclide name to :class:`openmc.lib.Nuclide` instances. + + :type: dict + +.. data:: tallies + + Mapping of tally ID to :class:`openmc.lib.Tally` instances. + + :type: dict + +.. data:: weight_windows + + Mapping of weight window ID to :class:`openmc.lib.WeightWindows` instances. + + :type: dict diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 95fdfeca9f..1eaf90c972 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -61,9 +61,15 @@ Core Functions atomic_mass atomic_weight + combine_distributions + decay_constant + decay_energy + decay_photon_energy dose_coefficients - gnd_name + gnds_name + half_life isotopes + kalbach_slope linearize thin water_density @@ -114,6 +120,7 @@ Angle-Energy Distributions IncoherentElasticAE IncoherentElasticAEDiscrete IncoherentInelasticAEDiscrete + MixedElasticAE Resonance Data -------------- diff --git a/docs/source/pythonapi/deplete.rst b/docs/source/pythonapi/deplete.rst index d4a0f128fe..f112cf8ccf 100644 --- a/docs/source/pythonapi/deplete.rst +++ b/docs/source/pythonapi/deplete.rst @@ -15,16 +15,18 @@ are: 1) A transport operator 2) A time-integration scheme -The former is responsible for executing a transport code, like OpenMC, -and retaining important information required for depletion. The most common examples -are reaction rates and power normalization data. The latter is responsible for -projecting reaction rates and compositions forward in calendar time across -some step size :math:`\Delta t`, and obtaining new compositions given a power -or power density. The :class:`Operator` is provided to handle communicating with -OpenMC. Several classes are provided that implement different time-integration -algorithms for depletion calculations, which are described in detail in Colin -Josey's thesis, `Development and analysis of high order neutron -transport-depletion coupling algorithms `_. +The former is responsible for calculating and retaining important information +required for depletion. The most common examples are reaction rates and power +normalization data. The latter is responsible for projecting reaction rates and +compositions forward in calendar time across some step size :math:`\Delta t`, +and obtaining new compositions given a power or power density. The +:class:`CoupledOperator` class is provided to obtain reaction rates via tallies +through OpenMC's transport solver, and the :class:`IndependentOperator` class is +provided to obtain reaction rates from cross-section data. Several classes are +provided that implement different time-integration algorithms for depletion +calculations, which are described in detail in Colin Josey's thesis, +`Development and analysis of high order neutron transport-depletion coupling +algorithms `_. .. autosummary:: :toctree: generated @@ -40,18 +42,30 @@ transport-depletion coupling algorithms `_. SICELIIntegrator SILEQIIntegrator -Each of these classes expects a "transport operator" to be passed. An operator -specific to OpenMC is available using the following class: +Each of these classes expects a "transport operator" to be passed. OpenMC +provides the following transport operator classes: .. autosummary:: :toctree: generated :nosignatures: :template: mycallable.rst - Operator + CoupledOperator + IndependentOperator -The :class:`Operator` must also have some knowledge of how nuclides transmute -and decay. This is handled by the :class:`Chain`. +The :class:`CoupledOperator` and :class:`IndependentOperator` classes must also +have some knowledge of how nuclides transmute and decay. This is handled by the +:class:`Chain` class. + +The :class:`IndependentOperator` class requires a set of fluxes and microscopic +cross sections. The following function can be used to generate this information: + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + get_microxs_and_flux Minimal Example --------------- @@ -64,19 +78,18 @@ A minimal example for performing depletion would be: >>> import openmc.deplete >>> geometry = openmc.Geometry.from_xml() >>> settings = openmc.Settings.from_xml() + >>> model = openmc.Model(geometry, settings) # Representation of a depletion chain >>> chain_file = "chain_casl.xml" - >>> operator = openmc.deplete.Operator( - ... geometry, settings, chain_file) + >>> operator = openmc.deplete.CoupledOperator(model, chain_file) # Set up 5 time steps of one day each >>> dt = [24 * 60 * 60] * 5 >>> power = 1e6 # constant power of 1 MW # Deplete using mid-point predictor-corrector - >>> cecm = openmc.deplete.CECMIntegrator( - ... operator, dt, power) + >>> cecm = openmc.deplete.CECMIntegrator(operator, dt, power) >>> cecm.integrate() Internal Classes and Functions @@ -131,10 +144,11 @@ data, such as number densities and reaction rates for each material. :template: myclass.rst AtomNumber + MicroXS OperatorResult ReactionRates Results - ResultsList + StepResult The following class and functions are used to solve the depletion equations, with :func:`cram.CRAM48` being the default. @@ -166,7 +180,13 @@ with :func:`cram.CRAM48` being the default. :type: bool -The following classes are used to help the :class:`openmc.deplete.Operator` +.. data:: pool.NUM_PROCESSES + + Number of worker processes used for depletion calculations, which rely on the + :class:`multiprocessing.pool.Pool` class. If set to ``None`` (default), the + number returned by :func:`os.cpu_count` is used. + +The following classes are used to help the :class:`openmc.deplete.CoupledOperator` compute quantities like effective fission yields, reaction rates, and total system energy. @@ -183,14 +203,56 @@ total system energy. helpers.FissionYieldCutoffHelper helpers.FluxCollapseHelper +The :class:`openmc.deplete.IndependentOperator` uses inner classes subclassed +from those listed above to perform similar calculations. + +The following classes are used to define external source rates or transfer rates +to model continuous removal or feed of nuclides during depletion. + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + transfer_rates.ExternalSourceRates + transfer_rates.TransferRates + +Intermediate Classes +-------------------- + +Specific implementations of abstract base classes may utilize some of +the same methods and data structures. These methods and data are stored +in intermediate classes. + +Methods common to tally-based implementation of :class:`FissionYieldHelper` +are stored in :class:`helpers.TalliedFissionYieldHelper` + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myclass.rst + + helpers.TalliedFissionYieldHelper + +Methods common to OpenMC-specific implementations of :class:`TransportOperator` +are stored in :class:`openmc_operator.OpenMCOperator` + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: mycallable.rst + + openmc_operator.OpenMCOperator + + Abstract Base Classes --------------------- A good starting point for extending capabilities in :mod:`openmc.deplete` is to examine the following abstract base classes. Custom classes can inherit from :class:`abc.TransportOperator` to implement alternative -schemes for collecting reaction rates and other data from a transport code -prior to depleting materials +schemes for collecting reaction rates and other data prior to depleting +materials .. autosummary:: :toctree: generated @@ -200,7 +262,9 @@ prior to depleting materials abc.TransportOperator The following classes are abstract classes used to pass information from -OpenMC simulations back on to the :class:`abc.TransportOperator` +transport simulations (in the case of transport-coupled depletion) or to +simply calculate these quantities directly (in the case of +transport-independent depletion) back on to the :class:`abc.TransportOperator` .. autosummary:: :toctree: generated @@ -210,7 +274,6 @@ OpenMC simulations back on to the :class:`abc.TransportOperator` abc.NormalizationHelper abc.FissionYieldHelper abc.ReactionRateHelper - abc.TalliedFissionYieldHelper Custom integrators or depletion solvers can be developed by subclassing from the following abstract base classes: @@ -223,3 +286,15 @@ the following abstract base classes: abc.Integrator abc.SIIntegrator abc.DepSystemSolver + +D1S Functions +------------- + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + d1s.prepare_tallies + d1s.time_correction_factors + d1s.apply_time_correction diff --git a/docs/source/pythonapi/mgxs.rst b/docs/source/pythonapi/mgxs.rst index bf5a845992..392914b367 100644 --- a/docs/source/pythonapi/mgxs.rst +++ b/docs/source/pythonapi/mgxs.rst @@ -41,6 +41,7 @@ Multi-group Cross Sections openmc.mgxs.KappaFissionXS openmc.mgxs.MultiplicityMatrixXS openmc.mgxs.NuFissionMatrixXS + openmc.mgxs.ReducedAbsorptionXS openmc.mgxs.ScatterXS openmc.mgxs.ScatterMatrixXS openmc.mgxs.ScatterProbabilityMatrix diff --git a/docs/source/pythonapi/model.rst b/docs/source/pythonapi/model.rst index a6c89be7cd..3034826bdd 100644 --- a/docs/source/pythonapi/model.rst +++ b/docs/source/pythonapi/model.rst @@ -11,9 +11,6 @@ Convenience Functions :template: myfunction.rst openmc.model.borated_water - openmc.model.cylinder_from_points - openmc.model.hexagonal_prism - openmc.model.rectangular_prism openmc.model.subdivide openmc.model.pin @@ -25,8 +22,17 @@ Composite Surfaces :nosignatures: :template: myclass.rst + openmc.model.ConicalFrustum + openmc.model.CruciformPrism + openmc.model.CylinderSector + openmc.model.HexagonalPrism + openmc.model.IsogonalOctagon + openmc.model.OrthogonalBox + openmc.model.Polygon openmc.model.RectangularParallelepiped + openmc.model.RectangularPrism openmc.model.RightCircularCylinder + openmc.model.Vessel openmc.model.XConeOneSided openmc.model.YConeOneSided openmc.model.ZConeOneSided diff --git a/docs/source/pythonapi/stats.rst b/docs/source/pythonapi/stats.rst index 1d4ef03026..c8318ba862 100644 --- a/docs/source/pythonapi/stats.rst +++ b/docs/source/pythonapi/stats.rst @@ -22,7 +22,14 @@ Univariate Probability Distributions openmc.stats.Legendre openmc.stats.Mixture openmc.stats.Normal - openmc.stats.Muir + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.stats.delta_function + openmc.stats.muir Angular Distributions --------------------- @@ -51,3 +58,12 @@ Spatial Distributions openmc.stats.SphericalIndependent openmc.stats.Box openmc.stats.Point + openmc.stats.MeshSpatial + openmc.stats.PointCloud + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.stats.spherical_uniform diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index e3138a53b0..0f887940ea 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -8,40 +8,43 @@ This quick install guide outlines the basic steps needed to install OpenMC on your computer. For more detailed instructions on configuring and installing OpenMC, see :ref:`usersguide_install` in the User's Manual. ----------------------------------------- -Installing on Linux/Mac with conda-forge ----------------------------------------- +---------------------------------- +Installing on Linux/Mac with Conda +---------------------------------- -`Conda `_ is an open source package management -system and environment management system for installing multiple versions of -software packages and their dependencies and switching easily between them. If -you have `conda` installed on your system, OpenMC can be installed via the -`conda-forge` channel. First, add the `conda-forge` channel with: +`Conda `_ is an open source package management +system and environments management system for installing multiple versions of +software packages and their dependencies and switching easily between them. +OpenMC can be installed in a `conda` environment. First, `conda` should be +`installed `_ +with either Anaconda Distribution or Miniconda. Once you have `conda` installed +on your system, OpenMC can be installed via the `conda-forge` channel. + +First, add the `conda-forge` channel with: .. code-block:: sh conda config --add channels conda-forge + conda config --set channel_priority strict -To list the versions of OpenMC that are available on the `conda-forge` channel, -in your terminal window or an Anaconda Prompt run: - -.. code-block:: sh - - conda search openmc - -OpenMC can then be installed with: - -.. code-block:: sh - - conda create -n openmc-env openmc - -This will install OpenMC in a conda environment called `openmc-env`. To activate -the environment, run: +Then create and activate a new conda enviroment called `openmc-env` (or whatever +you wish) with OpenMC installed. .. code-block:: sh + conda create --name openmc-env openmc conda activate openmc-env +If you are installing on macOS with an Apple silicon ARM-based processor, you +will also need to specify the `--platform` option: + +.. code-block:: sh + + conda create --name openmc-env --platform osx-64 openmc + +You are now in a conda environment called `openmc-env` that has OpenMC +installed. + ------------------------------------------- Installing on Linux/Mac/Windows with Docker ------------------------------------------- @@ -90,31 +93,53 @@ can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ .. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html --------------------------------- -Installing from Source on Ubuntu --------------------------------- +------------------------------- +Manually Installing from Source +------------------------------- -To build OpenMC from source, several :ref:`prerequisites ` are -needed. If you are using Ubuntu or higher, all prerequisites can be installed -directly from the package manager: +Obtaining prerequisites on Ubuntu +--------------------------------- + +When building OpenMC from source, all :ref:`prerequisites ` can +be installed using the package manager: .. code-block:: sh sudo apt install g++ cmake libhdf5-dev libpng-dev -After the packages have been installed, follow the instructions below for -building and installing OpenMC from source. +After the packages have been installed, follow the instructions to build from +source below. -------------------------------------------- -Installing from Source on Linux or Mac OS X -------------------------------------------- +Obtaining prerequisites on macOS +-------------------------------- + +For an OpenMC build with multithreading enabled, a package manager like +`Homebrew `_ should first be installed. Then, the following +packages should be installed, for example in Homebrew via: + +.. code-block:: sh + + brew install llvm cmake xtensor hdf5 python libomp libpng + +The compiler provided by the above LLVM package should be used in place of the +one provisioned by XCode, which does not support the multithreading library used +by OpenMC. To ensure CMake picks up the correct compiler, make sure that either +the :envvar:`CXX` environment variable is set to the brew-installed ``clang++`` +or that the directory containing it is on your :envvar:`PATH` environment +variable. Common locations for the brew-installed compiler are +``/opt/homebrew/opt/llvm/bin`` and ``/usr/local/opt/llvm/bin``. + +After the packages have been installed, follow the instructions to build from +source below. + +Building Source on Linux or macOS +--------------------------------- All OpenMC source code is hosted on `GitHub `_. If you have `git -`_, the `gcc `_ compiler suite, -`CMake `_, and `HDF5 -`_ installed, you can download and -install OpenMC be entering the following commands in a terminal: +`_, a modern C++ compiler, `CMake `_, +and `HDF5 `_ installed, you can +download and install OpenMC by entering the following commands in a terminal: .. code-block:: sh @@ -134,14 +159,14 @@ should specify an installation directory where you have write access, e.g. cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .. The :mod:`openmc` Python package must be installed separately. The easiest way -to install it is using `pip `_, which is -included by default in Python 3.4+. From the root directory of the OpenMC -distribution/repository, run: +to install it is using `pip `_. +From the root directory of the OpenMC repository, run: .. code-block:: sh - pip install . + python -m pip install . -If you want to build a parallel version of OpenMC (using OpenMP or MPI), -directions can be found in the :ref:`detailed installation instructions +By default, OpenMC will be built with multithreading support. To build +distributed-memory parallel versions of OpenMC using MPI or to configure other +options, directions can be found in the :ref:`detailed installation instructions `. diff --git a/docs/source/releasenotes/0.13.1.rst b/docs/source/releasenotes/0.13.1.rst new file mode 100644 index 0000000000..70c8609ac3 --- /dev/null +++ b/docs/source/releasenotes/0.13.1.rst @@ -0,0 +1,182 @@ +==================== +What's New in 0.13.1 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes many bug fixes as well as improvements in +geometry modeling, mesh functionality, source specification, depletion +capabilities, and other general enhancements. The depletion module features a +new transport operator, :class:`openmc.deplete.IndependentOperator`, that allows +a depletion calculation to be performed using arbitrary one-group cross sections +(e.g., generated by an external solver) along with a +:class:`openmc.deplete.MicroXS` class for managing one-group cross sections. The +track file generation capability has been significantly overhauled and a new +:class:`openmc.Tracks` class was introduced to allow access to information in +track files from the Python API. Support has been added for new ENDF thermal +scattering evaluations that use mixed coherent/incoherent elastic scattering. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +- The ``openmc.deplete.Operator`` class has been renamed + :class:`openmc.deplete.CoupledOperator`. +- The ``openmc.deplete.ResultsList`` class has been renamed to + :class:`openmc.deplete.Results` and no longer requires you to call the + ``from_hdf5()`` method in order to create it; instead, you can directly + instantiate it. +- A few methods that represent k-effective have been renamed for the sake of + consistency: + + - ``openmc.StatePoint.k_combined`` is now :attr:`openmc.StatePoint.keff` + - ``openmc.deplete.ResultsList.get_eigenvalue`` is now + :meth:`openmc.deplete.Results.get_keff` + +- The :class:`openmc.stats.SphericalIndependent` class, which used to + accept a distribution for ``theta`` now accepts a distribution for ``cos_theta`` + instead in order to more easily handle the common case of specifying a uniform + spatial distribution over a sphere (also see the new + :func:`openmc.stats.spherical_uniform` function). + +- If you are building OpenMC from source, note that several of our CMake options + have been changed: + + ========== ====================== + Old option New option + ========== ====================== + debug --- + optimize --- + profile OPENMC_ENABLE_PROFILE + coverage OPENMC_ENABLE_COVERAGE + openmp OPENMC_USE_OPENMP + --- OPENMC_USE_MPI + dagmc OPENMC_USE_DAGMC + libmesh OPENMC_USE_LIBMESH + ========== ====================== + + The ``debug`` and ``optimize`` options have been removed; instead, use the + standard `CMAKE_BUILD_TYPE + `_ + variable. + +------------ +New Features +------------ + +- Two new composite surfaces: :class:`openmc.model.IsogonalOctagon` and + :class:`openmc.model.CylinderSector`. +- The :class:`~openmc.DAGMCUniverse` class now has a + :attr:`~openmc.DAGMCUniverse.bounding_box` attribute and a + :meth:`~openmc.DAGMCUniverse.bounding_region` method. +- When translating a :class:`~openmc.Region` using the + :meth:`~openmc.Region.translate` method, there is now an ``inplace`` argument. +- The :class:`~openmc.Material` class has several new methods and attributes: + + - The :meth:`~openmc.Material.add_components` methods allows you to add + multiple nuclides/elements to a material with a single call by passing a + dictionary. + - The :meth:`~openmc.Material.get_activity` method returns the activity of a + material in Bq, Bq/g, or Bq/cm³. + - The :meth:`~openmc.Material.remove_element` method removes an element from a + material + - The :meth:`~openmc.Material.get_nuclide_atoms` method gives the number of + atoms of each nuclide in a material + +- All mesh classes now have a ``volumes`` property that provides the volume of + each mesh element as well as ``write_data_to_vtk`` methods. +- Support for externally managed MOAB meshes or libMesh meshes. +- Multiple discrete distributions can be merged with the new + :meth:`~openmc.stats.Discrete.merge` method. +- The :func:`openmc.stats.spherical_uniform` function creates a uniform + distribution over a sphere using the + :class:`~openmc.stats.SphericalIndependent` class. +- Univariate distributions in the :mod:`openmc.stats` module now have + ``sample()`` methods. +- An ``openmc_sample_external_source`` function has been added to the C API with + a corresponding Python binding :func:`openmc.lib.sample_external_source`. +- The track file generation capability has been completely overhauled. Track + files now include much more information, and a new :class:`~openmc.Tracks` + class allows access to track file information from the Python API and has a + :meth:`~openmc.Tracks.write_to_vtk` method for writing a VTK file. Multiple + tracks are now written to a single file (one per MPI rank). +- A new :func:`openmc.wwinp_to_wws` function that converts weight windows from a + ``wwinp`` file to a list of :class:`~openmc.WeightWindows` objects. +- The new :meth:`openmc.EnergyFilter.from_group_structure` method provides a + way of creating an energy filter with a group structure identified by name. +- The :class:`openmc.data.Decay` class now has a + :attr:`~openmc.data.Decay.sources` property that provides radioactive decay + source distributions. +- A :class:`openmc.mgxs.ReducedAbsorptionXS` class produces a multigroup cross + section representing "reduced" absorption (absorption less neutron production + from (n,xn) reactions). +- Added support in the Python API and HDF5 nuclear data format for new ENDF + thermal neutron scattering evaluations with mixed coherent elastic and + incoherent elastic. +- CMake now relies on ``find_package(MPI)`` for a more standard means of + identifying an MPI compiler configuration. + +--------- +Bug Fixes +--------- + +- `Fix bug when a rotation matrix is passed to Halfspace.rotate `_ +- `Fix bug for spherical mesh string repr `_ +- `Fix package_data specification to include pyx files `_ +- `Allow meshes with same ID to appear in multiple files `_ +- `Fix overwritten variable in get_libraries_from_xsdata `_ +- `Write output files to correct directory `_ +- `Allow CMake to properly find third-party packages `_ +- `Fix Region.from_expression when ")(" appears in specification `_ +- `Move lost particle reset from finalize() to reset() `_ +- `Minor typo fixes in test_lattice.py `_ +- `Fix color assignment in Universe.plot `_ +- `Several depletion-related fixes `_ +- `Allow control of C++ standard used by compiler `_ +- `Fix IO format documentation for surface source read/write `_ +- `Make sure basis gets set in Plot.from_geometry `_ +- `Improve robustness of torus distance calculation `_ +- `Allow use of redundant fission when adjusting KERMA in from_njoy `_ +- `Disable GNU extensions for CMake target `_ +- `Two from_xml fixes `_ +- `Fix for rare infinite loop when finding cell `_ +- `Allow photon heating to be tallied by nuclide `_ +- `Use UTF-8 encoding when reading dose coefficients `_ +- `Fix a corner case in Region.from_expression `_ +- `Fix bug in spherical and cylindrical meshes `_ +- `Ensure weight window bounds are flattened when writing to XML `_ +- `Fix for std::cout sync bug in output.cpp `_ +- `Allow compiling against fmt v9 `_ +- `Fix TimeFilter for small time intervals `_ + +------------ +Contributors +------------ + +- `David Andrs `_ +- `Hunter Belanger `_ +- `Helen Brooks `_ +- `Rémi Delaporte-Mathurin `_ +- `Joffrey Dorville `_ +- `Christopher Fichtlscherer `_ +- `Lewis Gross `_ +- `Andrew Johnson `_ +- `Kalin Kiesling `_ +- `Amanda Lund `_ +- `Richard Morrison `_ +- `Patrick Myers `_ +- `Adam Nelson `_ +- `April Novak `_ +- `Ethan Peterson `_ +- `Gavin Ridley `_ +- `Paul Romano `_ +- `Jonathan Shimwell `_ +- `Patrick Shriwise `_ +- `Amelia Trainer `_ +- `John Tramm `_ +- `Bob Urberger `_ +- `Olek Yardas `_ diff --git a/docs/source/releasenotes/0.13.2.rst b/docs/source/releasenotes/0.13.2.rst new file mode 100644 index 0000000000..b0c9b0691e --- /dev/null +++ b/docs/source/releasenotes/0.13.2.rst @@ -0,0 +1,99 @@ +==================== +What's New in 0.13.2 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes several bug fixes, performance improvements for +complex geometries and depletion simulations, and other general enhancements. +Notably, a capability has been added to compute the photon spectra from decay of +unstable nuclides. Alongside that, a new :data:`openmc.config` configuration +variable has been introduced that allows easier configuration of data sources. +Additionally, users can now perform cell or material rejection when sampling +external source distributions. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +- If you are building against libMesh for unstructured mesh tally support, + version 1.6 or higher is now required. +- The ``openmc.stats.Muir`` class has been replaced by a + :func:`openmc.stats.muir` function that returns an instance of + :class:`openmc.stats.Normal`. + +------------ +New Features +------------ + +- The :meth:`openmc.Material.get_nuclide_atom_densities` method now takes an + optional ``nuclide`` argument. +- Functions/methods in the :mod:`openmc.deplete` module now accept times in + Julian years (``'a'``). +- The :meth:`openmc.Universe.plot` method now allows a pre-existing axes object + to be passed in. +- Performance optimization for geometries with many complex regions. +- Performance optimization for depletion by avoiding deepcopies and caching + reaction rates. +- The :class:`openmc.RegularMesh` class now has a + :meth:`~openmc.RegularMesh.from_domain` classmethod. +- The :class:`openmc.CylindricalMesh` class now has a + :meth:`~openmc.CylindricalMesh.from_domain` classmethod. +- Improved method to condense diffusion coefficients from the :mod:`openmc.mgxs` + module. +- A new :data:`openmc.config` configuration variable has been introduced that + allows data sources to be specified at runtime or via environment variables. +- The :class:`openmc.EnergyFunctionFilter` class now supports multiple + interpolation schemes, not just linear-linear interpolation. +- The :class:`openmc.DAGMCUniverse` class now has ``material_names``, + ``n_cells``, and ``n_surfaces`` attributes. +- A new :func:`openmc.data.decay_photon_energy` function has been added that + returns the energy spectrum of photons emitted from the decay of an unstable + nuclide. +- The :class:`openmc.Material` class also has a new + :attr:`~openmc.Material.decay_photon_energy` attribute that gives the decay + photon energy spectrum from the material based on its constituent nuclides. +- The :class:`openmc.deplete.StepResult` now has a + :meth:`~openmc.deplete.StepResult.get_material` method. +- The :class:`openmc.Source` class now takes a ``domains`` argument that + specifies a list of cells, materials, or universes that is used to reject + source sites (i.e., if the sampled sites are not within the specified domain, + they are rejected). + +--------- +Bug Fixes +--------- + +- `Delay call to Tally::set_strides `_ +- `Fix reading reference direction from XML for angular distributions `_ +- `Fix erroneous behavior in Material.add_components `_ +- `Fix reading thermal elastic data from ACE `_ +- `Fix reading source file with time attribute `_ +- `Fix conversion of multiple thermal scattering data files from ACE `_ +- `Fix reading values from wwinp file `_ +- `Handle possibility of .ppm file in Universe.plot `_ +- `Update volume calc types to mitigate overflow issues `_ + +------------ +Contributors +------------ + +- `Lewis Gross `_ +- `Andrew Johnson `_ +- `Miriam Kreher `_ +- `James Logan `_ +- `Jose Ignacio Marquez Damien `_ +- `Josh May `_ +- `Patrick Myers `_ +- `Adam Nelson `_ +- `April Novak `_ +- `Ethan Peterson `_ +- `Gavin Ridley `_ +- `Paul Romano `_ +- `Patrick Shriwise `_ +- `Jonathan Shimwell `_ +- `Olek Yardas `_ diff --git a/docs/source/releasenotes/0.13.3.rst b/docs/source/releasenotes/0.13.3.rst new file mode 100644 index 0000000000..c2debbcb31 --- /dev/null +++ b/docs/source/releasenotes/0.13.3.rst @@ -0,0 +1,134 @@ +==================== +What's New in 0.13.3 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes many bug fixes, performance improvements, and +several notable new features. Some of the highlights include support for MCPL +source files, NCrystal thermal scattering materials, and a new +:class:`openmc.stats.MeshSpatial` class that allows a source distribution to be +specified over a mesh. Additionally, OpenMC now allows you to export your model +as a single XML file rather than separate XML files for geometry, materials, +settings, and tallies. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +- Atomic mass data used in :func:`openmc.data.atomic_mass` has been updated to + AME 2020, which results in slightly different masses. + +------------ +New Features +------------ + +- Support was added for `MCPL `_ files to be + used as external sources. Additionally, source points and surfaces sources can + be written as MCPL files instead of HDF5 files. (`#2116 + `_) +- Support was added for `NCrystal `_ + thermal scattering materials. (`#2222 + `_) +- The :class:`~openmc.CylindricalMesh` and :class:`~openmc.SphericalMesh` + classes now have an ``origin`` attribute that changes the center of the mesh. + (`#2256 `_) +- A new :class:`openmc.model.Polygon` class allows defining generalized 2D + polygons. (`#2266 `_) +- A new :func:`openmc.data.decay_energy` function and + :meth:`openmc.Material.get_decay_heat` method enable determination of decay + heat from a single nuclide or material. (`#2287 + `_) +- Full models can now be written as a single XML file rather than separate + geometry, materials, settings, and tallies XML files. (`#2291 + `_) +- Discrete distributions are now sampled using alias sampling, which is O(1) in + time. (`#2329 `_) +- The new :class:`openmc.stats.MeshSpatial` allows a spatial source distribution + to be specified with source strengths for each mesh element. (`#2334 + `_) +- The new :meth:`openmc.Geometry.get_surfaces_by_name` method returns a list of + matching surfaces in a geometry. (`#2347 + `_) +- A new :attr:`openmc.Settings.create_delayed_neutrons` attribute controls + whether delayed neutrons are created during a simulation. (`#2348 + `_) +- The :meth:`openmc.deplete.Results.export_to_materials` method now takes a + ``path`` argument. (`#2364 `_) +- A new :meth:`openmc.EnergyFilter.get_tabular` method allows one to create a + tabular distribution based on tally results using an energy filter. (`#2371 + `_) +- Several methods in the :class:`openmc.Material` class that require a volume to + be set (e.g., :meth:`~openmc.Material.get_mass`) now accept a ``volume`` + argument. (`#2412 `_) + +--------- +Bug Fixes +--------- + +- Fix for finding redundant surfaces (`#2263 `_) +- Adds tolerance for temperatures slightly out of bounds (`#2265 `_) +- Fix getter/setter for weight window bounds (`#2275 `_) +- Make sure Chain.reduce preserves decay source (`#2283 `_) +- Fix array shape for weight window bounds (`#2284 `_) +- Fix for non-zero CDF start points in TSL data (`#2290 `_) +- Fix a case where inelastic scattering yield is zero (`#2295 `_) +- Prevent Compton profile out-of-bounds memory access (`#2297 `_) +- Produce light particles from decay (`#2301 `_) +- Fix zero runtime attributes in depletion statepoints (`#2302 `_) +- Fix bug in openmc.Universe.get_nuclide_densities (`#2310 `_) +- Only show print output from depletion on rank 0 (`#2311 `_) +- Fix photon transport with no atomic relaxation data (`#2312 `_) +- Fix for precedence in region expressions (`#2318 `_) +- Allow source particles with energy below cutoff (`#2319 `_) +- Fix IncidentNeutron.from_njoy for high temperatures (`#2320 `_) +- Add capability to unset cell temperatures (`#2323 `_) +- Fix in plot_xs when S(a,b) tables are present (`#2335 `_) +- Various fixes for tally triggers (`#2344 `_) +- Raise error when mesh is flat (`#2363 `_) +- Don't call normalize inside Tabular.mean (`#2375 `_) +- Avoid out-of-bounds access in inelastic scatter sampling (`#2378 `_) +- Use correct direction for anisotropic fission (`#2381 `_) +- Fix several thermal scattering nuclide assignments (`#2382 `_) +- Fix _materials_by_id attribute in Model (`#2385 `_) +- Updates to batch checks for simulation restarts (`#2390 `_) +- write_data_to_vtk volume normalization correction (`#2397 `_) +- Enable generation of JEFF 3.3 depletion chain (`#2410 `_) +- Fix spherical to Cartesian coordinate conversion (`#2417 `_) +- Handle zero photon cross sections in IncidentPhoton.from_ace (`#2433 `_) +- Fix hybrid depletion when nuclides are not present (`#2436 `_) +- Fix bug in cylindrical and spherical meshes (`#2439 `_) +- Improvements to mesh radial boundary coincidence (`#2443 `_) + +------------ +Contributors +------------ + +- `Hunter Belanger `_ +- `Rémi Delaporte-Mathurin `_ +- `Christopher Fichtlscherer `_ +- `Valerio Giusti `_ +- `Chris Keckler `_ +- `Kalin Kiesling `_ +- `Thomas Kittelmann `_ +- `Erik Knudsen `_ +- `Colin Larmier `_ +- `Amanda Lund `_ +- `Jose Ignacio Marquez Damien `_ +- `Josh May `_ +- `Patrick Myers `_ +- `Baptiste Mouginot `_ +- `April Novak `_ +- `Matthew Nyberg `_ +- `Ethan Peterson `_ +- `Gavin Ridley `_ +- `Paul Romano `_ +- `Patrick Shriwise `_ +- `Jonathan Shimwell `_ +- `Paul Wilson `_ +- `Olek Yardas `_ +- `Jiankai Yu `_ diff --git a/docs/source/releasenotes/0.14.0.rst b/docs/source/releasenotes/0.14.0.rst new file mode 100644 index 0000000000..f82aa30850 --- /dev/null +++ b/docs/source/releasenotes/0.14.0.rst @@ -0,0 +1,284 @@ +==================== +What's New in 0.14.0 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes many bug fixes, performance improvements, and +several notable new features. Some of the highlights include projection plots, +pulse height tallies for photons, weight window generation, and an ability to +specify continuous removal or feed of nuclides/elements during depletion. +Additionally, one of the longstanding annoyances of depletion calculations, +namely the need to include initial "dilute" nuclides, has been eliminated. There +are also a wide array of general improvements in the Python API. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +- The :class:`openmc.deplete.MicroXS` has been completely redesigned and + improved. See further comments below under "New Features". (`#2572 + `_, `#2579 + `_, `#2595 + `_, `#2700 + `_) +- The ``rectangular_prism`` function has been replaced by the + :class:`openmc.model.RectangularPrism` class and the ``hexagonal_prism`` + function has been replaced by the :class:`openmc.model.HexagonalPrism` class. + Note that whereas the ``rectangular_prism`` and ``hexagonal_prism`` functions + returned a region representing the interior of the prism, the new + :class:`~openmc.model.RectangularPrism` and + :class:`~openmc.model.HexagonalPrism` classes return composite surfaces, so + you need to use the unary ``-`` or ``+`` operators to obtain a region that can + be assigned to a cell. (`#2739 + `_) +- The ``Source`` class has been refactored and split up into three separate + classes: :class:`~openmc.IndependentSource`, :class:`~openmc.FileSource`, and + :class:`~openmc.CompiledSource`. (`#2524 + `_) +- The ``vertices`` and ``centroids`` attributes on mesh classes now always + return Cartesian coordinates and the shape of the returned arrays has changed + to allow `ijk` indexing using a tuple (i.e., `xyz = vertices[i, j, k]`). + (`#2711 `_) +- The :attr:`openmc.Material.decay_photon_energy` attribute has been replaced by + the :meth:`openmc.Material.get_decay_photon_energy` method. (`#2715 + `_) + +------------ +New Features +------------ + +- A new :class:`openmc.WireframeRayTracePlot` class enables the generation of orthographic or + perspective projection plots. (`#1926 + `_) +- The :class:`openmc.model.RightCircularCylinder` class now supports optional + filleted edges. (`#2309 `_) +- Continuous removal or feed of nuclides/elements between materials can now be + modeled during depletion via the + :meth:`openmc.deplete.abc.Integrator.add_transfer_rate` method. (`#2358 + `_, `#2564 + `_, `#2626 + `_) +- The MAGIC method for global weight window generation has been implemented as + part of the C++ API. (`#2359 + `_) +- A new capability for pulse height tallies (currently limited to photons) has + been added and can be used via the "pulse-height" tally score. (`#2452 + `_) +- A :class:`openmc.model.CruciformPrism` class has been added that provides a + generalized cruciform prism composite surface. (`#2457 + `_) +- Type hints have been added in various places throughout the Python API. + (`#2462 `_, `#2467 + `_, `#2468 + `_, `#2470 + `_, `#2471 + `_, `#2601 + `_) +- Voxel plots can now be generated through the :meth:`openmc.Plot.to_vtk` + method. (`#2464 `_) +- The :class:`openmc.mgxs.EnergyGroups` class now allows you to alternatively + pass a string of the group structure name (e.g., "CCFE-709") instead of the + energy group boundaries. (`#2466 + `_) +- Several enhancements have been made to the :meth:`openmc.Universe.plot` method + (addition of axis labels with units, ability to show legend and/or outlines, automatic + determination of origin/width, ability to pass total number of pixels). + (`#2472 `_, `#2482 + `_, `#2483 + `_, `#2492 + `_, `#2513 + `_, `#2575 + `_) +- Functionality in the Python dealing with bounding boxes now relies on a new + :class:`openmc.BoundingBox` class. (`#2475 + `_) +- Users now have more flexibility in specifying nuclides and reactions in the + :func:`openmc.plot_xs` function. (`#2478 + `_) +- The import time of the :mod:`openmc` Python module has been improved by + deferring the import of matplotlib. (`#2488 + `_) +- Mesh clases in the Python API now support a ``bounding_box`` property. (`#2507 + `_, `#2620 + `_, `#2621 + `_) +- The ``Source`` class has been refactored and split up into three separate + classes: :class:`~openmc.IndependentSource`, :class:`~openmc.FileSource`, and + :class:`~openmc.CompiledSource`. (`#2524 + `_) +- Support was added for curvilinear elements when exporting cylindrical and + spherical meshes to VTK. (`#2533 + `_) +- The :class:`openmc.Tally` class now has a + :attr:`~openmc.Tally.multiply_density` attribute that indicates whether + reaction rate tallies should include the number density of the nuclide of + interest. (`#2539 `_) +- The :func:`~openmc.wwinp_to_wws` function now supports ``wwinp`` files with + cylindrical or spherical meshes. (`#2556 + `_) +- Depletion no longer relies on adding initial "dilute" nuclides to each + depletable material in order to compute reaction rates. (`#2559 + `_, `#2568 + `_) +- The :class:`openmc.deplete.Results` class now has + :meth:`~openmc.deplete.Results.get_mass` (`#2565 + `_), + :meth:`~openmc.deplete.Results.get_activity` (`#2617 + `_), and + :meth:`~openmc.deplete.Results.get_decay_heat` (`#2625 + `_) methods. +- The :meth:`openmc.deplete.StepResult.save` method now supports a ``path`` + argument. (`#2567 `_) +- The :class:`openmc.deplete.MicroXS` has been completely redesigned and + improved. First, it no longer relies on the :mod:`openmc.mgxs` module, no + longer subclasses :class:`pandas.DataFrame`, and doesn't require adding + initial "dilute" nuclides into material compositions. It now enables users to + specify an energy group structure to collect multigroup cross sections, + specify nuclides/reactions, and works with mesh domains in addition to the + existing domains. A new :func:`openmc.deplete.get_microxs_and_flux` function + was added that improves the workflow for calculating microscopic cross + sections along with fluxes. Altogether, these changes make it straightforward + to switch between coupled and independent operators for depletion/activation + calculations. (`#2572 `_, + `#2579 `_, `#2595 + `_, `#2700 + `_) +- The :class:`openmc.Geometry` class now has ``merge_surfaces`` and + ``surface_precision`` arguments. (`#2602 + `_) +- Several predefined energy group structures have been added ("MPACT-51", + "MPACT-60", "MPACT-69", "SCALE-252"). (`#2614 + `_) +- When running a depletion calculation, you are now allowed to include nuclides + in the initial material compositions that do not have neutron cross sections + (decay-only nuclides). (`#2616 + `_) +- The :class:`~openmc.CylindricalMesh` and :class:`~openmc.SphericalMesh` + classes can now be fully formed using the constructor. (`#2619 + `_) +- A time cutoff can now be specified in the :attr:`openmc.Settings.cutoff` + attribute. (`#2631 `_) +- The :meth:`openmc.Material.add_element` method now supports a + ``cross_sections`` argument that allows a cross section data source to be + specified. (`#2633 `_) +- The :class:`~openmc.Cell` class now has a :meth:`~openmc.Cell.plot` method. + (`#2648 `_) +- The :class:`~openmc.Geometry` class now has a :meth:`~openmc.Geometry.plot` + method. (`#2661 `_) +- When weight window checks are performed can now be explicitly specified with + the :attr:`openmc.Settings.weight_window_checkpoints` attribute. (`#2670 + `_) +- The :class:`~openmc.Settings` class now has a + :attr:`~openmc.Settings.max_write_lost_particles` attribute that can limit the + number of lost particle files written. (`#2688 + `_) +- The :class:`~openmc.deplete.CoupledOperator` class now has a + ``diff_volume_method`` argument that specifies how the volume of new materials + should be determined. (`#2691 + `_) +- The :meth:`openmc.DAGMCUniverse.bounding_region` method now has a + ``padding_distance`` argument. (`#2701 + `_) +- A new :meth:`openmc.Material.get_decay_photon_energy` method replaces the + :attr:`decay_photon_energy` attribute and includes an ability to eliminate + low-importance points. This is facilitated by a new + :meth:`openmc.stats.Discrete.clip` method. (`#2715 + `_) +- The :meth:`openmc.model.Model.differentiate_depletable_mats` method allows + depletable materials to be differentiated independent of the depletion + calculation itself. (`#2718 + `_) +- Albedos can now be specified on surface boundary conditions. (`#2724 + `_) + +--------- +Bug Fixes +--------- + +- Enable use of NCrystal materials in plot_xs (`#2435 `_) +- Avoid segfault from extern "C" std::string (`#2455 `_) +- Fix several issues with the Model class (`#2465 `_) +- Provide alternative batch estimation message (`#2479 `_) +- Correct index check for remove_tally (`#2494 `_) +- Support for NCrystal material in from_xml_element (`#2496 `_) +- Fix compilation with gcc 5 (`#2498 `_) +- Fixed in the Tally::add_filter method (`#2501 `_) +- Fix meaning of "masking" for plots (`#2510 `_) +- Fix description of statepoint.batches in Settings class (`#2514 `_) +- Reorder list initialization of Plot constructor (`#2519 `_) +- Added mkdir to cwd argument in Model.run (`#2523 `_) +- Fix export of spherical coordinates in SphericalMesh (`#2538 `_) +- Add virtual destructor on PlottableInterface (`#2541 `_) +- Ensure parent directory is created during depletion (`#2543 `_) +- Fix potential out-of-bounds access in TimeFilter (`#2532 `_) +- Remove use of sscanf for reading surface coefficients (`#2574 `_) +- Fix torus intersection bug (`#2589 `_) +- Multigroup per-thread cache fixes (`#2591 `_) +- Bank surface source particles in all active cycles (`#2592 `_) +- Fix for muir standard deviation (`#2598 `_) +- Check for zero fission cross section (`#2600 `_) +- XML read fixes in Plot classes (`#2623 `_) +- Added infinity check in VolumeCalculation (`#2634 `_) +- Fix sampling issue in Mixture distributions (`#2658 `_) +- Prevent segfault in distance to boundary calculation (`#2659 `_) +- Several CylindricalMesh fixes (`#2676 + `_, `#2680 + `_, `#2684 + `_, `#2710 + `_) +- Add type checks on Intersection, Union, Complement (`#2685 `_) +- Fixed typo in CF4Integrator docstring (`#2704 `_) +- Ensure property setters are used in CylindricalMesh and SphericalMesh (`#2709 `_) +- Fix sample_external_source bug (`#2713 `_) +- Fix localization issue affecting openmc-plotter (`#2723 `_) +- Correct openmc.lib wrapper for evaluate_legendre (`#2729 `_) +- Bug fix in Region.from_expression during tokenization (`#2733 `_) +- Fix bug in temperature interpolation (`#2734 `_) +- Check for invalid domain IDs in volume calculations (`#2742 `_) +- Skip boundary condition check for volume calculations (`#2743 `_) +- Fix loop over coordinates for source domain rejection (`#2751 `_) + +------------ +Contributors +------------ + +- `April Novak `_ +- `Baptiste Mouginot `_ +- `Ben Collins `_ +- `Chritopher Billingham `_ +- `Christopher Fichtlscherer `_ +- `Christina Cai `_ +- `Lorenzo Chierici `_ +- `Huw Rhys Jones `_ +- `Emilio Castro `_ +- `Erik Knudsen `_ +- `Ethan Peterson `_ +- `Egor Afanasenko `_ +- `Paul Wilson `_ +- `Gavin Ridley `_ +- `Hunter Belanger `_ +- `Jack Fletcher `_ +- `John Vincent Cauilan `_ +- `Josh May `_ +- `John Tramm `_ +- `Kevin McLaughlin `_ +- `Yue Jin `_ +- `Lewis Gross `_ +- `Luke Labrie-Cleary `_ +- `Patrick Myers `_ +- `Nicola Rizzi `_ +- `Yuvraj Jain `_ +- `Paul Romano `_ +- `Patrick Shriwise `_ +- `Rosie Barker `_ +- `Jonathan Shimwell `_ +- `John Tchakerian `_ +- `Travis Labossiere-Hickman `_ +- `Xinyan Wang `_ +- `Olek Yardas `_ +- `Zoe Prieto `_ diff --git a/docs/source/releasenotes/0.15.0.rst b/docs/source/releasenotes/0.15.0.rst new file mode 100644 index 0000000000..069bfd7836 --- /dev/null +++ b/docs/source/releasenotes/0.15.0.rst @@ -0,0 +1,262 @@ +==================== +What's New in 0.15.0 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes many bug fixes, performance improvements, and +several notable new features. The major highlight of this release is the +introduction of a new transport solver based on the random ray method, which is +fully described in the :ref:`user's guide `. Other notable additions +include a mesh-based source class (:class:`openmc.MeshSource`), a generalization +of source domain rejection through the notion of "constraints", and new methods +on mesh-based classes for computing material volume fractions and homogenized +materials. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +Previously, specifying domain rejection for a source was only possible on the +:class:`~openmc.IndependentSoure` class and worked by specifying a `domains` +argument. This capability has been generalized to all source classes and +expanded as well; specifying a domain to reject on should now be done with the +`constraints` argument as follows:: + + source = openmc.IndependentSource(..., constraints={'domains': [cell]}) + +The `domains` argument is deprecated and will be removed in a future version of +OpenMC. Similarly, the ``only_fissionable`` argument to +:class:`openmc.stats.Box` has been replaced by a `'fissionable'` constraint. +That is, instead of specifying:: + + space = openmc.stats.Box(lower_left, upper_right, only_fissionable=True) + source = openmc.IndependentSource(space=space) + +You should now provide the constraint as:: + + space = openmc.stats.Box(lower_left, upper_right) + source = openmc.IndependentSource(space=space, constraints={'fissionable': True}) + +The :attr:`openmc.Settings.max_splits` attribute was renamed to +``max_history_splits`` and its default value has been changed to 1e7 (`#2954 +`_). + +------------ +New Features +------------ + +- When running OpenMC in volume calculation mode, only atomic weight ratio data + is loaded from data files which reduces initialization time. (`#2741 + `_) +- Introduced a ``GeometryState`` class in C++ to better separate particle and + geometry data. (`#2744 `_)) +- A new :class:`openmc.MaterialFromFilter` class allows filtering tallies by + which material a particle came from. (`#2750 + `_) +- Implemented a :meth:`openmc.deplete.MicroXS.from_multigroup_flux` method that + generates microscopic cross sections for depletion from a predetermined + multigroup flux. (`#2755 `_) +- A new :class:`openmc.MeshSource` class enables the specification of a source + distribution over a mesh, where each mesh element has a different + energy/angle/time distribution. (`#2759 + `_) +- Improve performance of depletion solver by utilizing CSR sparse matrix + representation. (`#2764 `_, + `#2771 `_) +- Added a :meth:`openmc.CylindricalMesh.get_indices_at_coords` method that + provides the mesh element index corresponding to a given point in space. + (`#2782 `_) +- Added a `path` argument to the :meth:`openmc.deplete.Integrator.integrate` + method. (`#2784 `_) +- Added a :meth:`openmc.Geometry.get_all_nuclides` method. (`#2796 + `_) +- A new capability to compute material volume fractions over mesh elements was + added in the :meth:`openmc.lib.Mesh.material_volumes` method. (`#2802 + `_) +- A new transport solver was added based on the `random ray + `_ method. (`#2823 + `_, `#2988 + `_) +- Added a :attr:`openmc.lib.Material.depletable` attribute. (`#2843 + `_) +- Added a :meth:`openmc.lib.Mesh.get_plot_bins` method and corresponding + ``openmc_mesh_get_plot_bins`` C API function that can be utilized to generate + mesh tally visualizations in the plotter application. (`#2854 + `_) +- Introduced a :func:`openmc.read_source_file` function that enables reading a + source file from the Python API. (`#2858 + `_) +- Added a ``bounding_box`` property on the :class:`openmc.RectilinearMesh` and + :class:`openmc.UnstructuredMesh` classes. (`#2861 + `_) +- Added a ``openmc_mesh_get_volumes`` C API function. (`#2869 + `_) +- The :attr:`openmc.Settings.surf_source_write` dictionary now accepts `cell`, + `cellfrom`, or `cellto` keys that limit surface source sites to those entering + or leaving specific cells. (`#2888 + `_) +- Added a :meth:`openmc.Region.plot` method that allows regions to be plotted + directly. (`#2895 `_) +- Implemented "contains" operator for the :class:`openmc.BoundingBox` class. + (`#2906 `_) +- Generalized source rejection via a new ``constraints`` argument to all source + classes. (`#2916 `_) +- Added a new :class:`openmc.MeshBornFilter` class that filters tally events + based on which mesh element a particle was born in. (`#2925 + `_) +- The :class:`openmc.Trigger` class now has a ``ignore_zeros`` argument that + results in any bins with zero score to be ignored when checking the trigger. + (`#2928 `_) +- Introduced a :attr:`openmc.Settings.max_events` attribute that controls the + maximum number of events a particle can undergo. (`#2945 + `_) +- Added support for :class:`openmc.UnstructuredMesh` in the + :class:`openmc.MeshSource` class. (`#2949 + `_) +- Added a :meth:`openmc.MeshBase.get_homogenized_materials` method that computes + homogenized materials over mesh elements. (`#2971 + `_) +- Add an ``options`` argument to :class:`openmc.UnstructuredMesh` that allows + configuring underlying data structures in MOAB. (`#2976 + `_) +- Type hints were added to several classes in the :mod:`openmc.deplete` module. + (`#2866 `_) + +--------- +Bug Fixes +--------- + +- Fix unit conversion in openmc.deplete.Results.get_mass (`#2761 `_) +- Fix Lagrangian interpolation (`#2775 `_) +- Depletion restart with MPI (`#2778 `_) +- Modify depletion transfer rates test to be more robust (`#2779 `_) +- Call simulation_finalize if needed when finalizing OpenMC (`#2790 `_) +- F90_NONE Removal (MGMC tallying optimization) (`#2785 `_) +- Correctly apply volumes to materials when using DAGMC geometries (`#2787 `_) +- Add inline to openmc::interpolate (`#2789 `_) +- Use huge_tree=True in lxml parsing (`#2791 `_) +- OpenMPMutex "Copying" (`#2794 `_) +- Do not link against several transitive dependencies of HDF5 (`#2797 `_) +- Added check to length of input arguments for IndependantOperator (`#2799 `_) +- Pytest Update Documentation (`#2801 `_) +- Move 'import lxml' to third-party block of imports (`#2803 `_) +- Fix creation of meshes when from loading settings from XML (`#2805 `_) +- Avoid high memory use when writing unstructured mesh VTK files (`#2806 `_) +- Consolidating thread information into the openmp interface header (`#2809 `_) +- Prevent underflow in calculation of speed (`#2811 `_) +- Provide error message if a cell path can't be determined (`#2812 `_) +- Fix distribcell labels for lattices used as fill in multiple cells (`#2813 `_) +- Make creation of spatial trees based on usage for unstructured mesh. (`#2815 `_) +- Ensure particle direction is normalized for plotting / volume calculations (`#2816 `_) +- Added missing meshes to documentation (`#2820 `_) +- Reset timers at correct place in deplete (`#2821 `_) +- Fix config change not propagating through to decay energies (`#2825 `_) +- Ensure that implicit complement cells appear last in DAGMC universes (`#2838 `_) +- Export model.tallies to XML in CoupledOperator (`#2840 `_) +- Fix locating h5m files references in DAGMC universes (`#2842 `_) +- Prepare for NumPy 2.0 (`#2845 `_) +- Added missing functions and classes to openmc.lib docs (`#2847 `_) +- Fix compilation on CentOS 7 (missing link to libdl) (`#2849 `_) +- Adding resulting nuclide to cross section plot legend (`#2851 `_) +- Updating file extension for Excel files when exporting MGXS data (`#2852 `_) +- Removed error raising when calling warn (`#2853 `_) +- Setting ``surf_source_`` attribute for DAGMC surfaces. (`#2857 `_) +- Changing y axis label for heating plots (`#2859 `_) +- Removed unused step_index arg from restart (`#2867 `_) +- Fix issue with Cell::get_contained_cells() utility function (`#2873 `_) +- Adding energy axis units to plot xs (`#2876 `_) +- Set OpenMCOperator materials when diff_burnable_mats = True (`#2877 `_) +- Fix expansion filter merging (`#2882 `_) +- Added checks that tolerance value is between 0 and 1 (`#2884 `_) +- Statepoint file loading refactor and CAPI function (`#2886 `_) +- Added check for length of value passed into EnergyFilter (`#2887 `_) +- Ensure that Model.run() works when specifying a custom XML path (`#2889 `_) +- Updating docker file base to bookworm (`#2890 `_) +- Clarifying documentation for cones (`#2892 `_) +- Abort on cmake config if openmp requested but not found (`#2893 `_) +- Tiny updates from experience building on Mac (`#2894 `_) +- Added damage-energy as optional reaction for micro (`#2903 `_) +- docs: add missing max_splits in settings specification (`#2910 `_) +- Changed CI to use latest actions to get away from the Node 16 deprecation. (`#2912 `_) +- Mkdir to always allow parents and exist ok (`#2914 `_) +- Fixed small sphinx typo (`#2915 `_) +- Hexagonal lattice iterators (`#2921 `_) +- Fix Chain.form_matrix to work with scipy 1.12 (`#2922 `_) +- Allow get_microxs_and_flux to use OPENMC_CHAIN_FILE environment variable (`#2934 `_) +- Polygon fix to better handle colinear points (`#2935 `_) +- Fix CMFD to work with scipy 1.13 (`#2936 `_) +- Print warning if no natural isotopes when using add_element (`#2938 `_) +- Update xtl and xtensor submodules (`#2941 `_) +- Ensure two surfaces with different boundary type are not considered redundant (`#2942 `_) +- Updated package versions in Dockerfile (`#2946 `_) +- Add MPI calls to DAGMC external test (`#2948 `_) +- Eliminate deprecation warnings from scipy and pandas (`#2951 `_) +- Update math function unit test with catch2 (`#2955 `_) +- Support track file writing for particle restart runs. (`#2957 `_) +- Make UWUW optional (`#2965 `_) +- Allow pure decay IndependentOperator (`#2966 `_) +- Added fix to cfloat_endf for length 11 endf floats (`#2967 `_) +- Moved apt get to optional CI parts (`#2970 `_) +- Update bounding_box docstrings (`#2972 `_) +- Added extra error checking on spherical mesh creation (`#2973 `_) +- Update CODEOWNERS file (`#2974 `_) +- Added error checking on cylindrical mesh (`#2977 `_) +- Correction for histogram interpolation of Tabular distributions (`#2981 `_) +- Enforce lower_left in lattice geometry (`#2982 `_) +- Update random_dist.h comment to be less specific (`#2991 `_) +- Apply memoization in get_all_universes (`#2995 `_) +- Make sure skewed dataset is cast to bool properly (`#3001 `_) +- Hexagonal lattice roundtrip (`#3003 `_) +- Fix CylinderSector and IsogonalOctagon translations (`#3018 `_) +- Sets used instead of lists when membership testing (`#3021 `_) +- Fixing plot xs for when plotting element string reaction (`#3029 `_) +- Fix shannon entropy broken link (`#3034 `_) +- Only add png or h5 extension if not present in plots.py (`#3036 `_) +- Fix non-existent path causing segmentation fault when saving plot (`#3038 `_) +- Resolve warnings related to numpy 2.0 (`#3044 `_) +- Update IsogonalOctagon to use xz basis (`#3045 `_) +- Determine whether nuclides are fissionable in volume calc mode (`#3047 `_) +- Avoiding more numpy 2.0 deprecation warnings (`#3049 `_) +- Set DAGMC cell instances on surface crossing (`#3052 `_) + +------------ +Contributors +------------ + +- `Aidan Crilly `_ +- `April Novak `_ +- `Davide Mancusi `_ +- `Baptiste Mouginot `_ +- `Chris Wagner `_ +- `Lorenzo Chierici `_ +- `Catherine Yu `_ +- `Erik Knudsen `_ +- `Ethan Peterson `_ +- `Gavin Ridley `_ +- `hsameer481 `_ +- `Hunter Belanger `_ +- `Isaac Meyer `_ +- `Jin Whan Bae `_ +- `Joffrey Dorville `_ +- `John Tramm `_ +- `Yue Jin `_ +- `Sigfrid Stjärnholm `_ +- `Kimberly Meagher `_ +- `lhchg `_ +- `Luke Labrie-Cleary `_ +- `Micah Gale `_ +- `Nicholas Linden `_ +- `pitkajuh `_ +- `Rosie Barker `_ +- `Paul Romano `_ +- `Patrick Shriwise `_ +- `Jonathan Shimwell `_ +- `Travis Labossiere-Hickman `_ +- `Vanessa Lulla `_ +- `Olek Yardas `_ +- `Perry Young `_ diff --git a/docs/source/releasenotes/0.15.1.rst b/docs/source/releasenotes/0.15.1.rst new file mode 100644 index 0000000000..d879b50edd --- /dev/null +++ b/docs/source/releasenotes/0.15.1.rst @@ -0,0 +1,224 @@ +==================== +What's New in 0.15.1 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes many bug fixes, performance improvements, and +several notable new features. The random ray solver continues to receive many +updates and improvements, which are listed below in more detail. A new +:class:`~openmc.SolidRayTracePlot` class has been added that enables attractive +3D visualization using Phong shading. Several composite surfaces have been +introduced (which help to further expand the capabilities of the +`openmc_mcnp_adapter `_). +The :meth:`openmc.Mesh.material_volumes` method has been completely +reimplemented with a new approach based on ray tracing that greatly improves +performance and can be executed in parallel. Tally results can be automatically +applied to input :class:`~openmc.Tally` objects with :meth:`openmc.Model.run`, +bypassing boilerplate code for collecting tally results from statepoint files. +Finally, a new :mod:`openmc.deplete.d1s` submodule has been added that enables +Direct 1-Step (D1S) calculations of shutdown dose rate for fusion applications. + +------------------------------------ +Compatibility Notes and Deprecations +------------------------------------ + +The ``openmc.ProjectionPlot`` class has been renamed to +:class:`openmc.WireframeRayTracePlot` to be in better alignment with the newly +introduced :class:`openmc.SolidRayTracePlot` class. + +NCrystal has been moved from a build-time dependency to a runtime dependency, +which means there is no longer a ``OPENMC_USE_NCRYSTAL`` CMake option. Instead, +OpenMC will look for an installed version of NCrystal using the +``ncrystal-config`` command. + +------------ +New Features +------------ + +- Numerous improvements have been made in the random ray solver: + - Calculation of Shannon entropy now works with random ray (`#3030 `_) + - Support for linear sources (`#3072 `_) + - Ability to slove for adjoint flux (`#3191 `_) + - Support randomized Quasi-Monte Carlo sampling (`#3268 `_) + - FW-CADIS weight window generation (`#3273 `_) + - Source region mesh subdivision(`#3333 `_) +- Several new composite surfaces have been added: + - :class:`openmc.model.OrthogonalBox` (`#3118 `_) + - :class:`openmc.model.ConicalFrustum` (`#3151 `_) + - :class:`openmc.model.Vessel` (`#3168 `_) +- The :meth:`openmc.Model.plot` method now supports plotting source sites + (`#2863 `_) +- The :func:`openmc.stats.delta_function` convenience function can be used for + specifying distributions with a single point (`#3090 + `_) +- Added a :meth:`openmc,Material.get_element_atom_densities` method (`#3103 + `_) +- Several third-party dependencies have been removed: + - Cython (`#3111 `_) + - gsl-lite (`#3225 `_) +- Added a new :class:`openmc.MuSurfaceFilter` class that filters tally events by + the cosine of angle of a surface crossing (`#2768 + `_) +- Introduced a :class:`openmc.ParticleList` class for manipulating a list of + source particles (`#3148 `_) +- Support dose coefficients from ICRP 74 in + :func:`openmc.data.dose_coefficients` (`#3020 + `_) +- Introduced a new :attr:`openmc.Settings.uniform_source_sampling` option + (`#3195 `_) +- Ability to differentiate materials in DAGMC universes (`#3056 + `_) +- Added methods to automatically apply results to existing Tally objects. + (`#2671 `_) +- Implemented a new :class:`openmc.SolidRayTracePlot` class that can produce a + 3D visualization based on Phong shading (`#2655 + `_) +- The :meth:`openmc.UnstructuredMesh.write_data_to_vtk` method now supports + writing a VTU file (`#3290 `_) +- Composite surfaces now have a + :attr:`~openmc.CompositeSurface.component_surfaces` attribute that provides + the underlying primitive surfaces (`#3167 + `_) +- A new :mod:`openmc.deplete.d1s` submodule has been added that enables Direct + 1-Step (D1S) calculations of shutdown dose rate for fusion applications + (`#3235 `_) + +--------------------------- +Bug Fixes and Small Changes +--------------------------- + +- run microxs with mpi (`#3028 `_) +- Rely on std::filesystem for file_utils (`#3042 `_) +- Random Ray Normalization Improvements (`#3051 `_) +- Alternative Random Ray Volume Estimators (`#3060 `_) +- Random Ray Testing Simplification (`#3061 `_) +- Fix hyperlinks in `random_ray.rst` (`#3064 `_) +- Add missing show_overlaps option to plots.xml input file documentation (`#3068 `_) +- Remove use of pkg_resources package (`#3069 `_) +- Add option for survival biasing source normalization (`#3070 `_) +- Enforce sequence type when setting ``Setting.track`` (`#3071 `_) +- Moving most of setup.py to pyproject.toml (`#3074 `_) +- Enforce non-negative percents for ``material.add_nuclide`` to prevent unintended ao/wo flipping (`#3075 `_) +- Include batch statistics discussion in methodology introduction (`#3076 `_) +- Add -DCMAKE_BUILD_TYPE=Release flag for MOAB in Dockerfile (`#3077 `_) +- Adjust decay data reader to better handle non-normalized branching ratios (`#3080 `_) +- Correct openmc.Geometry initializer to accept iterables of ``openmc.Cell`` (`#3081 `_) +- Replace all deprecated Python typing imports and syntax with updated forms (`#3085 `_) +- Fix ParticleFilter to work with set inputs (`#3092 `_) +- packages used for testing moved to tests section of pyprojects.toml (`#3094 `_) +- removed unused which function in CI scripts (`#3095 `_) +- Improve description of probabilities for ``openmc.stats.Tabular`` class (`#3099 `_) +- Ensure RegularMesh repr shows value for width of the mesh (`#3100 `_) +- Replacing endf c functions with package (`#3101 `_) +- Fix random ray solver to correctly simulate fixed source problems with fissionable materials (`#3106 `_) +- Improve error for nuclide temperature not found (`#3110 `_) +- Added error if cross sections path is a folder (`#3115 `_) +- Implement bounding_box operation for meshes (`#3119 `_) +- allowing varible offsets for ``polygon.offset`` (`#3120 `_) +- Write surface source files per batch (`#3124 `_) +- Mat ids reset (`#3125 `_) +- Tweaking title of feature issue template (`#3127 `_) +- Fix a typo in feature request template (`#3128 `_) +- Update quickinstall instructions for macOS (`#3130 `_) +- adapt the openmc-update-inputs script for surfaces (`#3131 `_) +- Theory documentation on PCG random number generator (`#3134 `_) +- Adding tmate action to CI for debugging (`#3138 `_) +- Add Versioning Support from `version.txt` (`#3140 `_) +- Correct failure due to progress bar values (`#3143 `_) +- Avoid writing subnormal nuclide densities to XML (`#3144 `_) +- Immediately resolve complement operators for regions (`#3145 `_) +- Improve Detection of libMesh Installation via `LIBMESH_ROOT` and CMake's PkgConfig (`#3149 `_) +- Fix for UWUW Macro Conflict (`#3150 `_) +- Consistency in treatment of paths for files specified within the Model class (`#3153 `_) +- Improve clipping of Mixture distributions (`#3154 `_) +- Fix check for trigger score name (`#3155 `_) +- Prepare point query data structures on meshes when applying Weight Windows (`#3157 `_) +- Add PointCloud spatial distribution (`#3161 `_) +- Update fmt submodule to version 11.0.2 (`#3162 `_) +- Move to support python 3.13 (`#3165 `_) +- avoid zero division if source rate of previous result is zero (`#3169 `_) +- Fix path handling for thermal ACE generation (`#3171 `_) +- Update `fmt` Formatters for Compatibility with Versions below 11 (`#3172 `_) +- added subfolders to txt search command in pyproject (`#3174 `_) +- added list to doc string arg for plot_xs (`#3178 `_) +- enable polymorphism for mix_materials (`#3180 `_) +- Fix plot_xs type hint (`#3184 `_) +- Enable adaptive mesh support on libMesh tallies (`#3185 `_) +- Reset values of lattice offset tables when allocated (`#3188 `_) +- Update surface_composite.py (`#3189 `_) +- add export_model_xml arguments to ``Model.plot_geometry`` and ``Model.calculate_volumes`` (`#3190 `_) +- Fixes in MicroXS.from_multigroup_flux (`#3192 `_) +- Fix documentation typo in ``boundary_type`` (`#3196 `_) +- Fix docstring for ``Model.plot`` (`#3198 `_) +- Apply weight windows at collisions in multigroup transport mode. (`#3199 `_) +- External sources alias sampler (`#3201 `_) +- Add test for flux bias with weight windows in multigroup mode (`#3202 `_) +- Fix bin index to DoF ID mapping bug in adaptive libMesh meshes (`#3206 `_) +- Ensure ``libMesh::ReplicatedMesh`` is used for LibMesh tallies (`#3208 `_) +- Set Model attributes only if needed (`#3209 `_) +- adding unstrucutred mesh file suffix to docstring (`#3211 `_) +- Write and read mesh name attribute (`#3221 `_) +- Adjust for secondary particle energy directly in heating scores (`#3227 `_) +- Correct normalization of thermal elastic in non standard ENDF-6 files (`#3234 `_) +- Adding '#define _USE_MATH_DEFINES' to make M_PI declared in Intel and MSVC compilers (`#3238 `_) +- updated link to log mapping technique (`#3241 `_) +- Fix for erroneously non-zero tally results of photon threshold reactions (`#3242 `_) +- Fix type comparison (`#3244 `_) +- Enable the LegendreFilter filter to be used in photon tallies for orders greater than P0. (`#3245 `_) +- Enable UWUW library when building with DAGMC in CI (`#3246 `_) +- Remove top-level import of ``openmc.lib`` (`#3250 `_) +- updated docker file to latest DAGMC (`#3251 `_) +- Write mesh type as a dataset always (`#3253 `_) +- Update to a consistent definition of the r2 parameter for cones (`#3254 `_) +- Add Patrick Shriwise to technical committee (`#3255 `_) +- Change `Zernike` documentation in polynomial.py (`#3258 `_) +- Bug fix for Polygon 'yz' basis (`#3259 `_) +- Add constant for invalid surface tokens. (`#3260 `_) +- Update plots.py for PathLike to string handling error (`#3261 `_) +- Fix bug in WeightWindowGenerator for empty energy bounds (`#3263 `_) +- Update recognized thermal scattering materials for ENDF/B-VIII.1 (`#3267 `_) +- simplify mechanism to detect if geometry entity is DAG (`#3269 `_) +- Fix bug in ``Surface.normalize`` (`#3270 `_) +- Tweak To Sphinx Install Documentation (`#3271 `_) +- add continue feature for depletion (`#3272 `_) +- Updates for building with NCrystal support (and fix CI) (`#3274 `_) +- Added missing documentation (`#3275 `_) +- fix the bug in function differentiate_mats() (`#3277 `_) +- Fix the bug in the ``Material.from_xml_element`` function (`#3278 `_) +- Doc typo fix for rand ray mgxs (`#3280 `_) +- Consolidate plotting capabilities in Model.plot (`#3282 `_) +- adding non elastic MT number (`#3285 `_) +- Fix ``Tabular.from_xml_element`` for histogram case (`#3287 `_) +- Random Ray Source Region Refactor (`#3288 `_) +- added terminal output showing compile options selected (`#3291 `_) +- Random ray consistency changes (`#3298 `_) +- Random Ray Explicit Void Treatment (`#3299 `_) +- removed old command line scripts (`#3300 `_) +- Avoid end of life ubuntu 20.04 in ReadTheDocs runner (`#3301 `_) +- Avoid error in CI from newlines in commit message (`#3302 `_) +- Handle reflex angles in CylinderSector (`#3303 `_) +- Relax requirement on polar/azimuthal axis for wwinp conversion (`#3307 `_) +- Add nuclides_to_ignore argument on Model export methods (`#3309 `_) +- Enable overlap plotting from Python API (`#3310 `_) +- Fix access order issues after applying tally results from `Model.run` (`#3313 `_) +- Random Ray Void Accuracy Fix (`#3316 `_) +- Fixes for problems encountered with version determination (`#3320 `_) +- Clarify effect of CMAKE_BUILD_TYPE in docs (`#3321 `_) +- Random Ray Linear Source Stability Improvement (`#3322 `_) +- Mark a canonical URL for docs (`#3324 `_) +- Random Ray Adjoint Source Logic Improvement (`#3325 `_) +- Reflect multigroup MicroXS in IndependentOperator docstrings (`#3327 `_) +- NCrystal becomes runtime rather than buildtime dependency (`#3328 `_) +- Adding per kg as unit option on material functions (`#3329 `_) +- Fix reading of horizontal field of view for ray-traced plots (`#3330 `_) +- Manually fix broken links (`#3331 `_) +- Update pugixml to v1.15 (`#3332 `_) +- Determine nuclides correctly for DAGMC models in d1s.get_radionuclides (`#3335 `_) +- openmc.Material.mix_materials() allows for keyword arguments (`#3336 `_) +- Fix bug in ``Mesh::material_volumes`` for void materials (`#3337 `_) +- added stable and unstable nuclides to the Chain object (`#3338 `_) diff --git a/docs/source/releasenotes/0.15.2.rst b/docs/source/releasenotes/0.15.2.rst new file mode 100644 index 0000000000..e72df37d38 --- /dev/null +++ b/docs/source/releasenotes/0.15.2.rst @@ -0,0 +1,20 @@ +==================== +What's New in 0.15.2 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This is a hotfix release to fix an MPI-related bug that was inadvertently +introduced in the prior release. + +--------------------------- +Bug Fixes and Small Changes +--------------------------- + +- Remove errant ``openmc.Settings.random_ray`` check and removed of not useful warning in MG mode (`#3344 `_) +- Throw an error if a spherical harmonics order larger than 10 is provided. (`#3354 `_) +- Correcting the size of the displacement list in the SourceSite MPI interface object (`#3356 `_) diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst index 75db7ebc9b..d24b83f9eb 100644 --- a/docs/source/releasenotes/index.rst +++ b/docs/source/releasenotes/index.rst @@ -7,6 +7,13 @@ Release Notes .. toctree:: :maxdepth: 1 + 0.15.2 + 0.15.1 + 0.15.0 + 0.14.0 + 0.13.3 + 0.13.2 + 0.13.1 0.13.0 0.12.2 0.12.1 diff --git a/docs/source/usersguide/basics.rst b/docs/source/usersguide/basics.rst index 250255f45a..c0bc2f976f 100644 --- a/docs/source/usersguide/basics.rst +++ b/docs/source/usersguide/basics.rst @@ -53,7 +53,7 @@ eXtensible Markup Language (XML) Unlike many other Monte Carlo codes which use an arbitrary-format ASCII file with "cards" to specify a particular geometry, materials, and associated run settings, the input files for OpenMC are structured in a set of `XML -`_ files. XML, which stands for eXtensible Markup +`_ files. XML, which stands for eXtensible Markup Language, is a simple format that allows data to be exchanged efficiently between different programs and interfaces. @@ -75,10 +75,7 @@ person. The nested tags *firstname*, *lastname*, *age*, and *occupation* indicate characteristics about the person being described. In much the same way, OpenMC input uses XML tags to describe the geometry, the -materials, and settings for a Monte Carlo simulation. Note that because the XML -files have a well-defined structure, they can be validated using the -:ref:`scripts_validate` script or using :ref:`Emacs nXML mode -`. +materials, and settings for a Monte Carlo simulation. Creating Input Files -------------------- @@ -86,12 +83,12 @@ Creating Input Files .. currentmodule:: openmc The most rudimentary option for creating input files is to simply write them -from scratch using the :ref:`XML format specifications -`. This approach will feel familiar to users of other -Monte Carlo codes such as MCNP and Serpent, with the added bonus that the XML -formats feel much more "readable". Alternatively, input files can be generated -using OpenMC's :ref:`Python API `, which is introduced in the -following section. +from scratch using the :ref:`XML format specifications `. +This approach will feel familiar to users of other Monte Carlo codes such as +MCNP and Serpent, with the added bonus that the XML formats feel much more +"readable". However, it is strongly recommended to generate input files using +OpenMC's :ref:`Python API `, which is introduced in the following +section. ---------- Python API @@ -181,14 +178,3 @@ energy electronvolt eV time second s ======= ============ ====== ------------------------------------- -ERSN-OpenMC Graphical User Interface ------------------------------------- - -A third-party Java-based user-friendly graphical user interface for creating XML -input files called ERSN-OpenMC_ is developed and maintained by members of the -Radiation and Nuclear Systems Group at the Faculty of Sciences Tetouan, Morocco. -The GUI also allows one to automatically download prerequisites for installing and -running OpenMC. - -.. _ERSN-OpenMC: https://github.com/EL-Bakkali-Jaafar/ERSN-OpenMC diff --git a/docs/source/usersguide/beginners.rst b/docs/source/usersguide/beginners.rst index eef927b846..6876a33240 100644 --- a/docs/source/usersguide/beginners.rst +++ b/docs/source/usersguide/beginners.rst @@ -109,8 +109,8 @@ familiar with. Whether you plan on working in Linux, macOS, or Windows, you should be comfortable working in a command line environment. There are many resources online for learning command line environments. If you are using Linux or Mac OS X (also Unix-derived), `this tutorial -`_ will help you get acquainted with -commonly-used commands. +`_ will help you get acquainted +with commonly-used commands. To reap the full benefits of OpenMC, you should also have basic proficiency in the use of `Python `_, as OpenMC includes a rich Python @@ -127,8 +127,8 @@ are hosted at `GitHub`_. In order to receive updates to the code directly, submit `bug reports`_, and perform other development tasks, you may want to sign up for a free account on GitHub. Once you have an account, you can follow `these instructions -`_ on -how to set up your computer for using GitHub. +`_ +on how to set up your computer for using GitHub. If you are new to nuclear engineering, you may want to review the NRC's `Reactor Concepts Manual`_. This manual describes the basics of nuclear power for @@ -149,9 +149,9 @@ and `Volume II`_. You may also find it helpful to review the following terms: .. _neutron transport: https://en.wikipedia.org/wiki/Neutron_transport .. _discretization: https://en.wikipedia.org/wiki/Discretization .. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry -.. _git: http://git-scm.com/ +.. _git: https://git-scm.com/ .. _git tutorials: https://git-scm.com/doc -.. _Reactor Concepts Manual: http://www.tayloredge.com/periodic/trivia/ReactorConcepts.pdf +.. _Reactor Concepts Manual: https://www.tayloredge.com/periodic/trivia/ReactorConcepts.pdf .. _Volume I: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v1 .. _Volume II: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v2 .. _OpenMC source code: https://github.com/openmc-dev/openmc diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/data.rst similarity index 60% rename from docs/source/usersguide/cross_sections.rst rename to docs/source/usersguide/data.rst index 58bf1a2933..8b2938556b 100644 --- a/docs/source/usersguide/cross_sections.rst +++ b/docs/source/usersguide/data.rst @@ -1,57 +1,76 @@ -.. _usersguide_cross_sections: +.. _usersguide_data: -=========================== -Cross Section Configuration -=========================== +================== +Data Configuration +================== -In order to run a simulation with OpenMC, you will need cross section data for -each nuclide or material in your problem. OpenMC can be run in continuous-energy -or multi-group mode. +OpenMC relies on a variety of physical data in order to carry out transport +simulations, depletion simulations, and other common tasks. As a user, you are +responsible for specifying one or more of the following: -In continuous-energy mode, OpenMC uses a native `HDF5 -`_ format (see :ref:`io_nuclear_data`) to -store all nuclear data. Pregenerated HDF5 libraries can be found at -https://openmc.org; unless you have specific data needs, it is highly -recommended to use one of the pregenerated libraries. Alternatively, if you have -ACE format data that was produced with NJOY_, such as that distributed with -MCNP_ or Serpent_, it can be converted to the HDF5 format using the :ref:`using -the Python API `. Several sources provide openly available -ACE data including the `ENDF/B`_, JEFF_, and TENDL_ libraries as well as the -`LANL Nuclear Data Team `_. In addition to -tabulated cross sections in the HDF5 files, OpenMC relies on :ref:`windowed -multipole ` data to perform on-the-fly Doppler broadening. +- **Cross sections (XML)** -- A :ref:`cross sections XML ` + file (commonly named ``cross_sections.xml``) contains a listing of other data + files, in particular neutron cross sections, photon cross sections, and + windowed multipole data. Each of those files, in turn, uses a `HDF5 + `_ format (see + :ref:`io_nuclear_data`). In order to run transport simulations with + continuous-energy cross sections, you need to specify this file. -In multi-group mode, OpenMC utilizes an HDF5-based library format which can be -used to describe nuclide- or material-specific quantities. +- **Depletion chain (XML)** -- A :ref:`depletion chain XML ` + file contains decay data, fission product yields, and information on what + neutron reactions can result in transmutation. This file is needed for + depletion/activation calculations as well as some basic functions in the + :mod:`openmc.data` module. + +- **Multigroup cross sections (HDF5)** -- OpenMC can also perform transport + simulations using multigroup data. In this case, multigroup cross sections are + stored in a single :ref:`HDF5 file `. Thus, in order to run a + multigroup transport simulation, this file needs to be specified. + +Each of the above files can specified in several ways. In the Python API, a +:ref:`runtime configuration variable ` +:data:`openmc.config` can be used to specify any of the above and is initialized +using a set of environment variables. Data configuration paths set in +:data:`openmc.config` will be expanded to absolute paths. + +.. _usersguide_data_runtime: --------------------- -Environment Variables +Runtime Configuration --------------------- -When :ref:`scripts_openmc` is run, it will look for several environment -variables that indicate where cross sections can be found. While the location of -cross sections can also be indicated through the -:attr:`openmc.Materials.cross_sections` attribute (or in the :ref:`materials.xml -` file), if you always use the same set of cross section data, it -is often easier to just set an environment variable that will be picked up by -default every time OpenMC is run. The following environment variables are used: +Data sources for OpenMC can be specified at runtime in Python using the +:data:`openmc.config` variable. This variable acts like a dictionary and stores +key-values pairs, where the values are file paths (strings or path-like objects) +and the key can be one of the following: -:envvar:`OPENMC_CROSS_SECTIONS` - Indicates the path to the :ref:`cross_sections.xml ` - summary file that is used to locate HDF5 format cross section libraries if the - user has not specified :attr:`openmc.Materials.cross_sections` (equivalently, - the :ref:`cross_sections` in :ref:`materials.xml `). +``"cross_sections"`` + Indicates the path to the :ref:`cross sections XML ` file + that lists HDF5 format neutron cross sections, photon cross sections, and + windowed multipole data. At startup, this is initialized with the value of the + :envvar:`OPENMC_CROSS_SECTIONS` environment variable. Note that the + :attr:`openmc.Materials.cross_sections` attribute will override this, if + specified. -:envvar:`OPENMC_MG_CROSS_SECTIONS` +``"chain_file"`` + Indicates the path to the :ref:`depletion chain XML ` file + that contains decay data, fission product yields, and what neutron reactions + may result in transmutation of a target nuclide. At startup, this is + initialized with the value of the :envvar:`OPENMC_CHAIN_FILE` environment + variable. + +``"mg_cross_sections"`` Indicates the path to an :ref:`HDF5 file ` that contains - multi-group cross sections if the user has not specified - :attr:`openmc.Materials.cross_sections` (equivalently, the - :ref:`cross_sections` in :ref:`materials.xml `). + multigroup cross sections. At startup, this is initialized with the value of + the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable. Note that the + :attr:`openmc.Materials.cross_sections` attribute will override this if + specified. -To set these environment variables persistently, export them from your shell -profile (``.profile`` or ``.bashrc`` in bash_). +If you want to persistently set the environment variables used to initialized +the configuration, export them from your shell profile (``.profile`` or +``.bashrc`` in bash_). -.. _bash: http://www.linuxfromscratch.org/blfs/view/6.3/postlfs/profile.html +.. _bash: https://www.linuxfromscratch.org/blfs/view/6.3/postlfs/profile.html -------------------------------- Continuous-Energy Cross Sections @@ -61,12 +80,13 @@ Using Pregenerated Libraries ---------------------------- Various evaluated nuclear data libraries have been processed into the HDF5 -format required by OpenMC and can be found at https://openmc.org. You -can find both libraries generated by the OpenMC development team as well as -libraries based on ACE files distributed elsewhere. To use these libraries, -download the archive file, unpack it, and then set your -:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of -the ``cross_sections.xml`` file contained in the unpacked directory. +format required by OpenMC and can be found at https://openmc.org. Unless you +have specific data needs, it is highly recommended to use one of the +pregenerated libraries. You can find both libraries generated by the OpenMC +development team as well as libraries based on ACE files distributed elsewhere. +To use these libraries, download the archive file, unpack it, and then specify +the path of the ``cross_sections.xml`` file contained in the unpacked directory +as described in :ref:`usersguide_data_runtime`. .. _create_xs_library: @@ -75,6 +95,12 @@ Manually Creating a Library from ACE files .. currentmodule:: openmc.data +If you have ACE format data that was produced with NJOY_, such as that +distributed with MCNP_ or Serpent_, it can be converted to the HDF5 format using +the using the Python API. Several sources provide openly available ACE data +including the `ENDF/B`_, JEFF_, and TENDL_ libraries as well as the `LANL +Nuclear Data Team `_. + The :mod:`openmc.data` module in the Python API enables users to directly convert ACE data to OpenMC's HDF5 format and create a corresponding :ref:`cross_sections.xml ` file. For those who prefer to use @@ -124,7 +150,7 @@ OpenMC. .. hint:: The :class:`IncidentNeutron` class allows you to view/modify cross sections, secondary angle/energy distributions, probability tables, etc. For a more thorough overview of the capabilities of this class, - see the `example notebook <../examples/nuclear-data.ipynb>`__. + see the `example notebook `_. Manually Creating a Library from ENDF files ------------------------------------------- @@ -224,6 +250,19 @@ relaxation sublibrary files are required: Once the HDF5 files have been generated, a library can be created using the :class:`DataLibrary` class as described in :ref:`create_xs_library`. +----------- +Chain Files +----------- + +Pregenerated depletion chain XML files can be found at https://openmc.org. +Additionally, depletion chains can be generated using the +:class:`openmc.deplete.Chain` class. In particular, the +:meth:`~openmc.deplete.Chain.from_endf` method allows a chain to be generated +starting from a set of ENDF incident neutron, decay, and fission product yield +sublibrary files. Once you've downloaded or generated a depletion chain XML +file, make sure to specify its path as described in +:ref:`usersguide_data_runtime`. + ----------------------- Windowed Multipole Data ----------------------- @@ -241,27 +280,27 @@ The `official ENDF/B-VII.1 HDF5 library multipole library, so if you are using this library, the windowed multipole data will already be available to you. --------------------------- -Multi-Group Cross Sections --------------------------- +.. _create_mgxs: -Multi-group cross section libraries are generally tailored to the specific +------------------------- +Multigroup Cross Sections +------------------------- + +Multigroup cross section libraries are generally tailored to the specific calculation to be performed. Therefore, at this point in time, OpenMC is not -distributed with any pre-existing multi-group cross section libraries. -However, if obtained or generated their own library, the user -should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable -to the absolute path of the file library expected to used most frequently. +distributed with any pre-existing multigroup cross section libraries. However, +if a multigroup library file is downloaded or generated, the path to the file +needs to be specified as described in :ref:`usersguide_data_runtime`. For an +example of how to create a multigroup library, see this `MG mode notebook +`_. -For an example of how to create a multi-group library, see the `example notebook -<../examples/mg-mode-part-i.ipynb>`__. - -.. _NJOY: http://www.njoy21.io/ +.. _NJOY: https://www.njoy21.io/ .. _NNDC: https://www.nndc.bnl.gov/endf .. _MCNP: https://mcnp.lanl.gov -.. _Serpent: http://montecarlo.vtt.fi -.. _ENDF/B: https://www.nndc.bnl.gov/endf/b7.1/acefiles.html +.. _Serpent: https://serpent.vtt.fi +.. _ENDF/B: https://www.nndc.bnl.gov/endf-b7.1/acefiles.html .. _JEFF: https://www.oecd-nea.org/dbdata/jeff/jeff33/ -.. _TENDL: https://tendl.web.psi.ch/tendl_2017/tendl2017.html +.. _TENDL: https://tendl.web.psi.ch/tendl_2023/tendl2023.html .. _Seltzer and Berger: https://doi.org/10.1016/0092-640X(86)90014-8 .. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html .. _Biggs et al.: https://doi.org/10.1016/0092-640X(75)90030-3 diff --git a/docs/source/usersguide/decay_sources.rst b/docs/source/usersguide/decay_sources.rst new file mode 100644 index 0000000000..d5a078135b --- /dev/null +++ b/docs/source/usersguide/decay_sources.rst @@ -0,0 +1,92 @@ +.. usersguide_decay_sources: + +============= +Decay Sources +============= + +Through the :ref:`depletion ` capabilities in OpenMC, it +is possible to simulate radiation emitted from the decay of activated materials. +For fusion energy systems, this is commonly done using what is known as the +`rigorous 2-step `_ (R2S) method. +In this method, a neutron transport calculation is used to determine the neutron +flux and reaction rates over a cell- or mesh-based spatial discretization of the +model. Then, the neutron flux in each discrete region is used to predict the +activated material composition using a depletion solver. Finally, a photon +transport calculation with a source based on the activity and energy spectrum of +the activated materials is used to determine a desired physical response (e.g., +a dose rate) at one or more locations of interest. + +Once a depletion simulation has been completed in OpenMC, the intrinsic decay +source can be determined as follows. First the activated material composition +can be determined using the :class:`openmc.deplete.Results` object. Indexing an +instance of this class with the timestep index returns a +:class:`~openmc.deplete.StepResult` object, which itself has a +:meth:`~openmc.deplete.StepResult.get_material` method. Once the activated +:class:`~openmc.Material` has been obtained, the +:meth:`~openmc.Material.get_decay_photon_energy` method will give the energy +spectrum of the decay photon source. The integral of the spectrum also indicates +the intensity of the source in units of [Bq]. Altogether, the workflow looks as +follows:: + + results = openmc.deplete.Results("depletion_results.h5") + + # Get results at last timestep + step = results[-1] + + # Get activated material composition for ID=1 + activated_mat = step.get_material('1') + + # Determine photon source + photon_energy = activated_mat.get_decay_photon_energy() + +By default, the :meth:`~openmc.Material.get_decay_photon_energy` method will +eliminate spectral lines with very low intensity, but this behavior can be +configured with the ``clip_tolerance`` argument. + +Direct 1-Step (D1S) Calculations +================================ + +OpenMC also includes built-in capability for performing shutdown dose rate +calculations using the `direct 1-step +`_ (D1S) method. In this method, +a single coupled neutron--photon transport calculation is used where the prompt +photon production is replaced with photons produced from the decay of +radionuclides in an activated material. To obtain properly scaled results, it is +also necessary to apply time correction factors. A normal neutron transport +calculation can be extended to a D1S calculation with a few helper functions. +First, import the ``d1s`` submodule, which is part of :mod:`openmc.deplete`:: + + from openmc.deplete import d1s + +First, you need to instruct OpenMC to use decay photon data instead of prompt +photon data. This is done with an attribute on the :class:`~openmc.Settings` +class:: + + model = openmc.Model() + ... + model.settings.use_decay_photons = True + +To prepare any tallies for use of the D1S method, you should call the +:func:`~openmc.deplete.d1s.prepare_tallies` function, which adds a +:class:`openmc.ParentNuclideFilter` (used later for assigning time correction +factors) to any applicable tally and returns a list of possible radionuclides +based on the :ref:`chain file `. Once the tallies are prepared, +the model can be simulated:: + + output_path = model.run() + +Finally, the time correction factors need to be computed and applied to the +relevant tallies. This can be done with the aid of the +:func:`~openmc.deplete.d1s.time_correction_factors` and +:func:`~openmc.deplete.d1s.apply_time_correction` functions:: + + # Compute time correction factors based on irradiation schedule + factors = d1s.time_correction_factors(nuclides, timesteps, source_rates) + + # Get tally from statepoint + with openmc.StatePoint(output_path) as sp: + dose_tally = sp.get_tally(name='dose tally') + + # Apply time correction factors + tally = d1s.apply_time_correction(dose_tally, factors, time_index) + diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 6fa2b89c93..261900ce61 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -4,63 +4,74 @@ Depletion and Transmutation =========================== -OpenMC supports coupled depletion, or burnup, calculations through the -:mod:`openmc.deplete` Python module. OpenMC solves the transport equation to -obtain transmutation reaction rates, and then the reaction rates are used to -solve a set of transmutation equations that determine the evolution of nuclide -densities within a material. The nuclide densities predicted as some future time -are then used to determine updated reaction rates, and the process is repeated -for as many timesteps as are requested. +OpenMC supports transport-coupled and transport-independent depletion, or +burnup, calculations through the :mod:`openmc.deplete` Python module. OpenMC +uses transmutation reaction rates to solve a set of transmutation equations +that determine the evolution of nuclide densities within a material. The +nuclide densities predicted at some future time are then used to determine +updated reaction rates, and the process is repeated for as many timesteps as +are requested. -The depletion module is designed such that the flux/reaction rate solution (the +The depletion module is designed such that the reaction rate solution (the transport "operator") is completely isolated from the solution of the -transmutation equations and the method used for advancing time. At present, the -:mod:`openmc.deplete` module offers a single transport operator, -:class:`openmc.deplete.Operator` (which uses the OpenMC transport solver), but -in principle additional operator classes based on other transport codes could be -implemented and no changes to the depletion solver itself would be needed. The -operator class requires a :class:`openmc.Geometry` instance and a -:class:`openmc.Settings` instance:: - - geom = openmc.Geometry() - settings = openmc.Settings() - ... - - op = openmc.deplete.Operator(geom, settings) - -Any material that contains a fissionable nuclide is depleted by default, but -this can behavior can be changed with the :attr:`Material.depletable` attribute. - -.. important:: The volume must be specified for each material that is depleted by - setting the :attr:`Material.volume` attribute. This is necessary - in order to calculate the proper normalization of tally results - based on the source rate. +transmutation equations and the method used for advancing time. :mod:`openmc.deplete` supports multiple time-integration methods for determining material compositions over time. Each method appears as a different class. For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation using the CE/CM algorithm (deplete over a timestep using the middle-of-step -reaction rates). An instance of :class:`openmc.deplete.Operator` is passed to -one of these functions along with the timesteps and power level:: +reaction rates). An instance of :class:`~openmc.deplete.abc.TransportOperator` +is passed to one of these Integrator classes along with the timesteps and power +level:: power = 1200.0e6 # watts timesteps = [10.0, 10.0, 10.0] # days openmc.deplete.CECMIntegrator(op, timesteps, power, timestep_units='d').integrate() -The coupled transport-depletion problem is executed, and once it is done a +The depletion problem is executed, and once it is done a ``depletion_results.h5`` file is written. The results can be analyzed using the -:class:`openmc.deplete.ResultsList` class. This class has methods that allow for +:class:`openmc.deplete.Results` class. This class has methods that allow for easy retrieval of k-effective, nuclide concentrations, and reaction rates over time:: - results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") - time, keff = results.get_eigenvalue() + results = openmc.deplete.Results("depletion_results.h5") + time, keff = results.get_keff() -Note that the coupling between the transport solver and the transmutation solver -happens in-memory rather than by reading/writing files on disk. +Note that the coupling between the reaction rate solver and the transmutation +solver happens in-memory rather than by reading/writing files on disk. OpenMC +has two categories of transport operators for obtaining transmutation reaction +rates. + +.. _coupled-depletion: + +Transport-coupled depletion +=========================== + +This category of operator solves the transport equation to obtain transmutation +reaction rates. At present, the :mod:`openmc.deplete` module offers a single +transport-coupled operator, :class:`openmc.deplete.CoupledOperator` (which uses +the OpenMC transport solver), but in principle additional transport-coupled +operator classes based on other transport codes could be implemented and no +changes to the depletion solver itself would be needed. The +:class:`openmc.deplete.CoupledOperator` class requires a :class:`~openmc.Model` +instance containing material, geometry, and settings information:: + + model = openmc.Model() + ... + + op = openmc.deplete.CoupledOperator(model) + +Any material that contains a fissionable nuclide is depleted by default, but +this can behavior can be changed with the :attr:`Material.depletable` attribute. + +.. important:: + + The volume must be specified for each material that is depleted by setting + the :attr:`Material.volume` attribute. This is necessary in order to + calculate the proper normalization of tally results based on the source rate. Fixed-Source Transmutation -========================== +-------------------------- When the ``power`` or ``power_density`` argument is used for one of the Integrator classes, it is assumed that OpenMC is running in k-eigenvalue mode, @@ -77,11 +88,11 @@ using the :attr:`Material.depletable` attribute:: mat = openmc.Material() mat.depletable = True -When constructing the :class:`~openmc.deplete.Operator`, you should indicate -that normalization of tally results will be done based on the source rate rather -than a power or power density:: +When constructing the :class:`~openmc.deplete.CoupledOperator`, you should +indicate that normalization of tally results will be done based on the source +rate rather than a power or power density:: - op = openmc.deplete.Operator(geometry, settings, normalization_mode='source-rate') + op = openmc.deplete.CoupledOperator(model, normalization_mode='source-rate') Finally, when creating a depletion integrator, use the ``source_rates`` argument:: @@ -92,19 +103,22 @@ timestep in the calculation. A zero source rate for a given timestep will result in a decay-only step, where all reaction rates are zero. Caveats -======= +------- + +.. _energy-deposition: Energy Deposition ------------------ +~~~~~~~~~~~~~~~~~ The default energy deposition mode, ``"fission-q"``, instructs the -:class:`openmc.deplete.Operator` to normalize reaction rates using the product -of fission reaction rates and fission Q values taken from the depletion chain. -This approach does not consider indirect contributions to energy deposition, -such as neutron heating and energy from secondary photons. In doing this, the -energy deposited during a transport calculation will be lower than expected. -This causes the reaction rates to be over-adjusted to hit the user-specific -power, or power density, leading to an over-depletion of burnable materials. +:class:`~openmc.deplete.CoupledOperator` to normalize reaction rates using the +product of fission reaction rates and fission Q values taken from the depletion +chain. This approach does not consider indirect contributions to energy +deposition, such as neutron heating and energy from secondary photons. In doing +this, the energy deposited during a transport calculation will be lower than +expected. This causes the reaction rates to be over-adjusted to hit the +user-specific power, or power density, leading to an over-depletion of burnable +materials. There are some remedies. First, the fission Q values can be directly set in a variety of ways. This requires knowing what the total fission energy release @@ -113,29 +127,32 @@ should be, including indirect components. Some examples are provided below:: # use a dictionary of fission_q values fission_q = {"U235": 202e+6} # energy in eV + # create a Model object + model = openmc.Model(geometry, settings) + # create a modified chain and write it to a new file chain = openmc.deplete.Chain.from_xml("chain.xml", fission_q) chain.export_to_xml("chain_mod_q.xml") - op = openmc.deplete.Operator(geometry, setting, "chain_mod_q.xml") + op = openmc.deplete.CoupledOperator(model, "chain_mod_q.xml") # alternatively, pass the modified fission Q directly to the operator - op = openmc.deplete.Operator(geometry, setting, "chain.xml", + op = openmc.deplete.CoupledOperator(model, "chain.xml", fission_q=fission_q) A more complete way to model the energy deposition is to use the modified -heating reactions described in :ref:`methods_heating`. These values can be used +heating reactions described in :ref:`methods_heating`. These values can be used to normalize reaction rates instead of using the fission reaction rates with:: - op = openmc.deplete.Operator(geometry, settings, "chain.xml", + op = openmc.deplete.CoupledOperator(model, "chain.xml", normalization_mode="energy-deposition") These modified heating libraries can be generated by running the latest version -of :meth:`openmc.data.IncidentNeutron.from_njoy`, and will eventually be bundled +of :meth:`openmc.data.IncidentNeutron.from_njoy()`, and will eventually be bundled into the distributed libraries. Local Spectra and Repeated Materials ------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is not uncommon to explicitly create a single burnable material across many locations. From a pure transport perspective, there is nothing wrong with @@ -160,7 +177,7 @@ the next transport step. This can be countered by instructing the operator to treat repeated instances of the same material as a unique material definition with:: - op = openmc.deplete.Operator(geometry, settings, chain_file, + op = openmc.deplete.CoupledOperator(model, chain_file, diff_burnable_mats=True) For our example problem, this would deplete fuel on the outer region of the @@ -177,3 +194,258 @@ across all material instances. This will increase the total memory usage and run time due to an increased number of tallies and material definitions. +Transport-independent depletion +=============================== + +This category of operator uses multigroup microscopic cross sections along with +multigroup flux spectra to obtain transmutation reaction rates. The cross +sections are pre-calculated, so there is no need for direct coupling between a +transport-independent operator and a transport solver. The :mod:`openmc.deplete` +module offers a single transport-independent operator, +:class:`~openmc.deplete.IndependentOperator`, and only one operator is needed +since, in theory, any transport code could calculate the multigroup microscopic +cross sections. The :class:`~openmc.deplete.IndependentOperator` class has two +constructors. The default constructor requires a :class:`openmc.Materials` +instance, a list of multigroup flux arrays, and a list of +:class:`~openmc.deplete.MicroXS` instances containing multigroup microscopic +cross sections in units of barns. This might look like the following:: + + materials = openmc.Materials([m1, m2, m3]) + ... + + # Assign fluxes (generated from any code) + flux_m1 = numpy.array([...]) + flux_m2 = numpy.array([...]) + flux_m3 = numpy.array([...]) + fluxes = [flux_m1, flux_m2, flux_m3] + + # Assign microscopic cross sections + micro_m1 = openmc.deplete.MicroXS.from_csv('xs_m1.csv') + micro_m2 = openmc.deplete.MicroXS.from_csv('xs_m2.csv') + micro_m3 = openmc.deplete.MicroXS.from_csv('xs_m3.csv') + micros = [micro_m1, micro_m2, micro_m3] + + # Create operator + op = openmc.deplete.IndependentOperator(materials, fluxes, micros) + +For more details on the :class:`~openmc.deplete.MicroXS` class, including how to +use OpenMC's transport solver to generate microscopic cross sections and fluxes +for use with :class:`~openmc.deplete.IndependentOperator`, see :ref:`micros`. + +.. note:: + + The same statements from :ref:`coupled-depletion` about which materials are + depleted and the requirement for depletable materials to have a specified + volume also apply here. + +An alternate constructor, +:meth:`~openmc.deplete.IndependentOperator.from_nuclides`, accepts a volume and +dictionary of nuclide concentrations in place of the :class:`openmc.Materials` +instance. Note that while the normal constructor allows multiple materials to be +depleted with a single operator, the +:meth:`~openmc.deplete.IndependentOperator.from_nuclides` classmethod only works +for a single material:: + + nuclides = {'U234': 8.92e18, + 'U235': 9.98e20, + 'U238': 2.22e22, + 'U236': 4.57e18, + 'O16': 4.64e22, + 'O17': 1.76e19} + volume = 0.5 + op = openmc.deplete.IndependentOperator.from_nuclides(volume, + nuclides, + flux, + micro_xs, + chain_file, + nuc_units='atom/cm3') + +A user can then define an integrator class as they would for a coupled +transport-depletion calculation and follow the same steps from there. + +.. note:: + + Ideally, multigroup cross section data should be available for every reaction + in the depletion chain. If cross section data is not present for a nuclide in + the depletion chain with at least one reaction, that reaction will not be + simulated. + +.. _micros: + +Loading and Generating Microscopic Cross Sections +------------------------------------------------- + +As mentioned above, any transport code could be used to calculate multigroup +microscopic cross sections and fluxes. The :mod:`openmc.deplete` module provides +the :class:`~openmc.deplete.MicroXS` class, which can either be instantiated +from pre-calculated cross sections in a ``.csv`` file or from data arrays +directly:: + + micro_xs = MicroXS.from_csv(micro_xs_path) + + nuclides = ['U234', 'U235', 'U238'] + reactions = ['fission', '(n,gamma)'] + data = np.array([[0.1, 0.2], + [0.3, 0.4], + [0.01, 0.5]]) + micro_xs = MicroXS(data, nuclides, reactions) + +.. important:: + + The cross section values are assumed to be in units of barns. Make sure your + cross sections are in the correct units before passing to a + :class:`~openmc.deplete.IndependentOperator` object. + +Additionally, a convenience function, +:func:`~openmc.deplete.get_microxs_and_flux`, can provide the needed fluxes and +cross sections using OpenMC's transport solver:: + + model = openmc.Model() + ... + + fluxes, micros = openmc.deplete.get_microxs_and_flux(model, materials) + +If you are running :func:`~openmc.deplete.get_microxs_and_flux` on a cluster +where temporary files are created on a local filesystem that is not shared +across nodes, you'll need to set an environment variable pointing to a local +directoy so that each MPI process knows where to store output files used to +calculate the microscopic cross sections. In order of priority, they are +:envvar:`TMPDIR`. :envvar:`TEMP`, and :envvar:`TMP`. Users interested in further +details can read the documentation for the `tempfile +`_ module. + +Caveats +------- + +Reaction Rate Normalization +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :class:`~openmc.deplete.IndependentOperator` class supports two methods for +normalizing reaction rates: + +.. important:: + + Make sure you set the correct parameter in the :class:`openmc.abc.Integrator` + class. Use the ``source_rates`` parameter when + ``normalization_mode == source-rate``, and use ``power`` or ``power_density`` + when ``normalization_mode == fission-q``. + +1. ``source-rate`` normalization, which assumes the ``source_rate`` provided by + the time integrator is a flux, and obtains the reaction rates by multiplying + the cross sections by the ``source-rate``. +2. ``fission-q`` normalization, which uses the ``power`` or ``power_density`` + provided by the time integrator to obtain normalized reaction rates by + computing a normalization factor as the ratio of the user-specified power to + the "observed" power based on fission reaction rates. The equation for the + normalization factor is + + .. math:: + :label: fission-q + + f = \frac{P}{\sum\limits_m \sum\limits_i \left(Q_i N_{i,m} \sum\limits_g + \sigma^f_{i,g,m} \phi_{g,m} \right)} + + where :math:`P` is the power, :math:`Q_i` is the fission Q value for nuclide + :math:`i`, :math:`\sigma_{i,g,m}^f` is the microscopic fission cross section + for nuclide :math:`i` in energy group :math:`g` for material :math:`m`, + :math:`\phi_{g,m}` is the neutron flux in group :math:`g` for material + :math:`m`, and :math:`N_{i,m}` is the number of atoms of nuclide :math:`i` + for material :math:`m`. Reaction rates are then multiplied by :math:`f` so + that the total fission power matches :math:`P`. This equation makes the same + assumptions and issues as discussed in :ref:`energy-deposition`. + Unfortunately, the proposed solution in that section does not apply here + since we are decoupled from transport code. However, there is a method to + converge to a more accurate value for flux by using substeps during time + integration. `This paper `_ + provides a good discussion of this method. + +.. warning:: + + The accuracy of results when using ``fission-q`` is entirely dependent on + your depletion chain. Make sure it has sufficient data to resolve the + dynamics of your particular scenario. + +Multiple Materials +~~~~~~~~~~~~~~~~~~ + +A transport-independent depletion simulation using ``source-rate`` normalization +will calculate reaction rates for each material independently. This can be +useful for running many different cases of a particular scenario. A +transport-independent depletion simulation using ``fission-q`` normalization +will sum the fission energy values across all materials into :math:`Q_i` in +Equation :math:numref:`fission-q`, and Equation :math:numref:`fission-q` +provides the normalization factor applied to reaction rates in each material. +This can be useful for running a scenario with multiple depletable materials +that are part of the same reactor. This behavior may change in the future. + +Time integration +~~~~~~~~~~~~~~~~ + +The values of the microscopic cross sections passed to +:class:`openmc.deplete.IndependentOperator` are fixed for the entire depletion +simulation. This implicit assumption may produce inaccurate results for certain +scenarios. + +Transfer Rates +============== + +Transfer rates define removal or feed of nuclides to or from one or more +depletable materials. This can be useful to model continuous fuel reprocessing, +online fission products separation, etc. + +Transfer rates are defined by calling the +:meth:`~openmc.deplete.abc.Integrator.add_transfer_rate()` method directly from +one of the Integrator classes:: + + ... + integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) + integrator.add_transfer_rate(...) + +Defining transfer rates +----------------------- + +The :meth:`~openmc.deplete.abc.Integrator.add_transfer_rate()` method requires a +:class:`~openmc.Material` instance (alternatively, a material id or +the name) as the depletable material from which nuclides are processed, +a list of elements that share the same transfer rate, and a transfer rate itself. + +.. caution:: + + Make sure you set the transfer rate value with the right sign. + A positive transfer rate assumes removal, while a negative one assumes feed. + +The ``transfer_rate_units`` argument specifies the units for the transfer rate. +The default is `1/s`, but '1/min', '1/h', '1/d' and '1/a' are also valid +options. + +For example, to define continuous removal of xenon from one material with a +removal rate value of 0.1 s\ :sup:`-1` (or a cycle time of 10 s), you'd use:: + + mat1 = openmc.Material(material_id=1, name='fuel') + + ... + + integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) + # by openmc.Material object + integrator.add_transfer_rate(mat1, ['Xe'], 0.1) + # or by material id + integrator.add_transfer_rate(1, ['Xe'], 0.1) + # or by material name + integrator.add_transfer_rate('fuel', ['Xe'], 0.1) + +Note that in this case the xenon isotopes that are removed will not be tracked. + +Defining a destination material +------------------------------- + +To transfer elements from one depletable material to another, the +``destination_material`` parameter needs to be passed to the +:meth:`~openmc.deplete.abc.Integrator.add_transfer_rate()` method. For example, +to transfer xenon from one material to another, you'd use:: + + ... + mat2 = openmc.Material(name='storage') + + ... + + integrator.add_transfer_rate(mat1, ['Xe'], 0.1, destination_material=mat2) diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index 68ecfce95c..6f14ebfa51 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -147,12 +147,13 @@ For many regions, a bounding-box can be determined automatically:: While a bounding box can be determined for regions involving half-spaces of spheres, cylinders, and axis-aligned planes, it generally cannot be determined if the region involves cones, non-axis-aligned planes, or other exotic -second-order surfaces. For example, the :func:`openmc.model.hexagonal_prism` -function returns the interior region of a hexagonal prism; because it is bounded -by a :class:`openmc.Plane`, trying to get its bounding box won't work:: +second-order surfaces. For example, the :class:`openmc.model.HexagonalPrism` +class returns a hexagonal prism surface; because it utilizes a +:class:`openmc.Plane`, trying to get the bounding box of its interior won't +work:: - >>> hex = openmc.model.hexagonal_prism() - >>> hex.bounding_box + >>> hex = openmc.model.HexagonalPrism() + >>> (-hex).bounding_box (array([-0.8660254, -inf, -inf]), array([ 0.8660254, inf, inf])) @@ -172,13 +173,17 @@ surface. To specify a vacuum boundary condition, simply change the outer_surface = openmc.Sphere(r=100.0) outer_surface.boundary_type = 'vacuum' -Reflective and periodic boundary conditions can be set with the strings -'reflective' and 'periodic'. Vacuum and reflective boundary conditions can be -applied to any type of surface. Periodic boundary conditions can be applied to -pairs of planar surfaces. If there are only two periodic surfaces they will be -matched automatically. Otherwise it is necessary to specify pairs explicitly -using the :attr:`Surface.periodic_surface` attribute as in the following -example:: +Reflective, periodic, and white boundary conditions can be set with the +strings 'reflective', 'periodic', and 'white' respectively. +Vacuum, reflective and white boundary conditions can be applied to any +type of surface. The 'white' boundary condition supports diffuse particle +reflection in contrast to specular reflection provided by the 'reflective' +boundary condition. + +Periodic boundary conditions can be applied to pairs of planar surfaces. +If there are only two periodic surfaces they will be matched automatically. +Otherwise it is necessary to specify pairs explicitly using the +:attr:`Surface.periodic_surface` attribute as in the following example:: p1 = openmc.Plane(a=0.3, b=5.0, d=1.0, boundary_type='periodic') p2 = openmc.Plane(a=0.3, b=5.0, d=-1.0, boundary_type='periodic') @@ -196,6 +201,20 @@ lies in the first quadrant of the Cartesian grid. If the geometry instead lies in the fourth quadrant, the :class:`YPlane` must be replaced by a :class:`Plane` with the normal vector pointing in the :math:`-y` direction. +Additionally, 'reflective', 'periodic', and 'white' boundary conditions have +an albedo parameter that can be used to modify the importance of particles +that encounter the boundary. The albedo value specifies the ratio between +the particle's importance after interaction with the boundary to its initial +importance. The following example creates a reflective planar surface which +reduces the reflected particles' importance by 33.3%:: + + x1 = openmc.XPlane(1.0, boundary_type='reflective', albedo=0.667) + + # This is equivalent + x1 = openmc.XPlane(1.0) + x1.boundary_type = 'reflective' + x1.albedo = 0.667 + .. _usersguide_cells: ----- @@ -410,7 +429,7 @@ code would work:: hexlat.universes = [outer_ring, middle_ring, inner_ring] If you need to create a hexagonal boundary (composed of six planar surfaces) for -a hexagonal lattice, :func:`openmc.model.hexagonal_prism` can be used. +a hexagonal lattice, :class:`openmc.model.HexagonalPrism` can be used. .. _usersguide_geom_export: @@ -455,7 +474,7 @@ applied as universes in the OpenMC geometry file. A geometry represented entirely by a DAGMC geometry will contain only the DAGMC universe. Using a :class:`openmc.DAGMCUniverse` looks like the following:: - dag_univ = openmc.DAGMCUniverse(filename='dagmc.h5m') + dag_univ = openmc.DAGMCUniverse('dagmc.h5m') geometry = openmc.Geometry(dag_univ) geometry.export_to_xml() @@ -476,13 +495,22 @@ It is important in these cases to understand the DAGMC model's position with respect to the CSG geometry. DAGMC geometries can be plotted with OpenMC to verify that the model matches one's expectations. -**Note:** DAGMC geometries used in OpenMC are currently required to be clean, -meaning that all surfaces have been `imprinted and merged -`_ -successfully and that the model is `watertight -`_. Future -implementations of DAGMC geometry will support small volume overlaps and -un-merged surfaces. +By default, when you specify a .h5m file for a :class:`~openmc.DAGMCUniverse` +instance, it will store the absolute path to the .h5m file. If you prefer to +store the relative path, you can set the ``'resolve_paths'`` configuration +variable:: + + openmc.config['resolve_paths'] = False + dag_univ = openmc.DAGMCUniverse('dagmc.h5m') + +.. note:: + DAGMC geometries used in OpenMC are currently required to be clean, + meaning that all surfaces have been `imprinted and merged + `_ successfully + and that the model is `watertight + `_. + Future implementations of DAGMC geometry will support small volume overlaps and + un-merged surfaces. Cell, Surface, and Material IDs ------------------------------- diff --git a/docs/source/usersguide/index.rst b/docs/source/usersguide/index.rst index fab353c773..5f8e0197e7 100644 --- a/docs/source/usersguide/index.rst +++ b/docs/source/usersguide/index.rst @@ -13,7 +13,7 @@ essential aspects of using OpenMC to perform simulations. beginners install - cross_sections + data basics materials geometry @@ -21,8 +21,12 @@ essential aspects of using OpenMC to perform simulations. tallies plots depletion + decay_sources + kinetics scripts processing parallel volume + variance_reduction + random_ray troubleshoot diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 7961b3e38b..64d4801799 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -8,40 +8,43 @@ Installation and Configuration .. _install_conda: ----------------------------------------- -Installing on Linux/Mac with conda-forge ----------------------------------------- +---------------------------------- +Installing on Linux/Mac with Conda +---------------------------------- -Conda_ is an open source package management system and environment management -system for installing multiple versions of software packages and their -dependencies and switching easily between them. If you have `conda` installed on -your system, OpenMC can be installed via the `conda-forge` channel. First, add -the `conda-forge` channel with: +`Conda`_ is an open source package management +system and environments management system for installing multiple versions of +software packages and their dependencies and switching easily between them. +OpenMC can be installed in a `conda` environment. First, `conda` should be +`installed `_ +with either Anaconda Distribution or Miniconda. Once you have `conda` installed +on your system, OpenMC can be installed via the `conda-forge` channel. + +First, add the `conda-forge` channel with: .. code-block:: sh conda config --add channels conda-forge + conda config --set channel_priority strict -To list the versions of OpenMC that are available on the `conda-forge` channel, -in your terminal window or an Anaconda Prompt run: - -.. code-block:: sh - - conda search openmc - -OpenMC can then be installed with: - -.. code-block:: sh - - conda create -n openmc-env openmc - -This will install OpenMC in a conda environment called `openmc-env`. To activate -the environment, run: +Then create and activate a new conda enviroment called `openmc-env` (or whatever +you wish) with OpenMC installed. .. code-block:: sh + conda create --name openmc-env openmc conda activate openmc-env +If you are installing on macOS with an Apple silicon ARM-based processor, you +will also need to specify the `--platform` option: + +.. code-block:: sh + + conda create --name openmc-env --platform osx-64 openmc + +You are now in a conda environment called `openmc-env` that has OpenMC +installed. + ------------------------------------------- Installing on Linux/Mac/Windows with Docker ------------------------------------------- @@ -88,9 +91,9 @@ other information use: .. note:: - It should be noted that by default OpenMC builds with ``-O2 -g`` flags which - are equivalent to a CMake build type of `RelwithDebInfo`. In addition, MPI - is OFF while OpenMP is ON. + It should be noted that by default OpenMC is built with + `-DCMAKE_BUILD_TYPE=RelwithDebInfo`. In addition, MPI is OFF while OpenMP is + ON. It is recommended to install OpenMC with the Python API. Information about this Spack recipe can be found with the following command: @@ -116,17 +119,18 @@ following command: configured defaults unless otherwise specfied in the specification on the command line. In the above example, assuming the default options weren't changed in Spack's package configuration, py-openmc will link against a - non-optimized non-MPI openmc. Even if an optimized openmc was built - separately, it will rebuild openmc with optimization OFF. Thus, if you are - trying to link against dependencies that were configured different than - defaults, ``^openmc[variants]`` will have to be present in the command. + non-MPI non-release build of openmc. Even if a release build of openmc was + built separately, it will rebuild openmc with the default build type. Thus, + if you are trying to link against dependencies that were configured + different than defaults, ``^openmc[variants]`` will have to be present in + the command. -For a more performant build of OpenMC with optimization turned ON and MPI -provided by OpenMPI, the following command can be used: +For a release build of OpenMC with MPI support on (provided by OpenMPI), the +following command can be used: .. code-block:: sh - spack install py-openmc+mpi ^openmc+optimize ^openmpi + spack install py-openmc +mpi ^openmpi ^openmc build_type=Release .. note:: @@ -146,7 +150,7 @@ This can be observed using Spack's ``spec`` tool: .. code-block:: - spack spec py-openmc+mpi ^openmc+optimize + spack spec py-openmc +mpi ^openmc build_type=Release Once installed, environment/lmod modules can be generated or Spack's ``load`` feature can be used to access the installed packages. @@ -203,7 +207,7 @@ Prerequisites respectively. To link against a parallel HDF5 library, make sure to set the HDF5_PREFER_PARALLEL CMake option, e.g.:: - CXX=mpicxx.mpich cmake -DHDF5_PREFER_PARALLEL=on .. + cmake -DHDF5_PREFER_PARALLEL=on -DOPENMC_USE_MPI=on .. Note that the exact package names may vary depending on your particular distribution and version. @@ -224,7 +228,7 @@ Prerequisites OpenMC's built-in plotting capabilities use the libpng library to produce compressed PNG files. In the absence of this library, OpenMC will fallback to writing PPM files, which are uncompressed and only supported by select - image viewers. libpng can be installed on Ddebian derivates with:: + image viewers. libpng can be installed on Debian derivates with:: sudo apt install libpng-dev @@ -252,7 +256,27 @@ Prerequisites In addition to turning this option on, the path to the DAGMC installation should be specified as part of the ``CMAKE_PREFIX_PATH`` variable:: - cmake -Ddagmc=on -DCMAKE_PREFIX_PATH=/path/to/dagmc/installation + cmake -DOPENMC_USE_DAGMC=on -DCMAKE_PREFIX_PATH=/path/to/dagmc/installation .. + + * MCPL_ library for reading and writing .mcpl files + + This option allows OpenMC to read and write MCPL (Monte Carlo Particle + Lists) files instead of .h5 files for sources (external source + distribution, k-eigenvalue source distribution, and surface sources). To + turn this option on in the CMake configuration step, add the following + option:: + + cmake -DOPENMC_USE_MCPL=on .. + + * NCrystal_ library for defining materials with enhanced thermal neutron transport + + OpenMC supports the creation of materials from NCrystal, which replaces + the scattering kernel treatment of ACE files with a modular, on-the-fly + approach. OpenMC does not need any particular build option to use this, + but NCrystal must be installed on the system. Refer to `NCrystal + documentation + `_ for how this is + achieved. * libMesh_ mesh library framework for numerical simulations of partial differential equations @@ -261,9 +285,9 @@ Prerequisites be used, but the implementation is currently restricted to collision estimators. In addition to turning this option on, the path to the libMesh installation should be specified as part of the ``CMAKE_PREFIX_PATH`` - variable.:: + variable:: - CXX=mpicxx cmake -Dlibmesh=on -DCMAKE_PREFIX_PATH=/path/to/libmesh/installation + cmake -DOPENMC_USE_LIBMESH=on -DOPENMC_USE_MPI=on -DCMAKE_PREFIX_PATH=/path/to/libmesh/installation .. Note that libMesh is most commonly compiled with MPI support. If that is the case, then OpenMC should be compiled with MPI support as well. @@ -277,6 +301,8 @@ Prerequisites .. _MOAB: https://bitbucket.org/fathomteam/moab .. _libMesh: https://libmesh.github.io/ .. _libpng: http://www.libpng.org/pub/png/libpng.html +.. _MCPL: https://github.com/mctools/mcpl +.. _NCrystal: https://github.com/mctools/ncrystal Obtaining the Source -------------------- @@ -331,63 +357,71 @@ CMakeLists.txt Options The following options are available in the CMakeLists.txt file: -debug - Enables debugging when compiling. The flags added are dependent on which - compiler is used. +OPENMC_ENABLE_COVERAGE + Compile and link code instrumented for coverage analysis. This is typically + used in conjunction with gcov_. (Default: off) -profile - Enables profiling using the GNU profiler, gprof. +OPENMC_ENABLE_PROFILE + Enables profiling using the GNU profiler, gprof. (Default: off) -optimize - Enables high-optimization using compiler-dependent flags. For gcc and - Intel C++, this compiles with -O3. - -openmp +OPENMC_USE_OPENMP Enables shared-memory parallelism using the OpenMP API. The C++ compiler being used must support OpenMP. (Default: on) -dagmc +OPENMC_USE_DAGMC Enables use of CAD-based DAGMC_ geometries and MOAB_ unstructured mesh tallies. Please see the note about DAGMC in the optional dependencies list for more information on this feature. The installation directory for DAGMC should also be defined as `DAGMC_ROOT` in the CMake configuration command. (Default: off) -libmesh +OPENMC_USE_LIBMESH Enables the use of unstructured mesh tallies with libMesh_. (Default: off) -coverage - Compile and link code instrumented for coverage analysis. This is typically - used in conjunction with gcov_. +OPENMC_USE_MPI + Turns on compiling with MPI (Default: off). For further information on MPI + options, please see the `FindMPI.cmake documentation + `_. -To set any of these options (e.g. turning on debug mode), the following form +OPENMC_FORCE_VENDORED_LIBS + Forces OpenMC to use the submodules located in the vendor directory, as + opposed to searching the system for already installed versions of those + modules. + +To set any of these options (e.g., turning on profiling), the following form should be used: .. code-block:: sh - cmake -Ddebug=on /path/to/openmc + cmake -DOPENMC_ENABLE_PROFILE=on /path/to/openmc .. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html .. _usersguide_compile_mpi: -Compiling with MPI -++++++++++++++++++ +Specifying the Build Type ++++++++++++++++++++++++++ -To compile with MPI, set the :envvar:`CXX` environment variable to the path to -the MPI C++ wrapper. For example, in a bash shell: +OpenMC can be configured for debug, release, or release with debug info by setting +the `CMAKE_BUILD_TYPE` option. + +Debug + Enable debug compiler flags with no optimization. On most platforms/compilers, + this is equivalent to `-O0 -g`. + +Release + Disable debug and enable optimization. On most platforms/compilers, this is + equivalent to `-O3 -DNDEBUG`. + +RelWithDebInfo + (Default if no type is specified.) Enable optimization and debug. On most + platforms/compilers, this is equivalent to `-O2 -g`. + +Example of configuring for Debug mode: .. code-block:: sh - export CXX=mpicxx - cmake /path/to/openmc - -Note that in many shells, environment variables can be set for a single command, -i.e. - -.. code-block:: sh - - CXX=mpicxx cmake /path/to/openmc + cmake -DCMAKE_BUILD_TYPE=Debug /path/to/openmc Selecting HDF5 Installation +++++++++++++++++++++++++++ @@ -413,11 +447,11 @@ can typically be set for a single command, i.e. .. _compile_linux: -Compiling on Linux and Mac OS X -------------------------------- +Compiling on Linux and macOS +---------------------------- -To compile OpenMC on Linux or Max OS X, run the following commands from within -the root directory of the source code: +To compile OpenMC on Linux or macOS, run the following commands from within the +root directory of the source code: .. code-block:: sh @@ -437,13 +471,13 @@ OpenMC locally by specifying an install prefix when running cmake: The ``CMAKE_INSTALL_PREFIX`` variable can be changed to any path for which you have write-access. -Compiling on Windows 10 ------------------------ +Compiling on Windows +-------------------- -Recent versions of Windows 10 include a subsystem for Linux that allows one to -run Bash within Ubuntu running in Windows. First, follow the installation guide -`here `_ to get Bash -on Ubuntu on Windows setup. Once you are within bash, obtain the necessary +Recent versions of Windows include a subsystem for Linux that allows one to run +Bash within Ubuntu running in Windows. First, follow the installation guide +`here `_ to get Bash on +Ubuntu on Windows set up. Once you are within bash, obtain the necessary :ref:`prerequisites ` via ``apt``. Finally, follow the :ref:`instructions for compiling on linux `. @@ -471,18 +505,12 @@ distribution/repository, run: .. code-block:: sh - pip install . + python -m pip install . pip will first check that all :ref:`required third-party packages ` have been installed, and if they are not present, they will be installed by downloading the appropriate packages from the Python -Package Index (`PyPI `_). However, do note that since pip -runs the ``setup.py`` script which requires NumPy, you will have to first -install NumPy: - -.. code-block:: sh - - pip install numpy +Package Index (`PyPI `_). Installing in "Development" Mode -------------------------------- @@ -495,10 +523,13 @@ to install the Python package in :ref:`"editable" mode `. Prerequisites ------------- -The Python API works with Python 3.6+. In addition to Python itself, the API -relies on a number of third-party packages. All prerequisites can be installed -using Conda_ (recommended), pip_, or through the package manager in most Linux -distributions. +In addition to Python itself, the OpenMC Python API relies on a number of +third-party packages. All prerequisites can be installed using Conda_ +(recommended), pip_, or through the package manager in most Linux distributions. +The current required Python version and up-to-date list of package dependencies +can be found in the `pyproject.toml `_ +file in the root directory of the OpenMC repository. An overview of these +dependencies is provided below. .. admonition:: Required :class: error @@ -512,10 +543,11 @@ distributions. are used for several optional features in the API. `pandas `_ - Pandas is used to generate tally DataFrames as demonstrated in - an `example notebook <../examples/pandas-dataframes.ipynb>`_. + Pandas is used to generate tally DataFrames as demonstrated in an `example + notebook + `_. - `h5py `_ + `h5py `_ h5py provides Python bindings to the HDF5 library. Since OpenMC outputs various HDF5 files, h5py is needed to provide access to data within these files from Python. @@ -528,8 +560,7 @@ distributions. Uncertainties are used for decay data in the :mod:`openmc.data` module. `lxml `_ - lxml is used for the :ref:`scripts_validate` script and various other - parts of the Python API. + lxml is used for various parts of the Python API. .. admonition:: Optional :class: note @@ -539,10 +570,6 @@ distributions. parallel runs. This package is needed if you plan on running depletion simulations in parallel using MPI. - `Cython `_ - Cython is used for resonance reconstruction for ENDF data converted to - :class:`openmc.data.IncidentNeutron`. - `vtk `_ The Python VTK bindings are needed to convert voxel and track files to VTK format. @@ -563,41 +590,16 @@ for OpenMC. Thus, the install process would proceed as follows: make install cd .. - MPICC= pip install mpi4py - HDF5_DIR= pip install --no-binary=h5py h5py + MPICC= python -m pip install mpi4py + HDF5_DIR= python -m pip install --no-binary=h5py h5py If you are using parallel HDF5, you'll also need to make sure the right MPI wrapper is used when installing h5py: .. code-block:: sh - CC= HDF5_MPI=ON HDF5_DIR= pip install --no-binary=h5py h5py + CC= HDF5_MPI=ON HDF5_DIR= python -m pip install --no-binary=h5py h5py -.. _usersguide_nxml: - ------------------------------------------------------ -Configuring Input Validation with GNU Emacs nXML mode ------------------------------------------------------ - -The `GNU Emacs`_ text editor has a built-in mode that extends functionality for -editing XML files. One of the features in nXML mode is the ability to perform -real-time `validation`_ of XML files against a `RELAX NG`_ schema. The OpenMC -source contains RELAX NG schemas for each type of user input file. In order for -nXML mode to know about these schemas, you need to tell emacs where to find a -"locating files" description. Adding the following lines to your ``~/.emacs`` -file will enable real-time validation of XML input files: - -.. code-block:: common-lisp - - (require 'rng-loc) - (add-to-list 'rng-schema-locating-files "~/openmc/schemas.xml") - -Make sure to replace the last string on the second line with the path to the -schemas.xml file in your own OpenMC source directory. - -.. _GNU Emacs: http://www.gnu.org/software/emacs/ -.. _validation: https://en.wikipedia.org/wiki/XML_validation -.. _RELAX NG: https://relaxng.org/ -.. _ctest: https://cmake.org/cmake/help/latest/manual/ctest.1.html +.. _Mamba: https://mamba.readthedocs.io/en/latest/ .. _Conda: https://conda.io/en/latest/ .. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/usersguide/kinetics.rst b/docs/source/usersguide/kinetics.rst new file mode 100644 index 0000000000..9024ff8227 --- /dev/null +++ b/docs/source/usersguide/kinetics.rst @@ -0,0 +1,133 @@ +.. _kinetics: + +=================== +Kinetics parameters +=================== + +OpenMC has the capability to estimate the following adjoint-weighted effective +generation time :math:`\Lambda_{\text{eff}}` and the effective delayed neutron +fraction :math:`\beta_{\text{eff}}`. These parameters are calculated using the +iterated fission probability (IFP) method [Hurwitz_1964]_ based on a similar +approach as in `Serpent 2 `_. The +implementation in OpenMC is limited to eigenvalue calculations and is described +in more details in [Dorville_2025]_. + +---------------------------------- +Iterated Fission Probability (IFP) +---------------------------------- + +With IFP, additional information needs to be recorded during the simulation +compared to a typical eigenvalue calculation. OpenMC stores an additional +set of values (neutron lifetime or delayed neutron group number for +:math:`\Lambda_{\text{eff}}` or :math:`\beta_{\text{eff}}`, respectively) +for every fission neutron simulated. Each set of values corresponds to +the values that are associated to the :math:`N_{\text{gen}}` direct ancestors +of any given fission neutron. + +:math:`N_{\text{gen}}` is referred to as the number of generations in the +IFP method and corresponds to the number of generations between the birth of +a fission neutron and the time its score is added to the IFP tally. By default, +OpenMC considers 10 generations but this value can be modified by the user via +the ``ifp_n_generation`` settings in the Python API:: + + settings.ifp_n_generation = 5 + +``ifp_n_generation`` should be greater than 0, but should also be lower than +or equal to the number of inactive batches declared for the calculation. +The respect of these constraints is verified by OpenMC before any calculation. + +OpenMC will automatically detect the type of data that needs to be stored based +on the tally scores selected by the user. This guarantees that only information +of interest are stored during a simulation and avoids using extra memory when +only one parameter is needed. The following table shows the tally scores that +are needed to compute kinetics parameters in OpenMC: + +.. table:: **OpenMC tally scores needed to calculate adjoint-weighted kinetics parameters** + :align: center + + =============================== ============================ ========================== ======== + OpenMC tally score \\ Parameter :math:`\Lambda_{\text{eff}}` :math:`\beta_{\text{eff}}` Both + =============================== ============================ ========================== ======== + ``ifp-time-numerator`` X X + ``ifp-beta-numerator`` X X + ``ifp-denominator`` X X X + =============================== ============================ ========================== ======== + +| + +.. note:: Because the memory footprint of additional data is generally non-negligible + with IFP, it is recommended to choose the value for ``ifp_n_generation`` carefully. + For example, using one generation for both kinetics parameters corresponds to store + one additional integer (for the delayed neutron group number used with + :math:`\beta_{\text{eff}}`) and one floating point value (for the neutron lifetime + used with :math:`\Lambda_{\text{eff}}`) for every fission neutron simulated once the + asymptotic regime is reached. + +----------------------------- +Obtaining kinetics parameters +----------------------------- + +The ``Model`` class can be used to automatically generate all IFP tallies using +the Python API with :attr:`openmc.Settings.ifp_n_generation` greater than 0 and +the :meth:`openmc.Model.add_ifp_kinetics_tallies` method:: + + model = openmc.Model(geometry, settings=settings) + model.add_kinetics_parameters_tallies(num_groups=6) # Add 6 precursor groups + +Alternatively, each of the tallies can be manually defined using group-wise or +total :math:`\beta_{\text{eff}}` specified by providing a 6-group +:class:`openmc.DelayedGroupFilter`:: + + beta_tally = openmc.Tally(name="group-beta-score") + beta_tally.scores = ["ifp-beta-numerator"] + + # Add DelayedGroupFilter to enable group-wise tallies + beta_tally.filters = [openmc.DelayedGroupFilter(list(range(1, 7)))] + +Here is an example showing how to declare the three available IFP scores in a +single tally:: + + tally = openmc.Tally(name="ifp-scores") + tally.scores = [ + "ifp-time-numerator", + "ifp-beta-numerator", + "ifp-denominator" + ] + +The effective generation time :math:`\Lambda_{\text{eff}}` is calculated +by dividing the result of the ``ifp-time-numerator`` score by the one obtained +for ``ifp-denominator`` and by the :math:`k_{\text{eff}}` of the simulation: + +.. math:: + :label: lambda_eff + + \Lambda_{\text{eff}} = \frac{S_{\text{ifp-time-numerator}}}{S_{\text{ifp-denominator}} \times k_{\text{eff}}} + +The effective delayed neutron fraction :math:`\beta_{\text{eff}}` is calculated +by dividing the result of the ``ifp-beta-numerator`` score by the one obtained +for ``ifp-denominator``: + +.. math:: + :label: beta_eff + + \beta_{\text{eff}} = \frac{S_{\text{ifp-beta-numerator}}}{S_{\text{ifp-denominator}}} + +The kinetics parameters can be retrieved directly from a statepoint file using +the :meth:`openmc.StatePoint.ifp_results` method:: + + with openmc.StatePoint(output_path) as sp: + generation_time, beta_eff = sp.get_kinetics_parameters() + +.. only:: html + + .. rubric:: References + +.. [Hurwitz_1964] H. Hurwitz Jr., "Naval Reactors Physics Handbook", volume 1, p. 864. + Radkowsky, A. (Ed.), Naval Reactors, Division of Reactor Development, U.S. + Atomic Energy Commission (1964). + +.. [Dorville_2025] J. Dorville, L. Labrie-Cleary, and P. K. Romano, "Implementation + of the Iterated Fission Probability Method in OpenMC to Compute Adjoint-Weighted + Kinetics Parameters", International Conference on Mathematics and Computational + Methods Applied to Nuclear Science and Engineering (M&C 2025), Denver, April 27-30, + 2025. diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 8b43f1a2ac..83af558057 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -101,6 +101,42 @@ you would need to add hydrogen and oxygen to a material and then assign the .. _usersguide_naming: +------------------------- +Adding NCrystal materials +------------------------- + +Additional support for thermal scattering can be added by using NCrystal_. The +:meth:`Material.from_ncrystal` class method generates a :class:`openmc.Material` +object from an `NCrystal configuration string +`_. +Temperature, material composition, and density are passed from the configuration +string and the `NCMAT file +`_ that define the +material, e.g.:: + + mat = openmc.Material.from_ncrystal('Al_sg225.ncmat;temp=300K') + +defines a material containing polycrystalline alumnium, + +:: + + mat = openmc.Material.from_ncrystal("""Ge_sg227.ncmat;dcutoff=0.5;mos=40arcsec; + dir1=@crys_hkl:5,1,1@lab:0,0,1; + dir2=@crys_hkl:0,-1,1@lab:0,1,0""") + +defines an oriented germanium single crystal with 40 arcsec mosaicity. + +NCrystal only handles low energy neutron interactions. Other interactions are +provided by standard ACE files. NCrystal_ comes with a `predefined library +`_ but more materials can +be added by creating NCMAT files or on-the-fly in the configuration string. + +.. warning:: Currently, NCrystal_ materials cannot be modified after they are created. + Density, temperature and composition should be defined in the + configuration string or the NCMAT file. + +.. _NCrystal: https://github.com/mctools/ncrystal + ------------------ Naming Conventions ------------------ @@ -222,3 +258,4 @@ been generated, you can tell OpenMC to use this file either by setting materials.cross_sections = '/path/to/cross_sections.xml' .. _MCNP: https://mcnp.lanl.gov/ + diff --git a/docs/source/usersguide/parallel.rst b/docs/source/usersguide/parallel.rst index be41896048..ecbdd20b62 100644 --- a/docs/source/usersguide/parallel.rst +++ b/docs/source/usersguide/parallel.rst @@ -101,5 +101,5 @@ performance on a machine when running in parallel: settings = openmc.Settings() settings.output = {'tallies': False} -.. _Haswell-EP: http://www.anandtech.com/show/8423/intel-xeon-e5-version-3-up-to-18-haswell-ep-cores-/4 -.. _bound: https://wiki.mpich.org/mpich/index.php/Using_the_Hydra_Process_Manager#Process-core_Binding +.. _Haswell-EP: https://www.anandtech.com/show/8423/intel-xeon-e5-version-3-up-to-18-haswell-ep-cores-/4 +.. _bound: https://github.com/pmodels/mpich/blob/main/doc/wiki/how_to/Using_the_Hydra_Process_Manager.md#process-core-binding diff --git a/docs/source/usersguide/plots.rst b/docs/source/usersguide/plots.rst index d57917a3df..da0c69bdd8 100644 --- a/docs/source/usersguide/plots.rst +++ b/docs/source/usersguide/plots.rst @@ -111,13 +111,159 @@ The voxel plot data is written to an :ref:`HDF5 file `. The voxel file can subsequently be converted into a standard mesh format that can be viewed in `ParaView `_, `VisIt `_, etc. This typically -will compress the size of the file significantly. The provided -:ref:`scripts_voxel` script can convert the HDF5 voxel file to VTK formats. Once -processed into a standard 3D file format, colors and masks can be defined using -the stored ID numbers to better explore the geometry. The process for doing this -will depend on the 3D viewer, but should be straightforward. +will compress the size of the file significantly. The +:func:`openmc.voxel_to_vtk` function can convert the HDF5 voxel file to VTK +formats. Once processed into a standard 3D file format, colors and masks can be +defined using the stored ID numbers to better explore the geometry. The process +for doing this will depend on the 3D viewer, but should be straightforward. .. note:: 3D voxel plotting can be very computer intensive for the viewing program (Visit, ParaView, etc.) if the number of voxels is large (>10 million or so). Thus if you want an accurate picture that renders smoothly, consider using only one voxel in a certain direction. + +---------------------- +Solid Ray-traced Plots +---------------------- + +.. image:: ../_images/phong_triso.png + :width: 300px + +The :class:`openmc.SolidRayTracePlot` class allows three dimensional +visualization of detailed geometric features without voxelization. The plot +above visualizes a geometry created by :class:`openmc.TRISO`, with the materials +in the fuel kernel distinguished by color. It was enclosed in a bounding box +such that some kernels are cut off, revealing the inner structure of the kernel. + +The `Phong reflection model +`_ approximates how light +reflects off of a surface. On a diffusely light-scattering material, the Phong +model prescribes the amount of light reflected from a surface as proportional to +the dot product between the normal vector of the surface and the vector between +that point on the surface and the light. With this assumption, visually +appealing plots of simulation geometries can be created. + +Solid ray-traced plots use the same ray tracing functions that neutrons and +photons do in OpenMC, so any input that does not leak particles can be +visualized in 3D using a solid ray-traced plot. That being said, these plots are +not useful for detecting overlap or undefined regions, so it is recommended to +use the slice plot approach for geometry debugging. + +Only a few inputs are required for a solid ray-traced plot. The camera location, +where the camera is looking, and a set of opaque material or cell IDs are +required. The colors of materials or cells are prescribed in the same way as +slice plots. The set of IDs that are opaque in the plot must correspond to +materials if coloring by material, or cells if coloring by cell. + +A minimal solid ray-traced plot input could be:: + + plot = openmc.SolidRayTracePlot() + plot.pixels = (600, 600) + plot.camera_position = (10.0, 20.0, -30.0) + plot.look_at = (4.0, 5.0, 1.0) + plot.color_by = 'cell' + + # optional. defaults to camera_position + plot.light_position = (10, 20, 30) + + # controls ambient lighting. Defaults to 10% + plot.diffuse_fraction = 0.1 + plot.opaque_domains = [cell2, cell3] + +These plots are then stored into a :class:`openmc.Plots` instance, just like the +slice plots. + +--------------- +Wireframe Plots +--------------- + +.. only:: html + + .. image:: ../_images/hexlat_anim.gif + :width: 200px + +The :class:`openmc.WireframeRayTracePlot` class also produces 3D visualizations +of OpenMC geometries without voxelization but is intended to show the inside of +a model using wireframing of cell or material boundaries in addition to cell +coloring based on the path length of camera rays through the model. The coloring +in these plots is a bit like turning the model into partially transparent +colored glass that can be seen through, without any refractive effects. This is +called volume rendering. The colors are specified in exactly the same interface +employed by slice plots. + +Similar to solid ray-traced plots, these use the native ray tracing capabilities +within OpenMC, so any geometry in which particles successfully run without +overlaps or leaks will work with wireframe plots. + +One drawback of wireframe plots is that particle tracks cannot be overlaid on +them at present. Moreover, checking for overlap regions is not currently +possible with wireframe plots. The image heading this section can be created by +adding the following code to the hexagonal lattice example packaged with OpenMC, +before exporting to plots.xml. + +:: + + r = 5 + import numpy as np + for i in range(100): + phi = 2 * np.pi * i/100 + thisp = openmc.WireframeRayTracePlot(plot_id = 4 + i) + thisp.filename = 'frame%s'%(str(i).zfill(3)) + thisp.look_at = [0, 0, 0] + thisp.camera_position = [r * np.cos(phi), r * np.sin(phi), 6 * np.sin(phi)] + thisp.pixels = [200, 200] + thisp.color_by = 'material' + thisp.colorize(geometry) + thisp.set_transparent(geometry) + thisp.xs[fuel] = 1.0 + thisp.xs[iron] = 1.0 + thisp.wireframe_domains = [fuel] + thisp.wireframe_thickness = 2 + + plot_file.append(thisp) + +This generates a sequence of png files that can be joined to form a gif. Each +image specifies a different camera position using some simple periodic functions +to create a perfectly looped gif. :attr:`~WireframeRayTracePlot.look_at` defines +where the camera's centerline should point at. +:attr:`~WireframeRayTracePlot.camera_position` similarly defines where the +camera is situated in the universe level we seek to plot. The other settings +resemble those employed by :class:`openmc.Plot`, with the exception of the +:meth:`~WireframeRayTracePlot.set_transparent` method and +:attr:`~WireframeRayTracePlot.xs` dictionary. These are used to control volume +rendering of material volumes. "xs" here stands for cross section, and it +defines material opacities in units of inverse centimeters. Setting this value +to a large number would make a material or cell opaque, and setting it to zero +makes a material transparent. Thus, the +:meth:`~WireframeRayTracePlot.set_transparent` method can be used to make all +materials in the geometry transparent. From there, individual material or cell +opacities can be tuned to produce the desired result. + +Two camera projections are available when using these plots, perspective and +orthographic. The default, perspective projection, is a cone of rays passing +through each pixel which radiate from the camera position and span the field of +view in the x and y positions. The horizontal field of view can be set with the +:attr:`~WireframeRayTracePlot.horizontal_field_of_view` attribute, which is to +be specified in units of degrees. The field of view only influences behavior in +perspective projection mode. + +In the orthographic projection, rays follow the same angle but originate from +different points. The horizontal width of this plane of ray starting points may +be set with the :attr:`~WireframeRayTracePlot.orthographic_width` attribute. If +this element is nonzero, the orthographic projection is employed. Left to its +default value of zero, the perspective projection is employed. + +Most importantly, wireframe plots come packaged with wireframe generation that +can target either all surface/cell/material boundaries in the geometry, or only +wireframing around specific regions. In the above example, we have set only the +fuel region from the hexagonal lattice example to have a wireframe drawn around +it. This is accomplished by setting the +:attr:`~WireframeRayTracePlot.wireframe_domains` attribute, which may be set to +either material IDs or cell IDs. The +:attr:`~WireframeRayTracePlot.wireframe_thickness` attribute sets the wireframe +thickness in units of pixels. + +.. note:: When setting specific material or cell regions to have wireframes + drawn around them, the plot must be colored by materials if wireframing + around specific materials and similarly colored by cell instance if + wireframing around specific cells. diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index e61ba33bf1..8b5ae53fac 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -34,29 +34,16 @@ as requested; it is used in many of the provided plotting utilities, OpenMC's regression test suite, and can be used in user-created scripts to carry out manipulations of the data. -An `example notebook <../examples/post-processing.ipynb>`_ demonstrates how to -extract data from a statepoint using the Python API. +An `example notebook`_ demonstrates how to extract data from a statepoint using +the Python API. Plotting in 2D -------------- -The `notebook example <../examples/post-processing.ipynb>`_ also demonstrates -how to plot a structured mesh tally in two dimensions using the Python API. One -can also use the :ref:`scripts_plot` script which provides an interactive GUI to -explore and plot structured mesh tallies for any scores and filter bins. - -.. image:: ../_images/plotmeshtally.png - :width: 400px - -Getting Data into MATLAB ------------------------- - -There is currently no front-end utility to dump tally data to MATLAB files, but -the process is straightforward. First extract the data using the Python API via -``openmc.statepoint`` and then use the `Scipy MATLAB IO routines -`_ to save to a MAT -file. Note that all arrays that are accessible in a statepoint are already in -NumPy arrays that can be reshaped and dumped to MATLAB in one step. +The `example notebook`_ also demonstrates how to plot a structured mesh tally in +two dimensions using the Python API. One can also use the `openmc-plotter +`_ application that provides an +interactive GUI to explore and plot a much wider variety of tallies. .. _usersguide_track: @@ -91,7 +78,7 @@ of three, e.g., if we wanted particles 3 and 4 from batch 1 and generation 2:: After running OpenMC, the working directory will contain a file of the form "track_(batch #)_(generation #)_(particle #).h5" for each particle tracked. These track files can be converted into VTK poly data files with the -:ref:`scripts_track` script. +:class:`openmc.Tracks` class. ---------------------- Source Site Processing @@ -101,5 +88,85 @@ For eigenvalue problems, OpenMC will store information on the fission source sites in the statepoint file by default. For each source site, the weight, position, sampled direction, and sampled energy are stored. To extract this data from a statepoint file, the ``openmc.statepoint`` module can be used. An -`example notebook <../examples/post-processing.ipynb>`_ demontrates how to -analyze and plot source information. +`example notebook`_ demontrates how to analyze and plot source information. + +.. _example notebook: https://nbviewer.jupyter.org/github/openmc-dev/openmc-notebooks/blob/main/post-processing.ipynb + +------------------------ +VTK Mesh File Generation +------------------------ + +VTK files of OpenMC meshes can be created using the +:meth:`openmc.Mesh.write_data_to_vtk` method. Data can be applied to the +elements of the resulting mesh from mesh filter objects. This data can be +provided either as a flat array or, in the case of structured meshes +(:class:`~openmc.RegularMesh`, :class:`~openmc.RectilinearMesh`, +:class:`~openmc.CylindricalMesh`, or :class:`SphericalMesh`), the data can be +shaped with dimensions that match the dimensions of the mesh itself. + + +.. image:: ../_images/sphere-mesh-vtk.png + :width: 400px + :align: center + :alt: OpenMC spherical mesh exported to VTK + + +For all mesh types, if a flat data array is provided to the mesh, it is expected +that the data is ordered in the same ordering as the :attr:`openmc.Mesh.indices` +for that mesh object. When providing data directly from a tally, as shown below, +a flat array for a given dataset can be passed directly to this method. + +:: + + # create model above + + # create a mesh tally + mesh = openmc.RegularMesh() + mesh.dimension = [10, 20, 30] + mesh.lower_left = [-5, -10, -15] + mesh.upper_right = [5, 10, 15] + mesh_filter = openmc.MeshFilter(mesh) + tally = openmc.Tally() + tally.filters = [mesh_filter] + tally.scores = ['flux'] + + model.tallies = [tally] + model.run(apply_tally_results=True) + + # provide the data as-is to the method + mesh.write_data_to_vtk('flux.vtk', {'flux-mean': tally.mean}) + +The :class:`~openmc.Tally` object also provides a way to expand the dimensions +of the mesh filter into a meaningful form where indexing the mesh filter +dimensions results in intuitive slicing of structured meshes by setting +``expand_dims=True`` when using :meth:`openmc.Tally.get_reshaped_data`. This +reshaping does cause flat indexing of the data to change, however. As noted +above, provided datasets are allowed to be shaped so long as such datasets have +shapes that match the mesh dimensions. The ability to pass datasets in this way +is useful when additional filters are applied to a tally. The example below +demonstrates such a case for tally with both a :class:`~openmc.MeshFilter` and +:class:`~openmc.EnergyFilter` applied. + +:: + + # create model above + + # create a mesh tally with energy filter + mesh = openmc.RegularMesh() + mesh.dimension = [10, 20, 30] + mesh.lower_left = [-5, -10, -15] + mesh.upper_right = [5, 10, 15] + mesh_filter = openmc.MeshFilter(mesh) + energy_filter = openmc.EnergyFilter([0.0, 1.0, 20.0e6]) + tally = openmc.Tally() + tally.filters = [mesh_filter, energy_filter] + tally.scores = ['flux'] + + model.tallies = [tally] + model.run(apply_tally_results=True) + + # get the data with mesh dimensions expanded, squeeze out length-one dimensions (nuclides, scores) + flux = tally.get_reshaped_data(expand_dims=True).squeeze() # shape: (10, 20, 30, 2) + + # write the lowest energy group to a VTK file + mesh.write_data_to_vtk('flux-group1.vtk', datasets={'flux-mean': flux[..., 0]}) diff --git a/docs/source/usersguide/random_ray.rst b/docs/source/usersguide/random_ray.rst new file mode 100644 index 0000000000..138ae910c9 --- /dev/null +++ b/docs/source/usersguide/random_ray.rst @@ -0,0 +1,1204 @@ +.. _random_ray: + +================= +Random Ray Solver +================= + +In general, the random ray solver mode uses most of the same settings and +:ref:`run strategies ` as the standard Monte Carlo solver +mode. For instance, random ray solves are also split up into :ref:`inactive and +active batches `. However, there are a couple of settings +that are unique to the random ray solver and a few areas that the random ray +run strategy differs, both of which will be described in this section. + +.. _quick_start: + +----------- +Quick Start +----------- + +While this page contains a comprehensive guide to the random ray solver and +its various parameters, the process of converting an existing continuous energy +Monte Carlo model to a random ray model can be largely automated via convenience +functions in OpenMC's Python interface:: + + # Define continuous energy model as normal + model = openmc.Model() + ... + + # Convert model to multigroup (will auto-generate MGXS library if needed) + model.convert_to_multigroup() + + # Convert model to random ray and initialize random ray parameters + # to reasonable defaults based on the specifics of the geometry + model.convert_to_random_ray() + + # (Optional) Overlay source region decomposition mesh to improve fidelity of the + # random ray solver. Adjust 'n' for fidelity vs runtime. + n = 100 + mesh = openmc.RegularMesh() + mesh.dimension = (n, n, n) + mesh.lower_left = model.geometry.bounding_box.lower_left + mesh.upper_right = model.geometry.bounding_box.upper_right + model.settings.random_ray['source_region_meshes'] = [(mesh, [model.geometry.root_universe])] + + # (Optional) Improve fidelity of the random ray solver by enabling linear sources + model.settings.random_ray['source_shape'] = 'linear' + + # (Optional) Increase the number of rays/batch, to reduce uncertainty + model.settings.particles = 500 + +The above strategy first converts the continuous energy model to a multigroup +one using the :meth:`openmc.Model.convert_to_multigroup` method. By default, +this will internally run a coarsely converged continuous energy Monte Carlo +simulation to produce an estimated multigroup macroscopic cross section set for +each material specified in the model, and store this data into a multigroup +cross section library file (``mgxs.h5``) that can be used by the random ray +solver. + +The :meth:`openmc.Model.convert_to_random_ray` method enables random ray mode +and performs an analysis of the model geometry to determine reasonable values +for all required parameters. If default behavior is not satisfactory, the user +can manually adjust the settings in the :attr:`~openmc.Settings.random_ray` +dictionary in the :class:`openmc.Settings` as described in the sections below. + +Finally a few optional steps are shown. The first (recommended) step overlays a +mesh over the geometry to create smaller source regions so that source +resolution improves and the random ray solver becomes more accurate. Varying the +mesh resolution can be used to trade off between accuracy and runtime. +High-fidelity fission reactor simulation may require source region sizes below 1 +cm, while larger fixed source problems with some tolerance for error may be able +to use source regions of 10 or 100 cm. + +We also enable linear sources, which can improve the accuracy of the random ray +solver and/or allow for a much coarser mesh resolution to be overlaid. Finally, +the number of rays per batch is adjusted. The goal here is to ensure that the +source region miss rate is below 1%, which is reported by OpenMC at the end of +the simulation (or before via a warning if it is very high). + +.. warning:: + If using a mesh filter for tallying or weight window generation, ensure that + the same mesh is used for source region decomposition via + ``model.settings.random_ray['source_region_meshes']``. + +------------------------ +Enabling Random Ray Mode +------------------------ + +To utilize the random ray solver, the :attr:`~openmc.Settings.random_ray` +dictionary must be present in the :class:`openmc.Settings` Python class. There +are a number of additional settings that must be specified within this +dictionary that will be discussed below. Additionally, the "multi-group" energy +mode must be specified. + +------- +Batches +------- + +In Monte Carlo simulations, inactive batches are used to let the fission source +develop into a stationary distribution before active batches are performed that +actually accumulate statistics. While this is true of random ray as well, in the +random ray mode the inactive batches are also used to let the scattering source +develop. Monte Carlo fully represents the scattering source within each +iteration (by its nature of fully simulating particles from birth to death +through any number of physical scattering events), whereas the scattering source +in random ray can only represent as many scattering events as batches have been +completed. For example, by iteration 10 in random ray, the scattering source +only captures the behavior of neutrons through their 10th scattering event. +Thus, while inactive batches are only required in an eigenvalue solve in Monte +Carlo, **inactive batches are required for both eigenvalue and fixed source +solves in random ray mode** due to this additional need to converge the +scattering source. + +.. warning:: + Unlike Monte Carlo, the random ray solver still requires usage of inactive + batches when in fixed source mode so as to develop the scattering source. + +The additional burden of converging the scattering source generally results in a +higher requirement for the number of inactive batches---often by an order of +magnitude or more. For instance, it may be reasonable to only use 50 inactive +batches for a light water reactor simulation with Monte Carlo, but random ray +might require 500 or more inactive batches. + +Similar to Monte Carlo, active batches are used in the random ray solver mode to +accumulate and converge statistics on unknown quantities (i.e., the random ray +sources, scalar fluxes, as well as any user-specified tallies). + +The batch parameters are set in the same manner as with the regular Monte Carlo +solver:: + + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.batches = 1200 + settings.inactive = 600 + +--------------- +Shannon Entropy +--------------- + +Similar to Monte Carlo, :ref:`Shannon entropy +` can be used to gauge whether the fission +source has fully developed. The Shannon entropy is calculated automatically +after each batch and is printed to the statepoint file. Unlike Monte Carlo, an +entropy mesh does not need to be defined, as the Shannon entropy is calculated +over FSRs using a volume-weighted approach. + +------------------------------- +Inactive Ray Length (Dead Zone) +------------------------------- + +A major issue with random ray is that the starting angular flux distribution for +each sampled ray is unknown. Thus, an on-the-fly method is used to build a high +quality approximation of the angular flux of the ray each iteration. This is +accomplished by running the ray through an inactive length (also known as a dead +zone length), where the ray is moved through the geometry and its angular flux +is solved for via the normal :ref:`MOC ` equation, but +no information is written back to the system. Thus, the ray is run in a "read +only" mode for the set inactive length. This parameter can be adjusted, in units +of cm, as:: + + settings.random_ray['distance_inactive'] = 40.0 + +After several mean free paths are traversed, the angular flux spectrum of the +ray becomes dominated by the in-scattering and fission source components that it +picked up when travelling through the geometry, while its original (incorrect) +starting angular flux is attenuated toward zero. Thus, longer selections of +inactive ray length will asymptotically approach the true angular flux. + +In practice, 10 mean free paths are sufficient (with light water reactors often +requiring only about 10--50 cm of inactive ray length for the error to become +undetectable). However, we caution that certain models with large quantities of +void regions (even if just limited to a few streaming channels) may require +significantly longer inactive ray lengths to ensure that the angular flux is +accurate before the conclusion of the inactive ray length. Additionally, +problems where a sensitive estimate of the uncollided flux is required (e.g., +the detector response to fast neutrons is required, and the detected is located +far away from the source in a moderator region) may require the user to specify +an inactive length that is derived from the pyhsical geometry of the simulation +problem rather than its material properties. For instance, consider a detector +placed 30 cm outside of a reactor core, with a moderator region separating the +detector from the core. In this case, rays sampled in the moderator region and +heading toward the detector will begin life with a highly scattered thermal +spectrum and will have an inaccurate fast spectrum. If the dead zone length is +only 20 cm, we might imagine such rays writing to the detector tally within +their active lengths, despite their inaccurate estimate of the uncollided fast +angular flux. Thus, an inactive length of 100--200 cm would ensure that any such +rays would still be within their inactive regions, and only rays that have +actually traversed through the core (and thus have an accurate representation of +the core's emitted fast flux) will score to the detector region while in their +active phase. + + +------------------------------------ +Active Ray Length and Number of Rays +------------------------------------ + +Once the inactive length of the ray has completed, the active region of the ray +begins. The ray is now run in regular mode, where changes in angular flux as it +traverses through each flat source region are written back to the system, so as +to contribute to the estimate for the iteration scalar flux (which is used to +compute the source for the next iteration). The active ray length can be +adjusted, in units of [cm], as:: + + settings.random_ray['distance_active'] = 400.0 + +Assuming that a sufficient inactive ray length is used so that the starting +angular flux is highly accurate, any selection of active length greater than +zero is theoretically acceptable. However, in order to adequately sample the +full integration domain, a selection of a very short track length would require +a very high number of rays to be selected. Due to the static costs per ray of +computing the starting angular flux in the dead zone, typically very short ray +lengths are undesireable. Thus, to amortize the per-ray cost of the inactive +region of the ray, it is desirable to select a very long inactive ray length. +For example, if the inactive length is set to 20 cm, a 200 cm active ray length +ensures that only about 10% of the overall simulation runtime is spent in the +inactive ray phase integration, making the dead zone a relatively inexpensive +way of estimating the angular flux. + +Thus, to fully amortize the cost of the dead zone integration, one might ask why +not simply run a single ray per iteration with an extremely long active length? +While this is also theoretically possible, this results in two issues. The first +problem is that each ray only represents a single angular sample. As we want to +sample the angular phase space of the simulation with similar fidelity to the +spatial phase space, we naturally want a lot of angles. This means in practice, +we want to balance the need to amortize the cost of the inactive region of the +ray with the need to sample lots of angles. The second problem is that +parallelism in OpenMC is expressed in terms of rays, with each being processed +by an independent MPI rank and/or OpenMP thread, thus we want to ensure each +thread has many rays to process. + +In practical terms, the best strategy is typically to set an active ray length +that is about 10 times that of the inactive ray length. This is often the right +balance between ensuring not too much time is spent in the dead zone, while +still adequately sampling the angular phase space. However, as discussed in the +previous section, some types of simulation may demand that additional thought be +applied to this parameter. For instance, in the same example where we have a +detector region far outside a reactor core, we want to make sure that there is +enough active ray length that rays exiting the core can reach the detector +region. For example, if the detector were to be 30 cm outside of the core, then +we would need to ensure that at least a few hundred cm of active length were +used so as to ensure even rays with indirect angles will be able to reach the +target region. + +The number of rays each iteration can be set by reusing the normal Monte Carlo +particle count selection parameter, as:: + + settings.particles = 2000 + +----------- +Ray Density +----------- + +In the preceding sections, it was argued that for most use cases, the inactive +length for a ray can be determined by taking a multiple of the mean free path +for the limiting energy group. The active ray length could then be set by taking +a multiple of the inactive length. With these parameters set, how many rays per +iteration should be run? + +There are three basic settings that control the density of the stochastic +quadrature being used to integrate the domain each iteration. These three +variables are: + +- The number of rays (in OpenMC settings parlance, "particles") +- The inactive distance per ray +- The active distance per ray + +While the inactive and active ray lengths can usually be chosen by simply +examining the geometry, tallies, and cross section data, one has much more +flexibility in the choice of the number of rays to run. Consider a few +scenarios: + +- If a choice of zero rays is made, then no information is gained by the system + after each batch. +- If a choice of rays close to zero is made, then some information is gained + after each batch, but many source regions may not have been visited that + iteration, which is not ideal numerically and can result in instability. + Empirically, we have found that the simulation can remain stable and produce + accurate results even when on average 20% or more of the cells have zero rays + passing through them each iteration. However, besides the cost of transporting + rays, a new neutron source must be computed based on the scalar flux at each + iteration. This cost is dictated only by the number of source regions and + energy groups---it is independent of the number of rays. Thus, in practical + terms, if too few rays are run, then the simulation runtime becomes dominated + by the fixed cost of source updates, making it inefficient overall given that + a huge number of active batches will likely be required to converge statistics + to acceptable levels. Additionally, if many cells are missed each iteration, + then the fission and scattering sources may not develop very quickly, + resulting in a need for far more inactive batches than might otherwise be + required. +- If a choice of running a very large number of rays is made such that you + guarantee that all cells are hit each iteration, this avoids any issues with + numerical instability. As even more rays are run, this reduces the number of + active batches that must be used to converge statistics and therefore + minimizes the fixed per-iteration source update costs. While this seems + advantageous, it has the same practical downside as with Monte Carlo---namely, + that the inactive batches tend to be overly well integrated, resulting in a + lot of wasted time. This issue is actually much more serious than in Monte + Carlo (where typically only tens of inactive batches are needed), as random + ray often requires hundreds or even thousands of inactive batches. Thus, + minimizing the cost of the source updates in the active phase needs to be + balanced against the increased cost of the inactive phase of the simulation. +- If a choice of rays is made such that relatively few (e.g., around 0.1%) of + cells are missed each iteration, the cost of the inactive batches of the + simulation is minimized. In this "goldilocks" regime, there is very little + chance of numerical instability, and enough information is gained by each cell + to progress the fission and scattering sources forward at their maximum rate. + However, the inactive batches can proceed with minimal cost. While this will + result in the active phase of the simulation requiring more batches (and + correspondingly higher source update costs), the added cost is typically far + less than the savings by making the inactive phase much cheaper. + +To help you set this parameter, OpenMC will report the average flat source +region miss rate at the end of the simulation. Additionally, OpenMC will alert +you if very high miss rates are detected, indicating that more rays and/or a +longer active ray length might improve numerical performance. Thus, a "guess and +check" approach to this parameter is recommended, where a very low guess is +made, a few iterations are performed, and then the simulation is restarted with +a larger value until the "low ray density" messages go away. + +.. note:: + In summary, the user should select an inactive length corresponding to many + times the mean free path of a particle, generally O(10--100) cm, to ensure accuracy of + the starting angular flux. The active length should be 10× the inactive + length to amortize its cost. The number of rays should be enough so that + nearly all :ref:`FSRs ` are hit at least once each power iteration (the hit fraction + is reported by OpenMC for empirical user adjustment). + +.. warning:: + For simulations where long range uncollided flux estimates need to be + accurately resolved (e.g., shielding, detector response, and problems with + significant void areas), make sure that selections for inactive and active + ray lengths are sufficiently long to allow for transport to occur between + source and target regions of interest. + +.. _usersguide_ray_source: + +---------- +Ray Source +---------- + +Random ray requires that the ray source be uniform in space and isotropic in +angle. To facilitate sampling, the user must specify a single random ray source +for sampling rays in both eigenvalue and fixed source solver modes. The random +ray integration source should be of type :class:`openmc.IndependentSource`, and +is specified as part of the :attr:`openmc.Settings.random_ray` dictionary. Note +that the source must not be limited to only fissionable regions. Additionally, +the source box must cover the entire simulation domain. In the case of a +simulation domain that is not box shaped, a box source should still be used to +bound the domain but with the source limited to rejection sampling the actual +simulation universe (which can be specified via the ``domains`` constraint of the +:class:`openmc.IndependentSource` Python class). Similar to Monte Carlo sources, +for two-dimensional problems (e.g., a 2D pincell) it is desirable to make the +source bounded near the origin of the infinite dimension. An example of an +acceptable ray source for a two-dimensional 2x2 lattice would look like: + +:: + + pitch = 1.26 + lower_left = (-pitch, -pitch, -pitch) + upper_right = ( pitch, pitch, pitch) + uniform_dist = openmc.stats.Box(lower_left, upper_right) + settings.random_ray['ray_source'] = openmc.IndependentSource(space=uniform_dist) + +.. note:: + The random ray source is not related to the underlying particle flux or + source distribution of the simulation problem. It is akin to the selection + of an integration quadrature. Thus, in fixed source mode, the ray source + still needs to be provided and still needs to be uniform in space and angle + throughout the simulation domain. In fixed source mode, the user will + provide physical particle fixed sources in addition to the random ray + source. + +-------------------------- +Quasi-Monte Carlo Sampling +-------------------------- + +By default OpenMC will use a pseudorandom number generator (PRNG) to sample ray +starting locations from a uniform distribution in space and angle. +Alternatively, a randomized Halton sequence may be sampled from, which is a form +of Randomized Qusi-Monte Carlo (RQMC) sampling. RQMC sampling with random ray +has been shown to offer reduced variance as compared to regular PRNG sampling, +as the Halton sequence offers a more uniform distribution of sampled points. +Randomized Halton sampling can be enabled as:: + + settings.random_ray['sample_method'] = 'halton' + +Default behavior using OpenMC's native PRNG can be manually specified as:: + + settings.random_ray['sample_method'] = 'prng' + +.. _subdivision_fsr: + +----------------------------- +Subdivision of Source Regions +----------------------------- + +While the scattering and fission sources in Monte Carlo are treated +continuously, they are assumed to have a shape (flat or linear) within a MOC or +random ray source region (SR). This introduces bias into the simulation that can +be remedied by reducing the physical size of the SR to be smaller than the +typical mean free paths of particles. While use of linear sources in OpenMC +greatly reduces the error stemming from this approximation, subdivision is still +typically required. + +In OpenMC, this subdivision can be done either manually by the user (by defining +additional surfaces and cells in the geometry) or automatically by assigning a +mesh to one or more cells, universes, or material types. The level of +subdivision needed will be dependent on the fidelity the user requires. For +typical light water reactor analysis, consider the following example of manual +subdivision of a two-dimensional 2x2 reflective pincell lattice: + +.. figure:: ../_images/2x2_materials.jpeg + :class: with-border + :width: 400 + + Material definition for an asymmetrical 2x2 lattice (1.26 cm pitch) + +.. figure:: ../_images/2x2_fsrs.jpeg + :class: with-border + :width: 400 + + Manual decomposition for an asymmetrical 2x2 lattice (1.26 cm pitch) + +Geometry cells can also be subdivided into small source regions by assigning a +mesh to a list of domains, with each domain being of type +:class:`openmc.Material`, :class:`openmc.Cell`, or :class:`openmc.Universe`. The +idea of defining a source region as a combination of a base geometry cell and a +mesh element is known as "cell-under-voxel" style geometry, although in OpenMC +the mesh can be any kind and is not restricted to 3D regular voxels. An example +of overlaying a simple 2D mesh over a geometry is given as:: + + sr_mesh = openmc.RegularMesh() + sr_mesh.dimension = (n, n) + sr_mesh.lower_left = (0.0, 0.0) + sr_mesh.upper_right = (x, y) + domain = geometry.root_universe + settings.random_ray['source_region_meshes'] = [(sr_mesh, [domain])] + +In the above example, we apply a single :math:`n \times n` uniform mesh over the +entire domain by assigning it to the root universe of the geometry. +Alternatively, we might want to apply a finer or coarser mesh to different +regions of a 3D problem, for instance, as:: + + fuel = openmc.Material(name='UO2 fuel') + ... + water = openmc.Material(name='hot borated water') + ... + clad = openmc.Material(name='Zr cladding') + ... + + coarse_mesh = openmc.RegularMesh() + coarse_mesh.dimension = (n, n, n) + coarse_mesh.lower_left = (0.0, 0.0, 0.0) + coarse_mesh.upper_right = (x, y, z) + + fine_mesh = openmc.RegularMesh() + fine_mesh.dimension = (2*n, 2*n, 2*n) + fine_mesh.lower_left = (0.0, 0.0, 0.0) + fine_mesh.upper_right = (x, y, z) + + settings.random_ray['source_region_meshes'] = [(fine_mesh, [fuel, clad]), (coarse_mesh, [water])] + +Note that we don't need to adjust the outer bounds of the mesh to tightly wrap +the domain we assign the mesh to. Rather, OpenMC will dynamically generate +source regions based on the mesh bins rays actually visit, such that no +additional memory is wasted even if a domain only intersects a few mesh bins. +Going back to our 2x2 lattice example, if using a mesh-based subdivision, this +might look as below: + +.. figure:: ../_images/2x2_sr_mesh.png + :class: with-border + :width: 400 + + 20x20 overlaid "cell-under-voxel" mesh decomposition for an asymmetrical 2x2 lattice (1.26 cm pitch) + +Note that mesh-bashed subdivision is much easier for a user to implement but +does have a few downsides compared to manual subdivision. Manual subdivision can +be done with the specifics of the geometry in mind. As in the pincell example, +it is more efficient to subdivide the fuel region into azimuthal sectors and +radial rings as opposed to a Cartesian mesh. This is more efficient because the +regions are a more uniform size and follow the material boundaries closer, +resulting in the need for fewer source regions. Fewer source regions tends to +equate to a faster computational speed and/or the need for fewer rays per batch +to achieve good statistics. Additionally, applying a mesh often tends to create +a few very small source regions, as shown in the above picture where corners of +the mesh happen to intersect close to the actual fuel-moderator interface. These +small regions are rarely visited by rays, which can result in inaccurate +estimates of the source within those small regions and, thereby, numerical +instability. However, OpenMC utilizes several techniques to detect these small +source regions and mitigate instabilities that are associated with them. In +conclusion, mesh overlay is a great way to subdivide any geometry into smaller +source regions. It can be used while retaining stability, though typically at +the cost of generating more source regions relative to an optimal manual +subdivision. + +.. _usersguide_flux_norm: + +------- +Tallies +------- + +Most tallies, filters, and scores that you would expect to work with a +multigroup solver like random ray are supported. For example, you can define 3D +mesh tallies with energy filters and flux, fission, and nu-fission scores, etc. +There are some restrictions though. For starters, it is assumed that all filter +mesh boundaries will conform to physical surface boundaries (or lattice +boundaries) in the simulation geometry. It is acceptable for multiple cells +(FSRs) to be contained within a mesh element (e.g., pincell-level or +assembly-level tallies should work), but it is currently left as undefined +behavior if a single simulation cell is contained in multiple mesh elements. + +Supported scores: + - flux + - total + - fission + - nu-fission + - events + +Supported Estimators: + - tracklength + +Supported Filters: + - cell + - cell instance + - distribcell + - energy + - material + - mesh + - universe + +Note that there is no difference between the analog, tracklength, and collision +estimators in random ray mode as individual particles are not being simulated. +Tracklength-style tally estimation is inherent to the random ray method. + +As discussed in the random ray theory section on :ref:`Random Ray +Tallies`, by default flux tallies in the random ray mode +are not normalized by the spatial tally volumes such that flux tallies are in +units of cm. While the volume information is readily available as a byproduct of +random ray integration, the flux value is reported in unnormalized units of cm +so that the user will be able to compare "apples to apples" with the default +flux tallies from the Monte Carlo solver (also reported by default in units of +cm). If volume normalized flux tallies (in units of cm\ :sup:`-2`) are desired, +then the user can set the ``volume_normalized_flux_tallies`` field in the +:attr:`openmc.Settings.random_ray` dictionary to ``True``. An example is given +below: + +:: + + settings.random_ray['volume_normalized_flux_tallies'] = True + +Note that MC mode flux tallies can also be normalized by volume, as discussed in +the :ref:`Volume Calculation Section` of the user guide. + +-------- +Plotting +-------- + +Visualization of geometry is handled in the same way as normal with OpenMC (see +:ref:`plotting guide ` for more details). That is, ``openmc +--plot`` is handled without any modifications, as the random ray solver uses the +same geometry definition as in Monte Carlo. + +In addition to OpenMC's standard geometry plotting mode, the random ray solver +also features an additional method of data visualization. If a ``plots.xml`` +file is present, any voxel plots that are defined will be output at the end of a +random ray simulation. Rather than being stored in HDF5 file format, the random +ray plotting will generate ``.vtk`` files that can be directly read and plotted +with `Paraview `_. + +In fixed source Monte Carlo (MC) simulations, by default the only thing global +tally provided is the leakage fraction. In a k-eigenvalue MC simulation, by +default global tallies are collected for the eigenvalue and leakage fraction. +Spatial flux information must be manually requested, and often fine-grained +spatial meshes are considered costly/unnecessary, so it is impractical in MC +mode to plot spatial flux or power info by default. Conversely, in random ray, +the solver functions by estimating the multigroup source and flux spectrums in +every fine-grained FSR each iteration. Thus, for random ray, in both fixed +source and eigenvalue simulations, the simulation always finishes with a well +converged flux estimate for all areas. As such, it is much more common in random +ray, MOC, and other deterministic codes to provide spatial flux information by +default. In the future, all FSR data will be made available in the statepoint +file, which facilitates plotting and manipulation through the Python API; at +present, statepoint support is not available. + +Only voxel plots will be used to generate output; other plot types present in +the ``plots.xml`` file will be ignored. The following fields will be written to +the VTK structured grid file: + + - material + - FSR index + - flux spectrum (for each energy group) + - total fission source (integrated across all energy groups) + +------------------------------------------ +Inputting Multigroup Cross Sections (MGXS) +------------------------------------------ + +Multigroup cross sections for use with OpenMC's random ray solver are input the +same way as with OpenMC's traditional multigroup Monte Carlo mode. There is more +information on generating multigroup cross sections via OpenMC in the +:ref:`multigroup materials ` user guide. You may also wish to use +an existing ``mgxs.h5`` MGXS library file, or define your own given a known set +of cross section data values (e.g., as taken from a benchmark specification). An +example of using OpenMC's Python interface to generate a correctly formatted +``mgxs.h5`` input file is given in the `OpenMC Jupyter notebook collection +`_. + +.. note:: + Currently only isotropic and isothermal multigroup cross sections are + supported in random ray mode. To represent multiple material temperatures, + separate materials can be defined each with a separate multigroup dataset + corresponding to a given temperature. + +.. _mgxs_gen: + +------------------------------------------- +Generating Multigroup Cross Sections (MGXS) +------------------------------------------- + +OpenMC is capable of generating multigroup cross sections by way of flux +collapsing data based on flux solutions obtained from a continuous energy Monte +Carlo solve. While it is a circular excercise in some respects to use continuous +energy Monte Carlo to generate cross sections to be used by a reduced-fidelity +multigroup transport solver, there are many use cases where this is nonetheless +highly desirable. For instance, generation of a multigroup library may enable +the same set of approximate multigroup cross section data to be used across a +variety of problem types (or through a multidimensional parameter sweep of +design variables) with only modest errors and at greatly reduced cost as +compared to using only continuous energy Monte Carlo. + +~~~~~~~~~~~~ +The Easy Way +~~~~~~~~~~~~ + +The easiest way to generate a multigroup cross section library is to use the +:meth:`openmc.Model.convert_to_multigroup` method. This method will +automatically output a multigroup cross section library file (``mgxs.h5``) from +a continuous energy Monte Carlo model and alter the material definitions in the +model to use these multigroup cross sections. An example is given below:: + + # Assume we already have a working continuous energy model + model.convert_to_multigroup( + method="material_wise", + groups="CASMO-2", + nparticles=2000, + overwrite_mgxs_library=False, + mgxs_path="mgxs.h5", + correction=None + ) + +The most important parameter to set is the ``method`` parameter, which can be +either "stochastic_slab", "material_wise", or "infinite_medium". An overview +of these methods is given below: + +.. list-table:: Comparison of Automatic MGXS Generation Methods + :header-rows: 1 + :widths: 10 30 30 30 + + * - Method + - Description + - Pros + - Cons + * - ``material_wise`` (default) + - * Higher Fidelity + * Runs a CE simulation with the original geometry and source, tallying + cross sections with a material filter. + - * Typically the most accurate of the three methods + * Accurately captures (averaged over the full problem domain) + both spatial and resonance self shielding effects + - * Potentially slower as the full geometry must be run + * If a material is only present far from the source and doesn't get tallied + to in the CE simulation, the MGXS will be zero for that material. + * - ``stochastic_slab`` + - * Medium Fidelity + * Runs a CE simulation with a greatly simplified geometry, where materials + are randomly assigned to layers in a 1D "stochastic slab sandwich" geometry + - * Still captures resonant self shielding and resonance effects between materials + * Fast due to the simplified geometry + * Able to produce cross section data for all materials, regardless of how + far they are from the source in the original geometry + - * Does not capture most spatial self shielding effects, e.g., no lattice physics. + * - ``infinite_medium`` + - * Lower Fidelity + * Runs one CE simulation per material independently. Each simulation is just + an infinite medium slowing down problem, with an assumed external source term. + - * Simple + - * Poor accuracy (no spatial information, no lattice physics, no resonance effects + between materials) + * May hang if a material has a k-infinity greater than 1.0 + +When selecting a non-default energy group structure, you can manually define +group boundaries or specify the name of a known group structure (a list of which +can be found at :data:`openmc.mgxs.GROUP_STRUCTURES`). The ``nparticles`` +parameter can be adjusted upward to improve the fidelity of the generated cross +section library. The ``correction`` parameter can be set to ``"P0"`` to enable +P0 transport correction. The ``overwrite_mgxs_library`` parameter can be set to +``True`` to overwrite an existing MGXS library file, or ``False`` to skip +generation and use an existing library file. + +.. note:: + MGXS transport correction (via setting the ``correction`` parameter in the + :meth:`openmc.Model.convert_to_multigroup` method to ``"P0"``) may + result in negative in-group scattering cross sections, which can cause + numerical instability. To mitigate this, during a random ray solve OpenMC + will automatically apply + `diagonal stabilization `_ + with a :math:`\rho` default value of 1.0, which can be adjusted with the + ``settings.random_ray['diagonal_stabilization_rho']`` parameter. + +Ultimately, the methods described above are all just approximations. +Approximations in the generated MGXS data will fundamentally limit the potential +accuracy of the random ray solver. However, the methods described above are all +useful in that they can provide a good starting point for a random ray +simulation, and if more fidelity is needed the user may wish to follow the +instructions below or experiment with transport correction techniques to improve +the fidelity of the generated MGXS data. + +~~~~~~~~~~~~ +The Hard Way +~~~~~~~~~~~~ + +We give here a quick summary of how to produce a multigroup cross section data +file (``mgxs.h5``) from a starting point of a typical continuous energy Monte +Carlo model. Notably, continuous energy models define materials as a mixture of +nuclides with different densities, whereas multigroup materials are simply +defined by which name they correspond to in a ``mgxs.h5`` library file. + +To generate the cross section data, we begin with a continuous energy Monte +Carlo model and add in the tallies that are needed to generate our library. In +this example, we will specify material-wise cross sections and a two-group +energy decomposition:: + + # Define geometry + ... + geometry = openmc.Geometry() + ... + + # Initialize MGXS library with a finished OpenMC geometry object + mgxs_lib = openmc.mgxs.Library(geometry) + + # Pick energy group structure + groups = openmc.mgxs.EnergyGroups('CASMO-2') + mgxs_lib.energy_groups = groups + + # Disable transport correction + mgxs_lib.correction = None + + # Specify needed cross sections for random ray + mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission', + 'nu-scatter matrix', 'multiplicity matrix', 'chi'] + + # Specify a "cell" domain type for the cross section tally filters + mgxs_lib.domain_type = "material" + + # Specify the cell domains over which to compute multi-group cross sections + mgxs_lib.domains = geometry.get_all_materials().values() + + # Do not compute cross sections on a nuclide-by-nuclide basis + mgxs_lib.by_nuclide = False + + # Check the library - if no errors are raised, then the library is satisfactory. + mgxs_lib.check_library_for_openmc_mgxs() + + # Construct all tallies needed for the multi-group cross section library + mgxs_lib.build_library() + + # Create a "tallies.xml" file for the MGXS Library + tallies = openmc.Tallies() + mgxs_lib.add_to_tallies_file(tallies, merge=True) + + # Export + tallies.export_to_xml() + + ... + +When selecting an energy decomposition, you can manually define group boundaries +or specify the name of known group structure (a list of which can be found at +:data:`openmc.mgxs.GROUP_STRUCTURES`). Once the above model has been run, the +resulting statepoint file will contain the needed flux and reaction rate tally +data so that a MGXS library file can be generated. Below is the postprocessing +script needed to generate the ``mgxs.h5`` library file given a statepoint file +(e.g., ``statepoint.100.h5``) file and summary file (e.g., ``summary.h5``) that +resulted from running our previous example:: + + import openmc + + summary = openmc.Summary('summary.h5') + geom = summary.geometry + mats = summary.materials + + groups = openmc.mgxs.EnergyGroups('CASMO-2') + mgxs_lib = openmc.mgxs.Library(geom) + mgxs_lib.energy_groups = groups + mgxs_lib.correction = None + mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission', + 'nu-scatter matrix', 'multiplicity matrix', 'chi'] + + # Specify a "cell" domain type for the cross section tally filters + mgxs_lib.domain_type = "material" + + # Specify the cell domains over which to compute multi-group cross sections + mgxs_lib.domains = geom.get_all_materials().values() + + # Do not compute cross sections on a nuclide-by-nuclide basis + mgxs_lib.by_nuclide = False + + # Check the library - if no errors are raised, then the library is satisfactory. + mgxs_lib.check_library_for_openmc_mgxs() + + # Construct all tallies needed for the multi-group cross section library + mgxs_lib.build_library() + + with openmc.StatePoint('statepoint.100.h5') as sp: + mgxs_lib.load_from_statepoint(sp) + + names = [mat.name for mat in mgxs_lib.domains] + + # Create a MGXS File which can then be written to disk + mgxs_file = mgxs_lib.create_mg_library(xs_type='macro', xsdata_names=names) + + # Write the file to disk using the default filename of "mgxs.h5" + mgxs_file.export_to_hdf5("mgxs.h5") + +Notably, the postprocessing script needs to match the same +:class:`openmc.mgxs.Library` settings that were used to generate the tallies but +is otherwise able to discern the rest of the simulation details from the +statepoint and summary files. Once the postprocessing script is successfully +run, the ``mgxs.h5`` file can be loaded by subsequent runs of OpenMC. + +If you want to convert continuous energy material objects in an OpenMC input +deck to multigroup ones from a ``mgxs.h5`` library, you can follow the below +example. Here we begin with the original continuous energy materials we used to +generate our MGXS library:: + + fuel = openmc.Material(name='UO2 (2.4%)') + fuel.set_density('g/cm3', 10.29769) + fuel.add_nuclide('U234', 4.4843e-6) + fuel.add_nuclide('U235', 5.5815e-4) + fuel.add_nuclide('U238', 2.2408e-2) + fuel.add_nuclide('O16', 4.5829e-2) + + water = openmc.Material(name='Hot borated water') + water.set_density('g/cm3', 0.740582) + water.add_nuclide('H1', 4.9457e-2) + water.add_nuclide('O16', 2.4672e-2) + water.add_nuclide('B10', 8.0042e-6) + water.add_nuclide('B11', 3.2218e-5) + water.add_s_alpha_beta('c_H_in_H2O') + + materials = openmc.Materials([fuel, water]) + +Once the ``mgxs.h5`` library file has been generated, we can then manually make +the necessary edits to the material definitions so that they load from the +multigroup library instead of defining their isotopic contents, as:: + + # Instantiate some Macroscopic Data + fuel_data = openmc.Macroscopic('UO2 (2.4%)') + water_data = openmc.Macroscopic('Hot borated water') + + # Instantiate some Materials and register the appropriate Macroscopic objects + fuel = openmc.Material(name='UO2 (2.4%)') + fuel.set_density('macro', 1.0) + fuel.add_macroscopic(fuel_data) + + water = openmc.Material(name='Hot borated water') + water.set_density('macro', 1.0) + water.add_macroscopic(water_data) + + # Instantiate a Materials collection and export to XML + materials = openmc.Materials([fuel, water]) + materials.cross_sections = "mgxs.h5" + +In the above example, our ``fuel`` and ``water`` materials will now load MGXS +data from the ``mgxs.h5`` file instead of loading continuous energy isotopic +cross section data. + +-------------- +Linear Sources +-------------- + +Linear Sources (LS), are supported with the eigenvalue and fixed source random +ray solvers. General 3D LS can be toggled by setting the ``source_shape`` field +in the :attr:`openmc.Settings.random_ray` dictionary to ``'linear'`` as:: + + settings.random_ray['source_shape'] = 'linear' + +LS enables the use of coarser mesh discretizations and lower ray populations, +offsetting the increased computation per ray. + +While OpenMC has no specific mode for 2D simulations, such simulations can be +performed implicitly by leaving one of the dimensions of the geometry unbounded +or by imposing reflective boundary conditions with no variation in between them +in that dimension. When 3D linear sources are used in a 2D random ray +simulation, the extremely long (or potentially infinite) spatial dimension along +one of the axes can cause the linear source to become noisy, leading to +potentially large increases in variance. To mitigate this, the user can force +the z-terms of the linear source to zero by setting the ``source_shape`` field +as:: + + settings.random_ray['source_shape'] = 'linear_xy' + +which will greatly improve the quality of the linear source term in 2D +simulations. + +--------------------------------- +Fixed Source and Eigenvalue Modes +--------------------------------- + +Both fixed source and eigenvalue modes are supported with the random ray solver +in OpenMC. Modes can be selected as described in the :ref:`run modes section +`. In both modes, a ray source must be provided to let +OpenMC know where to sample ray starting locations from, as discussed in the +:ref:`ray source section `. In fixed source mode, at +least one regular source must be provided as well that represents the physical +particle fixed source. As discussed in the :ref:`fixed source methodology +section `, the types of fixed sources supported +in the random ray solver mode are limited compared to what is possible with the +Monte Carlo solver. + +Currently, all of the following conditions must be met for the particle source +to be valid in random ray mode: + +- Either a point source must be used, or a domain constraint must be specified + that indicates which cells, universes, or materials the source applies to. In + either case, this implicitly limits the source type to being volumetric, as + even in the point source case the source will be "smeared" throughout the + source region that contains the point source coordinate. A source domain is + specified via the ``domains`` constraint placed on the + :class:`openmc.IndependentSource` Python class. +- The source must be isotropic (default for a source) +- The source must use a discrete (i.e., multigroup) energy distribution. The + discrete energy distribution is input by defining a + :class:`openmc.stats.Discrete` Python class, and passed as the ``energy`` + field of the :class:`openmc.IndependentSource` Python class. + +Any other spatial distribution information contained in a particle source will +be ignored. Only the specified cell, material, or universe domains will be used +to define the spatial location of the source, as the source will be applied +during a pre-processing stage of OpenMC to all source regions that are contained +within the specified domains for the source. + +When defining a :class:`openmc.stats.Discrete` object, note that the ``x`` field +will correspond to the discrete energy points, and the ``p`` field will +correspond to the discrete probabilities. It is recommended to select energy +points that fall within energy groups rather than on boundaries between the +groups. That is, if the problem contains two energy groups (with bin edges of +1.0e-5, 1.0e-1, 1.0e7), then a good selection for the ``x`` field might be +points of 1.0e-2 and 1.0e1. + +:: + + # Define geometry, etc. + ... + source_cell = openmc.Cell(fill=source_mat, name='cell where fixed source will be') + ... + # Define physical neutron fixed source + energy_points = [1.0e-2, 1.0e1] + strengths = [0.25, 0.75] + energy_distribution = openmc.stats.Discrete(x=energy_points, p=strengths) + neutron_source = openmc.IndependentSource( + energy=energy_distribution, + constraints={'domains': [source_cell]} + ) + + # Add fixed source and ray sampling source to settings file + settings.source = [neutron_source] + +.. _usersguide_vol_estimators: + +----------------------------- +Alternative Volume Estimators +----------------------------- + +As discussed in the random ray theory section on :ref:`volume estimators +`, there are several possible derivations for the scalar +flux estimate. These options deal with different ways of treating the +accumulation over ray lengths crossing each FSR (a quantity directly +proportional to volume), which can be computed using several methods. The +following methods are currently available in OpenMC: + +.. list-table:: Comparison of Estimators + :header-rows: 1 + :widths: 10 30 30 30 + + * - Estimator + - Description + - Pros + - Cons + * - ``simulation_averaged`` + - Accumulates total active ray lengths in each FSR over all iterations, + improving the estimate of the volume in each cell each iteration. + - * Virtually unbiased after several iterations + * Asymptotically approaches the true analytical volume + * Typically most efficient in terms of speed vs. accuracy + - * Higher variance + * Can lead to negative fluxes and numerical instability in pathological + cases + * - ``naive`` + - Treats the volume as composed only of the active ray length through each + FSR per iteration, being a biased but numerically consistent ratio + estimator. + - * Low variance + * Unlikely to result in negative fluxes + * Recommended in cases where the simulation averaged estimator is + unstable + - * Biased estimator + * Requires more rays or longer active ray length to mitigate bias + * - ``hybrid`` (default) + - Applies the naive estimator to all cells that contain an external (fixed) + source contribution. Applies the simulation averaged estimator to all + other cells. + - * High accuracy/low bias of the simulation averaged estimator in most + cells + * Stability of the naive estimator in cells with fixed sources + - * Can lead to slightly negative fluxes in cells where the simulation + averaged estimator is used + +These estimators can be selected by setting the ``volume_estimator`` field in the +:attr:`openmc.Settings.random_ray` dictionary. For example, to use the naive +estimator, the following code would be used: + +:: + + settings.random_ray['volume_estimator'] = 'naive' + +----------------- +Adjoint Flux Mode +----------------- + +The adjoint flux random ray solver mode can be enabled as:: + + settings.random_ray['adjoint'] = True + +When enabled, OpenMC will first run a forward transport simulation followed by +an adjoint transport simulation. The purpose of the forward solve is to compute +the adjoint external source when an external source is present in the +simulation. Simulation settings (e.g., number of rays, batches, etc.) will be +identical for both simulations. At the conclusion of the run, all results (e.g., +tallies, plots, etc.) will be derived from the adjoint flux rather than the +forward flux but are not labeled any differently. The initial forward flux +solution will not be stored or available in the final statepoint file. Those +wishing to do analysis requiring both the forward and adjoint solutions will +need to run two separate simulations and load both statepoint files. + +.. note:: + When adjoint mode is selected, OpenMC will always perform a full forward + solve and then run a full adjoint solve immediately afterwards. Statepoint + and tally results will be derived from the adjoint flux, but will not be + labeled any differently. + +--------------------------------------- +Putting it All Together: Example Inputs +--------------------------------------- + +~~~~~~~~~~~~~~~~~~ +Eigenvalue Example +~~~~~~~~~~~~~~~~~~ + +An example of a settings definition for an eigenvalue random ray simulation is +given below: + +:: + + # Geometry and MGXS material definition of 2x2 lattice (not shown) + pitch = 1.26 + group_edges = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + ... + + # Instantiate a settings object for a random ray solve + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.batches = 1200 + settings.inactive = 600 + settings.particles = 2000 + + settings.random_ray['distance_inactive'] = 40.0 + settings.random_ray['distance_active'] = 400.0 + + # Create an initial uniform spatial source distribution for sampling rays + lower_left = (-pitch, -pitch, -pitch) + upper_right = ( pitch, pitch, pitch) + uniform_dist = openmc.stats.Box(lower_left, upper_right) + settings.random_ray['ray_source'] = openmc.IndependentSource(space=uniform_dist) + + settings.export_to_xml() + + # Define tallies + + # Create a mesh filter + mesh = openmc.RegularMesh() + mesh.dimension = (2, 2) + mesh.lower_left = (-pitch/2, -pitch/2) + mesh.upper_right = (pitch/2, pitch/2) + mesh_filter = openmc.MeshFilter(mesh) + + # Create a multigroup energy filter + energy_filter = openmc.EnergyFilter(group_edges) + + # Create tally using our two filters and add scores + tally = openmc.Tally() + tally.filters = [mesh_filter, energy_filter] + tally.scores = ['flux', 'fission', 'nu-fission'] + + # Instantiate a Tallies collection and export to XML + tallies = openmc.Tallies([tally]) + tallies.export_to_xml() + + # Create voxel plot + plot = openmc.Plot() + plot.origin = [0, 0, 0] + plot.width = [2*pitch, 2*pitch, 1] + plot.pixels = [1000, 1000, 1] + plot.type = 'voxel' + + # Instantiate a Plots collection and export to XML + plots = openmc.Plots([plot]) + plots.export_to_xml() + +All other inputs (e.g., geometry, materials) will be unchanged from a typical +Monte Carlo run (see the :ref:`geometry ` and +:ref:`multigroup materials ` user guides for more information). + +There is also a complete example of a pincell available in the +``openmc/examples/pincell_random_ray`` folder. + +~~~~~~~~~~~~~~~~~~~~ +Fixed Source Example +~~~~~~~~~~~~~~~~~~~~ + +An example of a settings definition for a fixed source random ray simulation is +given below: + +:: + + # Geometry and MGXS material definition of 2x2 lattice (not shown) + pitch = 1.26 + source_cell = openmc.Cell(fill=source_mat, name='cell where fixed source will be') + ebins = [1e-5, 1e-1, 20.0e6] + ... + + # Instantiate a settings object for a random ray solve + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.batches = 1200 + settings.inactive = 600 + settings.particles = 2000 + settings.run_mode = 'fixed source' + settings.random_ray['distance_inactive'] = 40.0 + settings.random_ray['distance_active'] = 400.0 + + # Create an initial uniform spatial source distribution for sampling rays + lower_left = (-pitch, -pitch, -pitch) + upper_right = ( pitch, pitch, pitch) + uniform_dist = openmc.stats.Box(lower_left, upper_right) + settings.random_ray['ray_source'] = openmc.IndependentSource(space=uniform_dist) + + # Define physical neutron fixed source + energy_points = [1.0e-2, 1.0e1] + strengths = [0.25, 0.75] + energy_distribution = openmc.stats.Discrete(x=energy_points, p=strengths) + neutron_source = openmc.IndependentSource( + energy=energy_distribution, + constraints={'domains': [source_cell]} + ) + + # Add fixed source and ray sampling source to settings file + settings.source = [neutron_source] + + settings.export_to_xml() + + # Define tallies + + # Create a mesh filter + mesh = openmc.RegularMesh() + mesh.dimension = (2, 2) + mesh.lower_left = (-pitch/2, -pitch/2) + mesh.upper_right = (pitch/2, pitch/2) + mesh_filter = openmc.MeshFilter(mesh) + + # Create a multigroup energy filter + energy_filter = openmc.EnergyFilter(ebins) + + # Create tally using our two filters and add scores + tally = openmc.Tally() + tally.filters = [mesh_filter, energy_filter] + tally.scores = ['flux'] + + # Instantiate a Tallies collection and export to XML + tallies = openmc.Tallies([tally]) + tallies.export_to_xml() + + # Create voxel plot + plot = openmc.Plot() + plot.origin = [0, 0, 0] + plot.width = [2*pitch, 2*pitch, 1] + plot.pixels = [1000, 1000, 1] + plot.type = 'voxel' + + # Instantiate a Plots collection and export to XML + plots = openmc.Plots([plot]) + plots.export_to_xml() + +All other inputs (e.g., geometry, material) will be unchanged from a typical +Monte Carlo run (see the :ref:`geometry ` and +:ref:`multigroup materials ` user guides for more information). diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index 963e91cf2d..0879d63efd 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -47,163 +47,9 @@ flags: -r, --restart file Restart a previous run from a state point or a particle restart file -s, --threads N Run with *N* OpenMP threads --t, --track Write tracks for all particles +-t, --track Write tracks for all particles (up to max_tracks) -v, --version Show version information -h, --help Show help message .. note:: If you're using the Python API, :func:`openmc.run` is equivalent to running ``openmc`` from the command line. - -.. _scripts_ace: - ----------------------- -``openmc-ace-to-hdf5`` ----------------------- - -This script can be used to create HDF5 nuclear data libraries used by OpenMC if -you have existing ACE files. There are four different ways you can specify ACE -libraries that are to be converted: - -1. List each ACE library as a positional argument. This is very useful in - conjunction with the usual shell utilities (``ls``, ``find``, etc.). -2. Use the ``--xml`` option to specify a pre-v0.9 cross_sections.xml file. -3. Use the ``--xsdir`` option to specify a MCNP xsdir file. -4. Use the ``--xsdata`` option to specify a Serpent xsdata file. - -The script does not use any extra information from cross_sections.xml/ xsdir/ -xsdata files to determine whether the nuclide is metastable. Instead, the -``--metastable`` argument can be used to specify whether the ZAID naming convention -follows the NNDC data convention (1000*Z + A + 300 + 100*m), or the MCNP data -convention (essentially the same as NNDC, except that the first metastable state -of Am242 is 95242 and the ground state is 95642). - -The optional ``--fission_energy_release`` argument will accept an HDF5 file -containing a library of fission energy release (ENDF MF=1 MT=458) data. A -library built from ENDF/B-VII.1 data is released with OpenMC and can be found at -openmc/data/fission_Q_data_endb71.h5. This data is necessary for -'fission-q-prompt' and 'fission-q-recoverable' tallies, but is not needed -otherwise. - --h, --help show help message and exit - --d DESTINATION, --destination DESTINATION - Directory to create new library in - --m META, --metastable META - How to interpret ZAIDs for metastable nuclides. META - can be either 'nndc' or 'mcnp'. (default: nndc) - ---xml XML Old-style cross_sections.xml that lists ACE libraries - ---xsdir XSDIR MCNP xsdir file that lists ACE libraries - ---xsdata XSDATA Serpent xsdata file that lists ACE libraries - ---fission_energy_release FISSION_ENERGY_RELEASE - HDF5 file containing fission energy release data - -.. _scripts_plot: - --------------------------- -``openmc-plot-mesh-tally`` --------------------------- - -``openmc-plot-mesh-tally`` provides a graphical user interface for plotting mesh -tallies. The path to the statepoint file can be provided as an optional arugment -(if omitted, a file dialog will be presented). - -.. _scripts_track: - ------------------------ -``openmc-track-to-vtk`` ------------------------ - -This script converts HDF5 :ref:`particle track files ` to VTK -poly data that can be viewed with ParaView or VisIt. The filenames of the -particle track files should be given as posititional arguments. The output -filename can also be changed with the ``-o`` flag: - --o OUT, --out OUT Output VTK poly filename - ------------------------- -``openmc-update-inputs`` ------------------------- - -If you have existing XML files that worked in a previous version of OpenMC that -no longer work with the current version, you can try to update these files using -``openmc-update-inputs``. If any of the given files do not match the most -up-to-date formatting, then they will be automatically rewritten. The old -out-of-date files will not be deleted; they will be moved to a new file with -'.original' appended to their name. - -Formatting changes that will be made: - -geometry.xml - Lattices containing 'outside' attributes/tags will be replaced with lattices - containing 'outer' attributes, and the appropriate cells/universes will be - added. Any 'surfaces' attributes/elements on a cell will be renamed 'region'. - -materials.xml - Nuclide names will be changed from ACE aliases (e.g., Am-242m) to HDF5/GND - names (e.g., Am242_m1). Thermal scattering table names will be changed from - ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). - ----------------------- -``openmc-update-mgxs`` ----------------------- - -This script updates OpenMC's deprecated multi-group cross section XML files to -the latest HDF5-based format. - --i IN, --input IN Input XML file --o OUT, --output OUT Output file in HDF5 format - -.. _scripts_validate: - ------------------------ -``openmc-validate-xml`` ------------------------ - -Input files can be checked before executing OpenMC using the -``openmc-validate-xml`` script which is installed alongside the Python API. Two -command line arguments can be set when running ``openmc-validate-xml``: - --i, --input-path Location of OpenMC input files. --r, --relaxng-path Location of OpenMC RelaxNG files - -If the RelaxNG path is not set, the script will search for these files because -it expects that the user is either running the script located in the install -directory ``bin`` folder or in ``src/utils``. Once executed, it will match -OpenMC XML files with their RelaxNG schema and check if they are valid. Below -is a table of the messages that will be printed after each file is checked. - -======================== =================================== -Message Description -======================== =================================== -[XML ERROR] Cannot parse XML file. -[NO RELAXNG FOUND] No RelaxNG file found for XML file. -[NOT VALID] XML file does not match RelaxNG. -[VALID] XML file matches RelaxNG. -======================== =================================== - -.. _scripts_voxel: - ---------------------------- -``openmc-voxel-to-vtk`` ---------------------------- - -When OpenMC generates :ref:`voxel plots `, they are in an -:ref:`HDF5 format ` that is not terribly useful by itself. The -``openmc-voxel-to-vtk`` script converts a voxel HDF5 file to a `VTK -`_ file. To run this script, you will need to have the VTK -Python bindings installed. To convert a voxel file, simply provide the path to -the file: - -.. code-block:: sh - - openmc-voxel-to-vtk voxel_1.h5 - -The ``openmc-voxel-to-vtk`` script also takes the following optional -command-line arguments: - --o, --output Path to output VTK file diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index dabad32bd5..1b2d4bc1a5 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -169,16 +169,20 @@ External Source Distributions External source distributions can be specified through the :attr:`Settings.source` attribute. If you have a single external source, you can -create an instance of :class:`openmc.Source` and use it to set the -:attr:`Settings.source` attribute. If you have multiple external sources with -varying source strengths, :attr:`Settings.source` should be set to a list of -:class:`openmc.Source` objects. +create an instance of any of the subclasses of :class:`openmc.SourceBase` +(:class:`openmc.IndependentSource`, :class:`openmc.FileSource`, +:class:`openmc.CompiledSource`) and use it to set the :attr:`Settings.source` +attribute. If you have multiple external sources with varying source strengths, +:attr:`Settings.source` should be set to a list of :class:`openmc.SourceBase` +objects. + +The :class:`openmc.IndependentSource` class is the primary class for defining +source distributions and has four main attributes that one can set: +:attr:`IndependentSource.space`, which defines the spatial distribution, +:attr:`IndependentSource.angle`, which defines the angular distribution, +:attr:`IndependentSource.energy`, which defines the energy distribution, and +:attr:`IndependentSource.time`, which defines the time distribution. -The :class:`openmc.Source` class has four main attributes that one can set: -:attr:`Source.space`, which defines the spatial distribution, -:attr:`Source.angle`, which defines the angular distribution, -:attr:`Source.energy`, which defines the energy distribution, and -:attr:`Source.time`, which defines the time distribution. The spatial distribution can be set equal to a sub-class of :class:`openmc.stats.Spatial`; common choices are :class:`openmc.stats.Point` or @@ -187,7 +191,11 @@ The spatial distribution can be set equal to a sub-class of :class:`openmc.stats.CartesianIndependent`. To independently specify distributions using spherical or cylindrical coordinates, you can use :class:`openmc.stats.SphericalIndependent` or -:class:`openmc.stats.CylindricalIndependent`, respectively. +:class:`openmc.stats.CylindricalIndependent`, respectively. Meshes can also be +used to represent spatial distributions with :class:`openmc.stats.MeshSpatial` +by specifying a mesh and source strengths for each mesh element. It is also +possible to define a "cloud" of source points, each with a different relative +probability, using :class:`openmc.stats.PointCloud`. The angular distribution can be set equal to a sub-class of :class:`openmc.stats.UnitSphere` such as :class:`openmc.stats.Isotropic`, @@ -218,38 +226,57 @@ distribution. This could be a probability mass function (:class:`openmc.stats.Tabular`). By default, if no time distribution is specified, particles are started at :math:`t=0`. + As an example, to create an isotropic, 10 MeV monoenergetic source uniformly distributed over a cube centered at the origin with an edge length of 10 cm, and emitting a pulse of particles from 0 to 10 µs, one would run:: - source = openmc.Source() + source = openmc.IndependentSource() source.space = openmc.stats.Box((-5, -5, -5), (5, 5, 5)) source.angle = openmc.stats.Isotropic() source.energy = openmc.stats.Discrete([10.0e6], [1.0]) source.time = openmc.stats.Uniform(0, 1e-6) settings.source = source -The :class:`openmc.Source` class also has a :attr:`Source.strength` attribute -that indicates the relative strength of a source distribution if multiple are -used. For example, to create two sources, one that should be sampled 70% of the -time and another that should be sampled 30% of the time:: +All subclasses of :class:`openmc.SourceBase` have a :attr:`SourceBase.strength` +attribute that indicates the relative strength of a source distribution if +multiple are used. For example, to create two sources, one that should be +sampled 70% of the time and another that should be sampled 30% of the time:: - src1 = openmc.Source() + src1 = openmc.IndependentSource() src1.strength = 0.7 ... - src2 = openmc.Source() + src2 = openmc.IndependentSource() src2.strength = 0.3 ... settings.source = [src1, src2] -Finally, the :attr:`Source.particle` attribute can be used to indicate the -source should be composed of particles other than neutrons. For example, the -following would generate a photon source:: +When the relative strengths are several orders of magnitude different, it may +happen that not enough statistics are obtained from the lower strength source. +This can be improved by sampling among the sources with equal probability, +applying the source strength as a weight on the sampled source particles. The +:attr:`Settings.uniform_source_sampling` attribute can be used to enable this +option:: - source = openmc.Source() + src1 = openmc.IndependentSource() + src1.strength = 100.0 + ... + + src2 = openmc.IndependentSource() + src2.strength = 1.0 + ... + + settings.source = [src1, src2] + settings.uniform_source_sampling = True + +Finally, the :attr:`IndependentSource.particle` attribute can be used to +indicate the source should be composed of particles other than neutrons. For +example, the following would generate a photon source:: + + source = openmc.IndependentSource() source.particle = 'photon' ... @@ -261,10 +288,10 @@ For a full list of all classes related to statistical distributions, see File-based Sources ------------------ -OpenMC can use a pregenerated HDF5 source file by specifying the ``filename`` -argument to :class:`openmc.Source`:: +OpenMC can use a pregenerated HDF5 source file through the +:class:`openmc.FileSource` class:: - settings.source = openmc.Source(filename='source.h5') + settings.source = openmc.FileSource('source.h5') Statepoint and source files are generated automatically when a simulation is run and can be used as the starting source in a new simulation. Alternatively, a @@ -272,6 +299,9 @@ source file can be manually generated with the :func:`openmc.write_source_file` function. This is particularly useful for coupling OpenMC with another program that generates a source to be used in OpenMC. +Surface Sources ++++++++++++++++ + A source file based on particles that cross one or more surfaces can be generated during a simulation using the :attr:`Settings.surf_source_write` attribute:: @@ -282,12 +312,67 @@ attribute:: } In this example, at most 10,000 source particles are stored when particles cross -surfaces with IDs of 1, 2, or 3. +surfaces with IDs of 1, 2, or 3. If no surface IDs are declared, particles +crossing any surface of the model will be banked:: -.. _custom_source: + settings.surf_source_write = {'max_particles': 10000} -Custom Sources --------------- +A cell ID can also be used to bank particles that are crossing any surface of +a cell that particles are either coming from or going to:: + + settings.surf_source_write = {'cell': 1, 'max_particles': 10000} + +In this example, particles that are crossing any surface that bounds cell 1 will +be banked excluding any surface that does not use a 'transmission' or 'vacuum' +boundary condition. + +.. note:: Surfaces with boundary conditions that are not "transmission" or "vacuum" + are not eligible to store any particles when using ``cell``, ``cellfrom`` + or ``cellto`` attributes. It is recommended to use surface IDs instead. + +Surface IDs can be used in combination with a cell ID:: + + settings.surf_source_write = { + 'cell': 1, + 'surfaces_ids': [1, 2, 3], + 'max_particles': 10000 + } + +In that case, only particles that are crossing the declared surfaces coming from +cell 1 or going to cell 1 will be banked. To account specifically for particles +leaving or entering a given cell, ``cellfrom`` and ``cellto`` are also available +to respectively account for particles coming from a cell:: + + settings.surf_source_write = { + 'cellfrom': 1, + 'max_particles': 10000 + } + +or particles going to a cell:: + + settings.surf_source_write = { + 'cellto': 1, + 'max_particles': 10000 + } + +.. note:: The ``cell``, ``cellfrom`` and ``cellto`` attributes cannot be + used simultaneously. + +To generate more than one surface source files when the maximum number of stored +particles is reached, ``max_source_files`` is available. The surface source bank +will be cleared in simulation memory each time a surface source file is written. +As an example, to write a maximum of three surface source files::: + + settings.surf_source_write = { + 'surfaces_ids': [1, 2, 3], + 'max_particles': 10000, + 'max_source_files': 3 + } + +.. _compiled_source: + +Compiled Sources +---------------- It is often the case that one may wish to simulate a complex source distribution that is not possible to represent with the classes described above. For these @@ -303,7 +388,7 @@ below. #include "openmc/source.h" #include "openmc/particle.h" - class CustomSource : public openmc::Source + class CompiledSource : public openmc::Source { openmc::SourceSite sample(uint64_t* seed) const { @@ -325,9 +410,9 @@ below. } }; - extern "C" std::unique_ptr openmc_create_source(std::string parameters) + extern "C" std::unique_ptr openmc_create_source(std::string parameters) { - return std::make_unique(); + return std::make_unique(); } The above source creates monodirectional 14.08 MeV neutrons that are distributed @@ -354,19 +439,21 @@ OpenMC shared library. This can be done by writing a CMakeLists.txt file: target_link_libraries(source OpenMC::libopenmc) After running ``cmake`` and ``make``, you will have a libsource.so (or .dylib) -file in your build directory. Setting the :attr:`openmc.Source.library` -attribute to the path of this shared library will indicate that it should be -used for sampling source particles at runtime. +file in your build directory. You can then use this as an external source during +an OpenMC run by passing the path of the shared library to the +:class:`openmc.CompiledSource` class, which is then set as the +:attr:`Settings.source` attribute:: -.. _parameterized_custom_source: + settings.source = openmc.CompiledSource('libsource.so') -Custom Parameterized Sources ----------------------------- +.. _parameterized_compiled_source: -Some custom sources may have values (parameters) that can be changed between -runs. This is supported by using the ``openmc_create_source()`` function to -pass parameters defined in the :attr:`openmc.Source.parameters` attribute to -the source class when it is created: +Parameterized Compiled Sources +------------------------------ + +Some compiled sources may have values (parameters) that can be changed between +runs. This is supported by using the ``openmc_create_source()`` function to pass +parameters to the source class when it is created: .. code-block:: c++ @@ -375,9 +462,9 @@ the source class when it is created: #include "openmc/source.h" #include "openmc/particle.h" - class CustomSource : public openmc::Source { + class CompiledSource : public openmc::Source { public: - CustomSource(double energy) : energy_{energy} { } + CompiledSource(double energy) : energy_{energy} { } // Samples from an instance of this class. openmc::SourceSite sample(uint64_t* seed) const @@ -402,13 +489,64 @@ the source class when it is created: double energy_; }; - extern "C" std::unique_ptr openmc_create_source(std::string parameter) { + extern "C" std::unique_ptr openmc_create_source(std::string parameter) { double energy = std::stod(parameter); - return std::make_unique(energy); + return std::make_unique(energy); } -As with the basic custom source functionality, the custom source library -location must be provided in the :attr:`openmc.Source.library` attribute. +When creating an instance of the :class:`openmc.CompiledSource` class, you will +need to pass both the path of the shared library as well as the parameters as a +string, which gets passed down to the ``openmc_create_source()`` function:: + + settings.source = openmc.CompiledSource('libsource.so', '3.5e6') + +.. _usersguide_source_constraints: + +Source Constraints +------------------ + +All source classes in OpenMC have the ability to apply a set of "constraints" +that limit which sampled source sites are actually used for transport. The most +common use case is to sample source sites over some simple spatial distribution +(e.g., uniform over a box) and then only accept those that appear in a given +cell or material. This can be done with a domain constraint, which can be +specified as follows:: + + source_cell = openmc.Cell(...) + ... + + spatial_dist = openmc.stats.Box((-10., -10., -10.), (10., 10., 10.)) + source = openmc.IndependentSource( + space=spatial_dist, + constraints={'domains': [source_cell]} + ) + +For k-eigenvalue problems, a convenient constraint is available that limits +source sites to those sampled in a fissionable material:: + + source = openmc.IndependentSource( + space=spatial_dist, constraints={'fissionable': True} + ) + +Constraints can also be placed on a range of energies or times:: + + # Only use source sites between 500 keV and 1 MeV and with times under 1 sec + source = openmc.FileSource( + 'source.h5', + constraints={'energy_bounds': [500.0e3, 1.0e6], 'time_bounds': [0.0, 1.0]} + ) + +Normally, when a source site is rejected, a new one will be resampled until one +is found that meets the constraints. However, the rejection strategy can be +changed so that a rejected site will just not be simulated by specifying:: + + source = openmc.IndependentSource( + space=spatial_dist, + constraints={'domains': [cell], 'rejection_strategy': 'kill'} + ) + +In this case, the actual number of particles simulated may be less than what you +specified in :attr:`Settings.particles`. .. _usersguide_entropy: @@ -474,7 +612,6 @@ selected:: Some features related to photon transport are not currently implemented, including: - * Tallying photon energy deposition. * Generating a photon source from a neutron calculation that can be used for a later fixed source photon calculation. * Photoneutron reactions. @@ -514,4 +651,141 @@ As an example, to write a statepoint file every five batches:: settings.batches = n settings.statepoint = {'batches': range(5, n + 5, 5)} -.. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html +Particle Track Files +-------------------- + +OpenMC can generate a particle track file that contains track information +(position, direction, energy, time, weight, cell ID, and material ID) for each +state along a particle's history. There are two ways to indicate which particles +and/or how many particles should have their tracks written. First, you can +identify specific source particles by their batch, generation, and particle ID +numbers:: + + settings.tracks = [ + (1, 1, 50), + (2, 1, 30), + (5, 1, 75) + ] + +In this example, track information would be written for the 50th particle in the +1st generation of batch 1, the 30th particle in the first generation of batch 2, +and the 75th particle in the 1st generation of batch 5. Unless you are using +more than one generation per batch (see :ref:`usersguide_particles`), the +generation number should be 1. Alternatively, you can run OpenMC in a mode where +track information is written for *all* particles, up to a user-specified limit:: + + openmc.run(tracks=True) + +In this case, you can control the maximum number of source particles for which +tracks will be written as follows:: + + settings.max_tracks = 1000 + +Particle track information is written to the ``tracks.h5`` file, which can be +analyzed using the :class:`~openmc.Tracks` class:: + + >>> tracks = openmc.Tracks('tracks.h5') + >>> tracks + [, + , + ] + +Each :class:`~openmc.Track` object stores a list of track information for every +primary/secondary particle. In the above example, the first source particle +produced 150 secondary particles for a total of 151 particles. Information for +each primary/secondary particle can be accessed using the +:attr:`~openmc.Track.particle_tracks` attribute:: + + >>> first_track = tracks[0] + >>> first_track.particle_tracks + [, + , + , + , + , + ... + , + ] + >>> photon = first_track.particle_tracks[1] + +The :class:`~openmc.ParticleTrack` class is a named tuple indicating the +particle type and then a NumPy array of the "states". The states array is a +compound type with a field for each physical quantity (position, direction, +energy, time, weight, cell ID, and material ID). For example, to get the +position for the above particle track:: + + >>> photon.states['r'] + array([(-11.92987939, -12.28467295, 0.67837495), + (-11.95213726, -12.2682 , 0.68783964), + (-12.2682 , -12.03428339, 0.82223855), + (-12.5913778 , -11.79510096, 0.95966298), + (-12.6622572 , -11.74264344, 0.98980293), + (-12.6907775 , -11.7215357 , 1.00193058)], + dtype=[('x', '>> tracks.filter(particle='photon') + [, + , + ] + +The :meth:`openmc.Tracks.filter` method returns a new :class:`~openmc.Tracks` +instance, whereas the :meth:`openmc.Track.filter` method returns a new +:class:`~openmc.Track` instance. + +.. note:: If you are using an MPI-enabled install of OpenMC and run a simulation + with more than one process, a separate track file will be written for + each MPI process with the filename ``tracks_p#.h5`` where # is the + rank of the corresponding process. Multiple track files can be + combined with the :meth:`openmc.Tracks.combine` method:: + + track_files = [f"tracks_p{rank}.h5" for rank in range(32)] + openmc.Tracks.combine(track_files, "tracks.h5") + +----------------------- +Restarting a Simulation +----------------------- + +OpenMC can be run in a mode where it reads in a statepoint file and continues a +simulation from the ending point of the statepoint file. A restart simulation +can be performed by passing the path to the statepoint file to the OpenMC +executable: + +.. code-block:: sh + + openmc -r statepoint.100.h5 + +From the Python API, the `restart_file` argument provides the same behavior: + +.. code-block:: python + + openmc.run(restart_file='statepoint.100.h5') + +or if using the :class:`~openmc.Model` class: + +.. code-block:: python + + model.run(restart_file='statepoint.100.h5') + +The restart simulation will execute until the number of batches specified in the +:class:`~openmc.Settings` object on a model (or in the :ref:`settings XML file +`) is satisfied. Note that if the number of batches in the +statepoint file is the same as that specified in the settings object (i.e., if +the inputs were not modified before the restart run), no particles will be +transported and OpenMC will exit immediately. + +.. note:: A statepoint file must match the input model to be successfully used in a restart simulation. diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index da2c5f26d5..e3b4e508bc 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -269,9 +269,8 @@ The following tables show all valid scores: |heating |Total nuclear heating in units of eV per source | | |particle. For neutrons, this corresponds to MT=301 | | |produced by NJOY's HEATR module while for photons, | - | |this is tallied from either direct photon energy | - | |deposition (analog estimator) or pre-generated | - | |photon heating number. See :ref:`methods_heating` | + | |this is tallied from direct photon energy | + | |deposition. See :ref:`methods_heating`. | +----------------------+---------------------------------------------------+ |heating-local |Total nuclear heating in units of eV per source | | |particle assuming energy from secondary photons is | @@ -318,6 +317,29 @@ The following tables show all valid scores: | |particle. This corresponds to MT=444 produced by | | |NJOY's HEATR module. | +----------------------+---------------------------------------------------+ + |pulse-height |The energy deposited by an entire photon's history | + | |(including its progeny). Units are eV per source | + | |particle. Note that this score can only be combined| + | |with a cell filter and an energy filter. | + +----------------------+---------------------------------------------------+ + |ifp-time-numerator |Adjoint-weighted lifetime of neutron produced by | + | |fission in units of seconds per source particle. | + | |This score is used to compute kinetics parameters | + | |using the iterated fission probability (IFP) | + | |method. | + +----------------------+---------------------------------------------------+ + |ifp-beta-numerator |Adjoint-weighted number of delayed fission events | + | |in units of number of delayed fission event per | + | |source particle. This score is used to compute | + | |kinetics parameters using the iterated fission | + | |probability (IFP) method. | + +----------------------+---------------------------------------------------+ + |ifp-denominator |Weights corresponding to the number of fission | + | |events in units of number of fission event per | + | |source particle. This score is used to compute | + | |kinetics parameters using the iterated fission | + | |probability (IFP) method. | + +----------------------+---------------------------------------------------+ .. _usersguide_tally_normalization: @@ -333,7 +355,7 @@ it is usually straightforward to convert units if the source rate is known. For example, if the system being modeled includes a source that is emitting 10\ :sup:`4` neutrons per second, the tally results just need to be multipled by 10\ :sup:`4`. This can either be done manually or using the -:attr:`openmc.Source.strength` attribute. +:attr:`openmc.SourceBase.strength` attribute. For a :math:`k`\ -eigenvalue calculation, normalizing tally results is not as simple because the source rate is not actually known. Instead, we typically know diff --git a/docs/source/usersguide/troubleshoot.rst b/docs/source/usersguide/troubleshoot.rst index dd1c86a13f..f86ed72740 100644 --- a/docs/source/usersguide/troubleshoot.rst +++ b/docs/source/usersguide/troubleshoot.rst @@ -15,18 +15,20 @@ error you are receiving is among the following options. Problems with Simulations ------------------------- -Segmentation Fault -****************** +RuntimeError: OpenMC aborted unexpectedly. +****************************************** -A segmentation fault occurs when the program tries to access a variable in -memory that was outside the memory allocated for the program. The best way to -debug a segmentation fault is to re-compile OpenMC with debug options turned -on. Create a new build directory and type the following commands: +This error usually indicates that OpenMC experienced a segmentation fault. A +segmentation fault occurs when the program tries to access a variable in memory +that was outside the memory allocated for the program. The best way to debug a +segmentation fault is to :ref:`compile OpenMC from source ` with +debug options turned on. Create a new build directory and type the following +commands: .. code-block:: sh mkdir build-debug && cd build-debug - cmake -Ddebug=on /path/to/openmc + cmake -DCMAKE_BUILD_TYPE=Debug /path/to/openmc make Now when you re-run your problem, it should report exactly where the program @@ -34,6 +36,28 @@ failed. If after reading the debug output, you are still unsure why the program failed, post a message on the `OpenMC Discourse Forum `_. +.. _troubleshoot_lost_particles: + +WARNING: After particle __ crossed surface __ it could not be located in any cell and it did not leak. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +During a simulation, particles can become "lost" if they reach a surface and +there is no cell defined on the other side of the surface. It is important to +ensure that 1) proper boundary conditions have been applied to the outer +surfaces of your model and 2) all space in your model is filled with a cell, +even regions that are void and have no material assigned to them. + +Please see the instructions in :ref:`troubleshoot_geometry` on how to resolve +issues with lost particles. + +ERROR: Maximum number of lost particles has been reached. +********************************************************* + +See the above description regarding :ref:`lost particles +`. When too many particles are lost, the simulation +will abort altogether. Again, please see the instructions in +:ref:`troubleshoot_geometry` on how to resolve issues with lost particles. + ERROR: No cross_sections.xml file was specified in settings.xml or in the OPENMC_CROSS_SECTIONS environment variable. ********************************************************************************************************************* @@ -57,30 +81,41 @@ it is recommended that data be extracted from statepoint files in a context mana .. code-block:: python with openmc.StatePoint('statepoint.10.h5') as sp: - k_eff = sp.k_combined + k_eff = sp.keff or that the :meth:`StatePoint.close` method is called before executing a subsequent OpenMC run. +.. _troubleshoot_geometry: + Geometry Debugging ****************** -Overlapping Cells -^^^^^^^^^^^^^^^^^ +To identify issues in your geometry, it is highly recommended to use the `OpenMC +Plot Explorer `_ GUI application. This +application enables you to interactively explore a model, identify regions that +may be missing a cell definition, and identify overlapping cells. -For fast run times, normal simulations do not check if the geometry is -incorrectly defined to have overlapping cells. This can lead to incorrect -results that may or may not be obvious when there are errors in the geometry -input file. The built-in 2D and 3D plotters will check for cell overlaps at -the center of every pixel or voxel position they process, however this might -not be a sufficient check to ensure correctly defined geometry. For instance, -if an overlap is of small aspect ratio, the plotting resolution might not be -high enough to produce any pixels in the overlapping area. +If you are having issues with lost particles, the following procedure may be +helpful. If OpenMC reports, for example, that a particle reaching surface 50 +could not be located, look at your geometry.xml to see which cells have a region +definition that includes surface 50, e.g.: -To reliably validate a geometry input, it is best to run the problem in +.. code-block:: xml + + + +This may indicate that you need to define a cell on the other side of cell 10. +At this point, using the OpenMC Plot Explorer to locate cell 10 may provide a +visual clue as to whether there is a missing or overlapping cell near cell 10. +Working with the unique integer IDs of cells may be cumbersome; if you provide +names to your cells, these names will show up in the Plot Explorer, which will +aid geometry debugging. + +Another method to check for overlapping cells in a geometry is to run the problem in geometry debugging mode with the ``-g``, ``-geometry-debug``, or ``--geometry-debug`` command-line options. This will enable checks for -overlapping cells at every move of esch simulated particle. Depending on the +overlapping cells at every move of each simulated particle. Depending on the complexity of the geometry input file, this could add considerable overhead to the run (these runs can still be done in parallel). As a result, for this run mode the user will probably want to run fewer particles than a normal @@ -92,33 +127,6 @@ output after a geometry debug run to see how many checks were performed in each cell, and then adjust the number of starting particles or starting source distributions accordingly to achieve good coverage. -ERROR: After particle __ crossed surface __ it could not be located in any cell and it did not leak. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This error can arise either if a problem is specified with no boundary -conditions or if there is an error in the geometry itself. First check to ensure -that all of the outer surfaces of your geometry have been given vacuum or -reflective boundary conditions. If proper boundary conditions have been applied -and you still receive this error, it means that a surface/cell/lattice in your -geometry has been specified incorrectly or is missing. - -The best way to debug this error is to turn on a trace for the particle getting -lost. After the error message, the code will display what batch, generation, and -particle number caused the error. In your settings.xml, add a :ref:`trace` -followed by the batch, generation, and particle number. This will give you -detailed output every time that particle enters a cell, crosses a boundary, or -has a collision. For example, if you received this error at cycle 5, generation -1, particle 4032, you would enter: - -.. code-block:: xml - - 5 1 4032 - -For large runs it is often advantageous to run only the offending particle by -using particle restart mode with the ``-r`` command-line option in conjunction -with the particle restart files that are created when particles are lost with -this error. - Depletion ********* diff --git a/docs/source/usersguide/variance_reduction.rst b/docs/source/usersguide/variance_reduction.rst new file mode 100644 index 0000000000..369e33e2d8 --- /dev/null +++ b/docs/source/usersguide/variance_reduction.rst @@ -0,0 +1,175 @@ +.. _variance_reduction: + +================== +Variance Reduction +================== + +Global variance reduction in OpenMC is accomplished by weight windowing +techniques. OpenMC is capable of generating weight windows using either the +MAGIC or FW-CADIS methods. Both techniques will produce a ``weight_windows.h5`` +file that can be loaded and used later on. In this section, we break down the +steps required to both generate and then apply weight windows. + +.. _ww_generator: + +------------------------------------ +Generating Weight Windows with MAGIC +------------------------------------ + +As discussed in the :ref:`methods section `, MAGIC +is an iterative method that uses flux tally information from a Monte Carlo +simulation to produce weight windows for a user-defined mesh. While generating +the weight windows, OpenMC is capable of applying the weight windows generated +from a previous batch while processing the next batch, allowing for progressive +improvement in the weight window quality across iterations. + +The typical way of generating weight windows is to define a mesh and then add an +:class:`openmc.WeightWindowGenerator` object to an :attr:`openmc.Settings` +instance, as follows:: + + # Define weight window spatial mesh + ww_mesh = openmc.RegularMesh() + ww_mesh.dimension = (10, 10, 10) + ww_mesh.lower_left = (0.0, 0.0, 0.0) + ww_mesh.upper_right = (100.0, 100.0, 100.0) + + # Create weight window object and adjust parameters + wwg = openmc.WeightWindowGenerator( + method='magic', + mesh=ww_mesh, + max_realizations=settings.batches + ) + + # Add generator to Settings instance + settings.weight_window_generators = wwg + +Notably, the :attr:`max_realizations` attribute is adjusted to the number of +batches, such that all iterations are used to refine the weight window +parameters. + +With the :class:`~openmc.WeightWindowGenerator` instance added to the +:attr:`~openmc.Settings`, the rest of the problem can be defined as normal. When +running, note that the second iteration and beyond may be several orders of +magnitude slower than the first. As the weight windows are applied in each +iteration, particles may be agressively split, resulting in a large number of +secondary (split) particles being generated per initial source particle. This is +not necessarily a bad thing, as the split particles are much more efficient at +exploring low flux regions of phase space as compared to initial particles. +Thus, even though the reported "particles/second" metric of OpenMC may be much +lower when generating (or just applying) weight windows as compared to analog +MC, it typically leads to an overall improvement in the figure of merit +accounting for the reduction in the variance. + +.. warning:: + The number of particles per batch may need to be adjusted downward + significantly to result in reasonable runtimes when weight windows are being + generated or used. + +At the end of the simulation, a ``weight_windows.h5`` file will be saved to disk +for later use. Loading it in another subsequent simulation will be discussed in +the "Using Weight Windows" section below. + +------------------------------------------------------ +Generating Weight Windows with FW-CADIS and Random Ray +------------------------------------------------------ + +Weight window generation with FW-CADIS and random ray in OpenMC uses the same +exact strategy as with MAGIC. An :class:`openmc.WeightWindowGenerator` object is +added to the :attr:`openmc.Settings` object, and a ``weight_windows.h5`` will be +generated at the end of the simulation. The only difference is that the code +must be run in random ray mode. A full description of how to enable and setup +random ray mode can be found in the :ref:`Random Ray User Guide `. + +.. note:: + It is a long term goal for OpenMC to be able to generate FW-CADIS weight + windows with only a few tweaks to an existing continuous energy Monte Carlo + input deck. However, at the present time, the workflow requires several + steps to generate multigroup cross section data and to configure the random + ray solver. A high level overview of the current workflow for generation of + weight windows with FW-CADIS using random ray is given below. + +1. Begin by making a deepy copy of your continuous energy Python model and then + convert the copy to be multigroup and use the random ray transport solver. + The conversion process can largely be automated as described in more detail + in the :ref:`random ray quick start guide `, summarized below:: + + # Define continuous energy model + ce_model = openmc.pwr_pin_cell() # example, replace with your model + + # Make a copy to convert to multigroup and random ray + model = copy.deepcopy(ce_model) + + # Convert model to multigroup (will auto-generate MGXS library if needed) + model.convert_to_multigroup() + + # Convert model to random ray and initialize random ray parameters + # to reasonable defaults based on the specifics of the geometry + model.convert_to_random_ray() + + # (Optional) Overlay source region decomposition mesh to improve fidelity of the + # random ray solver. Adjust 'n' for fidelity vs runtime. + n = 10 + mesh = openmc.RegularMesh() + mesh.dimension = (n, n, n) + mesh.lower_left = model.geometry.bounding_box.lower_left + mesh.upper_right = model.geometry.bounding_box.upper_right + model.settings.random_ray['source_region_meshes'] = [(mesh, [model.geometry.root_universe])] + + # (Optional) Improve fidelity of the random ray solver by enabling linear sources + model.settings.random_ray['source_shape'] = 'linear' + + # (Optional) Increase the number of rays/batch, to reduce uncertainty + model.settings.particles = 500 + + If you need to improve the fidelity of the MGXS library, there is more + information on generating multigroup cross sections via OpenMC in the + :ref:`random ray MGXS guide `. + +2. Add in a :class:`~openmc.WeightWindowGenerator` in a similar manner as for + MAGIC generation with Monte Carlo and set the :attr:`method` attribute set to + ``"fw_cadis"``:: + + # Create weight window object and adjust parameters, using the same mesh + # we used for source region decomposition + wwg = openmc.WeightWindowGenerator( + method='fw_cadis', + mesh=mesh, + max_realizations=settings.batches + ) + + # Add generator to openmc.settings object + settings.weight_window_generators = wwg + +.. warning:: + If using FW-CADIS weight window generation, ensure that the selected weight + window mesh does not subdivide any source regions in the problem. This can + be ensured by using the same mesh for both source region subdivision (i.e., + assigning to ``model.settings.random_ray['source_region_meshes']``) and for + weight window generation. + +3. When running your multigroup random ray input deck, OpenMC will automatically + run a forward solve followed by an adjoint solve, with a + ``weight_windows.h5`` file generated at the end. The ``weight_windows.h5`` + file will contain FW-CADIS generated weight windows. This file can be used in + identical manner as one generated with MAGIC, as described below. + +-------------------- +Using Weight Windows +-------------------- + +To use a ``weight_windows.h5`` weight window file with OpenMC's Monte Carlo +solver, the Python input just needs to load the h5 file:: + + settings.weight_window_checkpoints = {'collision': True, 'surface': True} + settings.survival_biasing = False + settings.weight_windows = openmc.WeightWindowsList.from_hdf5('weight_windows.h5') + settings.weight_windows_on = True + +The :class:`~openmc.WeightWindowGenerator` instance is not needed to load an +existing ``weight_windows.h5`` file. Inclusion of a +:class:`~openmc.WeightWindowGenerator` instance will cause OpenMC to generate +*new* weight windows and thus overwrite the existing ``weight_windows.h5`` file. +Weight window mesh information is embedded into the weight window file, so the +mesh does not need to be redefined. Monte Carlo solves that load a weight window +file as above will utilize weight windows to reduce the variance of the +simulation. diff --git a/examples/assembly/assembly.py b/examples/assembly/assembly.py index ec1bb7e9f7..d94cb69bfc 100644 --- a/examples/assembly/assembly.py +++ b/examples/assembly/assembly.py @@ -99,11 +99,11 @@ def assembly_model(): assembly.universes[gt_pos[:, 0], gt_pos[:, 1]] = guide_tube_pin() # Create outer boundary of the geometry to surround the lattice - outer_boundary = openmc.model.rectangular_prism( + outer_boundary = openmc.model.RectangularPrism( pitch, pitch, boundary_type='reflective') # Create a cell filled with the lattice - main_cell = openmc.Cell(fill=assembly, region=outer_boundary) + main_cell = openmc.Cell(fill=assembly, region=-outer_boundary) # Finally, create geometry by providing a list of cells that fill the root # universe @@ -112,11 +112,10 @@ def assembly_model(): model.settings.batches = 150 model.settings.inactive = 50 model.settings.particles = 1000 - model.settings.source = openmc.Source(space=openmc.stats.Box( - (-pitch/2, -pitch/2, -1), - (pitch/2, pitch/2, 1), - only_fissionable=True - )) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box((-pitch/2, -pitch/2, -1), (pitch/2, pitch/2, 1)), + constraints={'fissionable': True} + ) # NOTE: We never actually created a Materials object. When you export/run # using the Model object, if no materials were assigned it will look through diff --git a/examples/custom_source/CMakeLists.txt b/examples/custom_source/CMakeLists.txt index 9498176944..ba5ae94adc 100644 --- a/examples/custom_source/CMakeLists.txt +++ b/examples/custom_source/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) project(openmc_sources CXX) add_library(source SHARED source_ring.cpp) find_package(OpenMC REQUIRED) diff --git a/examples/custom_source/build_xml.py b/examples/custom_source/build_xml.py index a0817d4223..ff6dae2bb6 100644 --- a/examples/custom_source/build_xml.py +++ b/examples/custom_source/build_xml.py @@ -8,8 +8,8 @@ mats = openmc.Materials([iron]) mats.export_to_xml() # Create a 5 cm x 5 cm box filled with iron -box = openmc.model.rectangular_prism(10.0, 10.0, boundary_type='vacuum') -cell = openmc.Cell(fill=iron, region=box) +box = openmc.model.RectangularPrism(10.0, 10.0, boundary_type='vacuum') +cell = openmc.Cell(fill=iron, region=-box) geometry = openmc.Geometry([cell]) geometry.export_to_xml() @@ -18,7 +18,7 @@ settings = openmc.Settings() settings.run_mode = 'fixed source' settings.batches = 10 settings.particles = 1000 -source = openmc.Source() +source = openmc.CompiledSource() source.library = 'build/libsource.so' settings.source = source settings.export_to_xml() diff --git a/examples/jezebel/jezebel.py b/examples/jezebel/jezebel.py index 5114ac714d..83b47a5e94 100644 --- a/examples/jezebel/jezebel.py +++ b/examples/jezebel/jezebel.py @@ -29,5 +29,5 @@ openmc.run() # Get the resulting k-effective value n = settings.batches with openmc.StatePoint(f'statepoint.{n}.h5') as sp: - keff = sp.k_combined + keff = sp.keff print(f'Final k-effective = {keff}') diff --git a/examples/lattice/hexagonal/build_xml.py b/examples/lattice/hexagonal/build_xml.py index c0aa815b41..9485d0aa45 100644 --- a/examples/lattice/hexagonal/build_xml.py +++ b/examples/lattice/hexagonal/build_xml.py @@ -114,8 +114,9 @@ settings_file.particles = particles # Create an initial uniform spatial source distribution over fissionable zones bounds = [-1, -1, -1, 1, 1, 1] -uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings_file.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) +settings_file.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) settings_file.keff_trigger = {'type' : 'std_dev', 'threshold' : 5E-4} settings_file.trigger_active = True diff --git a/examples/lattice/nested/build_xml.py b/examples/lattice/nested/build_xml.py index 06641f5ac5..2db23a46b3 100644 --- a/examples/lattice/nested/build_xml.py +++ b/examples/lattice/nested/build_xml.py @@ -124,8 +124,9 @@ settings_file.particles = particles # Create an initial uniform spatial source distribution over fissionable zones bounds = [-1, -1, -1, 1, 1, 1] -uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings_file.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) +settings_file.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) settings_file.export_to_xml() diff --git a/examples/lattice/simple/build_xml.py b/examples/lattice/simple/build_xml.py index 17544b87f5..56c4661216 100644 --- a/examples/lattice/simple/build_xml.py +++ b/examples/lattice/simple/build_xml.py @@ -115,8 +115,9 @@ settings_file.particles = particles # Create an initial uniform spatial source distribution over fissionable zones bounds = [-1, -1, -1, 1, 1, 1] -uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings_file.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) +settings_file.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) settings_file.trigger_active = True settings_file.trigger_max_batches = 100 diff --git a/examples/parameterized_custom_source/CMakeLists.txt b/examples/parameterized_custom_source/CMakeLists.txt index 3024e90cff..20dac4d8f4 100644 --- a/examples/parameterized_custom_source/CMakeLists.txt +++ b/examples/parameterized_custom_source/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) project(openmc_sources CXX) add_library(parameterized_source SHARED parameterized_source_ring.cpp) find_package(OpenMC REQUIRED) diff --git a/examples/parameterized_custom_source/build_xml.py b/examples/parameterized_custom_source/build_xml.py index 5edb204df2..5ac6bf9287 100644 --- a/examples/parameterized_custom_source/build_xml.py +++ b/examples/parameterized_custom_source/build_xml.py @@ -8,8 +8,8 @@ mats = openmc.Materials([iron]) mats.export_to_xml() # Create a 5 cm x 5 cm box filled with iron -box = openmc.model.rectangular_prism(10.0, 10.0, boundary_type='vacuum') -cell = openmc.Cell(fill=iron, region=box) +box = openmc.model.RectangularPrism(10.0, 10.0, boundary_type='vacuum') +cell = openmc.Cell(fill=iron, region=-box) geometry = openmc.Geometry([cell]) geometry.export_to_xml() @@ -18,7 +18,7 @@ settings = openmc.Settings() settings.run_mode = 'fixed source' settings.batches = 10 settings.particles = 1000 -source = openmc.Source() +source = openmc.CompiledSource() source.library = 'build/libparameterized_source.so' source.parameters = 'radius=3.0, energy=14.08e6' settings.source = source diff --git a/examples/pincell/build_xml.py b/examples/pincell/build_xml.py index c5d614ea90..aed0c790f1 100644 --- a/examples/pincell/build_xml.py +++ b/examples/pincell/build_xml.py @@ -43,13 +43,13 @@ clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') # Create a region represented as the inside of a rectangular prism pitch = 1.25984 -box = openmc.rectangular_prism(pitch, pitch, boundary_type='reflective') +box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') # Create cells, mapping materials to regions fuel = openmc.Cell(fill=uo2, region=-fuel_or) gap = openmc.Cell(fill=helium, region=+fuel_or & -clad_ir) clad = openmc.Cell(fill=zircaloy, region=+clad_ir & -clad_or) -water = openmc.Cell(fill=borated_water, region=+clad_or & box) +water = openmc.Cell(fill=borated_water, region=+clad_or & -box) # Create a geometry and export to XML geometry = openmc.Geometry([fuel, gap, clad, water]) @@ -67,8 +67,9 @@ settings.particles = 1000 # Create an initial uniform spatial source distribution over fissionable zones lower_left = (-pitch/2, -pitch/2, -1) upper_right = (pitch/2, pitch/2, 1) -uniform_dist = openmc.stats.Box(lower_left, upper_right, only_fissionable=True) -settings.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(lower_left, upper_right) +settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) # For source convergence checks, add a mesh that can be used to calculate the # Shannon entropy diff --git a/examples/pincell_depletion/restart_depletion.py b/examples/pincell_depletion/restart_depletion.py index fd0a4a8dde..de9fc16cb1 100644 --- a/examples/pincell_depletion/restart_depletion.py +++ b/examples/pincell_depletion/restart_depletion.py @@ -12,7 +12,7 @@ with openmc.StatePoint(statepoint) as sp: geometry = sp.summary.geometry # Load previous depletion results -previous_results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") +previous_results = openmc.deplete.Results("depletion_results.h5") ############################################################################### # Transport calculation settings @@ -26,8 +26,9 @@ settings.particles = 10000 # Create an initial uniform spatial source distribution over fissionable zones bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] -uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) +settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) entropy_mesh = openmc.RegularMesh() entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] @@ -43,7 +44,7 @@ model = openmc.Model(geometry=geometry, settings=settings) # Create depletion "operator" chain_file = 'chain_simple.xml' -op = openmc.deplete.Operator(model, chain_file, previous_results) +op = openmc.deplete.CoupledOperator(model, chain_file, previous_results) # Perform simulation using the predictor algorithm time_steps = [1.0, 1.0, 1.0, 1.0, 1.0] # days @@ -56,36 +57,36 @@ integrator.integrate() ############################################################################### # Open results file -results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") +results = openmc.deplete.Results("depletion_results.h5") # Obtain K_eff as a function of time -time, keff = results.get_eigenvalue() +time, keff = results.get_keff(time_units='d') # Obtain U235 concentration as a function of time -time, n_U235 = results.get_atoms('1', 'U235') +uo2 = geometry.get_all_material_cells()[1] +_, n_U235 = results.get_atoms(uo2, 'U235') # Obtain Xe135 capture reaction rate as a function of time -time, Xe_capture = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') +_, Xe_capture = results.get_reaction_rate(uo2, 'Xe135', '(n,gamma)') ############################################################################### # Generate plots ############################################################################### -days = 24*60*60 fig, ax = plt.subplots() -ax.errorbar(time/days, keff[:, 0], keff[:, 1], label="K-effective") +ax.errorbar(time, keff[:, 0], keff[:, 1], label="K-effective") ax.set_xlabel("Time [d]") ax.set_ylabel("Keff") plt.show() fig, ax = plt.subplots() -ax.plot(time/days, n_U235, label="U235") +ax.plot(time, n_U235, label="U235") ax.set_xlabel("Time [d]") ax.set_ylabel("U235 atoms") plt.show() fig, ax = plt.subplots() -ax.plot(time/days, Xe_capture, label="Xe135 capture") +ax.plot(time, Xe_capture, label="Xe135 capture") ax.set_xlabel("Time [d]") ax.set_ylabel("Xe135 capture rate") plt.show() diff --git a/examples/pincell_depletion/run_depletion.py b/examples/pincell_depletion/run_depletion.py index 45dfd2a875..013c83c86a 100644 --- a/examples/pincell_depletion/run_depletion.py +++ b/examples/pincell_depletion/run_depletion.py @@ -41,13 +41,13 @@ pitch = 1.25984 fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR') clad_ir = openmc.ZCylinder(r=0.40005, name='Clad IR') clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') -box = openmc.model.rectangular_prism(pitch, pitch, boundary_type='reflective') +box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') # Define cells fuel = openmc.Cell(fill=uo2, region=-fuel_or) gap = openmc.Cell(fill=helium, region=+fuel_or & -clad_ir) clad = openmc.Cell(fill=zircaloy, region=+clad_ir & -clad_or) -water = openmc.Cell(fill=borated_water, region=+clad_or & box) +water = openmc.Cell(fill=borated_water, region=+clad_or & -box) # Define overall geometry geometry = openmc.Geometry([fuel, gap, clad, water]) @@ -71,8 +71,9 @@ settings.particles = 1000 # Create an initial uniform spatial source distribution over fissionable zones bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] -uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) -settings.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) +settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) entropy_mesh = openmc.RegularMesh() entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] @@ -88,7 +89,7 @@ model = openmc.Model(geometry=geometry, settings=settings) # Create depletion "operator" chain_file = 'chain_simple.xml' -op = openmc.deplete.Operator(model, chain_file) +op = openmc.deplete.CoupledOperator(model, chain_file) # Perform simulation using the predictor algorithm time_steps = [1.0, 1.0, 1.0, 1.0, 1.0] # days @@ -101,36 +102,35 @@ integrator.integrate() ############################################################################### # Open results file -results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") +results = openmc.deplete.Results("depletion_results.h5") # Obtain K_eff as a function of time -time, keff = results.get_eigenvalue() +time, keff = results.get_keff(time_units='d') # Obtain U235 concentration as a function of time -time, n_U235 = results.get_atoms('1', 'U235') +_, n_U235 = results.get_atoms(uo2, 'U235') # Obtain Xe135 capture reaction rate as a function of time -time, Xe_capture = results.get_reaction_rate('1', 'Xe135', '(n,gamma)') +_, Xe_capture = results.get_reaction_rate(uo2, 'Xe135', '(n,gamma)') ############################################################################### # Generate plots ############################################################################### -days = 24*60*60 fig, ax = plt.subplots() -ax.errorbar(time/days, keff[:, 0], keff[:, 1], label="K-effective") +ax.errorbar(time, keff[:, 0], keff[:, 1], label="K-effective") ax.set_xlabel("Time [d]") ax.set_ylabel("Keff") plt.show() fig, ax = plt.subplots() -ax.plot(time/days, n_U235, label="U235") +ax.plot(time, n_U235, label="U235") ax.set_xlabel("Time [d]") ax.set_ylabel("U235 atoms") plt.show() fig, ax = plt.subplots() -ax.plot(time/days, Xe_capture, label="Xe135 capture") +ax.plot(time, Xe_capture, label="Xe135 capture") ax.set_xlabel("Time [d]") ax.set_ylabel("Xe135 capture rate") plt.show() diff --git a/examples/pincell_multigroup/build_xml.py b/examples/pincell_multigroup/build_xml.py index 1f34683919..0971e5de63 100644 --- a/examples/pincell_multigroup/build_xml.py +++ b/examples/pincell_multigroup/build_xml.py @@ -90,11 +90,11 @@ fuel_or = openmc.ZCylinder(r=0.54, name='Fuel OR') # Create a region represented as the inside of a rectangular prism pitch = 1.26 -box = openmc.rectangular_prism(pitch, pitch, boundary_type='reflective') +box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') # Instantiate Cells fuel = openmc.Cell(fill=uo2, region=-fuel_or, name='fuel') -moderator = openmc.Cell(fill=water, region=+fuel_or & box, name='moderator') +moderator = openmc.Cell(fill=water, region=+fuel_or & -box, name='moderator') # Create a geometry with the two cells and export to XML geometry = openmc.Geometry([fuel, moderator]) @@ -113,8 +113,9 @@ settings.particles = 1000 # Create an initial uniform spatial source distribution over fissionable zones lower_left = (-pitch/2, -pitch/2, -1) upper_right = (pitch/2, pitch/2, 1) -uniform_dist = openmc.stats.Box(lower_left, upper_right, only_fissionable=True) -settings.source = openmc.source.Source(space=uniform_dist) +uniform_dist = openmc.stats.Box(lower_left, upper_right) +settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) settings.export_to_xml() ############################################################################### diff --git a/examples/pincell_pulsed/run_pulse.py b/examples/pincell_pulsed/run_pulse.py new file mode 100644 index 0000000000..b6a61ad3d8 --- /dev/null +++ b/examples/pincell_pulsed/run_pulse.py @@ -0,0 +1,101 @@ +import matplotlib.pyplot as plt +import numpy as np +import openmc + +############################################################################### +# Create materials for the problem + +uo2 = openmc.Material(name="UO2 fuel at 2.4% wt enrichment") +uo2.set_density("g/cm3", 10.29769) +uo2.add_element("U", 1.0, enrichment=2.4) +uo2.add_element("O", 2.0) + +helium = openmc.Material(name="Helium for gap") +helium.set_density("g/cm3", 0.001598) +helium.add_element("He", 2.4044e-4) + +zircaloy = openmc.Material(name="Zircaloy 4") +zircaloy.set_density("g/cm3", 6.55) +zircaloy.add_element("Sn", 0.014, "wo") +zircaloy.add_element("Fe", 0.00165, "wo") +zircaloy.add_element("Cr", 0.001, "wo") +zircaloy.add_element("Zr", 0.98335, "wo") + +borated_water = openmc.Material(name="Borated water") +borated_water.set_density("g/cm3", 0.740582) +borated_water.add_element("B", 2.0e-4) # 3x the original pincell +borated_water.add_element("H", 5.0e-2) +borated_water.add_element("O", 2.4e-2) +borated_water.add_s_alpha_beta("c_H_in_H2O") + +############################################################################### +# Define problem geometry + +# Create cylindrical surfaces +fuel_or = openmc.ZCylinder(r=0.39218, name="Fuel OR") +clad_ir = openmc.ZCylinder(r=0.40005, name="Clad IR") +clad_or = openmc.ZCylinder(r=0.45720, name="Clad OR") + +# Create a region represented as the inside of a rectangular prism +pitch = 1.25984 +box = openmc.model.RectangularPrism(pitch, pitch, boundary_type="reflective") + +# Create cells, mapping materials to regions +fuel = openmc.Cell(fill=uo2, region=-fuel_or) +gap = openmc.Cell(fill=helium, region=+fuel_or & -clad_ir) +clad = openmc.Cell(fill=zircaloy, region=+clad_ir & -clad_or) +water = openmc.Cell(fill=borated_water, region=+clad_or & -box) + +# Create a model and assign geometry +model = openmc.Model() +model.geometry = openmc.Geometry([fuel, gap, clad, water]) + +############################################################################### +# Define problem settings + +# Set the mode +model.settings.run_mode = "fixed source" + +# Indicate how many batches and particles to run +model.settings.batches = 10 +model.settings.particles = 10000 + +# Set time cutoff (we only care about t < 100 seconds, see tally below) +model.settings.cutoff = {"time_neutron": 100} + +# Create the neutron pulse source (by default, isotropic direction, t=0) +space = openmc.stats.Point() # At the origin (0, 0, 0) +energy = openmc.stats.delta_function(14.1e6) # At 14.1 MeV +model.settings.source = openmc.IndependentSource(space=space, energy=energy) + +############################################################################### +# Define tallies + +# Create time filter +t_grid = np.insert(np.logspace(-6, 2, 100), 0, 0.0) +time_filter = openmc.TimeFilter(t_grid) + +# Tally for total neutron density in time +density_tally = openmc.Tally(name="Density") +density_tally.filters = [time_filter] +density_tally.scores = ["inverse-velocity"] + +# Add tallies to model +model.tallies = openmc.Tallies([density_tally]) + + +# Run the model +model.run(apply_tally_results=True) + +# Bin-averaged result +density_mean = density_tally.mean.ravel() / np.diff(t_grid) + +# Plot particle density versus time +fig, ax = plt.subplots() +ax.stairs(density_mean, t_grid) +ax.set_xscale("log") +ax.set_yscale("log") +ax.set_xlabel("Time [s]") +ax.set_ylabel("Total density") +ax.grid() +plt.show() diff --git a/examples/pincell_random_ray/build_xml.py b/examples/pincell_random_ray/build_xml.py new file mode 100644 index 0000000000..b3dd8020a5 --- /dev/null +++ b/examples/pincell_random_ray/build_xml.py @@ -0,0 +1,203 @@ +import numpy as np +import openmc +import openmc.mgxs + +############################################################################### +# Create multigroup data + +# Instantiate the energy group data +group_edges = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] +groups = openmc.mgxs.EnergyGroups(group_edges) + +# Instantiate the 7-group (C5G7) cross section data +uo2_xsdata = openmc.XSdata('UO2', groups) +uo2_xsdata.order = 0 +uo2_xsdata.set_total( + [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, + 0.5644058]) +uo2_xsdata.set_absorption([8.0248e-03, 3.7174e-03, 2.6769e-02, 9.6236e-02, + 3.0020e-02, 1.1126e-01, 2.8278e-01]) +scatter_matrix = np.array( + [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]) +scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) +uo2_xsdata.set_scatter_matrix(scatter_matrix) +uo2_xsdata.set_fission([7.21206e-03, 8.19301e-04, 6.45320e-03, + 1.85648e-02, 1.78084e-02, 8.30348e-02, + 2.16004e-01]) +uo2_xsdata.set_nu_fission([2.005998e-02, 2.027303e-03, 1.570599e-02, + 4.518301e-02, 4.334208e-02, 2.020901e-01, + 5.257105e-01]) +uo2_xsdata.set_chi([5.8791e-01, 4.1176e-01, 3.3906e-04, 1.1761e-07, 0.0000e+00, + 0.0000e+00, 0.0000e+00]) + +h2o_xsdata = openmc.XSdata('LWTR', groups) +h2o_xsdata.order = 0 +h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435, + 0.718, 1.2544497, 2.650379]) +h2o_xsdata.set_absorption([6.0105e-04, 1.5793e-05, 3.3716e-04, + 1.9406e-03, 5.7416e-03, 1.5001e-02, + 3.7239e-02]) +scatter_matrix = np.array( + [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000], + [0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010], + [0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034], + [0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390], + [0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]) +scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) +h2o_xsdata.set_scatter_matrix(scatter_matrix) + +mg_cross_sections_file = openmc.MGXSLibrary(groups) +mg_cross_sections_file.add_xsdatas([uo2_xsdata, h2o_xsdata]) +mg_cross_sections_file.export_to_hdf5() + +############################################################################### +# Create materials for the problem + +# Instantiate some Materials and register the appropriate macroscopic data +uo2 = openmc.Material(name='UO2 fuel') +uo2.set_density('macro', 1.0) +uo2.add_macroscopic('UO2') + +water = openmc.Material(name='Water') +water.set_density('macro', 1.0) +water.add_macroscopic('LWTR') + +# Instantiate a Materials collection and export to XML +materials_file = openmc.Materials([uo2, water]) +materials_file.cross_sections = "mgxs.h5" +materials_file.export_to_xml() + +############################################################################### +# Define problem geometry + +# The geometry we will define a simplified pincell with fuel radius 0.54 cm +# surrounded by moderator (same as in the multigroup example). +# In random ray, we typically want several radial regions and azimuthal +# sectors in both the fuel and moderator areas of the pincell. This is +# due to the flat source approximation requiring that source regions are +# small compared to the typical mean free path of a neutron. Below we +# sudivide the basic pincell into 8 aziumthal sectors (pizza slices) and +# 5 concentric rings in both the fuel and moderator. + +# TODO: When available in OpenMC, use cylindrical lattice instead to +# simplify definition and improve runtime performance. + +pincell_base = openmc.Universe() + +# These are the subdivided radii (creating 5 concentric regions in the +# fuel and moderator) +ring_radii = [0.241, 0.341, 0.418, 0.482, 0.54, 0.572, 0.612, 0.694, 0.786] +fills = [uo2, uo2, uo2, uo2, uo2, water, water, water, water, water] + +# We then create cells representing the bounded rings, with special +# treatment for both the innermost and outermost cells +cells = [] +for r in range(10): + cell = [] + if r == 0: + outer_bound = openmc.ZCylinder(r=ring_radii[r]) + cell = openmc.Cell(fill=fills[r], region=-outer_bound) + elif r == 9: + inner_bound = openmc.ZCylinder(r=ring_radii[r-1]) + cell = openmc.Cell(fill=fills[r], region=+inner_bound) + else: + inner_bound = openmc.ZCylinder(r=ring_radii[r-1]) + outer_bound = openmc.ZCylinder(r=ring_radii[r]) + cell = openmc.Cell(fill=fills[r], region=+inner_bound & -outer_bound) + pincell_base.add_cell(cell) + +# We then generate 8 planes to bound 8 azimuthal sectors +azimuthal_planes = [] +for i in range(8): + angle = 2 * i * openmc.pi / 8 + normal_vector = (-openmc.sin(angle), openmc.cos(angle), 0) + azimuthal_planes.append(openmc.Plane(a=normal_vector[0], b=normal_vector[1], c=normal_vector[2], d=0)) + +# Create a cell for each azimuthal sector using the pincell base class +azimuthal_cells = [] +for i in range(8): + azimuthal_cell = openmc.Cell(name=f'azimuthal_cell_{i}') + azimuthal_cell.fill = pincell_base + azimuthal_cell.region = +azimuthal_planes[i] & -azimuthal_planes[(i+1) % 8] + azimuthal_cells.append(azimuthal_cell) + +# Create the (subdivided) geometry with the azimuthal universes +pincell = openmc.Universe(cells=azimuthal_cells) + +# Create a region represented as the inside of a rectangular prism +pitch = 1.26 +box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') +pincell_bounded = openmc.Cell(fill=pincell, region=-box, name='pincell') + +# Create a geometry (specifying merge surfaces option to remove +# all the redundant cylinder/plane surfaces) and export to XML +geometry = openmc.Geometry([pincell_bounded], merge_surfaces=True) +geometry.export_to_xml() + +############################################################################### +# Define problem settings + +# Instantiate a Settings object, set all runtime parameters, and export to XML +settings = openmc.Settings() +settings.energy_mode = "multi-group" +settings.batches = 600 +settings.inactive = 300 +settings.particles = 50 + +# Create an initial uniform spatial source distribution for sampling rays. +# Note that this must be uniform in space and angle. +lower_left = (-pitch/2, -pitch/2, -1) +upper_right = (pitch/2, pitch/2, 1) +uniform_dist = openmc.stats.Box(lower_left, upper_right) +settings.random_ray['ray_source'] = openmc.IndependentSource(space=uniform_dist) +settings.random_ray['distance_inactive'] = 40.0 +settings.random_ray['distance_active'] = 400.0 + +settings.export_to_xml() + +############################################################################### +# Define tallies + +# Create a mesh that will be used for tallying +mesh = openmc.RegularMesh() +mesh.dimension = (2, 2) +mesh.lower_left = (-pitch/2, -pitch/2) +mesh.upper_right = (pitch/2, pitch/2) + +# Create a mesh filter that can be used in a tally +mesh_filter = openmc.MeshFilter(mesh) + +# Let's also create a filter to measure each group +# indepdendently +energy_filter = openmc.EnergyFilter(group_edges) + +# Now use the mesh filter in a tally and indicate what scores are desired +tally = openmc.Tally(name="Mesh and Energy tally") +tally.filters = [mesh_filter, energy_filter] +tally.scores = ['flux', 'fission', 'nu-fission'] + +# Instantiate a Tallies collection and export to XML +tallies = openmc.Tallies([tally]) +tallies.export_to_xml() + +############################################################################### +# Exporting to OpenMC plots.xml file +############################################################################### + +plot = openmc.Plot() +plot.origin = [0, 0, 0] +plot.width = [pitch, pitch, pitch] +plot.pixels = [1000, 1000, 1] +plot.type = 'voxel' + +# Instantiate a Plots collection and export to XML +plots = openmc.Plots([plot]) +plots.export_to_xml() diff --git a/include/openmc/bank.h b/include/openmc/bank.h index 95386514d7..fd8fbd73ee 100644 --- a/include/openmc/bank.h +++ b/include/openmc/bank.h @@ -22,6 +22,14 @@ extern SharedArray surf_source_bank; extern SharedArray fission_bank; +extern vector> ifp_source_delayed_group_bank; + +extern vector> ifp_source_lifetime_bank; + +extern vector> ifp_fission_delayed_group_bank; + +extern vector> ifp_fission_lifetime_bank; + extern vector progeny_per_particle; } // namespace simulation diff --git a/include/openmc/boundary_condition.h b/include/openmc/boundary_condition.h index 0f16fa3b31..af40131f1c 100644 --- a/include/openmc/boundary_condition.h +++ b/include/openmc/boundary_condition.h @@ -1,12 +1,16 @@ #ifndef OPENMC_BOUNDARY_CONDITION_H #define OPENMC_BOUNDARY_CONDITION_H +#include "openmc/hdf5_interface.h" +#include "openmc/particle.h" #include "openmc/position.h" +#include namespace openmc { // Forward declare some types used in function arguments. class Particle; +class RandomRay; class Surface; //============================================================================== @@ -15,6 +19,8 @@ class Surface; class BoundaryCondition { public: + virtual ~BoundaryCondition() = default; + //! Perform tracking operations for a particle that strikes the boundary. //! \param p The particle that struck the boundary. This class is not meant //! to directly modify anything about the particle, but it will do so @@ -22,8 +28,44 @@ public: //! \param surf The specific surface on the boundary the particle struck. virtual void handle_particle(Particle& p, const Surface& surf) const = 0; + //! Modify the incident particle's weight according to the boundary's albedo. + //! \param p The particle that struck the boundary. This function calculates + //! the reduction in the incident particle's weight as it interacts + //! with a boundary. The lost weight is tallied before the remaining weight + //! is reassigned to the incident particle. Implementations of the + //! handle_particle function typically call this method in its body. + //! \param surf The specific surface on the boundary the particle struck. + void handle_albedo(Particle& p, const Surface& surf) const + { + if (!has_albedo()) + return; + double initial_wgt = p.wgt(); + // Treat the lost weight fraction as leakage, similar to VacuumBC. + // This ensures the lost weight is tallied properly. + p.wgt() *= (1.0 - albedo_); + p.cross_vacuum_bc(surf); + p.wgt() = initial_wgt * albedo_; + }; + //! Return a string classification of this BC. virtual std::string type() const = 0; + + //! Write albedo data of this BC to hdf5. + void to_hdf5(hid_t surf_group) const + { + if (has_albedo()) { + write_string(surf_group, "albedo", fmt::format("{}", albedo_), false); + } + }; + + //! Set albedo of this BC. + void set_albedo(double albedo) { albedo_ = albedo; } + + //! Return if this BC has an albedo. + bool has_albedo() const { return (albedo_ > 0.0); } + +private: + double albedo_ = -1.0; }; //============================================================================== @@ -69,6 +111,10 @@ public: std::string type() const override { return "periodic"; } + int i_surf() const { return i_surf_; } + + int j_surf() const { return j_surf_; } + protected: int i_surf_; int j_surf_; diff --git a/include/openmc/bounding_box.h b/include/openmc/bounding_box.h new file mode 100644 index 0000000000..d02d92cb41 --- /dev/null +++ b/include/openmc/bounding_box.h @@ -0,0 +1,65 @@ +#ifndef OPENMC_BOUNDING_BOX_H +#define OPENMC_BOUNDING_BOX_H + +#include // for min, max + +#include "openmc/constants.h" +#include "openmc/position.h" + +namespace openmc { + +//============================================================================== +//! Coordinates for an axis-aligned cuboid that bounds a geometric object. +//============================================================================== + +struct BoundingBox { + double xmin = -INFTY; + double xmax = INFTY; + double ymin = -INFTY; + double ymax = INFTY; + double zmin = -INFTY; + double zmax = INFTY; + + inline BoundingBox operator&(const BoundingBox& other) + { + BoundingBox result = *this; + return result &= other; + } + + inline BoundingBox operator|(const BoundingBox& other) + { + BoundingBox result = *this; + return result |= other; + } + + // intersect operator + inline BoundingBox& operator&=(const BoundingBox& other) + { + xmin = std::max(xmin, other.xmin); + xmax = std::min(xmax, other.xmax); + ymin = std::max(ymin, other.ymin); + ymax = std::min(ymax, other.ymax); + zmin = std::max(zmin, other.zmin); + zmax = std::min(zmax, other.zmax); + return *this; + } + + // union operator + inline BoundingBox& operator|=(const BoundingBox& other) + { + xmin = std::min(xmin, other.xmin); + xmax = std::max(xmax, other.xmax); + ymin = std::min(ymin, other.ymin); + ymax = std::max(ymax, other.ymax); + zmin = std::min(zmin, other.zmin); + zmax = std::max(zmax, other.zmax); + return *this; + } + + inline Position min() const { return {xmin, ymin, zmin}; } + inline Position max() const { return {xmax, ymax, zmax}; } +}; + +} // namespace openmc + +#endif diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 0929a11f76..d8041ef414 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -17,6 +17,8 @@ int openmc_cell_get_fill( int openmc_cell_get_id(int32_t index, int32_t* id); int openmc_cell_get_temperature( int32_t index, const int32_t* instance, double* T); +int openmc_cell_get_density( + int32_t index, const int32_t* instance, double* rho); int openmc_cell_get_translation(int32_t index, double xyz[]); int openmc_cell_get_rotation(int32_t index, double rot[], size_t* n); int openmc_cell_get_name(int32_t index, const char** name); @@ -27,8 +29,13 @@ int openmc_cell_set_fill( int openmc_cell_set_id(int32_t index, int32_t id); int openmc_cell_set_temperature( int32_t index, double T, const int32_t* instance, bool set_contained = false); +int openmc_cell_set_density(int32_t index, double rho, const int32_t* instance, + bool set_contained = false); int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); +int openmc_dagmc_universe_get_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n); +int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n); int openmc_energy_filter_get_bins( int32_t index, const double** energies, size_t* n); int openmc_energy_filter_set_bins( @@ -38,6 +45,9 @@ int openmc_energyfunc_filter_get_energy( int openmc_energyfunc_filter_get_y(int32_t index, size_t* n, const double** y); int openmc_energyfunc_filter_set_data( int32_t index, size_t n, const double* energies, const double* y); +int openmc_energyfunc_filter_set_interpolation( + int32_t index, const char* interp); +int openmc_energyfunc_filter_get_interpolation(int32_t index, int* interp); int openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_materials( @@ -47,6 +57,7 @@ int openmc_extend_meshes( int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); int openmc_filter_get_type(int32_t index, char* type); +int openmc_filter_get_num_bins(int32_t index, int* n_bins); int openmc_filter_set_id(int32_t index, int32_t id); int openmc_finalize(); int openmc_find_cell(const double* xyz, int32_t* index, int32_t* instance); @@ -64,6 +75,7 @@ int openmc_get_nuclide_index(const char name[], int* index); int openmc_add_unstructured_mesh( const char filename[], const char library[], int* id); int64_t openmc_get_seed(); +uint64_t openmc_get_stride(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_get_tally_next_id(int32_t* id); int openmc_global_tallies(double** ptr); @@ -89,6 +101,8 @@ int openmc_material_set_id(int32_t index, int32_t id); int openmc_material_get_name(int32_t index, const char** name); int openmc_material_set_name(int32_t index, const char* name); int openmc_material_set_volume(int32_t index, double volume); +int openmc_material_get_depletable(int32_t index, bool* depletable); +int openmc_material_set_depletable(int32_t index, bool depletable); int openmc_material_filter_get_bins( int32_t index, const int32_t** bins, size_t* n); int openmc_material_filter_set_bins( @@ -99,6 +113,10 @@ int openmc_mesh_filter_get_translation(int32_t index, double translation[3]); int openmc_mesh_filter_set_translation(int32_t index, double translation[3]); int openmc_mesh_get_id(int32_t index, int32_t* id); int openmc_mesh_set_id(int32_t index, int32_t id); +int openmc_mesh_get_n_elements(int32_t index, size_t* n); +int openmc_mesh_get_volumes(int32_t index, double* volumes); +int openmc_mesh_material_volumes(int32_t index, int nx, int ny, int nz, + int max_mats, int32_t* materials, double* volumes); int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_new_filter(const char* type, int32_t* index); @@ -118,10 +136,13 @@ int openmc_regular_mesh_get_params( int openmc_regular_mesh_set_dimension(int32_t index, int n, const int* dims); int openmc_regular_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width); +int openmc_remove_tally(int32_t index); int openmc_reset(); int openmc_reset_timers(); int openmc_run(); +int openmc_sample_external_source(size_t n, uint64_t* seed, void* sites); void openmc_set_seed(int64_t new_seed); +void openmc_set_stride(uint64_t new_stride); int openmc_set_n_batches( int32_t n_batches, bool set_max_batches, bool add_statepoint_batch); int openmc_simulation_finalize(); @@ -138,6 +159,7 @@ int openmc_sphharm_filter_get_cosine(int32_t index, char cosine[]); int openmc_sphharm_filter_set_order(int32_t index, int order); int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]); int openmc_statepoint_write(const char* filename, bool* write_source); +int openmc_statepoint_load(const char* filename); int openmc_tally_allocate(int32_t index, const char* type); int openmc_tally_get_active(int32_t index, bool* active); int openmc_tally_get_estimator(int32_t index, int* estimator); @@ -158,6 +180,46 @@ int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); int openmc_tally_set_writable(int32_t index, bool writable); +int openmc_get_weight_windows_index(int32_t id, int32_t* idx); +int openmc_weight_windows_get_id(int32_t index, int32_t* id); +int openmc_weight_windows_set_id(int32_t index, int32_t id); + +//! Updates weight window values using the specified tally +//! \param[in] ww_idx Index of the weight window object +//! \param[in] tally_idx Index of the tally to use for the update +//! \param[in] value Tally value to use for the update (one of 'mean', +//! 'rel_err') \param[in] threshold Relative error threshold for applied results +//! \param[in] ratio Upper to lower weight window bound ratio +int openmc_weight_windows_update_magic(int32_t ww_idx, int32_t tally_idx, + const char* value, double threshold, double ratio); + +int openmc_extend_weight_windows( + int32_t n, int32_t* index_start, int32_t* index_end); +int openmc_weight_windows_get_mesh(int32_t index, int32_t* mesh_idx); +int openmc_weight_windows_set_mesh(int32_t index, int32_t mesh_idx); +int openmc_weight_windows_set_energy_bounds( + int32_t index, double* e_bounds, size_t e_bounds_size); +int openmc_weight_windows_get_energy_bounds( + int32_t index, const double** e_bounds, size_t* e_bounds_size); +int openmc_weight_windows_set_particle(int32_t index, int particle); +int openmc_weight_windows_get_particle(int32_t index, int* particle); +int openmc_weight_windows_get_bounds(int32_t index, const double** lower_bounds, + const double** upper_bounds, size_t* size); +int openmc_weight_windows_set_bounds(int32_t index, const double* lower_bounds, + const double* upper_bounds, size_t size); +int openmc_weight_windows_get_survival_ratio(int32_t index, double* ratio); +int openmc_weight_windows_set_survival_ratio(int32_t index, double ratio); +int openmc_weight_windows_get_max_lower_bound_ratio( + int32_t index, double* lb_ratio); +int openmc_weight_windows_set_max_lower_bound_ratio( + int32_t index, double lb_ratio); +int openmc_weight_windows_get_weight_cutoff(int32_t index, double* cutoff); +int openmc_weight_windows_set_weight_cutoff(int32_t index, double cutoff); +int openmc_weight_windows_get_max_split(int32_t index, int* max_split); +int openmc_weight_windows_set_max_split(int32_t index, int max_split); +size_t openmc_weight_windows_size(); +int openmc_weight_windows_export(const char* filename = nullptr); +int openmc_weight_windows_import(const char* filename = nullptr); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params( int32_t index, double* x, double* y, double* r); @@ -165,6 +227,8 @@ int openmc_zernike_filter_set_order(int32_t index, int order); int openmc_zernike_filter_set_params( int32_t index, const double* x, const double* y, const double* r); +int openmc_particle_filter_get_bins(int32_t idx, int bins[]); + //! Sets the mesh and energy grid for CMFD reweight //! \param[in] meshtyally_id id of CMFD Mesh Tally //! \param[in] cmfd_indices indices storing spatial and energy dimensions of diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 075e907682..a581291dfc 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -10,13 +10,14 @@ #include "hdf5.h" #include "pugixml.hpp" -#include +#include "openmc/bounding_box.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/neighbor_list.h" #include "openmc/position.h" #include "openmc/surface.h" +#include "openmc/universe.h" #include "openmc/vector.h" namespace openmc { @@ -27,7 +28,6 @@ namespace openmc { enum class Fill { MATERIAL, UNIVERSE, LATTICE }; -// TODO: Convert to enum constexpr int32_t OP_LEFT_PAREN {std::numeric_limits::max()}; constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits::max() - 1}; constexpr int32_t OP_COMPLEMENT {std::numeric_limits::max() - 2}; @@ -39,6 +39,7 @@ constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; //============================================================================== class Cell; +class GeometryState; class ParentCell; class CellInstance; class Universe; @@ -48,38 +49,102 @@ namespace model { extern std::unordered_map cell_map; extern vector> cells; -extern std::unordered_map universe_map; -extern vector> universes; } // namespace model -//============================================================================== -//! A geometry primitive that fills all space and contains cells. //============================================================================== -class Universe { +class Region { public: - int32_t id_; //!< Unique ID - vector cells_; //!< Cells within this universe - int filled_with_triso_base_ = -1; //!< ID of cell filled with virtual lattice + //---------------------------------------------------------------------------- + // Constructors + Region() {} + explicit Region(std::string region_spec, int32_t cell_id); - //! \brief Write universe information to an HDF5 group. - //! \param group_id An HDF5 group id. - virtual void to_hdf5(hid_t group_id) const; + //---------------------------------------------------------------------------- + // Methods - virtual bool find_cell(Particle& p) const; + //! \brief Determine if a cell contains the particle at a given location. + //! + //! The bounds of the cell are determined by a logical expression involving + //! surface half-spaces. The expression used is given in infix notation + //! + //! The function is split into two cases, one for simple cells (those + //! involving only the intersection of half-spaces) and one for complex cells. + //! Both cases use short circuiting; however, in the case fo complex cells, + //! the complexity increases with the binary operators involved. + //! \param r The 3D Cartesian coordinate to check. + //! \param u A direction used to "break ties" the coordinates are very + //! close to a surface. + //! \param on_surface The signed index of a surface that the coordinate is + //! known to be on. This index takes precedence over surface sense + //! calculations. + bool contains(Position r, Direction u, int32_t on_surface) const; - BoundingBox bounding_box() const; + //! Find the oncoming boundary of this cell. + std::pair distance( + Position r, Direction u, int32_t on_surface) const; - const GeometryType& geom_type() const { return geom_type_; } - GeometryType& geom_type() { return geom_type_; } + //! Get the BoundingBox for this cell. + BoundingBox bounding_box(int32_t cell_id) const; - unique_ptr partitioner_; + //! Get the CSG expression as a string + std::string str() const; + + //! Get a vector containing all the surfaces in the region expression + vector surfaces() const; + + //---------------------------------------------------------------------------- + // Accessors + + //! Get Boolean of if the cell is simple or not + bool is_simple() const { return simple_; } + + //! Get a vector of the region expression in postfix notation + vector generate_postfix(int32_t cell_id) const; private: - GeometryType geom_type_ = GeometryType::CSG; + //---------------------------------------------------------------------------- + // Private Methods + + //! Determine if a particle is inside the cell for a simple cell (only + //! intersection operators) + bool contains_simple(Position r, Direction u, int32_t on_surface) const; + + //! Determine if a particle is inside the cell for a complex cell. + //! + //! Uses the comobination of half-spaces and binary operators to determine + //! if short circuiting can be used. Short cicuiting uses the relative and + //! absolute depth of parentheses in the expression. + bool contains_complex(Position r, Direction u, int32_t on_surface) const; + + //! BoundingBox if the paritcle is in a simple cell. + BoundingBox bounding_box_simple() const; + + //! BoundingBox if the particle is in a complex cell. + BoundingBox bounding_box_complex(vector postfix) const; + + //! Enfource precedence: Parenthases, Complement, Intersection, Union + void add_precedence(); + + //! Add parenthesis to enforce precedence + int64_t add_parentheses(int64_t start); + + //! Remove complement operators from the expression + void remove_complement_ops(); + + //! Remove complement operators by using DeMorgan's laws + void apply_demorgan( + vector::iterator start, vector::iterator stop); + + //---------------------------------------------------------------------------- + // Private Data + + //! Definition of spatial region as Boolean expression of half-spaces + // TODO: Should this be a vector of some other type + vector expression_; + bool simple_; //!< Does the region contain only intersections? }; -//============================================================================== //============================================================================== class Cell { @@ -117,7 +182,7 @@ public: //! Find the oncoming boundary of this cell. virtual std::pair distance( - Position r, Direction u, int32_t on_surface, Particle* p) const = 0; + Position r, Direction u, int32_t on_surface, GeometryState* p) const = 0; //! Write all information needed to reconstruct the cell to an HDF5 group. //! \param group_id An HDF5 group id. @@ -136,6 +201,12 @@ public: //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; + //! Get a vector of surfaces in the cell + virtual vector surfaces() const { return vector(); } + + //! Check if the cell region expression is simple + virtual bool is_simple() const { return true; } + //---------------------------------------------------------------------------- // Accessors @@ -145,6 +216,18 @@ public: //! \return Temperature in [K] double temperature(int32_t instance = -1) const; + //! Get the density multiplier of a cell instance + //! \param[in] instance Instance index. If -1 is given, the density multiplier + //! for the first instance is returned. + //! \return Density multiplier + double density_mult(int32_t instance = -1) const; + + //! Get the density of a cell instance in g/cm3 + //! \param[in] instance Instance index. If -1 is given, the density + //! for the first instance is returned. + //! \return Density in [g/cm3] + double density(int32_t instance = -1) const; + //! Set the temperature of a cell instance //! \param[in] T Temperature in [K] //! \param[in] instance Instance index. If -1 is given, the temperature for @@ -155,6 +238,18 @@ public: void set_temperature( double T, int32_t instance = -1, bool set_contained = false); + //! Set the density of a cell instance + //! \param[in] density Density [g/cm3] + //! \param[in] instance Instance index. If -1 is given, the density + //! for all instances is set. + //! \param[in] set_contained If this cell is not filled with a material, + //! collect all contained cells with material fills and set their + //! densities. + void set_density( + double density, int32_t instance = -1, bool set_contained = false); + + int32_t n_instances() const; + //! Set the rotation matrix of a cell instance //! \param[in] rot The rotation matrix of length 3 or 9 void set_rotation(const vector& rot); @@ -176,6 +271,42 @@ public: std::unordered_map> get_contained_cells( int32_t instance = 0, Position* hint = nullptr) const; + //! Determine the material index corresponding to a specific cell instance, + //! taking into account presence of distribcell material + //! \param[in] instance of the cell + //! \return material index + int32_t material(int32_t instance) const + { + // If distributed materials are used, then each instance has its own + // material definition. If distributed materials are not used, then + // all instances used the same material stored at material_[0]. The + // presence of distributed materials is inferred from the size of + // the material_ vector being greater than one. + if (material_.size() > 1) { + return material_[instance]; + } else { + return material_[0]; + } + } + + //! Determine the temperature index corresponding to a specific cell instance, + //! taking into account presence of distribcell temperature + //! \param[in] instance of the cell + //! \return temperature index + double sqrtkT(int32_t instance) const + { + // If distributed materials are used, then each instance has its own + // temperature definition. If distributed materials are not used, then + // all instances used the same temperature stored at sqrtkT_[0]. The + // presence of distributed materials is inferred from the size of + // the sqrtkT_ vector being greater than one. + if (sqrtkT_.size() > 1) { + return sqrtkT_[instance]; + } else { + return sqrtkT_[0]; + } + } + protected: //! Determine the path to this cell instance in the geometry hierarchy //! \param[in] instance of the cell to find parent cells for @@ -189,13 +320,12 @@ protected: //! \param[in] p particle used to do a fast search for parent cells //! \return parent cells vector find_parent_cells( - int32_t instance, Particle& p) const; + int32_t instance, GeometryState& p) const; //! Determine the path to this cell instance in the geometry hierarchy //! \param[in] instance of the cell to find parent cells for //! \return parent cells - vector exhaustive_find_parent_cells( - int32_t instance) const; + vector exhaustive_find_parent_cells(int32_t instance) const; //! Inner function for retrieving contained cells void get_contained_cells_inner( @@ -206,23 +336,19 @@ public: //---------------------------------------------------------------------------- // Data members - int32_t id_; //!< Unique ID - std::string name_; //!< User-defined name - Fill type_; //!< Material, universe, or lattice - int32_t universe_; //!< Universe # this cell is in - int32_t fill_; //!< Universe # filling this cell - int32_t n_instances_ {0}; //!< Number of instances of this cell - GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) - bool virtual_lattice_; //!< If the cell is the base of a virtual triso lattice + int32_t id_; //!< Unique ID + std::string name_; //!< User-defined name + Fill type_; //!< Material, universe, or lattice + int32_t universe_; //!< Universe # this cell is in + int32_t fill_; //!< Universe # filling this cell + bool virtual_lattice_; //!< If the cell is the base of a virtual triso lattice bool triso_particle_; - //! \brief Specification of the virtual lattice vector vl_lower_left_; vector vl_pitch_; vector vl_shape_; vector> vl_triso_distribution_; - //! \brief Index corresponding to this cell in distribcell arrays int distribcell_index_ {C_NONE}; @@ -237,11 +363,8 @@ public: //! T. The units are sqrt(eV). vector sqrtkT_; - //! Definition of spatial region as Boolean expression of half-spaces - vector region_; - //! Reverse Polish notation for region expression - vector rpn_; - bool simple_; //!< Does the region contain only intersections? + //! \brief Unitless density multiplier(s) within this cell. + vector density_mult_; //! \brief Neighboring cells in the same universe. NeighborList neighbors_; @@ -257,6 +380,9 @@ public: vector rotation_; vector offset_; //!< Distribcell offset table + + // Right now, either CSG or DAGMC cells are used. + virtual GeometryType geom_type() const = 0; }; struct CellInstanceItem { @@ -268,35 +394,38 @@ struct CellInstanceItem { class CSGCell : public Cell { public: - CSGCell(); - + //---------------------------------------------------------------------------- + // Constructors + CSGCell() = default; explicit CSGCell(pugi::xml_node cell_node); - bool contains(Position r, Direction u, int32_t on_surface) const override; + //---------------------------------------------------------------------------- + // Methods + vector surfaces() const override { return region_.surfaces(); } - std::pair distance( - Position r, Direction u, int32_t on_surface, Particle* p) const override; + std::pair distance(Position r, Direction u, + int32_t on_surface, GeometryState* p) const override; + + std::pair distance_in_virtual_lattice( + Position r, Direction u, int32_t on_surface, GeometryState* p) const; + + bool contains(Position r, Direction u, int32_t on_surface) const override + { + return region_.contains(r, u, on_surface); + } + + BoundingBox bounding_box() const override + { + return region_.bounding_box(id_); + } void to_hdf5_inner(hid_t group_id) const override; - BoundingBox bounding_box() const override; + bool is_simple() const override { return region_.is_simple(); } + + virtual GeometryType geom_type() const override { return GeometryType::CSG; } protected: - bool contains_simple(Position r, Direction u, int32_t on_surface) const; - bool contains_complex(Position r, Direction u, int32_t on_surface) const; - BoundingBox bounding_box_simple() const; - static BoundingBox bounding_box_complex(vector rpn); - - //! Applies DeMorgan's laws to a section of the RPN - //! \param start Starting point for token modification - //! \param stop Stopping point for token modification - static void apply_demorgan( - vector::iterator start, vector::iterator stop); - - //! Removes complement operators from the RPN - //! \param rpn The rpn to remove complement operators from. - static void remove_complement_ops(vector& rpn); - //! Returns the beginning position of a parenthesis block (immediately before //! two surface tokens) in the RPN given a starting position at the end of //! that block (immediately after two surface tokens) @@ -304,35 +433,9 @@ protected: //! \param rpn The rpn being searched static vector::iterator find_left_parenthesis( vector::iterator start, const vector& rpn); -}; - -//============================================================================== -//! Speeds up geometry searches by grouping cells in a search tree. -// -//! Currently this object only works with universes that are divided up by a -//! bunch of z-planes. It could be generalized to other planes, cylinders, -//! and spheres. -//============================================================================== - -class UniversePartitioner { -public: - explicit UniversePartitioner(const Universe& univ); - - //! Return the list of cells that could contain the given coordinates. - const vector& get_cells(Position r, Direction u) const; private: - //! A sorted vector of indices to surfaces that partition the universe - vector surfs_; - - //! Vectors listing the indices of the cells that lie within each partition - // - //! There are n+1 partitions with n surfaces. `partitions_.front()` gives the - //! cells that lie on the negative side of `surfs_.front()`. - //! `partitions_.back()` gives the cells that lie on the positive side of - //! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched - //! between `surfs_[i-1]` and `surfs_[i]`. - vector> partitions_; + Region region_; }; //============================================================================== @@ -347,8 +450,8 @@ struct CellInstance { return index_cell == other.index_cell && instance == other.instance; } - gsl::index index_cell; - gsl::index instance; + int64_t index_cell; + int64_t instance; }; //! Structure necessary for inserting CellInstance into hashed STL data @@ -366,9 +469,8 @@ struct CellInstanceHash { void read_cells(pugi::xml_node node); -#ifdef DAGMC -class DAGUniverse; -#endif +//! Add cells to universes +void populate_universes(); } // namespace openmc #endif // OPENMC_CELL_H diff --git a/include/openmc/chain.h b/include/openmc/chain.h new file mode 100644 index 0000000000..a3bc6f3a36 --- /dev/null +++ b/include/openmc/chain.h @@ -0,0 +1,97 @@ +//! \file chain.h +//! \brief Depletion chain and associated information + +#ifndef OPENMC_CHAIN_H +#define OPENMC_CHAIN_H + +#include +#include +#include + +#include "pugixml.hpp" + +#include "openmc/angle_energy.h" // for AngleEnergy +#include "openmc/distribution.h" // for UPtrDist +#include "openmc/memory.h" // for unique_ptr +#include "openmc/vector.h" + +namespace openmc { + +//============================================================================== +// Data for a nuclide in the depletion chain +//============================================================================== + +class ChainNuclide { +public: + // Types + struct Product { + std::string name; //!< Reaction product name + double branching_ratio; //!< Branching ratio + }; + + // Constructors, destructors + ChainNuclide(pugi::xml_node node); + ~ChainNuclide(); + + //! Compute the decay constant for the nuclide + //! \return Decay constant in [1/s] + double decay_constant() const { return std::log(2.0) / half_life_; } + + const Distribution* photon_energy() const { return photon_energy_.get(); } + const std::unordered_map>& reaction_products() const + { + return reaction_products_; + } + +private: + // Data members + std::string name_; //!< Name of nuclide + double half_life_ {0.0}; //!< Half-life in [s] + double decay_energy_ {0.0}; //!< Decay energy in [eV] + std::unordered_map> + reaction_products_; //!< Map of MT to reaction products + UPtrDist photon_energy_; //!< Decay photon energy distribution +}; + +//============================================================================== +// Angle-energy distribution for decay photon +//============================================================================== + +class DecayPhotonAngleEnergy : public AngleEnergy { +public: + explicit DecayPhotonAngleEnergy(const Distribution* dist) + : photon_energy_(dist) + {} + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + //! \param[inout] seed Pseudorandom seed pointer + void sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const override; + +private: + const Distribution* photon_energy_; +}; + +//============================================================================== +// Global variables +//============================================================================== + +namespace data { + +extern std::unordered_map chain_nuclide_map; +extern vector> chain_nuclides; + +} // namespace data + +//============================================================================== +// Non-member functions +//============================================================================== + +void read_chain_file_xml(); + +} // namespace openmc + +#endif // OPENMC_CHAIN_H diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 8427c2d558..a0d1646131 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -5,6 +5,7 @@ #define OPENMC_CONSTANTS_H #include +#include #include #include "openmc/array.h" @@ -24,14 +25,15 @@ using double_4dvec = vector>>>; constexpr int HDF5_VERSION[] {3, 0}; // Version numbers for binary files -constexpr array VERSION_STATEPOINT {17, 0}; +constexpr array VERSION_STATEPOINT {18, 1}; constexpr array VERSION_PARTICLE_RESTART {2, 0}; -constexpr array VERSION_TRACK {2, 0}; -constexpr array VERSION_SUMMARY {6, 0}; +constexpr array VERSION_TRACK {3, 0}; +constexpr array VERSION_SUMMARY {6, 1}; constexpr array VERSION_VOLUME {1, 0}; constexpr array VERSION_VOXEL {2, 0}; constexpr array VERSION_MGXS_LIBRARY {1, 0}; -constexpr array VERSION_PROPERTIES {1, 0}; +constexpr array VERSION_PROPERTIES {1, 1}; +constexpr array VERSION_WEIGHT_WINDOWS {1, 0}; // ============================================================================ // ADJUSTABLE PARAMETERS @@ -50,9 +52,27 @@ constexpr double FP_PRECISION {1e-14}; constexpr double FP_REL_PRECISION {1e-5}; constexpr double FP_COINCIDENT {1e-12}; +// Coincidence tolerances +constexpr double TORUS_TOL {1e-10}; +constexpr double RADIAL_MESH_TOL {1e-10}; + // Maximum number of random samples per history constexpr int MAX_SAMPLE {100000}; +// Avg. number of hits per batch to be defined as a "small" +// source region in the random ray solver +constexpr double MIN_HITS_PER_BATCH {1.5}; + +// The minimum flux value to be considered non-zero when computing adjoint +// sources. Positive values below this cutoff will be treated as zero, so as to +// prevent extremely large adjoint source terms from being generated. +constexpr double ZERO_FLUX_CUTOFF {1e-22}; + +// The minimum macroscopic cross section value considered non-void for the +// random ray solver. Materials with any group with a cross section below this +// value will be converted to pure void. +constexpr double MINIMUM_MACRO_XS {1e-6}; + // ============================================================================ // MATH AND PHYSICAL CONSTANTS @@ -271,9 +291,9 @@ enum class MgxsType { // ============================================================================ // TALLY-RELATED CONSTANTS -enum class TallyResult { VALUE, SUM, SUM_SQ }; +enum class TallyResult { VALUE, SUM, SUM_SQ, SIZE }; -enum class TallyType { VOLUME, MESH_SURFACE, SURFACE }; +enum class TallyType { VOLUME, MESH_SURFACE, SURFACE, PULSE_HEIGHT }; enum class TallyEstimator { ANALOG, TRACKLENGTH, COLLISION }; @@ -301,7 +321,11 @@ enum TallyScore { SCORE_INVERSE_VELOCITY = -13, // flux-weighted inverse velocity SCORE_FISS_Q_PROMPT = -14, // prompt fission Q-value SCORE_FISS_Q_RECOV = -15, // recoverable fission Q-value - SCORE_DECAY_RATE = -16 // delayed neutron precursor decay rate + SCORE_DECAY_RATE = -16, // delayed neutron precursor decay rate + SCORE_PULSE_HEIGHT = -17, // pulse-height + SCORE_IFP_TIME_NUM = -18, // IFP lifetime numerator + SCORE_IFP_BETA_NUM = -19, // IFP delayed fraction numerator + SCORE_IFP_DENOM = -20 // IFP common denominator }; // Global tally parameters @@ -310,7 +334,9 @@ enum class GlobalTally { K_COLLISION, K_ABSORPTION, K_TRACKLENGTH, LEAKAGE }; // Miscellaneous constexpr int C_NONE {-1}; -constexpr int F90_NONE {0}; // TODO: replace usage of this with C_NONE + +// Default value of generation for IFP +constexpr int DEFAULT_IFP_N_GENERATION {10}; // Interpolation rules enum class Interpolation { @@ -318,7 +344,11 @@ enum class Interpolation { lin_lin = 2, lin_log = 3, log_lin = 4, - log_log = 5 + log_log = 5, + // skip 6 b/c ENDF-6 reserves this value for + // "special one-dimensional interpolation law" + quadratic = 7, + cubic = 8 }; enum class RunMode { @@ -330,11 +360,21 @@ enum class RunMode { VOLUME }; +enum class SolverType { MONTE_CARLO, RANDOM_RAY }; + +enum class RandomRayVolumeEstimator { NAIVE, SIMULATION_AVERAGED, HYBRID }; +enum class RandomRaySourceShape { FLAT, LINEAR, LINEAR_XY }; +enum class RandomRaySampleMethod { PRNG, HALTON }; + //============================================================================== // Geometry Constants enum class GeometryType { CSG, DAG }; +// a surface token cannot be zero due to the unsigned nature of zero for integer +// representations. This value represents no surface. +constexpr int32_t SURFACE_NONE {0}; + } // namespace openmc #endif // OPENMC_CONSTANTS_H diff --git a/include/openmc/cross_sections.h b/include/openmc/cross_sections.h index 2b0473becd..06140a6a8c 100644 --- a/include/openmc/cross_sections.h +++ b/include/openmc/cross_sections.h @@ -62,6 +62,11 @@ extern vector libraries; //! libraries void read_cross_sections_xml(); +//! Read cross sections file (either XML or multigroup H5) and populate data +//! libraries +//! \param[in] root node of the cross_sections.xml +void read_cross_sections_xml(pugi::xml_node root); + //! Load nuclide and thermal scattering data from HDF5 files // //! \param[in] nuc_temps Temperatures for each nuclide in [K] diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 64bf4d2091..0e27402a15 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -3,7 +3,8 @@ namespace openmc { extern "C" const bool DAGMC_ENABLED; -} +extern "C" const bool UWUW_ENABLED; +} // namespace openmc // always include the XML interface header #include "openmc/xml_interface.h" @@ -19,14 +20,21 @@ void check_dagmc_root_univ(); } // namespace openmc -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED #include "DagMC.hpp" +#include "dagmcmetadata.hpp" #include "openmc/cell.h" #include "openmc/particle.h" #include "openmc/position.h" #include "openmc/surface.h" +#include "openmc/vector.h" + +#include // for shared_ptr, unique_ptr +#include +#include +#include // for pair class UWUW; @@ -36,15 +44,20 @@ class DAGSurface : public Surface { public: DAGSurface(std::shared_ptr dag_ptr, int32_t dag_idx); + moab::EntityHandle mesh_handle() const; + double evaluate(Position r) const override; double distance(Position r, Direction u, bool coincident) const override; Direction normal(Position r) const override; - Direction reflect(Position r, Direction u, Particle* p) const override; + Direction reflect( + Position r, Direction u, GeometryState* p = nullptr) const override; inline void to_hdf5_inner(hid_t group_id) const override {}; + virtual GeometryType geom_type() const override { return GeometryType::DAG; } + // Accessor methods - const std::shared_ptr& dagmc_ptr() const { return dagmc_ptr_; } + moab::DagMC* dagmc_ptr() const { return dagmc_ptr_.get(); } int32_t dag_index() const { return dag_index_; } private: @@ -56,17 +69,21 @@ class DAGCell : public Cell { public: DAGCell(std::shared_ptr dag_ptr, int32_t dag_idx); + moab::EntityHandle mesh_handle() const; + bool contains(Position r, Direction u, int32_t on_surface) const override; - std::pair distance( - Position r, Direction u, int32_t on_surface, Particle* p) const override; + std::pair distance(Position r, Direction u, + int32_t on_surface, GeometryState* p) const override; BoundingBox bounding_box() const override; void to_hdf5_inner(hid_t group_id) const override; + virtual GeometryType geom_type() const override { return GeometryType::DAG; } + // Accessor methods - const std::shared_ptr& dagmc_ptr() const { return dagmc_ptr_; } + moab::DagMC* dagmc_ptr() const { return dagmc_ptr_.get(); } int32_t dag_index() const { return dag_index_; } private: @@ -87,6 +104,11 @@ public: explicit DAGUniverse(const std::string& filename, bool auto_geom_ids = false, bool auto_mat_ids = false); + //! Alternative DAGMC universe constructor for external DAGMC instance + explicit DAGUniverse(std::shared_ptr external_dagmc_ptr, + const std::string& filename = "", bool auto_geom_ids = false, + bool auto_mat_ids = false); + //! Initialize the DAGMC accel. data structures, indices, material //! assignments, etc. void initialize(); @@ -110,12 +132,32 @@ public: void write_uwuw_materials_xml( const std::string& outfile = "uwuw_materials.xml") const; + //! Assign a material to a cell from uwuw material library + //! \param[in] vol_handle The DAGMC material assignment string + //! \param[in] c The OpenMC cell to which the material is assigned + void uwuw_assign_material( + moab::EntityHandle vol_handle, std::unique_ptr& c) const; + //! Assign a material to a cell based //! \param[in] mat_string The DAGMC material assignment string //! \param[in] c The OpenMC cell to which the material is assigned void legacy_assign_material( std::string mat_string, std::unique_ptr& c) const; + //! Assign a material overriding normal assignement to a cell + //! \param[in] c The OpenMC cell to which the material is assigned + void override_assign_material(std::unique_ptr& c) const; + + //! Return the index into the model cells vector for a given DAGMC volume + //! handle in the universe + //! \param[in] vol MOAB handle to the DAGMC volume set + int32_t cell_index(moab::EntityHandle vol) const; + + //! Return the index into the model surfaces vector for a given DAGMC surface + //! handle in the universe + //! \param[in] surf MOAB handle to the DAGMC surface set + int32_t surface_index(moab::EntityHandle surf) const; + //! Generate a string representing the ranges of IDs present in the DAGMC //! model. Contiguous chunks of IDs are represented as a range (i.e. 1-10). If //! there is a single ID a chunk, it will be represented as a single number @@ -123,10 +165,12 @@ public: //! string of the ID ranges for entities of dimension \p dim std::string dagmc_ids_for_dim(int dim) const; - bool find_cell(Particle& p) const override; + bool find_cell(GeometryState& p) const override; void to_hdf5(hid_t universes_group) const override; + virtual GeometryType geom_type() const override { return GeometryType::DAG; } + // Data Members std::shared_ptr dagmc_instance_; //!< DAGMC Instance for this universe @@ -136,13 +180,20 @@ public: //!< universe in OpenMC's surface vector // Accessors + moab::DagMC* dagmc_ptr() const { return dagmc_instance_.get(); } bool has_graveyard() const { return has_graveyard_; } private: + void set_id(); //!< Deduce the universe id from model::universes + void init_dagmc(); //!< Create and initialise DAGMC pointer + void init_metadata(); //!< Create and initialise dagmcMetaData pointer + void init_geometry(); //!< Create cells and surfaces from DAGMC entities + std::string filename_; //!< Name of the DAGMC file used to create this universe std::shared_ptr - uwuw_; //!< Pointer to the UWUW instance for this universe + uwuw_; //!< Pointer to the UWUW instance for this universe + std::unique_ptr dmd_ptr; //! Pointer to DAGMC metadata object bool adjust_geometry_ids_; //!< Indicates whether or not to automatically //!< generate new cell and surface IDs for the //!< universe @@ -150,17 +201,21 @@ private: //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume + std::unordered_map> + material_overrides_; //!< Map of material overrides + //!< keys correspond to the DAGMCCell id + //!< values are a list of material ids used + //!< for the override }; //============================================================================== // Non-member functions //============================================================================== -int32_t next_cell( - DAGUniverse* dag_univ, DAGCell* cur_cell, DAGSurface* surf_xed); +int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ); } // namespace openmc -#endif // DAGMC +#endif // OPENMC_DAGMC_ENABLED #endif // OPENMC_DAGMC_H diff --git a/include/openmc/distribution.h b/include/openmc/distribution.h index 8403f2c678..854cf7d771 100644 --- a/include/openmc/distribution.h +++ b/include/openmc/distribution.h @@ -10,6 +10,7 @@ #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr +#include "openmc/span.h" #include "openmc/vector.h" // for vector namespace openmc { @@ -22,6 +23,10 @@ class Distribution { public: virtual ~Distribution() = default; virtual double sample(uint64_t* seed) const = 0; + + //! Return integral of distribution + //! \return Integral of distribution + virtual double integral() const { return 1.0; }; }; using UPtrDist = unique_ptr; @@ -31,6 +36,40 @@ using UPtrDist = unique_ptr; //! \return Unique pointer to distribution UPtrDist distribution_from_xml(pugi::xml_node node); +//============================================================================== +//! A discrete distribution index (probability mass function) +//============================================================================== + +class DiscreteIndex { +public: + DiscreteIndex() {}; + DiscreteIndex(pugi::xml_node node); + DiscreteIndex(span p); + + void assign(span p); + + //! Sample a value from the distribution + //! \param seed Pseudorandom number seed pointer + //! \return Sampled value + size_t sample(uint64_t* seed) const; + + // Properties + const vector& prob() const { return prob_; } + const vector& alias() const { return alias_; } + double integral() const { return integral_; } + +private: + vector prob_; //!< Probability of accepting the uniformly sampled bin, + //!< mapped to alias method table + vector alias_; //!< Alias table + double integral_; //!< Integral of distribution + + //! Normalize distribution so that probabilities sum to unity + void normalize(); + + //! Initialize alias tables for distribution + void init_alias(); +}; //============================================================================== //! A discrete distribution (probability mass function) @@ -39,23 +78,24 @@ UPtrDist distribution_from_xml(pugi::xml_node node); class Discrete : public Distribution { public: explicit Discrete(pugi::xml_node node); - Discrete(const double* x, const double* p, int n); + Discrete(const double* x, const double* p, size_t n); //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; + + double integral() const override { return di_.integral(); }; // Properties const vector& x() const { return x_; } - const vector& p() const { return p_; } + const vector& prob() const { return di_.prob(); } + const vector& alias() const { return di_.alias(); } private: vector x_; //!< Possible outcomes - vector p_; //!< Probability of each outcome - - //! Normalize distribution so that probabilities sum to unity - void normalize(); + DiscreteIndex di_; //!< discrete probability distribution of + //!< outcome indices }; //============================================================================== @@ -70,7 +110,7 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; double a() const { return a_; } double b() const { return b_; } @@ -94,16 +134,17 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; double a() const { return std::pow(offset_, ninv_); } double b() const { return std::pow(offset_ + span_, ninv_); } double n() const { return 1 / ninv_ - 1; } + private: //! Store processed values in object to allow for faster sampling double offset_; //!< a^(n+1) - double span_; //!< b^(n+1) - a^(n+1) - double ninv_; //!< 1/(n+1) + double span_; //!< b^(n+1) - a^(n+1) + double ninv_; //!< 1/(n+1) }; //============================================================================== @@ -118,7 +159,7 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; double theta() const { return theta_; } @@ -138,7 +179,7 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; double a() const { return a_; } double b() const { return b_; } @@ -162,7 +203,7 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; double mean_value() const { return mean_value_; } double std_dev() const { return std_dev_; } @@ -172,35 +213,6 @@ private: double std_dev_; //!< standard deviation [eV] }; -//============================================================================== -//! Muir (fusion) spectrum derived from Normal with extra params e0 is mean -//! std dev is sqrt(4*e0*kt/m) -//============================================================================== - -class Muir : public Distribution { -public: - explicit Muir(pugi::xml_node node); - Muir(double e0, double m_rat, double kt) - : e0_ {e0}, m_rat_ {m_rat}, kt_ {kt} {}; - - //! Sample a value from the distribution - //! \param seed Pseudorandom number seed pointer - //! \return Sampled value - double sample(uint64_t* seed) const; - - double e0() const { return e0_; } - double m_rat() const { return m_rat_; } - double kt() const { return kt_; } - -private: - // example DT fusion m_rat = 5 (D = 2 + T = 3) - // ion temp = 20000 eV - // mean neutron energy 14.08e6 eV - double e0_; //!< mean neutron energy [eV] - double m_rat_; //!< ratio of reactant masses relative to atomic mass unit - double kt_; //!< ion temperature [eV] -}; - //============================================================================== //! Histogram or linear-linear interpolated tabular distribution //============================================================================== @@ -214,19 +226,21 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; - // x property + // properties vector& x() { return x_; } const vector& x() const { return x_; } const vector& p() const { return p_; } Interpolation interp() const { return interp_; } + double integral() const override { return integral_; }; private: vector x_; //!< tabulated independent variable vector p_; //!< tabulated probability density vector c_; //!< cumulative distribution at tabulated values Interpolation interp_; //!< interpolation rule + double integral_; //!< Integral of distribution //! Initialize tabulated probability density function //! \param x Array of values for independent variable @@ -248,7 +262,7 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; const vector& x() const { return x_; } @@ -267,17 +281,19 @@ public: //! Sample a value from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled value - double sample(uint64_t* seed) const; + double sample(uint64_t* seed) const override; + + double integral() const override { return integral_; } private: // Storrage for probability + distribution using DistPair = std::pair; vector - distribution_; //!< sub-distributions + cummulative probabilities + distribution_; //!< sub-distributions + cummulative probabilities + double integral_; //!< integral of distribution }; - } // namespace openmc #endif // OPENMC_DISTRIBUTION_H diff --git a/include/openmc/distribution_energy.h b/include/openmc/distribution_energy.h index d8512aa45f..9b08ed039d 100644 --- a/include/openmc/distribution_energy.h +++ b/include/openmc/distribution_energy.h @@ -37,7 +37,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: int primary_flag_; //!< Indicator of whether the photon is a primary or @@ -58,7 +58,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: double threshold_; //!< Energy threshold in lab, (A + 1)/A * |Q| @@ -79,7 +79,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: //! Outgoing energy for a single incoming energy @@ -110,7 +110,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: Tabulated1D theta_; //!< Incoming energy dependent parameter @@ -130,7 +130,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: Tabulated1D theta_; //!< Incoming energy dependent parameter @@ -150,7 +150,7 @@ public: //! \param[in] E Incident particle energy in [eV] //! \param[inout] seed Pseudorandom number seed pointer //! \return Sampled energy in [eV] - double sample(double E, uint64_t* seed) const; + double sample(double E, uint64_t* seed) const override; private: Tabulated1D a_; //!< Energy-dependent 'a' parameter diff --git a/include/openmc/distribution_multi.h b/include/openmc/distribution_multi.h index 991294f794..9e84d03d57 100644 --- a/include/openmc/distribution_multi.h +++ b/include/openmc/distribution_multi.h @@ -22,6 +22,8 @@ public: explicit UnitSphereDistribution(pugi::xml_node node); virtual ~UnitSphereDistribution() = default; + static unique_ptr create(pugi::xml_node node); + //! Sample a direction from the distribution //! \param seed Pseudorandom number seed pointer //! \return Direction sampled @@ -42,7 +44,7 @@ public: //! Sample a direction from the distribution //! \param seed Pseudorandom number seed pointer //! \return Direction sampled - Direction sample(uint64_t* seed) const; + Direction sample(uint64_t* seed) const override; // Observing pointers Distribution* mu() const { return mu_.get(); } @@ -66,7 +68,7 @@ public: //! Sample a direction from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled direction - Direction sample(uint64_t* seed) const; + Direction sample(uint64_t* seed) const override; }; //============================================================================== @@ -82,7 +84,7 @@ public: //! Sample a direction from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled direction - Direction sample(uint64_t* seed) const; + Direction sample(uint64_t* seed) const override; }; using UPtrAngle = unique_ptr; diff --git a/include/openmc/distribution_spatial.h b/include/openmc/distribution_spatial.h index 9cceee184f..9c3bc743ff 100644 --- a/include/openmc/distribution_spatial.h +++ b/include/openmc/distribution_spatial.h @@ -4,7 +4,9 @@ #include "pugixml.hpp" #include "openmc/distribution.h" +#include "openmc/mesh.h" #include "openmc/position.h" +#include "openmc/span.h" namespace openmc { @@ -18,6 +20,8 @@ public: //! Sample a position from the distribution virtual Position sample(uint64_t* seed) const = 0; + + static unique_ptr create(pugi::xml_node node); }; //============================================================================== @@ -31,7 +35,7 @@ public: //! Sample a position from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled position - Position sample(uint64_t* seed) const; + Position sample(uint64_t* seed) const override; // Observer pointers Distribution* x() const { return x_.get(); } @@ -55,7 +59,7 @@ public: //! Sample a position from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled position - Position sample(uint64_t* seed) const; + Position sample(uint64_t* seed) const override; Distribution* r() const { return r_.get(); } Distribution* phi() const { return phi_.get(); } @@ -70,7 +74,7 @@ private: }; //============================================================================== -//! Distribution of points specified by spherical coordinates r,theta,phi +//! Distribution of points specified by spherical coordinates r,cos_theta,phi //============================================================================== class SphericalIndependent : public SpatialDistribution { @@ -80,18 +84,77 @@ public: //! Sample a position from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled position - Position sample(uint64_t* seed) const; + Position sample(uint64_t* seed) const override; Distribution* r() const { return r_.get(); } - Distribution* theta() const { return theta_.get(); } + Distribution* cos_theta() const { return cos_theta_.get(); } Distribution* phi() const { return phi_.get(); } Position origin() const { return origin_; } private: - UPtrDist r_; //!< Distribution of r coordinates - UPtrDist theta_; //!< Distribution of theta coordinates - UPtrDist phi_; //!< Distribution of phi coordinates - Position origin_; //!< Cartesian coordinates of the sphere center + UPtrDist r_; //!< Distribution of r coordinates + UPtrDist cos_theta_; //!< Distribution of cos_theta coordinates + UPtrDist phi_; //!< Distribution of phi coordinates + Position origin_; //!< Cartesian coordinates of the sphere center +}; + +//============================================================================== +//! Distribution of points within a mesh +//============================================================================== + +class MeshSpatial : public SpatialDistribution { +public: + explicit MeshSpatial(pugi::xml_node node); + explicit MeshSpatial(int32_t mesh_id, span strengths); + + //! Sample a position from the distribution + //! \param seed Pseudorandom number seed pointer + //! \return Sampled position + Position sample(uint64_t* seed) const override; + + //! Sample the mesh for an element and position within that element + //! \param seed Pseudorandom number seed pointer + //! \return Sampled element index and position within that element + std::pair sample_mesh(uint64_t* seed) const; + + //! Sample a mesh element + //! \param seed Pseudorandom number seed pointer + //! \return Sampled element index + int32_t sample_element_index(uint64_t* seed) const; + + //! For unstructured meshes, ensure that elements are all linear tetrahedra + void check_element_types() const; + + // Accessors + const Mesh* mesh() const { return model::meshes.at(mesh_idx_).get(); } + int32_t n_sources() const { return this->mesh()->n_bins(); } + + double total_strength() { return this->elem_idx_dist_.integral(); } + +private: + int32_t mesh_idx_ {C_NONE}; + DiscreteIndex elem_idx_dist_; //!< Distribution of + //!< mesh element indices +}; + +//============================================================================== +//! Distribution of points +//============================================================================== + +class PointCloud : public SpatialDistribution { +public: + explicit PointCloud(pugi::xml_node node); + explicit PointCloud( + std::vector point_cloud, span strengths); + + //! Sample a position from the distribution + //! \param seed Pseudorandom number seed pointer + //! \return Sampled position + Position sample(uint64_t* seed) const override; + +private: + std::vector point_cloud_; + DiscreteIndex point_idx_dist_; //!< Distribution of Position indices }; //============================================================================== @@ -105,7 +168,7 @@ public: //! Sample a position from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled position - Position sample(uint64_t* seed) const; + Position sample(uint64_t* seed) const override; // Properties bool only_fissionable() const { return only_fissionable_; } @@ -131,7 +194,7 @@ public: //! Sample a position from the distribution //! \param seed Pseudorandom number seed pointer //! \return Sampled position - Position sample(uint64_t* seed) const; + Position sample(uint64_t* seed) const override; Position r() const { return r_; } diff --git a/include/openmc/endf.h b/include/openmc/endf.h index 7beb8e4520..4a737eb881 100644 --- a/include/openmc/endf.h +++ b/include/openmc/endf.h @@ -53,6 +53,10 @@ public: //! \param[in] dset Dataset containing coefficients explicit Polynomial(hid_t dset); + //! Construct polynomial from coefficients + //! \param[in] coef Polynomial coefficients + explicit Polynomial(vector coef) : coef_(coef) {} + //! Evaluate the polynomials //! \param[in] x independent variable //! \return Polynomial evaluated at x @@ -126,6 +130,26 @@ private: debye_waller_; //!< Debye-Waller integral divided by atomic mass in [eV^-1] }; +//============================================================================== +//! Sum of multiple 1D functions +//============================================================================== + +class Sum1D : public Function1D { +public: + // Constructors + explicit Sum1D(hid_t group); + + //! Evaluate each function and sum results + //! \param[in] x independent variable + //! \return Function evaluated at x + double operator()(double E) const override; + + const unique_ptr& functions(int i) const { return functions_[i]; } + +private: + vector> functions_; //!< individual functions +}; + //! Read 1D function from HDF5 dataset //! \param[in] group HDF5 group containing dataset //! \param[in] name Name of dataset diff --git a/vendor/faddeeva/Faddeeva.hh b/include/openmc/external/Faddeeva.hh similarity index 100% rename from vendor/faddeeva/Faddeeva.hh rename to include/openmc/external/Faddeeva.hh diff --git a/include/openmc/file_utils.h b/include/openmc/file_utils.h index f9c23468df..db65640344 100644 --- a/include/openmc/file_utils.h +++ b/include/openmc/file_utils.h @@ -1,19 +1,35 @@ #ifndef OPENMC_FILE_UTILS_H #define OPENMC_FILE_UTILS_H -#include // for ifstream #include namespace openmc { +// NOTE: This is a thin wrapper over std::filesystem because we +// pass strings around a lot. Objects like settings::path_input +// are extern std::string to play with other libraries and languages + +//! Determine if a path is a directory +//! \param[in] path Path to check +//! \return Whether the path is a directory +bool dir_exists(const std::string& path); + //! Determine if a file exists //! \param[in] filename Path to file //! \return Whether file exists -inline bool file_exists(const std::string& filename) -{ - std::ifstream s {filename}; - return s.good(); -} +bool file_exists(const std::string& filename); + +//! Determine directory containing given file +//! \param[in] filename Path to file +//! \return Name of directory containing file excluding the final directory +//! separator +std::string dir_name(const std::string& filename); + +// Gets the file extension of whatever string is passed in. This is defined as +// a sequence of strictly alphanumeric characters which follow the last period, +// i.e. at least one alphabet character is present, and zero or more numbers. +// If such a sequence of characters is not found, an empty string is returned. +std::string get_file_extension(const std::string& filename); } // namespace openmc diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 001e58c4cc..d41a98e301 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -11,7 +11,7 @@ namespace openmc { class BoundaryInfo; -class Particle; +class GeometryState; //============================================================================== // Global variables @@ -39,7 +39,7 @@ inline bool coincident(double d1, double d2) //! Check for overlapping cells at a particle's position. //============================================================================== -bool check_cell_overlap(Particle& p, bool error = true); +bool check_cell_overlap(GeometryState& p, bool error = true); //============================================================================== //! Get the cell instance for a particle at the specified universe level @@ -50,7 +50,7 @@ bool check_cell_overlap(Particle& p, bool error = true); //! should be computed. \return The instance of the cell at the specified level. //============================================================================== -int cell_instance_at_level(const Particle& p, int level); +int cell_instance_at_level(const GeometryState& p, int level); //============================================================================== //! Locate a particle in the geometry tree and set its geometry data fields. @@ -60,20 +60,26 @@ int cell_instance_at_level(const Particle& p, int level); //! \return True if the particle's location could be found and ascribed to a //! valid geometry coordinate stack. //============================================================================== -bool exhaustive_find_cell(Particle& p); -bool neighbor_list_find_cell(Particle& p); // Only usable on surface crossings +bool exhaustive_find_cell(GeometryState& p, bool verbose = false); +bool neighbor_list_find_cell( + GeometryState& p, bool verbose = false); // Only usable on surface crossings + +bool find_cell_in_virtual_lattice(GeometryState& p, + bool verbose = + false); // Only usable on triso surface crossings in virtual lattice //============================================================================== //! Move a particle into a new lattice tile. //============================================================================== -void cross_lattice(Particle& p, const BoundaryInfo& boundary); +void cross_lattice( + GeometryState& p, const BoundaryInfo& boundary, bool verbose = false); //============================================================================== //! Find the next boundary a particle will intersect. //============================================================================== -BoundaryInfo distance_to_boundary(Particle& p); +BoundaryInfo distance_to_boundary(GeometryState& p); } // namespace openmc diff --git a/include/openmc/geometry_aux.h b/include/openmc/geometry_aux.h index b248d491ac..4dafdea5c2 100644 --- a/include/openmc/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -10,17 +10,21 @@ #include #include "openmc/vector.h" +#include "openmc/xml_interface.h" namespace openmc { namespace model { -extern std::unordered_map> - universe_cell_counts; extern std::unordered_map universe_level_counts; } // namespace model +//! Read geometry from XML file void read_geometry_xml(); +//! Read geometry from XML node +//! \param[in] root node of geometry XML element +void read_geometry_xml(pugi::xml_node root); + //============================================================================== //! Replace Universe, Lattice, and Material IDs with indices. //============================================================================== @@ -33,6 +37,12 @@ void adjust_indices(); void assign_temperatures(); +//============================================================================== +//! Finalize densities (compute density multipliers). +//============================================================================== + +void finalize_cell_densities(); + //============================================================================== //! \brief Obtain a list of temperatures that each nuclide/thermal scattering //! table appears at in the model. Later, this list is used to determine the @@ -74,15 +84,13 @@ void prepare_distribcell( const std::vector* user_distribcells = nullptr); //============================================================================== -//! Recursively search through the geometry and count cell instances. +//! Recursively search through the geometry and count universe instances. //! -//! This function will update the Cell::n_instances value for each cell in the -//! geometry. -//! \param univ_indx The index of the universe to begin searching from (probably -//! the root universe). +//! This function will update Universe.n_instances_ for each +//! universe in the geometry. //============================================================================== -void count_cell_instances(int32_t univ_indx); +void count_universe_instances(); //============================================================================== //! Recursively search through universes and count universe instances. @@ -120,6 +128,14 @@ std::string distribcell_path( int maximum_levels(int32_t univ); +//============================================================================== +//! Check whether or not a universe is the root universe using its ID. +//! \param univ_id The ID of the universe to check. +//! \return Whether or not it is the root universe. +//============================================================================== + +bool is_root_universe(int32_t univ_id); + //============================================================================== //! Deallocates global vectors and maps for cells, universes, and lattices. //============================================================================== diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index e9d9b4f968..0092c08f8d 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -61,6 +61,8 @@ void ensure_exists(hid_t obj_id, const char* name, bool attribute = false); vector group_names(hid_t group_id); vector object_shape(hid_t obj_id); std::string object_name(hid_t obj_id); +hid_t open_object(hid_t group_id, const std::string& name); +void close_object(hid_t obj_id); //============================================================================== // Fortran compatibility functions diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h new file mode 100644 index 0000000000..01904d13c9 --- /dev/null +++ b/include/openmc/ifp.h @@ -0,0 +1,187 @@ +#ifndef OPENMC_IFP_H +#define OPENMC_IFP_H + +#include "openmc/message_passing.h" +#include "openmc/particle.h" +#include "openmc/particle_data.h" +#include "openmc/settings.h" + +namespace openmc { + +//! Check the value of the IFP parameter for beta effective or both. +//! +//! \return true if "BetaEffective" or "Both", false otherwise. +bool is_beta_effective_or_both(); + +//! Check the value of the IFP parameter for generation time or both. +//! +//! \return true if "GenerationTime" or "Both", false otherwise. +bool is_generation_time_or_both(); + +//! Resize IFP vectors +//! +//! \param[in,out] delayed_groups List of delayed group numbers +//! \param[in,out] lifetimes List of lifetimes +//! \param[in] n Dimension to resize vectors +template +void resize_ifp_data(vector& delayed_groups, vector& lifetimes, int64_t n) +{ + if (is_beta_effective_or_both()) { + delayed_groups.resize(n); + } + if (is_generation_time_or_both()) { + lifetimes.resize(n); + } +} + +//! Update a list of values by adding a new value if the size +//! of the list can accomodate the new value or by shifting all +//! values to the left (removing the first value of the list +//! and adding the new value at the end of the list). +//! +//! \param[in] value Value to add to the list +//! \param[in] data Initial version of the list +//! \return Updated list +template +vector _ifp(const T& value, const vector& data) +{ + vector updated; + size_t source_idx = data.size(); + + if (source_idx < settings::ifp_n_generation) { + updated.resize(source_idx + 1); + for (size_t i = 0; i < source_idx; i++) { + updated[i] = data[i]; + } + updated[source_idx] = value; + } else if (source_idx == settings::ifp_n_generation) { + updated.resize(source_idx); + for (size_t i = 0; i < source_idx - 1; i++) { + updated[i] = data[i + 1]; + } + updated[source_idx - 1] = value; + } + return updated; +} + +//! \brief Iterated Fission Probability (IFP) method. +//! +//! Add the IFP information in the IFP banks using the same index +//! as the one used to append the fission site to the fission bank. +//! The information stored are the delayed group number and lifetime +//! of the neutron that created the fission event. +//! Multithreading protection is guaranteed by the index returned by the +//! thread_safe_append call in physics.cpp. +//! +//! \param[in] p Particle +//! \param[in] idx Bank index from the thread_safe_append call in physics.cpp +void ifp(const Particle& p, int64_t idx); + +//! Resize the IFP banks used in the simulation +void resize_simulation_ifp_banks(); + +//! Retrieve IFP data from the IFP fission banks. +//! +//! \param[in] i_bank Index in the fission banks +//! \param[in,out] delayed_groups Delayed group numbers +//! \param[in,out] lifetimes Lifetimes lists +void copy_ifp_data_from_fission_banks( + int i_bank, vector& delayed_groups, vector& lifetimes); + +#ifdef OPENMC_MPI + +//! Deserialization information for transfer of IFP data using MPI +struct DeserializationInfo { + int64_t index_local; //!< local index + int64_t n; //!< number of sites sent +}; + +//! Broadcast the number of generation determined by the size of the first +//! element on the first processor. +//! +//! \param[in] n_generation Number of generations +//! \param[in] delayed_groups List of delayed group numbers lists +//! \param[in] lifetimes List of lifetimes lists +void broadcast_ifp_n_generation(int& n_generation, + const vector>& delayed_groups, + const vector>& lifetimes); + +//! Send IFP data using MPI. +//! +//! \param[in] idx Index of the first site +//! \param[in] n Number of sites to send +//! \param[in] n_generation Number of generations +//! \param[in] neighbor Index of the neighboring processor +//! \param[in] requests MPI requests +//! \param[in] delayed_groups List of delayed group numbers lists +//! \param[out] send_delayed_groups Delayed group numbers buffer +//! \param[in] lifetimes List of lifetimes lists +//! \param[out] send_lifetimes Lifetimes buffer +void send_ifp_info(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, const vector>& delayed_groups, + vector& send_delayed_groups, const vector>& lifetimes, + vector& send_lifetimes); + +//! Receive IFP data using MPI. +//! +//! \param[in] idx Index of the first site +//! \param[in] n Number of sites to receive +//! \param[in] n_generation Number of generations +//! \param[in] neighbor Index of the neighboring processor +//! \param[in] requests MPI requests +//! \param[in] delayed_groups List of delayed group numbers +//! \param[in] lifetimes List of lifetimes +//! \param[out] deserialization Information to deserialize the received data +void receive_ifp_data(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, vector& delayed_groups, + vector& lifetimes, vector& deserialization); + +//! Copy partial IFP data from local lists to source banks. +//! +//! \param[in] idx Index of the first site +//! \param[in] n Number of sites to copy +//! \param[in] i_bank Index in the IFP source banks +//! \param[in] delayed_groups List of delayed group numbers lists +//! \param[in] lifetimes List of lifetimes lists +void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank, + const vector>& delayed_groups, + const vector>& lifetimes); + +//! Deserialize IFP information received using MPI and store it in +//! the IFP source banks. +//! +//! \param[in] n_generation Number of generations +//! \param[out] deserialization Information to deserialize the received data +//! \param[in] delayed_groups List of delayed group numbers +//! \param[in] lifetimes List of lifetimes +void deserialize_ifp_info(int n_generation, + const vector& deserialization, + const vector& delayed_groups, const vector& lifetimes); + +#endif + +//! Copy IFP temporary vectors to source banks. +//! +//! \param[in] delayed_groups List of delayed group numbers lists +//! \param[in] lifetimes List of lifetimes lists +void copy_complete_ifp_data_to_source_banks( + const vector>& delayed_groups, + const vector>& lifetimes); + +//! Allocate temporary vectors for IFP data. +//! +//! \param[in,out] delayed_groups List of delayed group numbers lists +//! \param[in,out] lifetimes List of delayed group numbers lists +void allocate_temporary_vector_ifp( + vector>& delayed_groups, vector>& lifetimes); + +//! Copy local IFP data to IFP fission banks. +//! +//! \param[in] delayed_groups_ptr Pointer to delayed group numbers +//! \param[in] lifetimes_ptr Pointer to lifetimes +void copy_ifp_data_to_fission_banks( + const vector* delayed_groups_ptr, const vector* lifetimes_ptr); + +} // namespace openmc + +#endif // OPENMC_IFP_H diff --git a/include/openmc/initialize.h b/include/openmc/initialize.h index 869be44414..a9b8b336f9 100644 --- a/include/openmc/initialize.h +++ b/include/openmc/initialize.h @@ -1,6 +1,8 @@ #ifndef OPENMC_INITIALIZE_H #define OPENMC_INITIALIZE_H +#include + #ifdef OPENMC_MPI #include "mpi.h" #endif @@ -11,7 +13,13 @@ int parse_command_line(int argc, char* argv[]); #ifdef OPENMC_MPI void initialize_mpi(MPI_Comm intracomm); #endif -void read_input_xml(); + +//! Read material, geometry, settings, and tallies from a single XML file +bool read_model_xml(); +//! Read inputs from separate XML files +void read_separate_xml_files(); +//! Write some output that occurs right after initialization +void initial_output(); } // namespace openmc diff --git a/include/openmc/interpolate.h b/include/openmc/interpolate.h new file mode 100644 index 0000000000..31ae8b0e3f --- /dev/null +++ b/include/openmc/interpolate.h @@ -0,0 +1,97 @@ +#ifndef OPENMC_INTERPOLATE_H +#define OPENMC_INTERPOLATE_H + +#include +#include + +#include "openmc/error.h" +#include "openmc/search.h" +#include "openmc/span.h" + +namespace openmc { + +inline double interpolate_lin_lin( + double x0, double x1, double y0, double y1, double x) +{ + return y0 + (x - x0) / (x1 - x0) * (y1 - y0); +} + +inline double interpolate_lin_log( + double x0, double x1, double y0, double y1, double x) +{ + return y0 + std::log(x / x0) / std::log(x1 / x0) * (y1 - y0); +} + +inline double interpolate_log_lin( + double x0, double x1, double y0, double y1, double x) +{ + return y0 * std::exp((x - x0) / (x1 - x0) * std::log(y1 / y0)); +} + +inline double interpolate_log_log( + double x0, double x1, double y0, double y1, double x) +{ + double f = std::log(x / x0) / std::log(x1 / x0); + return y0 * std::exp(f * std::log(y1 / y0)); +} + +inline double interpolate_lagrangian( + span xs, span ys, int idx, double x, int order) +{ + double output {0.0}; + + for (int i = 0; i < order + 1; i++) { + double numerator {1.0}; + double denominator {1.0}; + for (int j = 0; j < order + 1; j++) { + if (i == j) + continue; + numerator *= (x - xs[idx + j]); + denominator *= (xs[idx + i] - xs[idx + j]); + } + output += (numerator / denominator) * ys[idx + i]; + } + + return output; +} + +inline double interpolate(span xs, span ys, + double x, Interpolation i = Interpolation::lin_lin) +{ + int idx = lower_bound_index(xs.begin(), xs.end(), x); + + if (idx == xs.size()) + idx--; + + switch (i) { + case Interpolation::histogram: + return ys[idx]; + case Interpolation::lin_lin: + return interpolate_lin_lin(xs[idx], xs[idx + 1], ys[idx], ys[idx + 1], x); + case Interpolation::log_log: + return interpolate_log_log(xs[idx], xs[idx + 1], ys[idx], ys[idx + 1], x); + case Interpolation::lin_log: + return interpolate_lin_log(xs[idx], xs[idx + 1], ys[idx], ys[idx + 1], x); + case Interpolation::log_lin: + return interpolate_log_lin(xs[idx], xs[idx + 1], ys[idx], ys[idx + 1], x); + case Interpolation::quadratic: + // move back one point if x is in the last interval of the x-grid + if (idx == xs.size() - 2 && idx > 0) + idx--; + return interpolate_lagrangian(xs, ys, idx, x, 2); + case Interpolation::cubic: + // if x is not in the first interval of the x-grid, move back one + if (idx > 0) + idx--; + // if the index was the last interval of the x-grid, move it back one more + if (idx == xs.size() - 3) + idx--; + return interpolate_lagrangian(xs, ys, idx, x, 3); + default: + fatal_error("Unsupported interpolation"); + } +} + +} // namespace openmc + +#endif diff --git a/include/openmc/lattice.h b/include/openmc/lattice.h index a2411467df..f87d28b21b 100644 --- a/include/openmc/lattice.h +++ b/include/openmc/lattice.h @@ -56,13 +56,14 @@ public: virtual ~Lattice() {} - virtual int32_t const& operator[](array const& i_xyz) = 0; + virtual const int32_t& operator[](const array& i_xyz) = 0; virtual LatticeIter begin(); - LatticeIter end(); + virtual LatticeIter end(); + virtual int32_t& back(); virtual ReverseLatticeIter rbegin(); - ReverseLatticeIter rend(); + virtual ReverseLatticeIter rend(); //! Convert internal universe values from IDs to indices using universe_map. void adjust_indices(); @@ -70,18 +71,19 @@ public: //! Allocate offset table for distribcell. void allocate_offset_table(int n_maps) { - offsets_.resize(n_maps * universes_.size(), C_NONE); + offsets_.resize(n_maps * universes_.size()); + std::fill(offsets_.begin(), offsets_.end(), C_NONE); } //! Populate the distribcell offset tables. - int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map, + int32_t fill_offset_table(int32_t target_univ_id, int map, std::unordered_map& univ_count_memo); //! \brief Check lattice indices. //! \param i_xyz[3] The indices for a lattice tile. //! \return true if the given indices fit within the lattice bounds. False //! otherwise. - virtual bool are_valid_indices(array const& i_xyz) const = 0; + virtual bool are_valid_indices(const array& i_xyz) const = 0; //! \brief Find the next lattice surface crossing //! \param r A 3D Cartesian coordinate. @@ -125,7 +127,7 @@ public: //! \param i_xyz[3] The indices for a lattice tile. //! \return Distribcell offset i.e. the largest instance number for the target //! cell found in the geometry tree under this lattice tile. - virtual int32_t& offset(int map, array const& i_xyz) = 0; + virtual int32_t& offset(int map, const array& i_xyz) = 0; //! \brief Get the distribcell offset for a lattice tile. //! \param The map index for the target cell. @@ -167,12 +169,12 @@ public: LatticeIter& operator++() { - while (indx_ < lat_.universes_.size()) { + while (indx_ < lat_.end().indx_) { ++indx_; if (lat_.is_valid_index(indx_)) return *this; } - indx_ = lat_.universes_.size(); + indx_ = lat_.end().indx_; return *this; } @@ -190,7 +192,7 @@ public: ReverseLatticeIter& operator++() { - while (indx_ > -1) { + while (indx_ > lat_.begin().indx_ - 1) { --indx_; if (lat_.is_valid_index(indx_)) return *this; @@ -206,26 +208,28 @@ class RectLattice : public Lattice { public: explicit RectLattice(pugi::xml_node lat_node); - int32_t const& operator[](array const& i_xyz); + const int32_t& operator[](const array& i_xyz) override; - bool are_valid_indices(array const& i_xyz) const; + bool are_valid_indices(const array& i_xyz) const override; std::pair> distance( - Position r, Direction u, const array& i_xyz) const; + Position r, Direction u, const array& i_xyz) const override; - void get_indices(Position r, Direction u, array& result) const; + void get_indices( + Position r, Direction u, array& result) const override; - int get_flat_index(const array& i_xyz) const; + int get_flat_index(const array& i_xyz) const override; - Position get_local_position(Position r, const array& i_xyz) const; + Position get_local_position( + Position r, const array& i_xyz) const override; - int32_t& offset(int map, array const& i_xyz); + int32_t& offset(int map, const array& i_xyz) override; - int32_t offset(int map, int indx) const; + int32_t offset(int map, int indx) const override; - std::string index_to_string(int indx) const; + std::string index_to_string(int indx) const override; - void to_hdf5_inner(hid_t group_id) const; + void to_hdf5_inner(hid_t group_id) const override; private: array n_cells_; //!< Number of cells along each axis @@ -239,32 +243,40 @@ class HexLattice : public Lattice { public: explicit HexLattice(pugi::xml_node lat_node); - int32_t const& operator[](array const& i_xyz); + const int32_t& operator[](const array& i_xyz) override; - LatticeIter begin(); + LatticeIter begin() override; - ReverseLatticeIter rbegin(); + ReverseLatticeIter rbegin() override; - bool are_valid_indices(array const& i_xyz) const; + LatticeIter end() override; + + int32_t& back() override; + + ReverseLatticeIter rend() override; + + bool are_valid_indices(const array& i_xyz) const override; std::pair> distance( - Position r, Direction u, const array& i_xyz) const; + Position r, Direction u, const array& i_xyz) const override; - void get_indices(Position r, Direction u, array& result) const; + void get_indices( + Position r, Direction u, array& result) const override; - int get_flat_index(const array& i_xyz) const; + int get_flat_index(const array& i_xyz) const override; - Position get_local_position(Position r, const array& i_xyz) const; + Position get_local_position( + Position r, const array& i_xyz) const override; - bool is_valid_index(int indx) const; + bool is_valid_index(int indx) const override; - int32_t& offset(int map, array const& i_xyz); + int32_t& offset(int map, const array& i_xyz) override; - int32_t offset(int map, int indx) const; + int32_t offset(int map, int indx) const override; - std::string index_to_string(int indx) const; + std::string index_to_string(int indx) const override; - void to_hdf5_inner(hid_t group_id) const; + void to_hdf5_inner(hid_t group_id) const override; private: enum class Orientation { diff --git a/include/openmc/material.h b/include/openmc/material.h index 709d205738..e36946c71b 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -4,14 +4,15 @@ #include #include +#include "openmc/span.h" #include "pugixml.hpp" #include "xtensor/xtensor.hpp" -#include #include #include "openmc/bremsstrahlung.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr +#include "openmc/ncrystal_interface.h" #include "openmc/particle.h" #include "openmc/vector.h" @@ -89,9 +90,22 @@ public: void set_densities( const vector& name, const vector& density); + //! Clone the material by deep-copying all members, except for the ID, + // which will get auto-assigned to the next available ID. After creating + // the new material, it is added to openmc::model::materials. + //! \return reference to the cloned material + Material& clone(); + //---------------------------------------------------------------------------- // Accessors + //! Get the atom density in [atom/b-cm] + //! \return Density in [atom/b-cm] + double atom_density(int32_t i, double rho_multiplier = 1.0) const + { + return atom_density_(i) * rho_multiplier; + } + //! Get density in [atom/b-cm] //! \return Density in [atom/b-cm] double density() const { return density_; } @@ -100,6 +114,10 @@ public: //! \return Density in [g/cm^3] double density_gpcc() const { return density_gpcc_; } + //! Get charge density in [e/b-cm] + //! \return Charge density in [e/b-cm] + double charge_density() const { return charge_density_; }; + //! Get name //! \return Material name const std::string& name() const { return name_; } @@ -111,18 +129,21 @@ public: // //! \param[in] density Density value //! \param[in] units Units of density - void set_density(double density, gsl::cstring_span units); + void set_density(double density, const std::string& units); + + //! Set temperature of the material + void set_temperature(double temperature) { temperature_ = temperature; }; //! Get nuclides in material //! \return Indices into the global nuclides vector - gsl::span nuclides() const + span nuclides() const { return {nuclide_.data(), nuclide_.size()}; } //! Get densities of each nuclide in material //! \return Densities in [atom/b-cm] - gsl::span densities() const + span densities() const { return {atom_density_.data(), atom_density_.size()}; } @@ -139,6 +160,7 @@ public: //! Get whether material is fissionable //! \return Whether material is fissionable bool fissionable() const { return fissionable_; } + bool& fissionable() { return fissionable_; } //! Get volume of material //! \return Volume in [cm^3] @@ -148,21 +170,28 @@ public: //! \return Temperature in [K] double temperature() const; + //! Whether or not the material is depletable + bool depletable() const { return depletable_; } + bool& depletable() { return depletable_; } + + //! Get pointer to NCrystal material object + //! \return Pointer to NCrystal material object + const NCrystalMat& ncrystal_mat() const { return ncrystal_mat_; }; + //---------------------------------------------------------------------------- // Data int32_t id_ {C_NONE}; //!< Unique ID std::string name_; //!< Name of material vector nuclide_; //!< Indices in nuclides vector vector element_; //!< Indices in elements vector + NCrystalMat ncrystal_mat_; //!< NCrystal material object xt::xtensor atom_density_; //!< Nuclide atom density in [atom/b-cm] double density_; //!< Total atom density in [atom/b-cm] double density_gpcc_; //!< Total atom density in [g/cm^3] + double charge_density_; //!< Total charge density in [e/b-cm] double volume_ {-1.0}; //!< Volume in [cm^3] - bool fissionable_ { - false}; //!< Does this material contain fissionable nuclides - bool depletable_ {false}; //!< Is the material depletable? - vector p0_; //!< Indicate which nuclides are to be treated with - //!< iso-in-lab scattering + vector p0_; //!< Indicate which nuclides are to be treated with + //!< iso-in-lab scattering // To improve performance of tallying, we store an array (direct address // table) that indicates for each nuclide in data::nuclides the index of the @@ -193,8 +222,11 @@ private: //---------------------------------------------------------------------------- // Private data members - gsl::index index_; + int64_t index_; + bool depletable_ {false}; //!< Is the material depletable? + bool fissionable_ { + false}; //!< Does this material contain fissionable nuclides //! \brief Default temperature for cells containing this material. //! //! A negative value indicates no default temperature was specified. @@ -218,6 +250,10 @@ double density_effect(const vector& f, const vector& e_b_sq, //! Read material data from materials.xml void read_materials_xml(); +//! Read material data XML node +//! \param[in] root node of materials XML element +void read_materials_xml(pugi::xml_node root); + void free_memory_material(); } // namespace openmc diff --git a/include/openmc/mcpl_interface.h b/include/openmc/mcpl_interface.h new file mode 100644 index 0000000000..a76d72e649 --- /dev/null +++ b/include/openmc/mcpl_interface.h @@ -0,0 +1,49 @@ +#ifndef OPENMC_MCPL_INTERFACE_H +#define OPENMC_MCPL_INTERFACE_H + +#include "openmc/particle_data.h" +#include "openmc/span.h" +#include "openmc/vector.h" + +#include + +namespace openmc { + +//============================================================================== +// Functions +//============================================================================== + +//! Get a vector of source sites from an MCPL file +// +//! \param[in] path Path to MCPL file +//! \return Vector of source sites +vector mcpl_source_sites(std::string path); + +//! Write an MCPL source file with stat:sum metadata +//! +//! This function writes particle data to an MCPL file. For MCPL >= 2.1.0, +//! it includes a stat:sum field (key: "openmc_np1") containing the total +//! number of source particles, which is essential for proper file merging +//! and weight normalization when using MCPL files with McStas/McXtrace. +//! +//! The stat:sum field follows the crash-safety pattern: +//! - Initially set to -1 when opening (indicates incomplete file) +//! - Updated with actual particle count before closing +//! +//! \param[in] filename Path to MCPL file +//! \param[in] source_bank Vector of SourceSites to write to file for this +//! MPI rank. +//! \param[in] bank_index Pointer to vector of site index ranges over all +//! MPI ranks. +void write_mcpl_source_point(const char* filename, span source_bank, + const vector& bank_index); + +//! Check if MCPL functionality is available +bool is_mcpl_interface_available(); + +//! Initialize the MCPL interface +void initialize_mcpl_interface_if_needed(); + +} // namespace openmc + +#endif // OPENMC_MCPL_INTERFACE_H diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index 21c58862d9..a56705c9ec 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -10,19 +10,23 @@ #include "pugixml.hpp" #include "xtensor/xtensor.hpp" +#include "openmc/bounding_box.h" +#include "openmc/error.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/particle.h" #include "openmc/position.h" +#include "openmc/span.h" #include "openmc/vector.h" +#include "openmc/xml_interface.h" -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED #include "moab/AdaptiveKDTree.hpp" #include "moab/Core.hpp" #include "moab/GeomUtil.hpp" #include "moab/Matrix3.hpp" #endif -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED #include "libmesh/bounding_box.h" #include "libmesh/dof_map.h" #include "libmesh/elem.h" @@ -36,6 +40,12 @@ namespace openmc { +//============================================================================== +// Constants +//============================================================================== + +enum class ElementType { UNSUPPORTED = -1, LINEAR_TET, LINEAR_HEX }; + //============================================================================== // Global variables //============================================================================== @@ -51,14 +61,72 @@ extern vector> meshes; } // namespace model -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED namespace settings { -// used when creating new libMesh::Mesh instances +// used when creating new libMesh::MeshBase instances extern unique_ptr libmesh_init; extern const libMesh::Parallel::Communicator* libmesh_comm; } // namespace settings #endif +//============================================================================== +//! Helper class for keeping track of volume for each material in a mesh element +// +//! This class is used in Mesh::material_volumes to manage for each mesh element +//! a list of (material, volume) pairs. The openmc.lib.Mesh class allocates two +//! 2D arrays, one for materials and one for volumes. Because we don't know a +//! priori how many materials there are in each element but at the same time we +//! can't dynamically size an array at runtime for performance reasons, we +//! assume a maximum number of materials per element. For each element, the set +//! of material indices are stored in a hash table with twice as many slots as +//! the assumed maximum number of materials per element. Collision resolution is +//! handled by open addressing with linear probing. +//============================================================================== + +namespace detail { + +class MaterialVolumes { +public: + MaterialVolumes(int32_t* mats, double* vols, int table_size) + : materials_(mats), volumes_(vols), table_size_(table_size) + {} + + //! Add volume for a given material in a mesh element + // + //! \param[in] index_elem Index of the mesh element + //! \param[in] index_material Index of the material within the model + //! \param[in] volume Volume to add + void add_volume(int index_elem, int index_material, double volume); + void add_volume_unsafe(int index_elem, int index_material, double volume); + + // Accessors + int32_t& materials(int i, int j) { return materials_[i * table_size_ + j]; } + const int32_t& materials(int i, int j) const + { + return materials_[i * table_size_ + j]; + } + + double& volumes(int i, int j) { return volumes_[i * table_size_ + j]; } + const double& volumes(int i, int j) const + { + return volumes_[i * table_size_ + j]; + } + + bool table_full() const { return table_full_; } + +private: + int32_t* materials_; //!< material index (bins, table_size) + double* volumes_; //!< volume in [cm^3] (bins, table_size) + int table_size_; //!< Size of hash table for each mesh element + bool table_full_ {false}; //!< Whether the hash table is full +}; + +} // namespace detail + +//============================================================================== +//! Base mesh class +//============================================================================== + class Mesh { public: // Constructors and destructor @@ -67,6 +135,18 @@ public: virtual ~Mesh() = default; // Methods + //! Perform any preparation needed to support point location within the mesh + virtual void prepare_for_point_location() {}; + + //! Return a position in the local coordinates of the mesh + virtual Position local_coords(const Position& r) const { return r; }; + + //! Sample a position within a mesh element + // + //! \param[in] bin Bin value of the mesh element sampled + //! \param[inout] seed Seed to use for random sampling + //! \return sampled position within mesh element + virtual Position sample_element(int32_t bin, uint64_t* seed) const = 0; //! Determine which bins were crossed by a particle // @@ -99,13 +179,20 @@ public: //! Get the number of mesh cell surfaces. virtual int n_surface_bins() const = 0; + int32_t id() const { return id_; } + + const std::string& name() const { return name_; } + //! Set the mesh ID void set_id(int32_t id = -1); + //! Write the mesh data to an HDF5 group + void to_hdf5(hid_t group) const; + //! Write mesh data to an HDF5 group // //! \param[in] group HDF5 group - virtual void to_hdf5(hid_t group) const = 0; + virtual void to_hdf5_inner(hid_t group) const = 0; //! Find the mesh lines that intersect an axis-aligned slice plot // @@ -123,12 +210,48 @@ public: //! \param[in] bin Mesh bin to generate a label for virtual std::string bin_label(int bin) const = 0; - //! Return the mesh type + //! Get the volume of a mesh bin + // + //! \param[in] bin Bin to return the volume for + //! \return Volume of the bin + virtual double volume(int bin) const = 0; + + //! Volumes of all elements in the mesh in bin ordering + vector volumes() const; + virtual std::string get_mesh_type() const = 0; + //! Determine volume of materials within each mesh element + // + //! \param[in] nx Number of samples in x direction + //! \param[in] ny Number of samples in y direction + //! \param[in] nz Number of samples in z direction + //! \param[in] max_materials Maximum number of materials in a single mesh + //! element + //! \param[inout] materials Array storing material indices + //! \param[inout] volumes Array storing volumes + void material_volumes(int nx, int ny, int nz, int max_materials, + int32_t* materials, double* volumes) const; + + //! Determine bounding box of mesh + // + //! \return Bounding box of mesh + BoundingBox bounding_box() const + { + auto ll = this->lower_left(); + auto ur = this->upper_right(); + return {ll.x, ur.x, ll.y, ur.y, ll.z, ur.z}; + } + + virtual Position lower_left() const = 0; + virtual Position upper_right() const = 0; + // Data members - int id_ {-1}; //!< User-specified ID - int n_dimension_; //!< Number of dimensions + xt::xtensor lower_left_; //!< Lower-left coordinates of mesh + xt::xtensor upper_right_; //!< Upper-right coordinates of mesh + int id_ {-1}; //!< Mesh ID + std::string name_; //!< User-specified name + int n_dimension_ {-1}; //!< Number of dimensions }; class StructuredMesh : public Mesh { @@ -153,6 +276,13 @@ public: } }; + Position sample_element(int32_t bin, uint64_t* seed) const override + { + return sample_element(get_indices_from_bin(bin), seed); + }; + + virtual Position sample_element(const MeshIndex& ijk, uint64_t* seed) const; + int get_bin(Position r) const override; int n_bins() const override; @@ -208,6 +338,30 @@ public: //! \param[in] i Direction index virtual int get_index_in_direction(double r, int i) const = 0; + //! Get the coordinate for the mesh grid boundary in the positive direction + //! + //! \param[in] ijk Array of mesh indices + //! \param[in] i Direction index + virtual double positive_grid_boundary(const MeshIndex& ijk, int i) const + { + auto msg = + fmt::format("Attempting to call positive_grid_boundary on a {} mesh.", + get_mesh_type()); + fatal_error(msg); + }; + + //! Get the coordinate for the mesh grid boundary in the negative direction + //! + //! \param[in] ijk Array of mesh indices + //! \param[in] i Direction index + virtual double negative_grid_boundary(const MeshIndex& ijk, int i) const + { + auto msg = + fmt::format("Attempting to call negative_grid_boundary on a {} mesh.", + get_mesh_type()); + fatal_error(msg); + }; + //! Get the closest distance from the coordinate r to the grid surface //! in i direction that bounds mesh cell ijk and that is larger than l //! The coordinate r does not have to be inside the mesh cell ijk. In @@ -230,14 +384,55 @@ public: //! Get shape as xt::xtensor xt::xtensor get_x_shape() const; + double volume(int bin) const override + { + return this->volume(get_indices_from_bin(bin)); + } + + Position lower_left() const override + { + int n = lower_left_.size(); + Position ll {lower_left_[0], 0.0, 0.0}; + ll.y = (n >= 2) ? lower_left_[1] : -INFTY; + ll.z = (n == 3) ? lower_left_[2] : -INFTY; + return ll; + }; + + Position upper_right() const override + { + int n = upper_right_.size(); + Position ur {upper_right_[0], 0.0, 0.0}; + ur.y = (n >= 2) ? upper_right_[1] : INFTY; + ur.z = (n == 3) ? upper_right_[2] : INFTY; + return ur; + }; + + //! Get the volume of a specified element + //! \param[in] ijk Mesh index to return the volume for + //! \return Volume of the bin + virtual double volume(const MeshIndex& ijk) const = 0; + // Data members - xt::xtensor lower_left_; //!< Lower-left coordinates of mesh - xt::xtensor upper_right_; //!< Upper-right coordinates of mesh std::array shape_; //!< Number of mesh elements in each dimension protected: }; +class PeriodicStructuredMesh : public StructuredMesh { + +public: + PeriodicStructuredMesh() = default; + PeriodicStructuredMesh(pugi::xml_node node) : StructuredMesh {node} {}; + + Position local_coords(const Position& r) const override + { + return r - origin_; + }; + + // Data members + Position origin_ {0.0, 0.0, 0.0}; //!< Origin of the mesh +}; + //============================================================================== //! Tessellation of n-dimensional Euclidean space by congruent squares or cubes //============================================================================== @@ -261,20 +456,19 @@ public: std::pair, vector> plot( Position plot_ll, Position plot_ur) const override; - void to_hdf5(hid_t group) const override; + void to_hdf5_inner(hid_t group) const override; - // New methods //! Get the coordinate for the mesh grid boundary in the positive direction //! //! \param[in] ijk Array of mesh indices //! \param[in] i Direction index - double positive_grid_boundary(const MeshIndex& ijk, int i) const; + double positive_grid_boundary(const MeshIndex& ijk, int i) const override; //! Get the coordinate for the mesh grid boundary in the negative direction //! //! \param[in] ijk Array of mesh indices //! \param[in] i Direction index - double negative_grid_boundary(const MeshIndex& ijk, int i) const; + double negative_grid_boundary(const MeshIndex& ijk, int i) const override; //! Count number of bank sites in each mesh bin / energy bin // @@ -284,8 +478,12 @@ public: xt::xtensor count_sites( const SourceSite* bank, int64_t length, bool* outside) const; + //! Return the volume for a given mesh index + double volume(const MeshIndex& ijk) const override; + // Data members double volume_frac_; //!< Volume fraction of each mesh element + double element_volume_; //!< Volume of each mesh element xt::xtensor width_; //!< Width of each mesh element }; @@ -308,27 +506,30 @@ public: std::pair, vector> plot( Position plot_ll, Position plot_ur) const override; - void to_hdf5(hid_t group) const override; + void to_hdf5_inner(hid_t group) const override; - // New methods //! Get the coordinate for the mesh grid boundary in the positive direction //! //! \param[in] ijk Array of mesh indices //! \param[in] i Direction index - double positive_grid_boundary(const MeshIndex& ijk, int i) const; + double positive_grid_boundary(const MeshIndex& ijk, int i) const override; //! Get the coordinate for the mesh grid boundary in the negative direction //! //! \param[in] ijk Array of mesh indices //! \param[in] i Direction index - double negative_grid_boundary(const MeshIndex& ijk, int i) const; + double negative_grid_boundary(const MeshIndex& ijk, int i) const override; - array, 3> grid_; + //! Return the volume for a given mesh index + double volume(const MeshIndex& ijk) const override; int set_grid(); + + // Data members + array, 3> grid_; }; -class CylindricalMesh : public StructuredMesh { +class CylindricalMesh : public PeriodicStructuredMesh { public: // Constructors CylindricalMesh() = default; @@ -343,18 +544,28 @@ public: static const std::string mesh_type; + Position sample_element(const MeshIndex& ijk, uint64_t* seed) const override; + MeshDistance distance_to_grid_boundary(const MeshIndex& ijk, int i, const Position& r0, const Direction& u, double l) const override; std::pair, vector> plot( Position plot_ll, Position plot_ur) const override; - void to_hdf5(hid_t group) const override; + void to_hdf5_inner(hid_t group) const override; - array, 3> grid_; + double volume(const MeshIndex& ijk) const override; + + // grid accessors + double r(int i) const { return grid_[0][i]; } + double phi(int i) const { return grid_[1][i]; } + double z(int i) const { return grid_[2][i]; } int set_grid(); + // Data members + array, 3> grid_; + private: double find_r_crossing( const Position& r, const Direction& u, double l, int shell) const; @@ -365,7 +576,7 @@ private: bool full_phi_ {false}; - constexpr inline int sanitize_angular_index(int idx, bool full, int N) const + inline int sanitize_angular_index(int idx, bool full, int N) const { if ((idx > 0) and (idx <= N)) { return idx; @@ -382,7 +593,7 @@ private: } }; -class SphericalMesh : public StructuredMesh { +class SphericalMesh : public PeriodicStructuredMesh { public: // Constructors SphericalMesh() = default; @@ -397,18 +608,25 @@ public: static const std::string mesh_type; + Position sample_element(const MeshIndex& ijk, uint64_t* seed) const override; + MeshDistance distance_to_grid_boundary(const MeshIndex& ijk, int i, const Position& r0, const Direction& u, double l) const override; std::pair, vector> plot( Position plot_ll, Position plot_ur) const override; - void to_hdf5(hid_t group) const override; + void to_hdf5_inner(hid_t group) const override; - array, 3> grid_; + double r(int i) const { return grid_[0][i]; } + double theta(int i) const { return grid_[1][i]; } + double phi(int i) const { return grid_[2][i]; } int set_grid(); + // Data members + array, 3> grid_; + private: double find_r_crossing( const Position& r, const Direction& u, double l, int shell) const; @@ -420,7 +638,7 @@ private: bool full_theta_ {false}; bool full_phi_ {false}; - constexpr inline int sanitize_angular_index(int idx, bool full, int N) const + inline int sanitize_angular_index(int idx, bool full, int N) const { if ((idx > 0) and (idx <= N)) { return idx; @@ -431,6 +649,8 @@ private: } } + double volume(const MeshIndex& ijk) const override; + inline int sanitize_theta(int idx) const { return sanitize_angular_index(idx, full_theta_, shape_[1]); @@ -446,18 +666,18 @@ class UnstructuredMesh : public Mesh { public: // Constructors - UnstructuredMesh() {}; + UnstructuredMesh() { n_dimension_ = 3; }; UnstructuredMesh(pugi::xml_node node); - UnstructuredMesh(const std::string& filename); static const std::string mesh_type; virtual std::string get_mesh_type() const override; // Overridden Methods + void surface_bins_crossed(Position r0, Position r1, const Direction& u, vector& bins) const override; - void to_hdf5(hid_t group) const override; + void to_hdf5_inner(hid_t group) const override; std::string bin_label(int bin) const override; @@ -485,11 +705,22 @@ public: //! \return The centroid of the bin virtual Position centroid(int bin) const = 0; - //! Get the volume of a mesh bin + //! Get the number of vertices in the mesh // - //! \param[in] bin Bin to return the volume for - //! \return Volume of the bin - virtual double volume(int bin) const = 0; + //! \return Number of vertices + virtual int n_vertices() const = 0; + + //! Retrieve a vertex of the mesh + // + //! \param[in] vertex ID + //! \return vertex coordinates + virtual Position vertex(int id) const = 0; + + //! Retrieve connectivity of a mesh element + // + //! \param[in] element ID + //! \return element connectivity as IDs of the vertices + virtual std::vector connectivity(int id) const = 0; //! Get the library used for this unstructured mesh virtual std::string library() const = 0; @@ -499,14 +730,36 @@ public: true}; //!< Write tallies onto the unstructured mesh at the end of a run std::string filename_; //!< Path to unstructured mesh file + ElementType element_type(int bin) const; + + Position lower_left() const override + { + return {lower_left_[0], lower_left_[1], lower_left_[2]}; + } + Position upper_right() const override + { + return {upper_right_[0], upper_right_[1], upper_right_[2]}; + } + protected: //! Set the length multiplier to apply to each point in the mesh void set_length_multiplier(const double length_multiplier); + //! Sample barycentric coordinates given a seed and the vertex positions and + //! return the sampled position + // + //! \param[in] coords Coordinates of the tetrahedron + //! \param[in] seed Random number generation seed + //! \return Sampled position within the tetrahedron + Position sample_tet(std::array coords, uint64_t* seed) const; + // Data members double length_multiplier_ { - 1.0}; //!< Constant multiplication factor to apply to mesh coordinates - bool specified_length_multiplier_ {false}; + -1.0}; //!< Multiplicative factor applied to mesh coordinates + std::string options_; //!< Options for search data structures + + //! Determine lower-left and upper-right bounds of mesh + void determine_bounds(); private: //! Setup method for the mesh. Builds data structures, @@ -514,7 +767,7 @@ private: virtual void initialize() = 0; }; -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED class MOABMesh : public UnstructuredMesh { public: @@ -528,6 +781,11 @@ public: // Overridden Methods + //! Perform any preparation needed to support use in mesh filters + void prepare_for_point_location() override; + + Position sample_element(int32_t bin, uint64_t* seed) const override; + void bins_crossed(Position r0, Position r1, const Direction& u, vector& bins, vector& lengths) const override; @@ -557,6 +815,16 @@ public: Position centroid(int bin) const override; + int n_vertices() const override; + + Position vertex(int id) const override; + + std::vector connectivity(int id) const override; + + //! Get the volume of a mesh bin + // + //! \param[in] bin Bin to return the volume for + //! \return Volume of the bin double volume(int bin) const override; private: @@ -621,6 +889,9 @@ private: //! \return MOAB EntityHandle of tet moab::EntityHandle get_ent_handle_from_bin(int bin) const; + //! Get a vertex index into the global range from a handle + int get_vert_idx_from_handle(moab::EntityHandle vert) const; + //! Get the bin for a given mesh cell index // //! \param[in] idx Index of the mesh cell. @@ -654,7 +925,8 @@ private: std::pair get_score_tags(std::string score) const; // Data members - moab::Range ehs_; //!< Range of tetrahedra EntityHandle's in the mesh + moab::Range ehs_; //!< Range of tetrahedra EntityHandle's in the mesh + moab::Range verts_; //!< Range of vertex EntityHandle's in the mesh moab::EntityHandle tetset_; //!< EntitySet containing all tetrahedra moab::EntityHandle kdtree_root_; //!< Root of the MOAB KDTree std::shared_ptr mbi_; //!< MOAB instance @@ -665,13 +937,14 @@ private: #endif -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED class LibMesh : public UnstructuredMesh { public: // Constructors LibMesh(pugi::xml_node node); LibMesh(const std::string& filename, double length_multiplier = 1.0); + LibMesh(libMesh::MeshBase& input_mesh, double length_multiplier = 1.0); static const std::string mesh_lib_type; @@ -679,6 +952,8 @@ public: void bins_crossed(Position r0, Position r1, const Direction& u, vector& bins, vector& lengths) const override; + Position sample_element(int32_t bin, uint64_t* seed) const override; + int get_bin(Position r) const override; int n_bins() const override; @@ -701,25 +976,45 @@ public: Position centroid(int bin) const override; + int n_vertices() const override; + + Position vertex(int id) const override; + + std::vector connectivity(int id) const override; + + //! Get the volume of a mesh bin + // + //! \param[in] bin Bin to return the volume for + //! \return Volume of the bin double volume(int bin) const override; -private: - void initialize() override; + libMesh::MeshBase* mesh_ptr() const { return m_; }; +protected: // Methods //! Translate a bin value to an element reference - const libMesh::Elem& get_element_from_bin(int bin) const; + virtual const libMesh::Elem& get_element_from_bin(int bin) const; //! Translate an element pointer to a bin index - int get_bin_from_element(const libMesh::Elem* elem) const; + virtual int get_bin_from_element(const libMesh::Elem* elem) const; + + libMesh::MeshBase* m_; //!< pointer to libMesh MeshBase instance, always set + //!< during intialization +private: + void initialize() override; + void set_mesh_pointer_from_filename(const std::string& filename); + void build_eqn_sys(); // Data members - unique_ptr m_; //!< pointer to the libMesh mesh instance + unique_ptr unique_m_ = + nullptr; //!< pointer to the libMesh MeshBase instance, only used if mesh is + //!< created inside OpenMC vector> pl_; //!< per-thread point locators unique_ptr - equation_systems_; //!< pointer to the equation systems of the mesh + equation_systems_; //!< pointer to the libMesh EquationSystems + //!< instance std::string eq_system_name_; //!< name of the equation system holding OpenMC results std::unordered_map @@ -730,6 +1025,39 @@ private: first_element_id_; //!< id of the first element in the mesh }; +class AdaptiveLibMesh : public LibMesh { +public: + // Constructor + AdaptiveLibMesh( + libMesh::MeshBase& input_mesh, double length_multiplier = 1.0); + + // Overridden methods + int n_bins() const override; + + void add_score(const std::string& var_name) override; + + void set_score_data(const std::string& var_name, const vector& values, + const vector& std_dev) override; + + void write(const std::string& filename) const override; + +protected: + // Overridden methods + int get_bin_from_element(const libMesh::Elem* elem) const override; + + const libMesh::Elem& get_element_from_bin(int bin) const override; + +private: + // Data members + const libMesh::dof_id_type num_active_; //!< cached number of active elements + + std::vector + bin_to_elem_map_; //!< mapping bin indices to dof indices for active + //!< elements + std::vector elem_to_bin_map_; //!< mapping dof indices to bin indices for + //!< active elements +}; + #endif //============================================================================== diff --git a/include/openmc/message_passing.h b/include/openmc/message_passing.h index b02d2938fc..a1641a9069 100644 --- a/include/openmc/message_passing.h +++ b/include/openmc/message_passing.h @@ -1,10 +1,14 @@ #ifndef OPENMC_MESSAGE_PASSING_H #define OPENMC_MESSAGE_PASSING_H +#include + #ifdef OPENMC_MPI #include #endif +#include "openmc/vector.h" + namespace openmc { namespace mpi { @@ -17,6 +21,18 @@ extern MPI_Datatype source_site; extern MPI_Comm intracomm; #endif +// Calculates global indices of the bank particles +// across all ranks using a parallel scan. This is used to write +// the surface source file in parallel runs. It will probably +// be used in the future for other types of bank like particles +// in flight used to kick off transient simulations. +// +// More abstractly, this just takes a number from each MPI rank, +// and returns a vector which is the exclusive parallel scan across +// all of those numbers, having a length of the number of MPI ranks +// plus one. +vector calculate_parallel_index_vector(int64_t size); + } // namespace mpi } // namespace openmc diff --git a/include/openmc/mgxs.h b/include/openmc/mgxs.h index 2da3c83639..9b1602f299 100644 --- a/include/openmc/mgxs.h +++ b/include/openmc/mgxs.h @@ -16,20 +16,6 @@ namespace openmc { -//============================================================================== -// Cache contains the cached data for an MGXS object -//============================================================================== - -struct CacheData { - double sqrtkT; // last temperature corresponding to t - int t; // temperature index - int a; // angle index - // last angle that corresponds to a - double u; - double v; - double w; -}; - //============================================================================== // MGXS contains the mgxs data for a nuclide/material //============================================================================== @@ -43,7 +29,6 @@ private: int num_delayed_groups; // number of delayed neutron groups vector xs; // Cross section data // MGXS Incoming Flux Angular grid information - bool is_isotropic; // used to skip search for angle indices if isotropic int n_pol; int n_azi; vector polar; @@ -96,12 +81,15 @@ private: bool equiv(const Mgxs& that); public: - std::string name; // name of dataset, e.g., UO2 - double awr; // atomic weight ratio - bool fissionable; // Is this fissionable - vector cache; // index and data cache + std::string name; // name of dataset, e.g., UO2 + double awr; // atomic weight ratio + bool fissionable; // Is this fissionable + bool is_isotropic { + true}; // used to skip search for angle indices if isotropic + bool exists_in_model {true}; // Is this present in model Mgxs() = default; + Mgxs(bool exists) : exists_in_model(exists) {} //! \brief Constructor that loads the Mgxs object from the HDF5 file //! @@ -135,13 +123,15 @@ public: //! @param mu Cosine of the change-in-angle, for scattering quantities; //! use nullptr if irrelevant. //! @param dg delayed group index; use nullptr if irrelevant. + //! @param t Temperature index. + //! @param a Angle index. //! @return Requested cross section value. - double get_xs( - MgxsType xstype, int gin, const int* gout, const double* mu, const int* dg); + double get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, + const int* dg, int t, int a); - inline double get_xs(MgxsType xstype, int gin) + inline double get_xs(MgxsType xstype, int gin, int t, int a) { - return get_xs(xstype, gin, nullptr, nullptr, nullptr); + return get_xs(xstype, gin, nullptr, nullptr, nullptr, t, a); } //! \brief Samples the fission neutron energy and if prompt or delayed. @@ -150,7 +140,10 @@ public: //! @param dg Sampled delayed group index. //! @param gout Sampled outgoing energy group. //! @param seed Pseudorandom seed pointer - void sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed); + //! @param t Temperature index. + //! @param a Angle index. + void sample_fission_energy( + int gin, int& dg, int& gout, uint64_t* seed, int t, int a); //! \brief Samples the outgoing energy and angle from a scatter event. //! @@ -159,23 +152,37 @@ public: //! @param mu Sampled cosine of the change-in-angle. //! @param wgt Weight of the particle to be adjusted. //! @param seed Pseudorandom seed pointer. + //! @param t Temperature index. + //! @param a Angle index. void sample_scatter( - int gin, int& gout, double& mu, double& wgt, uint64_t* seed); + int gin, int& gout, double& mu, double& wgt, uint64_t* seed, int t, int a); //! \brief Calculates cross section quantities needed for tracking. //! //! @param p The particle whose attributes set which MGXS to get. void calculate_xs(Particle& p); - //! \brief Sets the temperature index in cache given a temperature + //! \brief Sets the temperature index in the particle's cache. + //! + //! @param p Particle. + void set_temperature_index(Particle& p); + + //! \brief Gets the temperature index given a temperature. //! //! @param sqrtkT Temperature of the material. - void set_temperature_index(double sqrtkT); + //! @return The temperature index corresponding to sqrtkT. + int get_temperature_index(double sqrtkT) const; - //! \brief Sets the angle index in cache given a direction + //! \brief Sets the angle index in the particle's cache. + //! + //! @param p Particle. + void set_angle_index(Particle& p); + + //! \brief Gets the angle index given a direction. //! //! @param u Incoming particle direction. - void set_angle_index(Direction u); + //! @return The angle index corresponding to u. + int get_angle_index(const Direction& u) const; //! \brief Provide const access to list of XsData held by this const vector& get_xsdata() const { return xs; } diff --git a/include/openmc/mgxs_interface.h b/include/openmc/mgxs_interface.h index 8bcdf6dc60..da074f825e 100644 --- a/include/openmc/mgxs_interface.h +++ b/include/openmc/mgxs_interface.h @@ -46,6 +46,9 @@ public: // Get the kT values which are used in the OpenMC model vector> get_mat_kTs(); + // Get the group index corresponding to a continuous energy + int get_group_index(double E); + int num_energy_groups_; int num_delayed_groups_; vector xs_names_; // available names in HDF5 file diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h new file mode 100644 index 0000000000..e9bd8ae79b --- /dev/null +++ b/include/openmc/ncrystal_interface.h @@ -0,0 +1,74 @@ +#ifndef OPENMC_NCRYSTAL_INTERFACE_H +#define OPENMC_NCRYSTAL_INTERFACE_H + +#include "openmc/ncrystal_load.h" +#include "openmc/particle.h" + +#include // for uint64_t +#include // for numeric_limits +#include + +namespace openmc { + +//============================================================================== +// Constants +//============================================================================== + +//! Energy in [eV] to switch between NCrystal and ENDF +constexpr double NCRYSTAL_MAX_ENERGY {5.0}; + +//============================================================================== +// Wrapper class for an NCrystal material +//============================================================================== + +class NCrystalMat { +public: + //---------------------------------------------------------------------------- + // Constructors + NCrystalMat() = default; // empty object + explicit NCrystalMat(const std::string& cfg); + + //---------------------------------------------------------------------------- + // Methods + + //! Return configuration string: + const std::string& cfg() const { return cfg_; } + + //! Get cross section from NCrystal material + // + //! \param[in] p Particle object + //! \return Cross section in [b] + double xs(const Particle& p) const; + + // Process scattering event + // + //! \param[in] p Particle object + void scatter(Particle& p) const; + + //! Whether the object holds a valid NCrystal material + operator bool() const { return !cfg_.empty(); } + + NCrystalMat clone() const + { + NCrystalMat c; + c.cfg_ = cfg_; + c.proc_ = proc_.clone(); + return c; + } + +private: + //---------------------------------------------------------------------------- + // Data members (only present when compiling with NCrystal support) + std::string cfg_; //!< NCrystal configuration string + NCrystalScatProc proc_; //!< NCrystal scatter process +}; + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro); + +} // namespace openmc + +#endif // OPENMC_NCRYSTAL_INTERFACE_H diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h new file mode 100644 index 0000000000..d85f240906 --- /dev/null +++ b/include/openmc/ncrystal_load.h @@ -0,0 +1,127 @@ +//! \file ncrystal_load.h +//! \brief Helper class taking care of loading NCrystal at runtime. + +#ifndef OPENMC_NCRYSTAL_LOAD_H +#define OPENMC_NCRYSTAL_LOAD_H + +#include // for swap +#include // for function +#include // for shared_ptr +#include // for move + +namespace NCrystalVirtualAPI { + +// NOTICE: Do NOT make ANY changes in the NCrystalVirtualAPI::VirtAPI_Type1_v1 +// class, it is required to stay exactly constant over time and compatible with +// the same definition used to compile the NCrystal library! But changes to +// white space, comments, and formatting is of course allowed. This API was +// introduced in NCrystal 4.1.0. + +//! Abstract base class for NCrystal interface which must be declared exactly as +// it is in NCrystal itself. + +class VirtAPI_Type1_v1 { +public: + // Note: neutron must be an array of length 4 with values {ekin,ux,uy,uz} + class ScatterProcess; + virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; + virtual const ScatterProcess* cloneScatter(const ScatterProcess*) const = 0; + virtual void deallocateScatter(const ScatterProcess*) const = 0; + virtual double crossSectionUncached( + const ScatterProcess&, const double* neutron) const = 0; + virtual void sampleScatterUncached(const ScatterProcess&, + std::function& rng, double* neutron) const = 0; + // Plumbing: + static constexpr unsigned interface_id = 1001; + virtual ~VirtAPI_Type1_v1() = default; + VirtAPI_Type1_v1() = default; + VirtAPI_Type1_v1(const VirtAPI_Type1_v1&) = delete; + VirtAPI_Type1_v1& operator=(const VirtAPI_Type1_v1&) = delete; + VirtAPI_Type1_v1(VirtAPI_Type1_v1&&) = delete; + VirtAPI_Type1_v1& operator=(VirtAPI_Type1_v1&&) = delete; +}; + +} // namespace NCrystalVirtualAPI + +namespace openmc { + +using NCrystalAPI = NCrystalVirtualAPI::VirtAPI_Type1_v1; + +//! Function which locates and loads NCrystal at runtime using the virtual API +std::shared_ptr load_ncrystal_api(); + +//! Class encapsulating exactly the parts of NCrystal needed by OpenMC + +class NCrystalScatProc final { +public: + //! Empty constructor which does not load NCrystal + NCrystalScatProc() {} + + //! Load NCrystal and instantiate a scattering process + //! \param cfgstr NCrystal cfg-string defining the material. + NCrystalScatProc(const char* cfgstr) + : api_(load_ncrystal_api()), p_(api_->createScatter(cfgstr)) + {} + + // Note: Neutron state array is {ekin,ux,uy,uz} + + //! Returns total scattering cross section in units of barns per atom. + //! \param neutron_state array {ekin,ux,uy,uz} with ekin (eV) and direction. + double cross_section(const double* neutron_state) const + { + return api_->crossSectionUncached(*p_, neutron_state); + } + + //! Returns total scattering cross section in units of barns per atom. + //! \param rng function returning random numbers in the unit interval + //! \param neutron_state array {ekin,ux,uy,uz} with ekin (eV) and direction. + void scatter(std::function& rng, double* neutron_state) const + { + api_->sampleScatterUncached(*p_, rng, neutron_state); + } + + //! Clones the object which is otherwise move-only + NCrystalScatProc clone() const + { + NCrystalScatProc c; + if (p_) { + c.api_ = api_; + c.p_ = api_->cloneScatter(p_); + } + return c; + } + + // Plumbing (move-only semantics, but supports explicit clone): + NCrystalScatProc(const NCrystalScatProc&) = delete; + NCrystalScatProc& operator=(const NCrystalScatProc&) = delete; + + NCrystalScatProc(NCrystalScatProc&& o) : api_(std::move(o.api_)), p_(nullptr) + { + std::swap(p_, o.p_); + } + + NCrystalScatProc& operator=(NCrystalScatProc&& o) + { + if (p_) { + api_->deallocateScatter(p_); + p_ = nullptr; + } + std::swap(api_, o.api_); + std::swap(p_, o.p_); + return *this; + } + + ~NCrystalScatProc() + { + if (p_) + api_->deallocateScatter(p_); + } + +private: + std::shared_ptr api_; + const NCrystalAPI::ScatterProcess* p_ = nullptr; +}; + +} // namespace openmc + +#endif diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h index b05c76bbb8..60b88a153b 100644 --- a/include/openmc/nuclide.h +++ b/include/openmc/nuclide.h @@ -7,7 +7,6 @@ #include #include // for pair -#include #include #include "openmc/array.h" @@ -17,6 +16,7 @@ #include "openmc/particle.h" #include "openmc/reaction.h" #include "openmc/reaction_product.h" +#include "openmc/span.h" #include "openmc/urr.h" #include "openmc/vector.h" #include "openmc/wmp.h" @@ -29,6 +29,7 @@ namespace openmc { class Nuclide { public: + //============================================================================ // Types, aliases using EmissionMode = ReactionProduct::EmissionMode; struct EnergyGrid { @@ -36,18 +37,32 @@ public: vector energy; }; + //============================================================================ // Constructors/destructors Nuclide(hid_t group, const vector& temperature); ~Nuclide(); + //============================================================================ + // Methods + //! Initialize logarithmic grid for energy searches void init_grid(); + //! Calculate microscopic cross sections + // + //! \param[in] i_sab Index in data::thermal_scatt + //! \param[in] i_log_union Log-grid search index + //! \param[in] sab_frac S(a,b) table fraction + //! \param[in,out] p Particle object void calculate_xs(int i_sab, int i_log_union, double sab_frac, Particle& p); + //! Calculate thermal scattering cross section + // + //! \param[in] i_sab Index in data::thermal_scatt + //! \param[in] sab_frac S(a,b) table fraction + //! \param[in,out] p Particle object void calculate_sab_xs(int i_sab, double sab_frac, Particle& p); - // Methods double nu(double E, EmissionMode mode, int group = 0) const; void calculate_elastic_xs(Particle& p) const; @@ -66,16 +81,17 @@ public: //! \param[in] energy Energy group boundaries in [eV] //! \param[in] flux Flux in each energy group (not normalized per eV) //! \return Reaction rate - double collapse_rate(int MT, double temperature, - gsl::span energy, gsl::span flux) const; + double collapse_rate(int MT, double temperature, span energy, + span flux) const; + //============================================================================ // Data members std::string name_; //!< Name of nuclide, e.g. "U235" int Z_; //!< Atomic number int A_; //!< Mass number int metastable_; //!< Metastable state double awr_; //!< Atomic weight ratio - gsl::index index_; //!< Index in the nuclides array + int64_t index_; //!< Index in the nuclides array // Temperature dependent cross section data vector kTs_; //!< temperatures in eV (k*T) @@ -122,7 +138,7 @@ private: // //! \param[in] T Temperature in [K] //! \return Temperature index and interpolation factor - std::pair find_temperature(double T) const; + std::pair find_temperature(double T) const; static int XS_TOTAL; static int XS_ABSORPTION; @@ -148,8 +164,8 @@ namespace data { // Minimum/maximum transport energy for each particle type. Order corresponds to // that of the ParticleType enum -extern array energy_min; -extern array energy_max; +extern array energy_min; +extern array energy_max; //! Minimum temperature in [K] that nuclide data is available at extern double temperature_min; diff --git a/include/openmc/openmp_interface.h b/include/openmc/openmp_interface.h index dac03ac591..30bd6f008d 100644 --- a/include/openmc/openmp_interface.h +++ b/include/openmc/openmp_interface.h @@ -7,6 +7,27 @@ namespace openmc { +//============================================================================== +//! Accessor functions related to number of threads and thread number +//============================================================================== +inline int num_threads() +{ +#ifdef _OPENMP + return omp_get_max_threads(); +#else + return 1; +#endif +} + +inline int thread_num() +{ +#ifdef _OPENMP + return omp_get_thread_num(); +#else + return 0; +#endif +} + //============================================================================== //! An object used to prevent concurrent access to a piece of data. // @@ -15,13 +36,15 @@ namespace openmc { class OpenMPMutex { public: - OpenMPMutex() + void init() { #ifdef _OPENMP omp_init_lock(&mutex_); #endif } + OpenMPMutex() { init(); } + ~OpenMPMutex() { #ifdef _OPENMP @@ -29,11 +52,22 @@ public: #endif } - // Mutexes cannot be copied. We need to explicitly delete the copy - // constructor and copy assignment operator to ensure the compiler doesn't - // "help" us by implicitly trying to copy the underlying mutexes. - OpenMPMutex(const OpenMPMutex&) = delete; - OpenMPMutex& operator=(const OpenMPMutex&) = delete; + // omp_lock_t objects cannot be deep copied, they can only be shallow + // copied. Thus, while shallow copying of an omp_lock_t object is + // completely valid (provided no race conditions exist), true copying + // of an OpenMPMutex object is not valid due to the action of the + // destructor. However, since locks are fungible, we can simply replace + // copying operations with default construction. This allows storage of + // OpenMPMutex objects within containers that may need to move/copy them + // (e.g., std::vector). It is left to the caller to understand that + // copying of OpenMPMutex does not produce two handles to the same mutex, + // rather, it produces two different mutexes. + + // Copy constructor + OpenMPMutex(const OpenMPMutex& other) { init(); } + + // Copy assignment operator + OpenMPMutex& operator=(const OpenMPMutex& other) { return *this; } //! Lock the mutex. // diff --git a/include/openmc/output.h b/include/openmc/output.h index 2ebe2711f1..940ea78ceb 100644 --- a/include/openmc/output.h +++ b/include/openmc/output.h @@ -40,6 +40,9 @@ void print_usage(); //! Display current version and copright/license information void print_version(); +//! Display compile flags employed, etc +void print_build_info(); + //! Display header listing what physical values will displayed void print_columns(); @@ -54,5 +57,33 @@ void print_results(); void write_tallies(); +void show_time(const char* label, double secs, int indent_level = 0); + } // namespace openmc #endif // OPENMC_OUTPUT_H + +////////////////////////////////////// +// Custom formatters +////////////////////////////////////// +namespace fmt { + +template +struct formatter> { + template + constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template +#if FMT_VERSION >= 110000 // Version 11.0.0 and above + auto format(const std::array& arr, FormatContext& ctx) const { +#else // For versions below 11.0.0 + auto format(const std::array& arr, FormatContext& ctx) + { +#endif + return format_to(ctx.out(), "({}, {})", arr[0], arr[1]); +} +}; // namespace fmt + +} // namespace fmt diff --git a/include/openmc/particle.h b/include/openmc/particle.h index da2f6a61fc..0f37719b94 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -5,7 +5,6 @@ //! \brief Particle type #include -#include #include #include "openmc/constants.h" @@ -35,6 +34,9 @@ public: Particle() = default; + //========================================================================== + // Methods + double speed() const; //! create a secondary particle @@ -45,7 +47,14 @@ public: //! \param u Direction of the secondary particle //! \param E Energy of the secondary particle in [eV] //! \param type Particle type - void create_secondary(double wgt, Direction u, double E, ParticleType type); + //! \return Whether a secondary particle was created + bool create_secondary(double wgt, Direction u, double E, ParticleType type); + + //! split a particle + // + //! creates a new particle with weight wgt + //! \param wgt Weight of the new particle + void split(double wgt); //! initialize from a source site // @@ -63,8 +72,12 @@ public: void event_revive_from_secondary(); void event_death(); + //! pulse-height recording + void pht_collision_energy(); + void pht_secondary_particles(); + //! Cross a surface and handle boundary conditions - void cross_surface(); + void cross_surface(const Surface& surf); //! Cross a vacuum boundary condition. // @@ -92,20 +105,21 @@ public: //! mark a particle as lost and create a particle restart file //! \param message A warning message to display - void mark_as_lost(const char* message); - - void mark_as_lost(const std::string& message) - { - mark_as_lost(message.c_str()); - } - - void mark_as_lost(const std::stringstream& message) - { - mark_as_lost(message.str()); - } + virtual void mark_as_lost(const char* message) override; + using GeometryState::mark_as_lost; //! create a particle restart HDF5 file void write_restart() const; + + //! Update microscopic cross section cache + // + //! \param[in] i_nuclide Index in data::nuclides + //! \param[in] i_grid Index on log union grid + //! \param[in] i_sab Index in data::thermal_scatt + //! \param[in] sab_frac S(a,b) table fraction + //! \param[in] ncrystal_xs Thermal scattering xs from NCrystal + void update_neutron_xs(int i_nuclide, int i_grid = C_NONE, int i_sab = C_NONE, + double sab_frac = 0.0, double ncrystal_xs = -1.0); }; //============================================================================ @@ -116,6 +130,8 @@ std::string particle_type_to_str(ParticleType type); ParticleType str_to_particle_type(std::string str); +void add_surf_source_to_bank(Particle& p, const Surface& surf); + } // namespace openmc #endif // OPENMC_PARTICLE_H diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index 38c0aa1dfe..e8188e093a 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -8,7 +8,7 @@ #include "openmc/tallies/filter_match.h" #include "openmc/vector.h" -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED #include "DagMC.hpp" #endif @@ -27,9 +27,6 @@ constexpr int MAX_DELAYED_GROUPS {8}; constexpr double CACHE_INVALID {-1.0}; -// Maximum number of collisions/crossings -constexpr int MAX_EVENTS {1000000}; - //========================================================================== // Aliases and type definitions @@ -50,12 +47,33 @@ struct SourceSite { double time {0.0}; double wgt {1.0}; int delayed_group {0}; - int surf_id {0}; + int surf_id {SURFACE_NONE}; ParticleType particle; + + // Extra attributes that don't show up in source written to file + int parent_nuclide {-1}; int64_t parent_id; int64_t progeny_id; }; +//! State of a particle used for particle track files +struct TrackState { + Position r; //!< Position in [cm] + Direction u; //!< Direction + double E; //!< Energy in [eV] + double time {0.0}; //!< Time in [s] + double wgt {1.0}; //!< Weight + int cell_id; //!< Cell ID + int cell_instance; //!< Cell instance + int material_id {-1}; //!< Material ID (default value indicates void) +}; + +//! Full history of a single particle's track states +struct TrackStateHistory { + ParticleType particle; + std::vector states; +}; + //! Saved ("banked") state of a particle, for nu-fission tallying struct NuBank { double E; //!< particle energy @@ -70,13 +88,37 @@ public: //! clear data from a single coordinate level void reset(); - Position r; //!< particle position - Direction u; //!< particle direction - int cell {-1}; - int universe {-1}; - int lattice {-1}; - array lattice_i {{-1, -1, -1}}; - bool rotated {false}; //!< Is the level rotated? + // accessors + Position& r() { return r_; } + const Position& r() const { return r_; } + + Direction& u() { return u_; } + const Direction& u() const { return u_; } + + int& cell() { return cell_; } + const int& cell() const { return cell_; } + + int& universe() { return universe_; } + const int& universe() const { return universe_; } + + int& lattice() { return lattice_; } + int lattice() const { return lattice_; } + + array& lattice_index() { return lattice_index_; } + const array& lattice_index() const { return lattice_index_; } + + bool& rotated() { return rotated_; } + const bool& rotated() const { return rotated_; } + +private: + // Data members + Position r_; //!< particle position + Direction u_; //!< particle direction + int cell_ {-1}; + int universe_ {-1}; + int lattice_ {-1}; + array lattice_index_ {{-1, -1, -1}}; + bool rotated_ {false}; //!< Is the level rotated? }; //============================================================================== @@ -112,9 +154,10 @@ struct NuclideMicroXS { // Energy and temperature last used to evaluate these cross sections. If // these values have changed, then the cross sections must be re-evaluated. - double last_E {0.0}; //!< Last evaluated energy - double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant - //!< * temperature (eV)) + double last_E {0.0}; //!< Last evaluated energy + double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant + //!< * temperature (eV)) + double ncrystal_xs {-1.0}; //!< NCrystal cross section }; //============================================================================== @@ -152,16 +195,246 @@ struct MacroXS { double pair_production; //!< macroscopic pair production xs }; +//============================================================================== +// Cache contains the cached data for an MGXS object +//============================================================================== + +struct CacheDataMG { + int material {-1}; //!< material index + double sqrtkT; //!< last temperature corresponding to t + int t {0}; //!< temperature index + int a {0}; //!< angle index + Direction u; //!< angle that corresponds to a +}; + //============================================================================== // Information about nearest boundary crossing //============================================================================== -struct BoundaryInfo { - double distance {INFINITY}; //!< distance to nearest boundary - int surface_index {0}; //!< if boundary is surface, index in surfaces vector - int coord_level; //!< coordinate level after crossing boundary - array - lattice_translation {}; //!< which way lattice indices will change +class BoundaryInfo { +public: + void reset() + { + distance_ = INFINITY; + surface_ = SURFACE_NONE; + coord_level_ = 0; + lattice_translation_ = {0, 0, 0}; + } + double& distance() { return distance_; } + const double& distance() const { return distance_; } + + int& surface() { return surface_; } + const int& surface() const { return surface_; } + + int coord_level() const { return coord_level_; } + int& coord_level() { return coord_level_; } + + array& lattice_translation() { return lattice_translation_; } + const array& lattice_translation() const + { + return lattice_translation_; + } + + // TODO: off-by-one + int surface_index() const { return std::abs(surface()) - 1; } + +private: + // Data members + double distance_ {INFINITY}; //!< distance to nearest boundary + int surface_ { + SURFACE_NONE}; //!< surface token, non-zero if boundary is surface + int coord_level_ {0}; //!< coordinate level after crossing boundary + array lattice_translation_ { + 0, 0, 0}; //!< which way lattice indices will change +}; + +/* + * Contains all geometry state information for a particle. + */ +class GeometryState { +public: + GeometryState(); + + /* + * GeometryState does not store any ID info, so give some reasonable behavior + * here. The Particle class redefines this. This is only here for the error + * reporting behavior that occurs in geometry.cpp. The explanation for + * mark_as_lost is the same. + */ + virtual void mark_as_lost(const char* message); + void mark_as_lost(const std::string& message); + void mark_as_lost(const std::stringstream& message); + + // resets all coordinate levels for the particle + void clear() + { + for (auto& level : coord_) { + level.reset(); + } + n_coord_ = 1; + + for (auto& cell : cell_last_) { + cell = C_NONE; + } + n_coord_last_ = 1; + } + + //! moves the particle by the specified distance to its next location + //! \param distance the distance the particle is moved + void move_distance(double distance); + + void advance_to_boundary_from_void(); + + // Initialize all internal state from position and direction + void init_from_r_u(Position r_a, Direction u_a) + { + clear(); + surface() = SURFACE_NONE; + material() = C_NONE; + r() = r_a; + u() = u_a; + r_last_current() = r_a; + r_last() = r_a; + u_last() = u_a; + } + + // Unique ID. This is not geometric info, but the + // error reporting in geometry.cpp requires this. + // We could save this to implement it in Particle, + // but that would require virtuals. + int64_t& id() { return id_; } + const int64_t& id() const { return id_; } + + // Number of current coordinate levels + int& n_coord() { return n_coord_; } + const int& n_coord() const { return n_coord_; } + + // Offset for distributed properties + int& cell_instance() { return cell_instance_; } + const int& cell_instance() const { return cell_instance_; } + + // Coordinates for all nesting levels + LocalCoord& coord(int i) { return coord_[i]; } + const LocalCoord& coord(int i) const { return coord_[i]; } + const vector& coord() const { return coord_; } + + // Innermost universe nesting coordinates + LocalCoord& lowest_coord() { return coord_[n_coord_ - 1]; } + const LocalCoord& lowest_coord() const { return coord_[n_coord_ - 1]; } + + // Last coordinates on all nesting levels, before crossing a surface + int& n_coord_last() { return n_coord_last_; } + const int& n_coord_last() const { return n_coord_last_; } + int& cell_last(int i) { return cell_last_[i]; } + const int& cell_last(int i) const { return cell_last_[i]; } + + // Coordinates at birth + Position& r_born() { return r_born_; } + const Position& r_born() const { return r_born_; } + + // Coordinates of last collision or reflective/periodic surface + // crossing for current tallies + Position& r_last_current() { return r_last_current_; } + const Position& r_last_current() const { return r_last_current_; } + + // Previous direction and spatial coordinates before a collision + Position& r_last() { return r_last_; } + const Position& r_last() const { return r_last_; } + Position& u_last() { return u_last_; } + const Position& u_last() const { return u_last_; } + + // Accessors for position in global coordinates + Position& r() { return coord_[0].r(); } + const Position& r() const { return coord_[0].r(); } + + // Accessors for position in local coordinates + Position& r_local() { return coord_[n_coord_ - 1].r(); } + const Position& r_local() const { return coord_[n_coord_ - 1].r(); } + + // Accessors for direction in global coordinates + Direction& u() { return coord_[0].u(); } + const Direction& u() const { return coord_[0].u(); } + + // Accessors for direction in local coordinates + Direction& u_local() { return coord_[n_coord_ - 1].u(); } + const Direction& u_local() const { return coord_[n_coord_ - 1].u(); } + + // Surface token for the surface that the particle is currently on + int& surface() { return surface_; } + const int& surface() const { return surface_; } + + // Surface index based on the current value of the surface_ attribute + int surface_index() const + { + // TODO: off-by-one + return std::abs(surface_) - 1; + } + + // Boundary information + BoundaryInfo& boundary() { return boundary_; } + + // Distance to the next collision + double& collision_distance() { return collision_distance_; } + +#ifdef OPENMC_DAGMC_ENABLED + // DagMC state variables + moab::DagMC::RayHistory& history() { return history_; } + Direction& last_dir() { return last_dir_; } +#endif + + // material of current and last cell + int& material() { return material_; } + const int& material() const { return material_; } + int& material_last() { return material_last_; } + const int& material_last() const { return material_last_; } + + // temperature of current and last cell + double& sqrtkT() { return sqrtkT_; } + const double& sqrtkT() const { return sqrtkT_; } + double& sqrtkT_last() { return sqrtkT_last_; } + + // density multiplier of the current and last cell + double& density_mult() { return density_mult_; } + const double& density_mult() const { return density_mult_; } + double& density_mult_last() { return density_mult_last_; } + +private: + int64_t id_ {-1}; //!< Unique ID + + int n_coord_ {1}; //!< number of current coordinate levels + int cell_instance_; //!< offset for distributed properties + vector coord_; //!< coordinates for all levels + + int n_coord_last_ {1}; //!< number of current coordinates + vector cell_last_; //!< coordinates for all levels + + Position r_born_; //!< coordinates at birth + Position r_last_current_; //!< coordinates of the last collision or + //!< reflective/periodic surface crossing for + //!< current tallies + Position r_last_; //!< previous coordinates + Direction u_last_; //!< previous direction coordinates + + int surface_ { + SURFACE_NONE}; //!< surface token for surface the particle is currently on + + BoundaryInfo boundary_; //!< Info about the next intersection + + int material_ {-1}; //!< index for current material + int material_last_ {-1}; //!< index for last material + + double sqrtkT_ {-1.0}; //!< sqrt(k_Boltzmann * temperature) in eV + double sqrtkT_last_ {0.0}; //!< last temperature + + double density_mult_ {1.0}; //!< density multiplier + double density_mult_last_ {1.0}; //!< last density multiplier + + double collision_distance_ {INFTY}; + +#ifdef OPENMC_DAGMC_ENABLED + moab::DagMC::RayHistory history_; + Direction last_dir_; +#endif }; //============================================================================ @@ -199,156 +472,116 @@ struct BoundaryInfo { * Algorithms.” Annals of Nuclear Energy 113 (March 2018): 506–18. * https://doi.org/10.1016/j.anucene.2017.11.032. */ -class ParticleData { - -public: - ParticleData(); - +class ParticleData : public GeometryState { private: //========================================================================== - // Data members (accessor methods are below) + // Data members -- see public: below for descriptions - // Cross section caches - vector neutron_xs_; //!< Microscopic neutron cross sections - vector photon_xs_; //!< Microscopic photon cross sections - MacroXS macro_xs_; //!< Macroscopic cross sections + vector neutron_xs_; + vector photon_xs_; + MacroXS macro_xs_; + CacheDataMG mg_xs_cache_; - int64_t id_; //!< Unique ID - ParticleType type_ {ParticleType::neutron}; //!< Particle type (n, p, e, etc.) + ParticleType type_ {ParticleType::neutron}; - int n_coord_ {1}; //!< number of current coordinate levels - int cell_instance_; //!< offset for distributed properties - vector coord_; //!< coordinates for all levels + double E_; + double E_last_; + int g_ {0}; + int g_last_; - // Particle coordinates before crossing a surface - int n_coord_last_ {1}; //!< number of current coordinates - vector cell_last_; //!< coordinates for all levels + double wgt_ {1.0}; + double wgt_born_ {1.0}; + double wgt_ww_born_ {-1.0}; + double mu_; + double time_ {0.0}; + double time_last_ {0.0}; + double wgt_last_ {1.0}; - // Energy data - double E_; //!< post-collision energy in eV - double E_last_; //!< pre-collision energy in eV - int g_ {0}; //!< post-collision energy group (MG only) - int g_last_; //!< pre-collision energy group (MG only) + bool fission_ {false}; + TallyEvent event_; + int event_nuclide_; + int event_mt_; + int delayed_group_ {0}; + int parent_nuclide_ {-1}; - // Other physical data - double wgt_ {1.0}; //!< particle weight - double mu_; //!< angle of scatter - double time_ {0.0}; //!< time in [s] - double time_last_ {0.0}; //!< previous time in [s] - bool alive_ {true}; //!< is particle alive? + int n_bank_ {0}; + double bank_second_E_ {0.0}; + double wgt_bank_ {0.0}; + int n_delayed_bank_[MAX_DELAYED_GROUPS]; - // Other physical data - Position r_last_current_; //!< coordinates of the last collision or - //!< reflective/periodic surface crossing for - //!< current tallies - Position r_last_; //!< previous coordinates - Direction u_last_; //!< previous direction coordinates - double wgt_last_ {1.0}; //!< pre-collision particle weight - double wgt_absorb_ {0.0}; //!< weight absorbed for survival biasing + int cell_born_ {-1}; - // What event took place - bool fission_ {false}; //!< did particle cause implicit fission - TallyEvent event_; //!< scatter, absorption - int event_nuclide_; //!< index in nuclides array - int event_mt_; //!< reaction MT - int delayed_group_ {0}; //!< delayed group + // Iterated Fission Probability + double lifetime_ {0.0}; //!< neutron lifetime [s] - // Post-collision physical data - int n_bank_ {0}; //!< number of fission sites banked - int n_bank_second_ {0}; //!< number of secondary particles banked - double wgt_bank_ {0.0}; //!< weight of fission sites banked - int n_delayed_bank_[MAX_DELAYED_GROUPS]; //!< number of delayed fission - //!< sites banked + int n_collision_ {0}; - // Indices for various arrays - int surface_ {0}; //!< index for surface particle is on - int cell_born_ {-1}; //!< index for cell particle was born in - int material_ {-1}; //!< index for current material - int material_last_ {-1}; //!< index for last material - - // Boundary information - BoundaryInfo boundary_; - - // Temperature of current cell - double sqrtkT_ {-1.0}; //!< sqrt(k_Boltzmann * temperature) in eV - double sqrtkT_last_ {0.0}; //!< last temperature - - // Statistical data - int n_collision_ {0}; //!< number of collisions - - // Track output bool write_track_ {false}; - // Current PRNG state - uint64_t seeds_[N_STREAMS]; // current seeds - int stream_; // current RNG stream + uint64_t seeds_[N_STREAMS]; + int stream_; - // Secondary particle bank vector secondary_bank_; - int64_t current_work_; // current work index + int64_t current_work_; - vector flux_derivs_; // for derivatives for this particle + vector flux_derivs_; - vector filter_matches_; // tally filter matches + vector filter_matches_; - vector> tracks_; // tracks for outputting to file + vector tracks_; - vector nu_bank_; // bank of most recently fissioned particles + vector nu_bank_; + + vector pht_storage_; - // Global tally accumulators double keff_tally_absorption_ {0.0}; double keff_tally_collision_ {0.0}; double keff_tally_tracklength_ {0.0}; double keff_tally_leakage_ {0.0}; - bool trace_ {false}; //!< flag to show debug information + bool trace_ {false}; - double collision_distance_; // distance to particle's next closest collision + int n_event_ {0}; - int n_event_ {0}; // number of events executed in this particle's history + int n_split_ {0}; + double ww_factor_ {0.0}; - // Weight window information - int n_split_ {0}; // Number of times this particle has been split - double ww_factor_ {0.0}; // Particle-specific factor for on-the-fly weight window adjustment - -// DagMC state variables -#ifdef DAGMC - moab::DagMC::RayHistory history_; - Direction last_dir_; -#endif - - int64_t n_progeny_ {0}; // Number of progeny produced by this particle + int64_t n_progeny_ {0}; public: + //---------------------------------------------------------------------------- + // Constructors + ParticleData(); + //========================================================================== // Methods and accessors - NuclideMicroXS& neutron_xs(int i) { return neutron_xs_[i]; } + // Cross section caches + NuclideMicroXS& neutron_xs(int i) + { + return neutron_xs_[i]; + } // Microscopic neutron cross sections const NuclideMicroXS& neutron_xs(int i) const { return neutron_xs_[i]; } + + // Microscopic photon cross sections ElementMicroXS& photon_xs(int i) { return photon_xs_[i]; } + + // Macroscopic cross sections MacroXS& macro_xs() { return macro_xs_; } const MacroXS& macro_xs() const { return macro_xs_; } - int64_t& id() { return id_; } - const int64_t& id() const { return id_; } + // Multigroup macroscopic cross sections + CacheDataMG& mg_xs_cache() { return mg_xs_cache_; } + const CacheDataMG& mg_xs_cache() const { return mg_xs_cache_; } + + // Particle type (n, p, e, gamma, etc) ParticleType& type() { return type_; } const ParticleType& type() const { return type_; } - int& n_coord() { return n_coord_; } - const int& n_coord() const { return n_coord_; } - int& cell_instance() { return cell_instance_; } - const int& cell_instance() const { return cell_instance_; } - LocalCoord& coord(int i) { return coord_[i]; } - const LocalCoord& coord(int i) const { return coord_[i]; } - const vector& coord() const { return coord_; } - - - int& n_coord_last() { return n_coord_last_; } - const int& n_coord_last() const { return n_coord_last_; } - int& cell_last(int i) { return cell_last_[i]; } - const int& cell_last(int i) const { return cell_last_[i]; } - + // Current particle energy, energy before collision, + // and corresponding multigroup group indices. Energy + // units are eV. double& E() { return E_; } const double& E() const { return E_; } double& E_last() { return E_last_; } @@ -358,112 +591,137 @@ public: int& g_last() { return g_last_; } const int& g_last() const { return g_last_; } + // Statistic weight of particle. Setting to zero indicates that the particle + // is dead. double& wgt() { return wgt_; } + double wgt() const { return wgt_; } + + // Statistic weight of particle at birth + double& wgt_born() { return wgt_born_; } + double wgt_born() const { return wgt_born_; } + + // Weight window value at birth + double& wgt_ww_born() { return wgt_ww_born_; } + const double& wgt_ww_born() const { return wgt_ww_born_; } + + // Statistic weight of particle at last collision + double& wgt_last() { return wgt_last_; } + const double& wgt_last() const { return wgt_last_; } + + // Whether particle is alive + bool alive() const { return wgt_ != 0.0; } + + // Polar scattering angle after a collision double& mu() { return mu_; } const double& mu() const { return mu_; } + + // Tracks the time of a particle as it traverses the problem. + // Units are seconds. double& time() { return time_; } const double& time() const { return time_; } double& time_last() { return time_last_; } const double& time_last() const { return time_last_; } - bool& alive() { return alive_; } - Position& r_last_current() { return r_last_current_; } - const Position& r_last_current() const { return r_last_current_; } - Position& r_last() { return r_last_; } - const Position& r_last() const { return r_last_; } - Position& u_last() { return u_last_; } - const Position& u_last() const { return u_last_; } - double& wgt_last() { return wgt_last_; } - const double& wgt_last() const { return wgt_last_; } - double& wgt_absorb() { return wgt_absorb_; } - const double& wgt_absorb() const { return wgt_absorb_; } + // Particle lifetime + double& lifetime() { return lifetime_; } + const double& lifetime() const { return lifetime_; } - bool& fission() { return fission_; } + // What event took place, described in greater detail below TallyEvent& event() { return event_; } const TallyEvent& event() const { return event_; } - int& event_nuclide() { return event_nuclide_; } + bool& fission() { return fission_; } // true if implicit fission + int& event_nuclide() { return event_nuclide_; } // index of collision nuclide const int& event_nuclide() const { return event_nuclide_; } - int& event_mt() { return event_mt_; } - int& delayed_group() { return delayed_group_; } + int& event_mt() { return event_mt_; } // MT number of collision + const int& event_mt() const { return event_mt_; } + int& delayed_group() { return delayed_group_; } // delayed group + const int& delayed_group() const { return delayed_group_; } + const int& parent_nuclide() const { return parent_nuclide_; } + int& parent_nuclide() { return parent_nuclide_; } // Parent nuclide - int& n_bank() { return n_bank_; } - int& n_bank_second() { return n_bank_second_; } - double& wgt_bank() { return wgt_bank_; } - int* n_delayed_bank() { return n_delayed_bank_; } - int& n_delayed_bank(int i) { return n_delayed_bank_[i]; } + // Post-collision data + double& bank_second_E() + { + return bank_second_E_; + } // energy of last reaction secondaries + const double& bank_second_E() const { return bank_second_E_; } - int& surface() { return surface_; } - const int& surface() const { return surface_; } + int& n_bank() { return n_bank_; } // number of banked fission sites + double& wgt_bank() { return wgt_bank_; } // weight of banked fission sites + int* n_delayed_bank() + { + return n_delayed_bank_; + } // number of delayed fission sites + int& n_delayed_bank(int i) + { + return n_delayed_bank_[i]; + } // number of delayed fission sites + + // Index of cell particle is born in int& cell_born() { return cell_born_; } const int& cell_born() const { return cell_born_; } - int& material() { return material_; } - const int& material() const { return material_; } - int& material_last() { return material_last_; } - - BoundaryInfo& boundary() { return boundary_; } - - double& sqrtkT() { return sqrtkT_; } - const double& sqrtkT() const { return sqrtkT_; } - double& sqrtkT_last() { return sqrtkT_last_; } + // Total number of collisions suffered by particle int& n_collision() { return n_collision_; } const int& n_collision() const { return n_collision_; } + // whether this track is to be written bool& write_track() { return write_track_; } + + // RNG state uint64_t& seeds(int i) { return seeds_[i]; } uint64_t* seeds() { return seeds_; } int& stream() { return stream_; } + // secondary particle bank SourceSite& secondary_bank(int i) { return secondary_bank_[i]; } decltype(secondary_bank_)& secondary_bank() { return secondary_bank_; } + + // Current simulation work index int64_t& current_work() { return current_work_; } const int64_t& current_work() const { return current_work_; } + + // Used in tally derivatives double& flux_derivs(int i) { return flux_derivs_[i]; } const double& flux_derivs(int i) const { return flux_derivs_[i]; } + + // Matches of tallies decltype(filter_matches_)& filter_matches() { return filter_matches_; } FilterMatch& filter_matches(int i) { return filter_matches_[i]; } + + // Tracks to output to file decltype(tracks_)& tracks() { return tracks_; } + + // Bank of recently fissioned particles decltype(nu_bank_)& nu_bank() { return nu_bank_; } NuBank& nu_bank(int i) { return nu_bank_[i]; } + // Interim pulse height tally storage + vector& pht_storage() { return pht_storage_; } + + // Global tally accumulators double& keff_tally_absorption() { return keff_tally_absorption_; } double& keff_tally_collision() { return keff_tally_collision_; } double& keff_tally_tracklength() { return keff_tally_tracklength_; } double& keff_tally_leakage() { return keff_tally_leakage_; } + // Shows debug info bool& trace() { return trace_; } - double& collision_distance() { return collision_distance_; } + + // Number of events particle has undergone int& n_event() { return n_event_; } + // Number of times variance reduction has caused a particle split int n_split() const { return n_split_; } int& n_split() { return n_split_; } + // Particle-specific factor for on-the-fly weight window adjustment double ww_factor() const { return ww_factor_; } double& ww_factor() { return ww_factor_; } -#ifdef DAGMC - moab::DagMC::RayHistory& history() { return history_; } - Direction& last_dir() { return last_dir_; } -#endif - + // Number of progeny produced by this particle int64_t& n_progeny() { return n_progeny_; } - // Accessors for position in global coordinates - Position& r() { return coord_[0].r; } - const Position& r() const { return coord_[0].r; } - - // Accessors for position in local coordinates - Position& r_local() { return coord_[n_coord_ - 1].r; } - const Position& r_local() const { return coord_[n_coord_ - 1].r; } - - // Accessors for direction in global coordinates - Direction& u() { return coord_[0].u; } - const Direction& u() const { return coord_[0].u; } - - // Accessors for direction in local coordinates - Direction& u_local() { return coord_[n_coord_ - 1].u; } - const Direction& u_local() const { return coord_[n_coord_ - 1].u; } - //! Gets the pointer to the particle's current PRN seed uint64_t* current_seed() { return seeds_ + stream_; } const uint64_t* current_seed() const { return seeds_ + stream_; } @@ -475,13 +733,8 @@ public: micro.last_E = 0.0; } - //! resets all coordinate levels for the particle - void clear() - { - for (auto& level : coord_) - level.reset(); - n_coord_ = 1; - } + //! Get track information based on particle's current state + TrackState get_track_state() const; void zero_delayed_bank() { diff --git a/include/openmc/photon.h b/include/openmc/photon.h index e8ec82d107..f6f28a4df1 100644 --- a/include/openmc/photon.h +++ b/include/openmc/photon.h @@ -7,7 +7,6 @@ #include "openmc/vector.h" #include "xtensor/xtensor.hpp" -#include #include #include @@ -34,9 +33,7 @@ public: int index_subshell; //!< index in SUBSHELLS int threshold; - double n_electrons; double binding_energy; - xt::xtensor cross_section; vector transitions; }; @@ -62,7 +59,7 @@ public: // Data members std::string name_; //!< Name of element, e.g. "Zr" int Z_; //!< Atomic number - gsl::index index_; //!< Index in global elements vector + int64_t index_; //!< Index in global elements vector // Microscopic cross sections xt::xtensor energy_; @@ -80,8 +77,11 @@ public: Tabulated1D coherent_anomalous_real_; Tabulated1D coherent_anomalous_imag_; - // Photoionization and atomic relaxation data + // Photoionization and atomic relaxation data. Subshell cross sections are + // stored separately to improve memory access pattern when calculating the + // total cross section vector shells_; + xt::xtensor cross_sections_; // Compton profile data xt::xtensor profile_pdf_; @@ -89,6 +89,11 @@ public: xt::xtensor binding_energy_; xt::xtensor electron_pdf_; + // Map subshells from Compton profile data obtained from Biggs et al, + // "Hartree-Fock Compton profiles for the elements" to ENDF/B atomic + // relaxation data + xt::xtensor subshell_map_; + // Stopping power data double I_; // mean excitation energy xt::xtensor n_electrons_; @@ -98,6 +103,9 @@ public: // Bremsstrahlung scaled DCS xt::xtensor dcs_; + // Whether atomic relaxation data is present + bool has_atomic_relaxation_ {false}; + // Constant data static constexpr int MAX_STACK_SIZE = 7; //!< maximum possible size of atomic relaxation stack diff --git a/include/openmc/physics.h b/include/openmc/physics.h index 262b3a8841..2472d97993 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -10,13 +10,6 @@ namespace openmc { -//============================================================================== -// Constants -//============================================================================== - -// Monoatomic ideal-gas scattering treatment threshold -constexpr double FREE_GAS_THRESHOLD {400.0}; - //============================================================================== // Non-member functions //============================================================================== @@ -87,8 +80,8 @@ Direction sample_target_velocity(const Nuclide& nuc, double E, Direction u, Direction sample_cxs_target_velocity( double awr, double E, Direction u, double kT, uint64_t* seed); -void sample_fission_neutron(int i_nuclide, const Reaction& rx, double E_in, - SourceSite* site, uint64_t* seed); +void sample_fission_neutron( + int i_nuclide, const Reaction& rx, SourceSite* site, Particle& p); //! handles all reactions with a single secondary neutron (other than fission), //! i.e. level scattering, (n,np), (n,na), etc. @@ -96,8 +89,9 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p); void sample_secondary_photons(Particle& p, int i_nuclide); -//!Split or Roulette particles based their weight and the lower weight window -// bound. +//! Split or Roulette particles based their weight and the lower weight window +//! bound. +// //! \param[in] p, particle to be split or rouletted with the weight window. void split_particle(Particle& p); diff --git a/include/openmc/physics_common.h b/include/openmc/physics_common.h index 1398557dc5..e38a3c7f88 100644 --- a/include/openmc/physics_common.h +++ b/include/openmc/physics_common.h @@ -9,7 +9,9 @@ namespace openmc { //! \brief Performs the russian roulette operation for a particle -void russian_roulette(Particle& p); +//! \param[in,out] p Particle object +//! \param[in] weight_survive Weight assigned to particles that survive +void russian_roulette(Particle& p, double weight_survive); } // namespace openmc #endif // OPENMC_PHYSICS_COMMON_H diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 650b7e16a1..7e27679eab 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -1,8 +1,10 @@ #ifndef OPENMC_PLOT_H #define OPENMC_PLOT_H +#include #include #include +#include #include "pugixml.hpp" #include "xtensor/xarray.hpp" @@ -23,12 +25,13 @@ namespace openmc { // Global variables //=============================================================================== -class Plot; +class PlottableInterface; namespace model { extern std::unordered_map plot_map; //!< map of plot ids to index -extern vector plots; //!< Plot instance container +extern vector> + plots; //!< Plot instance container extern uint64_t plotter_seed; // Stream index used by the plotter @@ -59,6 +62,14 @@ struct RGBColor { return red == other.red && green == other.green && blue == other.blue; } + RGBColor& operator*=(const double x) + { + red *= x; + green *= x; + blue *= x; + return *this; + } + // Members uint8_t red, green, blue; }; @@ -66,6 +77,57 @@ struct RGBColor { // some default colors const RGBColor WHITE {255, 255, 255}; const RGBColor RED {255, 0, 0}; +const RGBColor BLACK {0, 0, 0}; + +/** + * \class PlottableInterface + * \brief Interface for plottable objects. + * + * PlottableInterface classes must have a unique ID in the plots.xml file. + * They guarantee the ability to create output in some form. This interface + * is designed to be implemented by classes that produce plot-relevant data + * which can be visualized. + */ +class PlottableInterface { +private: + void set_id(pugi::xml_node plot_node); + int id_; // unique plot ID + + void set_bg_color(pugi::xml_node plot_node); + void set_universe(pugi::xml_node plot_node); + void set_default_colors(pugi::xml_node plot_node); + void set_user_colors(pugi::xml_node plot_node); + void set_overlap_color(pugi::xml_node plot_node); + void set_mask(pugi::xml_node plot_node); + +protected: + // Plot output filename, derived classes have logic to set it + std::string path_plot_; + +public: + enum class PlotColorBy { cells = 0, mats = 1 }; + + // Creates the output image named path_plot_ + virtual void create_output() const = 0; + + // Print useful info to the terminal + virtual void print_info() const = 0; + + const std::string& path_plot() const { return path_plot_; } + std::string& path_plot() { return path_plot_; } + int id() const { return id_; } + int level() const { return level_; } + + // Public color-related data + PlottableInterface(pugi::xml_node plot_node); + virtual ~PlottableInterface() = default; + int level_; // Universe level to plot + bool color_overlaps_; // Show overlapping cells? + PlotColorBy color_by_; // Plot coloring (cell/material) + RGBColor not_found_ {WHITE}; // Plot background color + RGBColor overlap_color_ {RED}; // Plot overlap color + vector colors_; // Plot colors +}; typedef xt::xtensor ImageData; @@ -74,7 +136,7 @@ struct IdData { IdData(size_t h_res, size_t v_res); // Methods - void set_value(size_t y, size_t x, const Particle& p, int level); + void set_value(size_t y, size_t x, const GeometryState& p, int level); void set_overlap(size_t y, size_t x); // Members @@ -86,39 +148,37 @@ struct PropertyData { PropertyData(size_t h_res, size_t v_res); // Methods - void set_value(size_t y, size_t x, const Particle& p, int level); + void set_value(size_t y, size_t x, const GeometryState& p, int level); void set_overlap(size_t y, size_t x); // Members xt::xtensor data_; //!< 2D array of temperature & density data }; -enum class PlotType { slice = 1, voxel = 2 }; - -enum class PlotBasis { xy = 1, xz = 2, yz = 3 }; - -enum class PlotColorBy { cells = 0, mats = 1 }; - //=============================================================================== // Plot class //=============================================================================== -class PlotBase { + +class SlicePlotBase { public: template T get_map() const; + enum class PlotBasis { xy = 1, xz = 2, yz = 3 }; + // Members public: - Position origin_; //!< Plot origin in geometry - Position width_; //!< Plot width in geometry - PlotBasis basis_; //!< Plot basis (XY/XZ/YZ) - array pixels_; //!< Plot size in pixels - bool color_overlaps_; //!< Show overlapping cells? - int level_; //!< Plot universe level + Position origin_; //!< Plot origin in geometry + Position width_; //!< Plot width in geometry + PlotBasis basis_; //!< Plot basis (XY/XZ/YZ) + array pixels_; //!< Plot size in pixels + bool slice_color_overlaps_; //!< Show overlapping cells? + int slice_level_ {-1}; //!< Plot universe level +private: }; template -T PlotBase::get_map() const +T SlicePlotBase::get_map() const { size_t width = pixels_[0]; @@ -156,15 +216,15 @@ T PlotBase::get_map() const xyz[out_i] = origin_[out_i] + width_[1] / 2. - out_pixel / 2.; // arbitrary direction - Direction dir = {0.7071, 0.7071, 0.0}; + Direction dir = {1. / std::sqrt(2.), 1. / std::sqrt(2.), 0.0}; #pragma omp parallel { - Particle p; + GeometryState p; p.r() = xyz; p.u() = dir; - p.coord(0).universe = model::root_universe; - int level = level_; + p.coord(0).universe() = model::root_universe; + int level = slice_level_; int j {}; #pragma omp for @@ -182,71 +242,320 @@ T PlotBase::get_map() const if (found_cell) { data.set_value(y, x, p, j); } - if (color_overlaps_ && check_cell_overlap(p, false)) { + if (slice_color_overlaps_ && check_cell_overlap(p, false)) { data.set_overlap(y, x); } } // inner for - } // outer for - } // omp parallel + } + } return data; } -class Plot : public PlotBase { +// Represents either a voxel or pixel plot +class Plot : public PlottableInterface, public SlicePlotBase { public: - // Constructor - Plot(pugi::xml_node plot); + enum class PlotType { slice = 1, voxel = 2 }; + + Plot(pugi::xml_node plot, PlotType type); - // Methods private: - void set_id(pugi::xml_node plot_node); - void set_type(pugi::xml_node plot_node); void set_output_path(pugi::xml_node plot_node); - void set_bg_color(pugi::xml_node plot_node); void set_basis(pugi::xml_node plot_node); void set_origin(pugi::xml_node plot_node); void set_width(pugi::xml_node plot_node); - void set_universe(pugi::xml_node plot_node); - void set_default_colors(pugi::xml_node plot_node); - void set_user_colors(pugi::xml_node plot_node); void set_meshlines(pugi::xml_node plot_node); - void set_mask(pugi::xml_node plot_node); - void set_overlap_color(pugi::xml_node plot_node); - // Members public: - int id_; //!< Plot ID + // Add mesh lines to ImageData + void draw_mesh_lines(ImageData& data) const; + void create_image() const; + void create_voxel() const; + + virtual void create_output() const; + virtual void print_info() const; + PlotType type_; //!< Plot type (Slice/Voxel) - PlotColorBy color_by_; //!< Plot coloring (cell/material) int meshlines_width_; //!< Width of lines added to the plot int index_meshlines_mesh_ {-1}; //!< Index of the mesh to draw on the plot RGBColor meshlines_color_; //!< Color of meshlines on the plot - RGBColor not_found_ {WHITE}; //!< Plot background color - RGBColor overlap_color_ {RED}; //!< Plot overlap color - vector colors_; //!< Plot colors - std::string path_plot_; //!< Plot output filename +}; + +/** + * \class RaytracePlot + * \brief Base class for plots that generate images through ray tracing. + * + * This class serves as a base for plots that create their visuals by tracing + * rays from a camera through the problem geometry. It inherits from + * PlottableInterface, ensuring that it provides an implementation for + * generating output specific to ray-traced visualization. WireframeRayTracePlot + * and SolidRayTracePlot provide concrete implementations of this class. + */ +class RayTracePlot : public PlottableInterface { +public: + RayTracePlot(pugi::xml_node plot); + + // Standard getters. No setting since it's done from XML. + const Position& camera_position() const { return camera_position_; } + const Position& look_at() const { return look_at_; } + const double& horizontal_field_of_view() const + { + return horizontal_field_of_view_; + } + + virtual void print_info() const; + +protected: + Direction camera_x_axis() const + { + return {camera_to_model_[0], camera_to_model_[3], camera_to_model_[6]}; + } + + Direction camera_y_axis() const + { + return {camera_to_model_[1], camera_to_model_[4], camera_to_model_[7]}; + } + + Direction camera_z_axis() const + { + return {camera_to_model_[2], camera_to_model_[5], camera_to_model_[8]}; + } + + void set_output_path(pugi::xml_node plot_node); + + /* + * Gets the starting position and direction for the pixel corresponding + * to this horizontal and vertical position. + */ + std::pair get_pixel_ray(int horiz, int vert) const; + + std::array pixels_; // pixel dimension of resulting image + +private: + void set_look_at(pugi::xml_node node); + void set_camera_position(pugi::xml_node node); + void set_field_of_view(pugi::xml_node node); + void set_pixels(pugi::xml_node node); + void set_orthographic_width(pugi::xml_node node); + + double horizontal_field_of_view_ {70.0}; // horiz. f.o.v. in degrees + Position camera_position_; // where camera is + Position look_at_; // point camera is centered looking at + + Direction up_ {0.0, 0.0, 1.0}; // which way is up + + /* The horizontal thickness, if using an orthographic projection. + * If set to zero, we assume using a perspective projection. + */ + double orthographic_width_ {C_NONE}; + + /* + * Cached camera-to-model matrix with column vectors of axes. The x-axis is + * the vector between the camera_position_ and look_at_; the y-axis is the + * cross product of the x-axis with the up_ vector, and the z-axis is the + * cross product of the x and y axes. + */ + std::array camera_to_model_; +}; + +class ProjectionRay; + +/** + * \class WireframeRayTracePlot + * \brief Creates plots that are like colorful x-ray imaging + * + * WireframeRayTracePlot is a specialized form of RayTracePlot designed for + * creating projection plots. This involves tracing rays from a camera through + * the problem geometry and rendering the results based on depth of penetration + * through materials or cells and their colors. + */ +class WireframeRayTracePlot : public RayTracePlot { + + friend class ProjectionRay; + +public: + WireframeRayTracePlot(pugi::xml_node plot); + + virtual void create_output() const; + virtual void print_info() const; + +private: + void set_opacities(pugi::xml_node node); + void set_wireframe_thickness(pugi::xml_node node); + void set_wireframe_ids(pugi::xml_node node); + void set_wireframe_color(pugi::xml_node node); + + /* Checks if a vector of two TrackSegments is equivalent. We define this + * to mean not having matching intersection lengths, but rather having + * a matching sequence of surface/cell/material intersections. + */ + struct TrackSegment; + bool trackstack_equivalent(const vector& track1, + const vector& track2) const; + + /* Used for drawing wireframe and colors. We record the list of + * surface/cell/material intersections and the corresponding lengths as a ray + * traverses the geometry, then color by iterating in reverse. + */ + struct TrackSegment { + int id; // material or cell ID (which is being colored) + double length; // length of this track intersection + + /* Recording this allows us to draw edges on the wireframe. For instance + * if two surfaces bound a single cell, it allows drawing that sharp edge + * where the surfaces intersect. + */ + int surface_index {-1}; // last surface index intersected in this segment + TrackSegment(int id_a, double length_a, int surface_a) + : id(id_a), length(length_a), surface_index(surface_a) + {} + }; + + // which color IDs should be wireframed. If empty, all cells are wireframed. + vector wireframe_ids_; + + // Thickness of the wireframe lines. Can set to zero for no wireframe. + int wireframe_thickness_ {1}; + + RGBColor wireframe_color_ {BLACK}; // wireframe color + vector xs_; // macro cross section values for cell volume rendering +}; + +/** + * \class SolidRayTracePlot + * \brief Plots 3D objects as the eye might see them. + * + * Plots a geometry with single-scattered Phong lighting plus a diffuse lighting + * contribution. The result is a physically reasonable, aesthetic 3D view of a + * geometry. + */ +class SolidRayTracePlot : public RayTracePlot { + friend class PhongRay; + +public: + SolidRayTracePlot(pugi::xml_node plot); + + virtual void create_output() const; + virtual void print_info() const; + +private: + void set_opaque_ids(pugi::xml_node node); + void set_light_position(pugi::xml_node node); + void set_diffuse_fraction(pugi::xml_node node); + + std::unordered_set opaque_ids_; + + double diffuse_fraction_ {0.1}; + + // By default, the light is at the camera unless otherwise specified. + Position light_location_; +}; + +// Base class that implements ray tracing logic, not necessarily through +// defined regions of the geometry but also outside of it. +class Ray : public GeometryState { + +public: + Ray(Position r, Direction u) { init_from_r_u(r, u); } + + // Called at every surface intersection within the model + virtual void on_intersection() = 0; + + /* + * Traces the ray through the geometry, calling on_intersection + * at every surface boundary. + */ + void trace(); + + // Stops the ray and exits tracing when called from on_intersection + void stop() { stop_ = true; } + + // Sets the dist_ variable + void compute_distance(); + +protected: + // Records how far the ray has traveled + double traversal_distance_ {0.0}; + +private: + // Max intersections before we assume ray tracing is caught in an infinite + // loop: + static const int MAX_INTERSECTIONS = 1000000; + + bool hit_something_ {false}; + bool stop_ {false}; + + unsigned event_counter_ {0}; +}; + +class ProjectionRay : public Ray { +public: + ProjectionRay(Position r, Direction u, const WireframeRayTracePlot& plot, + vector& line_segments) + : Ray(r, u), plot_(plot), line_segments_(line_segments) + {} + + virtual void on_intersection() override; + +private: + /* Store a reference to the plot object which is running this ray, in order + * to access some of the plot settings which influence the behavior where + * intersections are. + */ + const WireframeRayTracePlot& plot_; + + /* The ray runs through the geometry, and records the lengths of ray segments + * and cells they lie in along the way. + */ + vector& line_segments_; +}; + +class PhongRay : public Ray { +public: + PhongRay(Position r, Direction u, const SolidRayTracePlot& plot) + : Ray(r, u), plot_(plot) + { + result_color_ = plot_.not_found_; + } + + virtual void on_intersection() override; + + const RGBColor& result_color() { return result_color_; } + +private: + const SolidRayTracePlot& plot_; + + /* After the ray is reflected, it is moving towards the + * camera. It does that in order to see if the exposed surface + * is shadowed by something else. + */ + bool reflected_ {false}; + + // Have to record the first hit ID, so that if the region + // does get shadowed, we recall what its color should be + // when tracing from the surface to the light. + int orig_hit_id_ {-1}; + + RGBColor result_color_; }; //=============================================================================== // Non-member functions //=============================================================================== -//! Add mesh lines to image data of a plot object -//! \param[in] plot object -//! \param[out] image data associated with the plot object -void draw_mesh_lines(Plot const& pl, ImageData& data); - -//! Write a PPM image using a plot object's image data -//! \param[in] plot object -//! \param[out] image data associated with the plot object -void output_ppm(Plot const& pl, const ImageData& data); +/* Write a PPM image + * filename - name of output file + * data - image data to write + */ +void output_ppm(const std::string& filename, const ImageData& data); #ifdef USE_LIBPNG -//! Write a PNG image using a plot object's image data -//! \param[in] plot object -//! \param[out] image data associated with the plot object -void output_png(Plot const& pl, const ImageData& data); +/* Write a PNG image + * filename - name of output file + * data - image data to write + */ +void output_png(const std::string& filename, const ImageData& data); #endif //! Initialize a voxel file @@ -279,17 +588,13 @@ void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace); //! Read plot specifications from a plots.xml file void read_plots_xml(); +//! Read plot specifications from an XML Node +//! \param[in] XML node containing plot info +void read_plots_xml(pugi::xml_node root); + //! Clear memory void free_memory_plot(); -//! Create an image for a plot object -//! \param[in] plot object -void create_image(Plot const& pl); - -//! Create an hdf5 voxel file for a plot object -//! \param[in] plot object -void create_voxel(Plot const& pl); - //! Create a randomly generated RGB color //! \return RGBColor with random value RGBColor random_color(); diff --git a/include/openmc/position.h b/include/openmc/position.h index 5ab0774f46..5d291d26b9 100644 --- a/include/openmc/position.h +++ b/include/openmc/position.h @@ -5,6 +5,7 @@ #include #include // for out_of_range +#include "fmt/format.h" #include "openmc/array.h" #include "openmc/vector.h" @@ -82,14 +83,35 @@ struct Position { return x * other.x + y * other.y + z * other.z; } inline double norm() const { return std::sqrt(x * x + y * y + z * z); } + inline Position cross(Position other) const + { + return {y * other.z - z * other.y, z * other.x - x * other.z, + x * other.y - y * other.x}; + } //! Reflect a direction across a normal vector //! \param[in] other Vector to reflect across //! \result Reflected vector Position reflect(Position n) const; - //! Rotate the position based on a rotation matrix - Position rotate(const vector& rotation) const; + //! Rotate the position by applying a rotation matrix + template + Position rotate(const T& rotation) const + { + return {x * rotation[0] + y * rotation[1] + z * rotation[2], + x * rotation[3] + y * rotation[4] + z * rotation[5], + x * rotation[6] + y * rotation[7] + z * rotation[8]}; + } + + //! Rotate the position by applying the inverse of a rotation matrix + //! using the fact that rotation matrices are orthonormal. + template + Position inverse_rotate(const T& rotation) const + { + return {x * rotation[0] + y * rotation[3] + z * rotation[6], + x * rotation[1] + y * rotation[4] + z * rotation[7], + x * rotation[2] + y * rotation[5] + z * rotation[8]}; + } // Data members double x = 0.; @@ -210,4 +232,22 @@ using Direction = Position; } // namespace openmc +namespace fmt { + +template<> +struct formatter : formatter { + template +#if FMT_VERSION >= 110000 // Version 11.0.0 and above + auto format(const openmc::Position& pos, FormatContext& ctx) const { +#else // For versions below 11.0.0 + auto format(const openmc::Position& pos, FormatContext& ctx) + { +#endif + return formatter::format( + fmt::format("({}, {}, {})", pos.x, pos.y, pos.z), ctx); +} +}; // namespace fmt + +} // namespace fmt + #endif // OPENMC_POSITION_H diff --git a/include/openmc/random_dist.h b/include/openmc/random_dist.h index 9bf55aa93f..32f055b53d 100644 --- a/include/openmc/random_dist.h +++ b/include/openmc/random_dist.h @@ -16,6 +16,17 @@ namespace openmc { double uniform_distribution(double a, double b, uint64_t* seed); +//============================================================================== +//! Sample an integer from uniform distribution [a, b] +// +//! \param a Lower bound of uniform distribution +//! \param b Upper bound of uniform distribtion +//! \param seed A pointer to the pseudorandom seed +//! \return Sampled variate +//============================================================================== + +int64_t uniform_int_distribution(int64_t a, int64_t b, uint64_t* seed); + //============================================================================== //! Samples an energy from the Maxwell fission distribution based on a direct //! sampling scheme. @@ -48,13 +59,13 @@ extern "C" double maxwell_spectrum(double T, uint64_t* seed); extern "C" double watt_spectrum(double a, double b, uint64_t* seed); //============================================================================== -//! Samples an energy from the Gaussian energy-dependent fission distribution. +//! Samples an energy from the Gaussian distribution. //! -//! Samples from a Normal distribution with a given mean and standard deviation +//! Samples from a normal distribution with a given mean and standard deviation //! The PDF is defined as s(x) = (1/2*sigma*sqrt(2) * e-((mu-x)/2*sigma)^2 //! Its sampled according to -//! http://www-pdg.lbl.gov/2009/reviews/rpp2009-rev-monte-carlo-techniques.pdf -//! section 33.4.4 +//! https://pdg.lbl.gov/2023/reviews/rpp2023-rev-monte-carlo-techniques.pdf +//! section 42.4.4 //! //! \param mean mean of the Gaussian distribution //! \param std_dev standard deviation of the Gaussian distribution @@ -64,23 +75,6 @@ extern "C" double watt_spectrum(double a, double b, uint64_t* seed); extern "C" double normal_variate(double mean, double std_dev, uint64_t* seed); -//============================================================================== -//! Samples an energy from the Muir (Gaussian) energy-dependent distribution. -//! -//! This is another form of the Gaussian distribution but with more easily -//! modifiable parameters -//! https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-05411-MS -//! -//! \param e0 peak neutron energy [eV] -//! \param m_rat ratio of the fusion reactants to AMU -//! \param kt the ion temperature of the reactants [eV] -//! \param seed A pointer to the pseudorandom seed -//! \result The sampled outgoing energy -//============================================================================== - -extern "C" double muir_spectrum( - double e0, double m_rat, double kt, uint64_t* seed); - } // namespace openmc #endif // OPENMC_RANDOM_DIST_H diff --git a/include/openmc/random_lcg.h b/include/openmc/random_lcg.h index 4157b7cfe7..5aecafed3c 100644 --- a/include/openmc/random_lcg.h +++ b/include/openmc/random_lcg.h @@ -15,6 +15,7 @@ constexpr int STREAM_SOURCE {1}; constexpr int STREAM_URR_PTABLE {2}; constexpr int STREAM_VOLUME {3}; constexpr int64_t DEFAULT_SEED {1}; +constexpr uint64_t DEFAULT_STRIDE {152917ULL}; //============================================================================== //! Generate a pseudo-random number using a linear congruential generator. @@ -98,5 +99,18 @@ extern "C" int64_t openmc_get_seed(); extern "C" void openmc_set_seed(int64_t new_seed); +//============================================================================== +//! Get OpenMC's stride. +//============================================================================== + +extern "C" uint64_t openmc_get_stride(); + +//============================================================================== +//! Set OpenMC's stride. +//! @param new_stride Stride. +//============================================================================== + +extern "C" void openmc_set_stride(uint64_t new_stride); + } // namespace openmc #endif // OPENMC_RANDOM_LCG_H diff --git a/include/openmc/random_ray/flat_source_domain.h b/include/openmc/random_ray/flat_source_domain.h new file mode 100644 index 0000000000..d4e8027346 --- /dev/null +++ b/include/openmc/random_ray/flat_source_domain.h @@ -0,0 +1,218 @@ +#ifndef OPENMC_RANDOM_RAY_FLAT_SOURCE_DOMAIN_H +#define OPENMC_RANDOM_RAY_FLAT_SOURCE_DOMAIN_H + +#include "openmc/constants.h" +#include "openmc/openmp_interface.h" +#include "openmc/position.h" +#include "openmc/random_ray/parallel_map.h" +#include "openmc/random_ray/source_region.h" +#include "openmc/source.h" +#include +#include + +namespace openmc { + +/* + * The FlatSourceDomain class encompasses data and methods for storing + * scalar flux and source region for all flat source regions in a + * random ray simulation domain. + */ + +class FlatSourceDomain { +public: + //---------------------------------------------------------------------------- + // Constructors and Destructors + FlatSourceDomain(); + virtual ~FlatSourceDomain() = default; + + //---------------------------------------------------------------------------- + // Methods + virtual void update_single_neutron_source(SourceRegionHandle& srh); + virtual void update_all_neutron_sources(); + void compute_k_eff(); + virtual void normalize_scalar_flux_and_volumes( + double total_active_distance_per_iteration); + + int64_t add_source_to_scalar_flux(); + virtual void batch_reset(); + void convert_source_regions_to_tallies(int64_t start_sr_id); + void reset_tally_volumes(); + void random_ray_tally(); + virtual void accumulate_iteration_flux(); + void output_to_vtk() const; + void convert_external_sources(); + void count_external_source_regions(); + void set_adjoint_sources(); + void flux_swap(); + virtual double evaluate_flux_at_point(Position r, int64_t sr, int g) const; + double compute_fixed_source_normalization_factor() const; + void flatten_xs(); + void transpose_scattering_matrix(); + void serialize_final_fluxes(vector& flux); + void apply_meshes(); + void apply_mesh_to_cell_instances(int32_t i_cell, int32_t mesh_idx, + int target_material_id, const vector& instances, + bool is_target_void); + void apply_mesh_to_cell_and_children(int32_t i_cell, int32_t mesh_idx, + int32_t target_material_id, bool is_target_void); + SourceRegionHandle get_subdivided_source_region_handle( + SourceRegionKey sr_key, Position r, Direction u); + void finalize_discovered_source_regions(); + void apply_transport_stabilization(); + int64_t n_source_regions() const + { + return source_regions_.n_source_regions(); + } + int64_t n_source_elements() const + { + return source_regions_.n_source_regions() * negroups_; + } + int64_t lookup_base_source_region_idx(const GeometryState& p) const; + SourceRegionKey lookup_source_region_key(const GeometryState& p) const; + int64_t lookup_mesh_bin(int64_t sr, Position r) const; + int lookup_mesh_idx(int64_t sr) const; + + //---------------------------------------------------------------------------- + // Static Data members + static bool volume_normalized_flux_tallies_; + static bool adjoint_; // If the user wants outputs based on the adjoint flux + static double + diagonal_stabilization_rho_; // Adjusts strength of diagonal stabilization + // for transport corrected MGXS data + + // Static variables to store source region meshes and domains + static std::unordered_map>> + mesh_domain_map_; + + //---------------------------------------------------------------------------- + // Static data members + static RandomRayVolumeEstimator volume_estimator_; + + //---------------------------------------------------------------------------- + // Public Data members + double k_eff_ {1.0}; // Eigenvalue + bool mapped_all_tallies_ {false}; // If all source regions have been visited + + int64_t n_external_source_regions_ {0}; // Total number of source regions with + // non-zero external source terms + + // 1D array representing source region starting offset for each OpenMC Cell + // in model::cells + vector source_region_offsets_; + + // 2D arrays stored in 1D representing values for all materials x energy + // groups + int n_materials_; + vector sigma_t_; + vector nu_sigma_f_; + vector sigma_f_; + vector chi_; + + // 3D arrays stored in 1D representing values for all materials x energy + // groups x energy groups + vector sigma_s_; + + // The abstract container holding all source region-specific data + SourceRegionContainer source_regions_; + + // Parallel hash map holding all source regions discovered during + // a single iteration. This is a threadsafe data structure that is cleaned + // out after each iteration and stored in the "source_regions_" container. + // It is keyed with a SourceRegionKey, which combines the base source + // region index and the mesh bin. + ParallelMap + discovered_source_regions_; + + // Map that relates a SourceRegionKey to the index at which the source + // region can be found in the "source_regions_" container. + std::unordered_map + source_region_map_; + + // Map that relates a SourceRegionKey to the external source index. This map + // is used to check if there are any point sources within a subdivided source + // region at the time it is discovered. + std::unordered_map, SourceRegionKey::HashFunctor> + external_point_source_map_; + + // Map that relates a base source region index to the external source index. + // This map is used to check if there are any volumetric sources within a + // subdivided source region at the time it is discovered. + std::unordered_map> external_volumetric_source_map_; + + // Map that relates a base source region index to a mesh index. This map + // is used to check which subdivision mesh is present in a source region. + std::unordered_map mesh_map_; + + // If transport corrected MGXS data is being used, there may be negative + // in-group scattering cross sections that can result in instability in MOC + // and random ray if used naively. This flag enables a stabilization + // technique. + bool is_transport_stabilization_needed_ {false}; + +protected: + //---------------------------------------------------------------------------- + // Methods + void apply_external_source_to_source_region( + int src_idx, SourceRegionHandle& srh); + void apply_external_source_to_cell_instances(int32_t i_cell, int src_idx, + int target_material_id, const vector& instances); + void apply_external_source_to_cell_and_children( + int32_t i_cell, int src_idx, int32_t target_material_id); + virtual void set_flux_to_flux_plus_source(int64_t sr, double volume, int g); + void set_flux_to_source(int64_t sr, int g); + virtual void set_flux_to_old_flux(int64_t sr, int g); + + //---------------------------------------------------------------------------- + // Private data members + int negroups_; // Number of energy groups in simulation + + double + simulation_volume_; // Total physical volume of the simulation domain, as + // defined by the 3D box of the random ray source + + // Volumes for each tally and bin/score combination. This intermediate data + // structure is used when tallying quantities that must be normalized by + // volume (i.e., flux). The vector is index by tally index, while the inner 2D + // xtensor is indexed by bin index and score index in a similar manner to the + // results tensor in the Tally class, though without the third dimension, as + // SUM and SUM_SQ do not need to be tracked. + vector> tally_volumes_; + +}; // class FlatSourceDomain + +//============================================================================ +//! Non-member functions +//============================================================================ + +// Returns the inputted value in big endian byte ordering. If the system is +// little endian, the byte ordering is flipped. If the system is big endian, +// the inputted value is returned as is. This function is necessary as +// .vtk binary files use big endian byte ordering. +template +T convert_to_big_endian(T in) +{ + // 4 byte integer + uint32_t test = 1; + + // 1 byte pointer to first byte of test integer + uint8_t* ptr = reinterpret_cast(&test); + + // If the first byte of test is 0, then the system is big endian. In this + // case, we don't have to do anything as .vtk files are big endian + if (*ptr == 0) + return in; + + // Otherwise, the system is in little endian, so we need to flip the + // endianness + uint8_t* orig = reinterpret_cast(&in); + uint8_t swapper[sizeof(T)]; + for (int i = 0; i < sizeof(T); i++) { + swapper[i] = orig[sizeof(T) - i - 1]; + } + T out = *reinterpret_cast(&swapper); + return out; +} + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_FLAT_SOURCE_DOMAIN_H diff --git a/include/openmc/random_ray/linear_source_domain.h b/include/openmc/random_ray/linear_source_domain.h new file mode 100644 index 0000000000..0098c78200 --- /dev/null +++ b/include/openmc/random_ray/linear_source_domain.h @@ -0,0 +1,42 @@ +#ifndef OPENMC_RANDOM_RAY_LINEAR_SOURCE_DOMAIN_H +#define OPENMC_RANDOM_RAY_LINEAR_SOURCE_DOMAIN_H + +#include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/moment_matrix.h" + +#include "openmc/openmp_interface.h" +#include "openmc/position.h" +#include "openmc/source.h" + +namespace openmc { + +/* + * The LinearSourceDomain class encompasses data and methods for storing + * scalar flux and source region for all linear source regions in a + * random ray simulation domain. + */ + +class LinearSourceDomain : public FlatSourceDomain { +public: + //---------------------------------------------------------------------------- + // Methods + void update_single_neutron_source(SourceRegionHandle& srh) override; + void normalize_scalar_flux_and_volumes( + double total_active_distance_per_iteration) override; + + void batch_reset() override; + void accumulate_iteration_flux() override; + void output_to_vtk() const; + double evaluate_flux_at_point(Position r, int64_t sr, int g) const override; + +protected: + //---------------------------------------------------------------------------- + // Methods + void set_flux_to_flux_plus_source(int64_t sr, double volume, int g) override; + void set_flux_to_old_flux(int64_t sr, int g) override; + +}; // class LinearSourceDomain + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_LINEAR_SOURCE_DOMAIN_H diff --git a/include/openmc/random_ray/moment_matrix.h b/include/openmc/random_ray/moment_matrix.h new file mode 100644 index 0000000000..c95bb2c128 --- /dev/null +++ b/include/openmc/random_ray/moment_matrix.h @@ -0,0 +1,90 @@ +#ifndef OPENMC_MOMENT_MATRIX_H +#define OPENMC_MOMENT_MATRIX_H + +#include + +#include "openmc/position.h" + +namespace openmc { + +// The MomentArray class is a 3-element array representing the x, y, and z +// moments. It is defined as an alias for the Position class to allow for +// dot products and other operations with Position objects. +// TODO: This class could in theory have 32-bit instead of 64-bit FP values. +using MomentArray = Position; + +// The MomentMatrix class is a sparse representation a 3x3 symmetric +// matrix, with elements labeled as follows: +// +// | a b c | +// | b d e | +// | c e f | +// +// This class uses FP64 values as objects that are accumulated to over many +// iterations. +class MomentMatrix { +public: + //---------------------------------------------------------------------------- + // Public data members + double a; + double b; + double c; + double d; + double e; + double f; + + //---------------------------------------------------------------------------- + // Constructors + MomentMatrix() = default; + MomentMatrix(double a, double b, double c, double d, double e, double f) + : a {a}, b {b}, c {c}, d {d}, e {e}, f {f} + {} + + //---------------------------------------------------------------------------- + // Methods + MomentMatrix inverse() const; + double determinant() const; + void compute_spatial_moments_matrix( + const Position& r, const Direction& u, const double& distance); + + inline void set_to_zero() { a = b = c = d = e = f = 0; } + + inline MomentMatrix& operator*=(double x) + { + a *= x; + b *= x; + c *= x; + d *= x; + e *= x; + f *= x; + return *this; + } + + inline MomentMatrix operator*(double x) const + { + MomentMatrix m_copy = *this; + m_copy *= x; + return m_copy; + } + + inline MomentMatrix& operator+=(const MomentMatrix& rhs) + { + a += rhs.a; + b += rhs.b; + c += rhs.c; + d += rhs.d; + e += rhs.e; + f += rhs.f; + return *this; + } + + MomentArray operator*(const MomentArray& rhs) const + { + return {a * rhs.x + b * rhs.y + c * rhs.z, + b * rhs.x + d * rhs.y + e * rhs.z, c * rhs.x + e * rhs.y + f * rhs.z}; + } +}; + +} // namespace openmc + +#endif // OPENMC_MOMENT_MATRIX_H diff --git a/include/openmc/random_ray/parallel_map.h b/include/openmc/random_ray/parallel_map.h new file mode 100644 index 0000000000..7f4f06d999 --- /dev/null +++ b/include/openmc/random_ray/parallel_map.h @@ -0,0 +1,193 @@ +#ifndef OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H +#define OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H + +#include "openmc/openmp_interface.h" + +#include +#include + +namespace openmc { + +/* + * The ParallelMap class allows for threadsafe access to a map-like data + * structure. It is implemented as a hash table with a fixed number of buckets, + * each of which contains a mutex lock and an unordered_map. The class provides + * a subset of the functionality of std::unordered_map. Users must first lock + * the object (using the key) before accessing or modifying the map. The object + * is locked by bucket, allowing for multiple threads to manipulate different + * keys simultaneously, though sometimes threads will need to wait if keys + * happen to be in the same bucket. The ParallelMap will generate pointers to + * hold values, rather than direct storage of values, so as to allow for + * pointers to values to remain valid even after the lock has been released + * (though locking of those values is then left to the user). Iterators to the + * class are provided but are not threadsafe, and are meant to be used only in a + * serial context (e.g., to dump the contents of the map to another data + * structure). + */ + +template +class ParallelMap { + + //---------------------------------------------------------------------------- + // Helper structs and classes + + struct Bucket { + OpenMPMutex lock_; + std::unordered_map, HashFunctor> map_; + }; + + // The iterator yields a pair: (const KeyType&, ValueType&) + class iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = void; // Not providing pointer semantics. + using reference = value_type; + + iterator(std::vector* buckets, std::size_t bucket_index, + typename std::unordered_map, + HashFunctor>::iterator inner_it) + : buckets_(buckets), bucket_index_(bucket_index), inner_it_(inner_it) + { + // Advance to the first valid element if necessary. + advance_to_valid(); + } + + // Dereference returns a pair of (key, value). + reference operator*() const + { + return {inner_it_->first, *inner_it_->second}; + } + + iterator& operator++() + { + ++inner_it_; + advance_to_valid(); + return *this; + } + + iterator operator++(int) + { + iterator tmp = *this; + ++(*this); + return tmp; + } + + bool operator==(const iterator& other) const + { + // Two iterators are equal if they refer to the same bucket vector and are + // both at end, or if they have the same bucket index and inner iterator. + return buckets_ == other.buckets_ && + bucket_index_ == other.bucket_index_ && + (bucket_index_ == buckets_->size() || + inner_it_ == other.inner_it_); + } + + bool operator!=(const iterator& other) const { return !(*this == other); } + + private: + // Helper function: if we are at the end of the current bucket, advance to + // the next non-empty bucket. + void advance_to_valid() + { + while (bucket_index_ < buckets_->size() && + inner_it_ == (*buckets_)[bucket_index_].map_.end()) { + ++bucket_index_; + if (bucket_index_ < buckets_->size()) + inner_it_ = (*buckets_)[bucket_index_].map_.begin(); + } + } + + std::vector* buckets_; + std::size_t bucket_index_; + typename std::unordered_map, + HashFunctor>::iterator inner_it_; + }; + +public: + //---------------------------------------------------------------------------- + // Constructor + ParallelMap(int n_buckets = 1000) : buckets_(n_buckets) {} + + //---------------------------------------------------------------------------- + // Public Methods + void lock(const KeyType& key) + { + Bucket& bucket = get_bucket(key); + bucket.lock_.lock(); + } + + void unlock(const KeyType& key) + { + Bucket& bucket = get_bucket(key); + bucket.lock_.unlock(); + } + + void clear() + { + for (auto& bucket : buckets_) { + bucket.map_.clear(); + } + } + + bool contains(const KeyType& key) + { + Bucket& bucket = get_bucket(key); + // C++20 + // return bucket.map_.contains(key); + return bucket.map_.find(key) != bucket.map_.end(); + } + + ValueType& operator[](const KeyType& key) + { + Bucket& bucket = get_bucket(key); + return *bucket.map_[key].get(); + } + + ValueType* emplace(KeyType key, const ValueType& value) + { + Bucket& bucket = get_bucket(key); + // Attempt to emplace the new element into the unordered_map within the + auto result = bucket.map_.emplace(key, std::make_unique(value)); + auto it = result.first; + return it->second.get(); + } + + // Return iterator to first element. + iterator begin() + { + std::size_t bucket_index = 0; + auto inner_it = buckets_.empty() + ? typename std::unordered_map, HashFunctor>::iterator() + : buckets_[0].map_.begin(); + return iterator(&buckets_, bucket_index, inner_it); + } + + // Return iterator to one-past-last element. + iterator end() + { + // End is signaled by bucket_index_ equal to buckets_.size() + return iterator(&buckets_, buckets_.size(), + typename std::unordered_map, + HashFunctor>::iterator()); + } + +private: + //---------------------------------------------------------------------------- + // Private Methods + Bucket& get_bucket(const KeyType& key) + { + return buckets_[hash(key) % buckets_.size()]; + } + + //---------------------------------------------------------------------------- + // Private Data Fields + HashFunctor hash; + vector buckets_; +}; + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H diff --git a/include/openmc/random_ray/random_ray.h b/include/openmc/random_ray/random_ray.h new file mode 100644 index 0000000000..40c67ef954 --- /dev/null +++ b/include/openmc/random_ray/random_ray.h @@ -0,0 +1,77 @@ +#ifndef OPENMC_RANDOM_RAY_H +#define OPENMC_RANDOM_RAY_H + +#include "openmc/memory.h" +#include "openmc/particle.h" +#include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/moment_matrix.h" +#include "openmc/source.h" + +namespace openmc { + +/* + * The RandomRay class encompasses data and methods for transporting random rays + * through the model. It is a small extension of the Particle class. + */ + +// TODO: Inherit from GeometryState instead of Particle +class RandomRay : public Particle { +public: + //---------------------------------------------------------------------------- + // Constructors + RandomRay(); + RandomRay(uint64_t ray_id, FlatSourceDomain* domain); + + //---------------------------------------------------------------------------- + // Methods + void event_advance_ray(); + void attenuate_flux(double distance, bool is_active, double offset = 0.0); + void attenuate_flux_inner( + double distance, bool is_active, int64_t sr, int mesh_bin, Position r); + void attenuate_flux_flat_source( + SourceRegionHandle& srh, double distance, bool is_active, Position r); + void attenuate_flux_flat_source_void( + SourceRegionHandle& srh, double distance, bool is_active, Position r); + void attenuate_flux_linear_source( + SourceRegionHandle& srh, double distance, bool is_active, Position r); + void attenuate_flux_linear_source_void( + SourceRegionHandle& srh, double distance, bool is_active, Position r); + + void initialize_ray(uint64_t ray_id, FlatSourceDomain* domain); + uint64_t transport_history_based_single_ray(); + SourceSite sample_prng(); + SourceSite sample_halton(); + + //---------------------------------------------------------------------------- + // Static data members + static double distance_inactive_; // Inactive (dead zone) ray length + static double distance_active_; // Active ray length + static unique_ptr ray_source_; // Starting source for ray sampling + static RandomRaySourceShape source_shape_; // Flag for linear source + static RandomRaySampleMethod sample_method_; // Flag for sampling method + + //---------------------------------------------------------------------------- + // Public data members + vector angular_flux_; + + bool ray_trace_only_ {false}; // If true, only perform geometry operations + +private: + //---------------------------------------------------------------------------- + // Private data members + vector delta_psi_; + vector delta_moments_; + vector mesh_bins_; + vector mesh_fractional_lengths_; + + int negroups_; + FlatSourceDomain* domain_ {nullptr}; // pointer to domain that has flat source + // data needed for ray transport + double distance_travelled_ {0}; + bool is_active_ {false}; + bool is_alive_ {true}; +}; // class RandomRay + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_H diff --git a/include/openmc/random_ray/random_ray_simulation.h b/include/openmc/random_ray/random_ray_simulation.h new file mode 100644 index 0000000000..3dec48bf26 --- /dev/null +++ b/include/openmc/random_ray/random_ray_simulation.h @@ -0,0 +1,66 @@ +#ifndef OPENMC_RANDOM_RAY_SIMULATION_H +#define OPENMC_RANDOM_RAY_SIMULATION_H + +#include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/linear_source_domain.h" + +namespace openmc { + +/* + * The RandomRaySimulation class encompasses data and methods for running a + * random ray simulation. + */ + +class RandomRaySimulation { +public: + //---------------------------------------------------------------------------- + // Constructors + RandomRaySimulation(); + + //---------------------------------------------------------------------------- + // Methods + void compute_segment_correction_factors(); + void apply_fixed_sources_and_mesh_domains(); + void prepare_fixed_sources_adjoint(); + void simulate(); + void output_simulation_results() const; + void instability_check( + int64_t n_hits, double k_eff, double& avg_miss_rate) const; + void print_results_random_ray(uint64_t total_geometric_intersections, + double avg_miss_rate, int negroups, int64_t n_source_regions, + int64_t n_external_source_regions) const; + + //---------------------------------------------------------------------------- + // Accessors + FlatSourceDomain* domain() const { return domain_.get(); } + +private: + //---------------------------------------------------------------------------- + // Data members + + // Contains all flat source region data + unique_ptr domain_; + + // Tracks the average FSR miss rate for analysis and reporting + double avg_miss_rate_ {0.0}; + + // Tracks the total number of geometric intersections by all rays for + // reporting + uint64_t total_geometric_intersections_ {0}; + + // Number of energy groups + int negroups_; + +}; // class RandomRaySimulation + +//============================================================================ +//! Non-member functions +//============================================================================ + +void openmc_run_random_ray(); +void validate_random_ray_inputs(); +void openmc_reset_random_ray(); + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_SIMULATION_H diff --git a/include/openmc/random_ray/source_region.h b/include/openmc/random_ray/source_region.h new file mode 100644 index 0000000000..0f5a747fff --- /dev/null +++ b/include/openmc/random_ray/source_region.h @@ -0,0 +1,679 @@ +#ifndef OPENMC_RANDOM_RAY_SOURCE_REGION_H +#define OPENMC_RANDOM_RAY_SOURCE_REGION_H + +#include "openmc/openmp_interface.h" +#include "openmc/position.h" +#include "openmc/random_ray/moment_matrix.h" +#include "openmc/settings.h" + +namespace openmc { + +//---------------------------------------------------------------------------- +// Helper Functions + +// The hash_combine function is the standard hash combine function from boost +// that is typically used for combining multiple hash values into a single hash +// as is needed for larger objects being stored in a hash map. The function is +// taken from: +// https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine +// which carries the following license: +// +// Boost Software License - Version 1.0 - August 17th, 2003 +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +inline void hash_combine(size_t& seed, const size_t v) +{ + seed ^= (v + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + +//---------------------------------------------------------------------------- +// Helper Structs and Classes + +// A mapping object that is used to map between a specific random ray +// source region and an OpenMC native tally bin that it should score to +// every iteration. +struct TallyTask { + int tally_idx; + int filter_idx; + int score_idx; + int score_type; + TallyTask(int tally_idx, int filter_idx, int score_idx, int score_type) + : tally_idx(tally_idx), filter_idx(filter_idx), score_idx(score_idx), + score_type(score_type) + {} + TallyTask() = default; + + // Comparison and Hash operators are defined to allow usage of the + // TallyTask struct as a key in an unordered_set + bool operator==(const TallyTask& other) const + { + return tally_idx == other.tally_idx && filter_idx == other.filter_idx && + score_idx == other.score_idx && score_type == other.score_type; + } + + struct HashFunctor { + size_t operator()(const TallyTask& task) const + { + size_t seed = 0; + hash_combine(seed, task.tally_idx); + hash_combine(seed, task.filter_idx); + hash_combine(seed, task.score_idx); + hash_combine(seed, task.score_type); + return seed; + } + }; +}; + +// The SourceRegionKey combines a base source region (i.e., a material +// filled cell instance) with a mesh bin. This key is used as a handle +// for dynamically discovered source regions when subdividing source +// regions with meshes. +class SourceRegionKey { +public: + int64_t base_source_region_id; + int64_t mesh_bin; + SourceRegionKey() = default; + SourceRegionKey(int64_t source_region, int64_t bin) + : base_source_region_id(source_region), mesh_bin(bin) + {} + + // Equality operator required by the unordered_map + bool operator==(const SourceRegionKey& other) const + { + return base_source_region_id == other.base_source_region_id && + mesh_bin == other.mesh_bin; + } + + // Less than operator required by std::sort + bool operator<(const SourceRegionKey& other) const + { + if (base_source_region_id < other.base_source_region_id) { + return true; + } else if (base_source_region_id > other.base_source_region_id) { + return false; + } else { + return mesh_bin < other.mesh_bin; + } + } + + // Hashing functor required by the unordered_map + struct HashFunctor { + size_t operator()(const SourceRegionKey& key) const + { + size_t seed = 0; + hash_combine(seed, key.base_source_region_id); + hash_combine(seed, key.mesh_bin); + return seed; + } + }; +}; + +// Forward declaration of SourceRegion +class SourceRegion; + +class SourceRegionHandle { +public: + //---------------------------------------------------------------------------- + // Constructors + SourceRegionHandle(SourceRegion& sr); + SourceRegionHandle() = default; + + // All fields are commented/described in the SourceRegion class definition + // below + + //---------------------------------------------------------------------------- + // Public Data members + int negroups_; + bool is_numerical_fp_artifact_ {false}; + bool is_linear_ {false}; + + // Scalar fields + int* material_; + int* is_small_; + int* n_hits_; + int* birthday_; + OpenMPMutex* lock_; + double* volume_; + double* volume_t_; + double* volume_sq_; + double* volume_sq_t_; + double* volume_naive_; + int* position_recorded_; + int* external_source_present_; + Position* position_; + Position* centroid_; + Position* centroid_iteration_; + Position* centroid_t_; + MomentMatrix* mom_matrix_; + MomentMatrix* mom_matrix_t_; + // A set of volume tally tasks. This more complicated data structure is + // convenient for ensuring that volumes are only tallied once per source + // region, regardless of how many energy groups are used for tallying. + std::unordered_set* volume_task_; + + // Mesh that subdivides this source region + int* mesh_; + int64_t* parent_sr_; + + // Energy group-wise 1D arrays + double* scalar_flux_old_; + double* scalar_flux_new_; + float* source_; + float* external_source_; + double* scalar_flux_final_; + + MomentArray* source_gradients_; + MomentArray* flux_moments_old_; + MomentArray* flux_moments_new_; + MomentArray* flux_moments_t_; + + // 2D array representing values for all energy groups x tally + // tasks. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array. + vector* tally_task_; + + //---------------------------------------------------------------------------- + // Public Accessors + + int& material() { return *material_; } + const int material() const { return *material_; } + + int& is_small() { return *is_small_; } + const int is_small() const { return *is_small_; } + + int& n_hits() { return *n_hits_; } + const int n_hits() const { return *n_hits_; } + + void lock() { lock_->lock(); } + void unlock() { lock_->unlock(); } + + double& volume() { return *volume_; } + const double volume() const { return *volume_; } + + double& volume_t() { return *volume_t_; } + const double volume_t() const { return *volume_t_; } + + double& volume_sq() { return *volume_sq_; } + const double volume_sq() const { return *volume_sq_; } + + double& volume_sq_t() { return *volume_sq_t_; } + const double volume_sq_t() const { return *volume_sq_t_; } + + double& volume_naive() { return *volume_naive_; } + const double volume_naive() const { return *volume_naive_; } + + int& position_recorded() { return *position_recorded_; } + const int position_recorded() const { return *position_recorded_; } + + int& external_source_present() { return *external_source_present_; } + const int external_source_present() const + { + return *external_source_present_; + } + + Position& position() { return *position_; } + const Position position() const { return *position_; } + + Position& centroid() { return *centroid_; } + const Position centroid() const { return *centroid_; } + + Position& centroid_iteration() { return *centroid_iteration_; } + const Position centroid_iteration() const { return *centroid_iteration_; } + + Position& centroid_t() { return *centroid_t_; } + const Position centroid_t() const { return *centroid_t_; } + + MomentMatrix& mom_matrix() { return *mom_matrix_; } + const MomentMatrix mom_matrix() const { return *mom_matrix_; } + + MomentMatrix& mom_matrix_t() { return *mom_matrix_t_; } + const MomentMatrix mom_matrix_t() const { return *mom_matrix_t_; } + + std::unordered_set& volume_task() + { + return *volume_task_; + } + const std::unordered_set& volume_task() + const + { + return *volume_task_; + } + + int& mesh() { return *mesh_; } + const int mesh() const { return *mesh_; } + + int64_t& parent_sr() { return *parent_sr_; } + const int64_t parent_sr() const { return *parent_sr_; } + + double& scalar_flux_old(int g) { return scalar_flux_old_[g]; } + const double scalar_flux_old(int g) const { return scalar_flux_old_[g]; } + + double& scalar_flux_new(int g) { return scalar_flux_new_[g]; } + const double scalar_flux_new(int g) const { return scalar_flux_new_[g]; } + + double& scalar_flux_final(int g) { return scalar_flux_final_[g]; } + const double scalar_flux_final(int g) const { return scalar_flux_final_[g]; } + + float& source(int g) { return source_[g]; } + const float source(int g) const { return source_[g]; } + + float& external_source(int g) { return external_source_[g]; } + const float external_source(int g) const { return external_source_[g]; } + + MomentArray& source_gradients(int g) { return source_gradients_[g]; } + const MomentArray source_gradients(int g) const + { + return source_gradients_[g]; + } + + MomentArray& flux_moments_old(int g) { return flux_moments_old_[g]; } + const MomentArray flux_moments_old(int g) const + { + return flux_moments_old_[g]; + } + + MomentArray& flux_moments_new(int g) { return flux_moments_new_[g]; } + const MomentArray flux_moments_new(int g) const + { + return flux_moments_new_[g]; + } + + MomentArray& flux_moments_t(int g) { return flux_moments_t_[g]; } + const MomentArray flux_moments_t(int g) const { return flux_moments_t_[g]; } + + vector& tally_task(int g) { return tally_task_[g]; } + const vector& tally_task(int g) const { return tally_task_[g]; } + +}; // class SourceRegionHandle + +class SourceRegion { +public: + //---------------------------------------------------------------------------- + // Constructors + SourceRegion(int negroups, bool is_linear); + SourceRegion() = default; + + //---------------------------------------------------------------------------- + // Public Data members + + //--------------------------------------- + // Scalar fields + + int material_ {0}; //!< Index in openmc::model::materials array + OpenMPMutex lock_; + double volume_ { + 0.0}; //!< Volume (computed from the sum of ray crossing lengths) + double volume_t_ {0.0}; //!< Volume totaled over all iterations + double volume_sq_ {0.0}; //!< Volume squared + double volume_sq_t_ {0.0}; //!< Volume squared totaled over all iterations + double volume_naive_ {0.0}; //!< Volume as integrated from this iteration only + int position_recorded_ {0}; //!< Has the position been recorded yet? + int external_source_present_ { + 0}; //!< Is an external source present in this region? + int is_small_ {0}; //!< Is it "small", receiving < 1.5 hits per iteration? + int n_hits_ {0}; //!< Number of total hits (ray crossings) + // Mesh that subdivides this source region + int mesh_ {C_NONE}; //!< Index in openmc::model::meshes array that subdivides + //!< this source region + int64_t parent_sr_ {C_NONE}; //!< Index of a parent source region + Position position_ { + 0.0, 0.0, 0.0}; //!< A position somewhere inside the region + Position centroid_ {0.0, 0.0, 0.0}; //!< The centroid + Position centroid_iteration_ { + 0.0, 0.0, 0.0}; //!< The centroid integrated from this iteration only + Position centroid_t_ { + 0.0, 0.0, 0.0}; //!< The centroid accumulated over all iterations + MomentMatrix mom_matrix_ { + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; //!< The spatial moment matrix + MomentMatrix mom_matrix_t_ {0.0, 0.0, 0.0, 0.0, 0.0, + 0.0}; //!< The spatial moment matrix accumulated over all iterations + + // A set of volume tally tasks. This more complicated data structure is + // convenient for ensuring that volumes are only tallied once per source + // region, regardless of how many energy groups are used for tallying. + std::unordered_set volume_task_; + + //--------------------------------------- + // Energy group-wise 1D arrays + + vector + scalar_flux_old_; //!< The scalar flux from the previous iteration + vector + scalar_flux_new_; //!< The scalar flux from the current iteration + vector + source_; //!< The total source term (fission + scattering + external) + vector external_source_; //!< The external source term + vector scalar_flux_final_; //!< The scalar flux accumulated over all + //!< active iterations (used for plotting, + //!< or computing adjoint sources) + + vector source_gradients_; //!< The linear source gradients + vector + flux_moments_old_; //!< The linear flux moments from the previous iteration + vector + flux_moments_new_; //!< The linear flux moments from the current iteration + vector + flux_moments_t_; //!< The linear flux moments accumulated over all active + //!< iterations (used for plotting) + + //--------------------------------------- + // 2D array representing values for all energy groups x tally + // tasks. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array. + vector> tally_task_; +}; // class SourceRegion + +class SourceRegionContainer { +public: + //---------------------------------------------------------------------------- + // Constructors + SourceRegionContainer(int negroups, bool is_linear) + : negroups_(negroups), is_linear_(is_linear) + {} + SourceRegionContainer() = default; + + //---------------------------------------------------------------------------- + // Public Accessors + int& material(int64_t sr) { return material_[sr]; } + const int material(int64_t sr) const { return material_[sr]; } + + int& is_small(int64_t sr) { return is_small_[sr]; } + const int is_small(int64_t sr) const { return is_small_[sr]; } + + int& n_hits(int64_t sr) { return n_hits_[sr]; } + const int n_hits(int64_t sr) const { return n_hits_[sr]; } + + OpenMPMutex& lock(int64_t sr) { return lock_[sr]; } + const OpenMPMutex& lock(int64_t sr) const { return lock_[sr]; } + + double& volume(int64_t sr) { return volume_[sr]; } + const double volume(int64_t sr) const { return volume_[sr]; } + + double& volume_t(int64_t sr) { return volume_t_[sr]; } + const double volume_t(int64_t sr) const { return volume_t_[sr]; } + + double& volume_sq(int64_t sr) { return volume_sq_[sr]; } + const double volume_sq(int64_t sr) const { return volume_sq_[sr]; } + + double& volume_sq_t(int64_t sr) { return volume_sq_t_[sr]; } + const double volume_sq_t(int64_t sr) const { return volume_sq_t_[sr]; } + + double& volume_naive(int64_t sr) { return volume_naive_[sr]; } + const double volume_naive(int64_t sr) const { return volume_naive_[sr]; } + + int& position_recorded(int64_t sr) { return position_recorded_[sr]; } + const int position_recorded(int64_t sr) const + { + return position_recorded_[sr]; + } + + int& external_source_present(int64_t sr) + { + return external_source_present_[sr]; + } + const int external_source_present(int64_t sr) const + { + return external_source_present_[sr]; + } + + Position& position(int64_t sr) { return position_[sr]; } + const Position position(int64_t sr) const { return position_[sr]; } + + Position& centroid(int64_t sr) { return centroid_[sr]; } + const Position centroid(int64_t sr) const { return centroid_[sr]; } + + Position& centroid_iteration(int64_t sr) { return centroid_iteration_[sr]; } + const Position centroid_iteration(int64_t sr) const + { + return centroid_iteration_[sr]; + } + + Position& centroid_t(int64_t sr) { return centroid_t_[sr]; } + const Position centroid_t(int64_t sr) const { return centroid_t_[sr]; } + + MomentMatrix& mom_matrix(int64_t sr) { return mom_matrix_[sr]; } + const MomentMatrix mom_matrix(int64_t sr) const { return mom_matrix_[sr]; } + + MomentMatrix& mom_matrix_t(int64_t sr) { return mom_matrix_t_[sr]; } + const MomentMatrix mom_matrix_t(int64_t sr) const + { + return mom_matrix_t_[sr]; + } + + MomentArray& source_gradients(int64_t sr, int g) + { + return source_gradients_[index(sr, g)]; + } + const MomentArray source_gradients(int64_t sr, int g) const + { + return source_gradients_[index(sr, g)]; + } + MomentArray& source_gradients(int64_t se) { return source_gradients_[se]; } + const MomentArray source_gradients(int64_t se) const + { + return source_gradients_[se]; + } + + MomentArray& flux_moments_old(int64_t sr, int g) + { + return flux_moments_old_[index(sr, g)]; + } + const MomentArray flux_moments_old(int64_t sr, int g) const + { + return flux_moments_old_[index(sr, g)]; + } + MomentArray& flux_moments_old(int64_t se) { return flux_moments_old_[se]; } + const MomentArray flux_moments_old(int64_t se) const + { + return flux_moments_old_[se]; + } + + MomentArray& flux_moments_new(int64_t sr, int g) + { + return flux_moments_new_[index(sr, g)]; + } + const MomentArray flux_moments_new(int64_t sr, int g) const + { + return flux_moments_new_[index(sr, g)]; + } + MomentArray& flux_moments_new(int64_t se) { return flux_moments_new_[se]; } + const MomentArray flux_moments_new(int64_t se) const + { + return flux_moments_new_[se]; + } + + MomentArray& flux_moments_t(int64_t sr, int g) + { + return flux_moments_t_[index(sr, g)]; + } + const MomentArray flux_moments_t(int64_t sr, int g) const + { + return flux_moments_t_[index(sr, g)]; + } + MomentArray& flux_moments_t(int64_t se) { return flux_moments_t_[se]; } + const MomentArray flux_moments_t(int64_t se) const + { + return flux_moments_t_[se]; + } + + double& scalar_flux_old(int64_t sr, int g) + { + return scalar_flux_old_[index(sr, g)]; + } + const double scalar_flux_old(int64_t sr, int g) const + { + return scalar_flux_old_[index(sr, g)]; + } + double& scalar_flux_old(int64_t se) { return scalar_flux_old_[se]; } + const double scalar_flux_old(int64_t se) const + { + return scalar_flux_old_[se]; + } + + double& scalar_flux_new(int64_t sr, int g) + { + return scalar_flux_new_[index(sr, g)]; + } + const double scalar_flux_new(int64_t sr, int g) const + { + return scalar_flux_new_[index(sr, g)]; + } + double& scalar_flux_new(int64_t se) { return scalar_flux_new_[se]; } + const double scalar_flux_new(int64_t se) const + { + return scalar_flux_new_[se]; + } + + double& scalar_flux_final(int64_t sr, int g) + { + return scalar_flux_final_[index(sr, g)]; + } + const double scalar_flux_final(int64_t sr, int g) const + { + return scalar_flux_final_[index(sr, g)]; + } + double& scalar_flux_final(int64_t se) { return scalar_flux_final_[se]; } + const double scalar_flux_final(int64_t se) const + { + return scalar_flux_final_[se]; + } + + float& source(int64_t sr, int g) { return source_[index(sr, g)]; } + const float source(int64_t sr, int g) const { return source_[index(sr, g)]; } + float& source(int64_t se) { return source_[se]; } + const float source(int64_t se) const { return source_[se]; } + + float& external_source(int64_t sr, int g) + { + return external_source_[index(sr, g)]; + } + const float external_source(int64_t sr, int g) const + { + return external_source_[index(sr, g)]; + } + float& external_source(int64_t se) { return external_source_[se]; } + const float external_source(int64_t se) const { return external_source_[se]; } + + vector& tally_task(int64_t sr, int g) + { + return tally_task_[index(sr, g)]; + } + const vector& tally_task(int64_t sr, int g) const + { + return tally_task_[index(sr, g)]; + } + vector& tally_task(int64_t se) { return tally_task_[se]; } + const vector& tally_task(int64_t se) const + { + return tally_task_[se]; + } + + std::unordered_set& volume_task(int64_t sr) + { + return volume_task_[sr]; + } + const std::unordered_set& volume_task( + int64_t sr) const + { + return volume_task_[sr]; + } + + int& mesh(int64_t sr) { return mesh_[sr]; } + const int mesh(int64_t sr) const { return mesh_[sr]; } + + int64_t& parent_sr(int64_t sr) { return parent_sr_[sr]; } + const int64_t parent_sr(int64_t sr) const { return parent_sr_[sr]; } + + //---------------------------------------------------------------------------- + // Public Methods + + void push_back(const SourceRegion& sr); + void assign(int n_source_regions, const SourceRegion& source_region); + void flux_swap(); + int64_t n_source_regions() const { return n_source_regions_; } + int64_t n_source_elements() const { return n_source_regions_ * negroups_; } + int& negroups() { return negroups_; } + const int negroups() const { return negroups_; } + bool& is_linear() { return is_linear_; } + const bool is_linear() const { return is_linear_; } + SourceRegionHandle get_source_region_handle(int64_t sr); + void adjoint_reset(); + +private: + //---------------------------------------------------------------------------- + // Private Data Members + int64_t n_source_regions_ {0}; + int negroups_ {0}; + bool is_linear_ {false}; + + // SoA storage for scalar fields (one item per source region) + vector material_; + vector is_small_; + vector n_hits_; + vector mesh_; + vector parent_sr_; + vector lock_; + vector volume_; + vector volume_t_; + vector volume_sq_; + vector volume_sq_t_; + vector volume_naive_; + vector position_recorded_; + vector external_source_present_; + vector position_; + vector centroid_; + vector centroid_iteration_; + vector centroid_t_; + vector mom_matrix_; + vector mom_matrix_t_; + // A set of volume tally tasks. This more complicated data structure is + // convenient for ensuring that volumes are only tallied once per source + // region, regardless of how many energy groups are used for tallying. + vector> volume_task_; + + // SoA energy group-wise 2D arrays flattened to 1D + vector scalar_flux_old_; + vector scalar_flux_new_; + vector scalar_flux_final_; + vector source_; + vector external_source_; + + vector source_gradients_; + vector flux_moments_old_; + vector flux_moments_new_; + vector flux_moments_t_; + + // SoA 3D array representing values for all source regions x energy groups x + // tally tasks. The outer two dimensions (source regions and energy groups) + // are flattened to 1D. Each group may have a different number of tally tasks + // associated with it, necessitating the use of a jagged array for the inner + // dimension. + vector> tally_task_; + + //---------------------------------------------------------------------------- + // Private Methods + + // Helper function for indexing + inline int index(int64_t sr, int g) const { return sr * negroups_ + g; } +}; + +} // namespace openmc + +#endif // OPENMC_RANDOM_RAY_SOURCE_REGION_H diff --git a/include/openmc/reaction.h b/include/openmc/reaction.h index 46705acf5e..3314d18666 100644 --- a/include/openmc/reaction.h +++ b/include/openmc/reaction.h @@ -7,9 +7,10 @@ #include #include "hdf5.h" -#include +#include "openmc/particle_data.h" #include "openmc/reaction_product.h" +#include "openmc/span.h" #include "openmc/vector.h" namespace openmc { @@ -25,7 +26,21 @@ public: //! Construct reaction from HDF5 data //! \param[in] group HDF5 group containing reaction data //! \param[in] temperatures Desired temperatures for cross sections - explicit Reaction(hid_t group, const vector& temperatures); + //! \param[in] name Name of the nuclide + explicit Reaction( + hid_t group, const vector& temperatures, std::string name); + + //! Calculate cross section given temperautre/grid index, interpolation factor + // + //! \param[in] i_temp Temperature index + //! \param[in] i_grid Energy grid index + //! \param[in] interp_factor Interpolation factor between grid points + double xs(int64_t i_temp, int64_t i_grid, double interp_factor) const; + + //! Calculate cross section + // + //! \param[in] micro Microscopic cross section cache + double xs(const NuclideMicroXS& micro) const; //! \brief Calculate reaction rate based on group-wise flux distribution // @@ -34,8 +49,8 @@ public: //! \param[in] flux Flux in each energy group (not normalized per eV) //! \param[in] grid Nuclide energy grid //! \return Reaction rate - double collapse_rate(gsl::index i_temp, gsl::span energy, - gsl::span flux, const vector& grid) const; + double collapse_rate(int64_t i_temp, span energy, + span flux, const vector& grid) const; //! Cross section at a single temperature struct TemperatureXS { diff --git a/include/openmc/reaction_product.h b/include/openmc/reaction_product.h index ce4fa8fc76..4fbbc1b626 100644 --- a/include/openmc/reaction_product.h +++ b/include/openmc/reaction_product.h @@ -7,6 +7,7 @@ #include "hdf5.h" #include "openmc/angle_energy.h" +#include "openmc/chain.h" #include "openmc/endf.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/particle.h" @@ -37,6 +38,10 @@ public: //! \param[in] group HDF5 group containing data explicit ReactionProduct(hid_t group); + //! Construct reaction product for decay photon from chain nuclide product + //! \param[in] product Chain nuclide product + explicit ReactionProduct(const ChainNuclide::Product& product); + //! Sample an outgoing angle and energy //! \param[in] E_in Incoming energy in [eV] //! \param[out] E_out Outgoing energy in [eV] @@ -50,6 +55,7 @@ public: unique_ptr yield_; //!< Yield as a function of energy vector applicability_; //!< Applicability of distribution vector distribution_; //!< Secondary angle-energy distribution + int parent_nuclide_ = -1; //!< Index of chain nuclide that is parent }; } // namespace openmc diff --git a/include/openmc/scattdata.h b/include/openmc/scattdata.h index 9f911d7db2..a75ef09d97 100644 --- a/include/openmc/scattdata.h +++ b/include/openmc/scattdata.h @@ -137,22 +137,23 @@ protected: public: void init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, - const double_3dvec& coeffs); + const double_3dvec& coeffs) override; - void combine( - const vector& those_scatts, const vector& scalars); + void combine(const vector& those_scatts, + const vector& scalars) override; //! \brief Find the maximal value of the angular distribution to use as a // bounding box with rejection sampling. void update_max_val(); - double calc_f(int gin, int gout, double mu); + double calc_f(int gin, int gout, double mu) override; - void sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed); + void sample( + int gin, int& gout, double& mu, double& wgt, uint64_t* seed) override; - size_t get_order() { return dist[0][0].size() - 1; }; + size_t get_order() override { return dist[0][0].size() - 1; }; - xt::xtensor get_matrix(size_t max_order); + xt::xtensor get_matrix(size_t max_order) override; }; //============================================================================== @@ -170,18 +171,19 @@ protected: public: void init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, - const double_3dvec& coeffs); + const double_3dvec& coeffs) override; - void combine( - const vector& those_scatts, const vector& scalars); + void combine(const vector& those_scatts, + const vector& scalars) override; - double calc_f(int gin, int gout, double mu); + double calc_f(int gin, int gout, double mu) override; - void sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed); + void sample( + int gin, int& gout, double& mu, double& wgt, uint64_t* seed) override; - size_t get_order() { return dist[0][0].size(); }; + size_t get_order() override { return dist[0][0].size(); }; - xt::xtensor get_matrix(size_t max_order); + xt::xtensor get_matrix(size_t max_order) override; }; //============================================================================== @@ -204,18 +206,19 @@ protected: public: void init(const xt::xtensor& in_gmin, const xt::xtensor& in_gmax, const double_2dvec& in_mult, - const double_3dvec& coeffs); + const double_3dvec& coeffs) override; - void combine( - const vector& those_scatts, const vector& scalars); + void combine(const vector& those_scatts, + const vector& scalars) override; - double calc_f(int gin, int gout, double mu); + double calc_f(int gin, int gout, double mu) override; - void sample(int gin, int& gout, double& mu, double& wgt, uint64_t* seed); + void sample( + int gin, int& gout, double& mu, double& wgt, uint64_t* seed) override; - size_t get_order() { return dist[0][0].size(); }; + size_t get_order() override { return dist[0][0].size(); }; - xt::xtensor get_matrix(size_t max_order); + xt::xtensor get_matrix(size_t max_order) override; }; //============================================================================== diff --git a/include/openmc/secondary_thermal.h b/include/openmc/secondary_thermal.h index 84ed68f51e..5b18902afb 100644 --- a/include/openmc/secondary_thermal.h +++ b/include/openmc/secondary_thermal.h @@ -150,6 +150,34 @@ private: //!< each incident energy }; +//============================================================================== +//! Mixed coherent/incoherent elastic angle-energy distribution +//============================================================================== + +class MixedElasticAE : public AngleEnergy { +public: + //! Construct from HDF5 file + // + //! \param[in] group HDF5 group + explicit MixedElasticAE( + hid_t group, const CoherentElasticXS& coh_xs, const Function1D& incoh_xs); + + //! Sample distribution for an angle and energy + //! \param[in] E_in Incoming energy in [eV] + //! \param[out] E_out Outgoing energy in [eV] + //! \param[out] mu Outgoing cosine with respect to current direction + //! \param[inout] seed Pseudorandom number seed pointer + void sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const override; + +private: + CoherentElasticAE coherent_dist_; //!< Coherent distribution + unique_ptr incoherent_dist_; //!< Incoherent distribution + + const CoherentElasticXS& coherent_xs_; //!< Ref. to coherent XS + const Function1D& incoherent_xs_; //!< Polymorphic ref. to incoherent XS +}; + } // namespace openmc #endif // OPENMC_SECONDARY_THERMAL_H diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 50be80a506..78bfa088e6 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -16,6 +16,22 @@ namespace openmc { +// Type of surface source write +enum class SSWCellType { + None, + Both, + From, + To, +}; + +// Type of IFP parameters +enum class IFPParameter { + None, + Both, + BetaEffective, + GenerationTime, +}; + //============================================================================== // Global variable declarations //============================================================================== @@ -28,36 +44,47 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? -extern "C" bool cmfd_run; //!< is a CMFD run? +extern bool create_delayed_neutrons; //!< create delayed fission neutrons? +extern "C" bool cmfd_run; //!< is a CMFD run? extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed extern "C" bool entropy_on; //!< calculate Shannon entropy? extern "C" bool - event_based; //!< use event-based mode (instead of history-based) + event_based; //!< use event-based mode (instead of history-based) +extern bool ifp_on; //!< Use IFP for kinetics parameters? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? -extern bool material_cell_offsets; //!< create material cells offsets? -extern "C" bool output_summary; //!< write summary.h5? -extern bool output_tallies; //!< write tallies.out? -extern bool particle_restart_run; //!< particle restart run? -extern "C" bool photon_transport; //!< photon transport turned on? -extern "C" bool reduce_tallies; //!< reduce tallies at end of batch? -extern bool res_scat_on; //!< use resonance upscattering method? -extern "C" bool restart_run; //!< restart run? -extern "C" bool run_CE; //!< run with continuous-energy data? -extern bool source_latest; //!< write latest source at each batch? -extern bool source_separate; //!< write source to separate file? -extern bool source_write; //!< write source in HDF5 files? -extern bool surf_source_write; //!< write surface source file? -extern bool surf_source_read; //!< read surface source file? -extern bool survival_biasing; //!< use survival biasing? -extern bool temperature_multipole; //!< use multipole data? -extern "C" bool trigger_on; //!< tally triggers enabled? -extern bool trigger_predict; //!< predict batches for triggers? -extern bool ufs_on; //!< uniform fission site method on? -extern bool urr_ptables_on; //!< use unresolved resonance prob. tables? -extern bool weight_windows_on; //!< are weight windows are enabled? -extern bool write_all_tracks; //!< write track files for every particle? -extern bool write_initial_source; //!< write out initial source file? +extern bool material_cell_offsets; //!< create material cells offsets? +extern "C" bool output_summary; //!< write summary.h5? +extern bool output_tallies; //!< write tallies.out? +extern bool particle_restart_run; //!< particle restart run? +extern "C" bool photon_transport; //!< photon transport turned on? +extern "C" bool reduce_tallies; //!< reduce tallies at end of batch? +extern bool res_scat_on; //!< use resonance upscattering method? +extern "C" bool restart_run; //!< restart run? +extern "C" bool run_CE; //!< run with continuous-energy data? +extern bool source_latest; //!< write latest source at each batch? +extern bool source_separate; //!< write source to separate file? +extern bool source_write; //!< write source in HDF5 files? +extern bool source_mcpl_write; //!< write source in mcpl files? +extern bool surf_source_write; //!< write surface source file? +extern bool surf_mcpl_write; //!< write surface mcpl file? +extern bool surf_source_read; //!< read surface source file? +extern bool survival_biasing; //!< use survival biasing? +extern bool survival_normalization; //!< use survival normalization? +extern bool temperature_multipole; //!< use multipole data? +extern "C" bool trigger_on; //!< tally triggers enabled? +extern bool trigger_predict; //!< predict batches for triggers? +extern bool uniform_source_sampling; //!< sample sources uniformly? +extern bool ufs_on; //!< uniform fission site method on? +extern bool urr_ptables_on; //!< use unresolved resonance prob. tables? +extern bool use_decay_photons; //!< use decay photons for D1S +extern "C" bool weight_windows_on; //!< are weight windows are enabled? +extern bool weight_window_checkpoint_surface; //!< enable weight window check + //!< upon surface crossing? +extern bool weight_window_checkpoint_collision; //!< enable weight window check + //!< upon collision? +extern bool write_all_tracks; //!< write track files for every particle? +extern bool write_initial_source; //!< write out initial source file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml @@ -65,44 +92,75 @@ extern std::string path_input; //!< directory where main .xml files resides extern std::string path_output; //!< directory where output files are written extern std::string path_particle_restart; //!< path to a particle restart file extern std::string path_sourcepoint; //!< path to a source file -extern "C" std::string path_statepoint; //!< path to a statepoint file +extern std::string path_statepoint; //!< path to a statepoint file +extern std::string weight_windows_file; //!< Location of weight window file to + //!< load on simulation initialization + +// This is required because the c_str() may not be the first thing in +// std::string. Sometimes it is, but it seems libc++ may not be like that +// on some computers, like the intel Mac. +extern "C" const char* path_statepoint_c; //!< C pointer to statepoint file name extern "C" int32_t n_inactive; //!< number of inactive batches extern "C" int32_t max_lost_particles; //!< maximum number of lost particles extern double rel_max_lost_particles; //!< maximum number of lost particles, relative to the //!< total number of particles +extern "C" int32_t + max_write_lost_particles; //!< maximum number of lost particles + //!< to be written to files extern "C" int32_t gen_per_batch; //!< number of generations per batch extern "C" int64_t n_particles; //!< number of particles per generation extern int64_t - max_particles_in_flight; //!< Max num. event-based particles in flight - + max_particles_in_flight; //!< Max num. event-based particles in flight +extern int max_particle_events; //!< Maximum number of particle events extern ElectronTreatment electron_treatment; //!< how to treat secondary electrons extern array energy_cutoff; //!< Energy cutoff in [eV] for each particle type +extern array + time_cutoff; //!< Time cutoff in [s] for each particle type +extern int + ifp_n_generation; //!< Number of generation for Iterated Fission Probability +extern IFPParameter + ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability extern int legendre_to_tabular_points; //!< number of points to convert Legendres extern int max_order; //!< Maximum Legendre order for multigroup data extern int n_log_bins; //!< number of bins for logarithmic energy grid extern int n_batches; //!< number of (inactive+active) batches extern int n_max_batches; //!< Maximum number of batches +extern int max_tracks; //!< Maximum number of particle tracks written to file extern ResScatMethod res_scat_method; //!< resonance upscattering method extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering extern vector - res_scat_nuclides; //!< Nuclides using res. upscattering treatment -extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) + res_scat_nuclides; //!< Nuclides using res. upscattering treatment +extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) +extern SolverType solver_type; //!< Solver Type (Monte Carlo or Random Ray) extern std::unordered_set sourcepoint_batch; //!< Batches when source should be written extern std::unordered_set statepoint_batch; //!< Batches when state should be written extern std::unordered_set source_write_surf_id; //!< Surface ids where sources will be written -extern int max_splits; //!< maximum number of particle splits for weight windows -extern int64_t max_surface_particles; //!< maximum number of particles to be - //!< banked on surfaces per process +extern double source_rejection_fraction; //!< Minimum fraction of source sites + //!< that must be accepted +extern double free_gas_threshold; //!< Threshold multiplier for free gas + //!< scattering treatment + +extern int + max_history_splits; //!< maximum number of particle splits for weight windows +extern int max_secondaries; //!< maximum number of secondaries in the bank +extern int64_t ssw_max_particles; //!< maximum number of particles to be + //!< banked on surfaces per process +extern int64_t ssw_max_files; //!< maximum number of surface source files + //!< to be created +extern int64_t ssw_cell_id; //!< Cell id for the surface source + //!< write setting +extern SSWCellType ssw_cell_type; //!< Type of option for the cell + //!< argument of surface source write extern TemperatureMethod temperature_method; //!< method for choosing temperatures extern double @@ -119,6 +177,7 @@ extern int trigger_batch_interval; //!< Batch interval for triggers extern "C" int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette + } // namespace settings //============================================================================== @@ -126,9 +185,12 @@ extern double weight_survive; //!< Survival weight after Russian roulette //============================================================================== //! Read settings from XML file -//! \param[in] root XML node for void read_settings_xml(); +//! Read settings from XML node +//! \param[in] root XML node for +void read_settings_xml(pugi::xml_node root); + void free_memory_settings(); } // namespace openmc diff --git a/include/openmc/shared_array.h b/include/openmc/shared_array.h index 6fee29b82d..7e9ef28c58 100644 --- a/include/openmc/shared_array.h +++ b/include/openmc/shared_array.h @@ -107,6 +107,9 @@ public: //! \param size The new size of the container void resize(int64_t size) { size_ = size; } + //! Return whether the array is full + bool full() const { return size_ == capacity_; } + //! Return the number of elements that the container has currently allocated //! space for. int64_t capacity() { return capacity_; } @@ -115,6 +118,12 @@ public: T* data() { return data_.get(); } const T* data() const { return data_.get(); } + //! Classic iterators + T* begin() { return data_.get(); } + const T* cbegin() const { return data_.get(); } + T* end() { return data_.get() + size_; } + const T* cend() const { return data_.get() + size_; } + private: //========================================================================== // Data members diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index dd8bb7cdfd..3e4e24e1d0 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -37,6 +37,7 @@ extern "C" int n_lost_particles; //!< cumulative number of lost particles extern "C" bool need_depletion_rx; //!< need to calculate depletion rx? extern "C" int restart_batch; //!< batch at which a restart job resumed extern "C" bool satisfy_triggers; //!< have tally triggers been satisfied? +extern int ssw_current_file; //!< current surface source file extern "C" int total_gen; //!< total number of generations simulated extern double total_weight; //!< Total source weight in a batch extern int64_t work_per_rank; //!< number of particles per MPI rank diff --git a/include/openmc/source.h b/include/openmc/source.h index 18fddc6899..1fbd319048 100644 --- a/include/openmc/source.h +++ b/include/openmc/source.h @@ -4,6 +4,9 @@ #ifndef OPENMC_SOURCE_H #define OPENMC_SOURCE_H +#include +#include + #include "pugixml.hpp" #include "openmc/distribution_multi.h" @@ -18,10 +21,9 @@ namespace openmc { // Constants //============================================================================== -// Maximum number of external source spatial resamples to encounter before an -// error is thrown. +// Minimum number of external source sites rejected before checking againts the +// source_rejection_fraction constexpr int EXTSRC_REJECT_THRESHOLD {10000}; -constexpr double EXTSRC_REJECT_FRACTION {0.05}; //============================================================================== // Global variables @@ -33,31 +35,90 @@ namespace model { extern vector> external_sources; +// Probability distribution for selecting external sources +extern DiscreteIndex external_sources_probability; + } // namespace model //============================================================================== //! Abstract source interface +// +//! The Source class provides the interface that must be implemented by derived +//! classes, namely the sample() method that returns a sampled source site. From +//! this base class, source rejection is handled within the +//! sample_with_constraints() method. However, note that some classes directly +//! check for constraints for efficiency reasons (like IndependentSource), in +//! which case the constraints_applied() method indicates that constraints +//! should not be checked a second time from the base class. //============================================================================== class Source { public: + // Domain types + enum class DomainType { UNIVERSE, MATERIAL, CELL }; + // Constructors, destructors + Source() = default; + explicit Source(pugi::xml_node node); virtual ~Source() = default; - // Methods that must be implemented + // Methods that can be overridden + virtual double strength() const { return strength_; } + + //! Sample a source site and apply constraints + // + //! \param[inout] seed Pseudorandom seed pointer + //! \return Sampled site + SourceSite sample_with_constraints(uint64_t* seed) const; + + //! Sample a source site (without applying constraints) + // + //! Sample from the external source distribution + //! \param[inout] seed Pseudorandom seed pointer + //! \return Sampled site virtual SourceSite sample(uint64_t* seed) const = 0; - // Methods that can be overridden - virtual double strength() const { return 1.0; } + static unique_ptr create(pugi::xml_node node); + +protected: + // Strategy used for rejecting sites when constraints are applied. KILL means + // that sites are always accepted but if they don't satisfy constraints, they + // are given weight 0. RESAMPLE means that a new source site will be sampled + // until constraints are met. + enum class RejectionStrategy { KILL, RESAMPLE }; + + // Indicates whether derived class already handles constraints + virtual bool constraints_applied() const { return false; } + + // Methods for constraints + void read_constraints(pugi::xml_node node); + bool satisfies_spatial_constraints(Position r) const; + bool satisfies_energy_constraints(double E) const; + bool satisfies_time_constraints(double time) const; + + // Data members + double strength_ {1.0}; //!< Source strength + std::unordered_set domain_ids_; //!< Domains to reject from + DomainType domain_type_; //!< Domain type for rejection + std::pair time_bounds_ {-std::numeric_limits::max(), + std::numeric_limits::max()}; //!< time limits + std::pair energy_bounds_ { + 0, std::numeric_limits::max()}; //!< energy limits + bool only_fissionable_ { + false}; //!< Whether site must be in fissionable material + RejectionStrategy rejection_strategy_ { + RejectionStrategy::RESAMPLE}; //!< Procedure for rejecting }; //============================================================================== -//! Source composed of independent spatial, angle, energy, and time distributions +//! Source composed of independent spatial, angle, energy, and time +//! distributions //============================================================================== class IndependentSource : public Source { public: // Constructors - IndependentSource(UPtrSpace space, UPtrAngle angle, UPtrDist energy, UPtrDist time); + IndependentSource( + UPtrSpace space, UPtrAngle angle, UPtrDist energy, UPtrDist time); explicit IndependentSource(pugi::xml_node node); //! Sample from the external source distribution @@ -67,7 +128,6 @@ public: // Properties ParticleType particle_type() const { return particle_; } - double strength() const override { return strength_; } // Make observing pointers available SpatialDistribution* space() const { return space_.get(); } @@ -75,9 +135,20 @@ public: Distribution* energy() const { return energy_.get(); } Distribution* time() const { return time_.get(); } + // Make domain type and ids available + DomainType domain_type() const { return domain_type_; } + const std::unordered_set& domain_ids() const { return domain_ids_; } + + // Setter for spatial distribution + void set_space(UPtrSpace space) { space_ = std::move(space); } + +protected: + // Indicates whether derived class already handles constraints + bool constraints_applied() const override { return true; } + private: + // Data members ParticleType particle_ {ParticleType::neutron}; //!< Type of particle emitted - double strength_ {1.0}; //!< Source strength UPtrSpace space_; //!< Spatial distribution UPtrAngle angle_; //!< Angular distribution UPtrDist energy_; //!< Energy distribution @@ -91,39 +162,93 @@ private: class FileSource : public Source { public: // Constructors - explicit FileSource(std::string path); + explicit FileSource(pugi::xml_node node); + explicit FileSource(const std::string& path); // Methods + void load_sites_from_file( + const std::string& path); //!< Load source sites from file + +protected: SourceSite sample(uint64_t* seed) const override; private: - vector sites_; //!< Source sites from a file + vector sites_; //!< Source sites }; //============================================================================== //! Wrapper for custom sources that manages opening/closing shared library //============================================================================== -class CustomSourceWrapper : public Source { +class CompiledSourceWrapper : public Source { public: // Constructors, destructors - CustomSourceWrapper(std::string path, std::string parameters); - ~CustomSourceWrapper(); + CompiledSourceWrapper(pugi::xml_node node); + ~CompiledSourceWrapper(); + double strength() const override { return compiled_source_->strength(); } + + void setup(const std::string& path, const std::string& parameters); + +protected: // Defer implementation to custom source library SourceSite sample(uint64_t* seed) const override { - return custom_source_->sample(seed); + return compiled_source_->sample(seed); } - double strength() const override { return custom_source_->strength(); } - private: void* shared_library_; //!< library from dlopen - unique_ptr custom_source_; + unique_ptr compiled_source_; }; -typedef unique_ptr create_custom_source_t(std::string parameters); +typedef unique_ptr create_compiled_source_t(std::string parameters); + +//============================================================================== +//! Mesh-based source with different distributions for each element +//============================================================================== + +// Helper class to sample spatial position on a single mesh element +class MeshElementSpatial : public SpatialDistribution { +public: + MeshElementSpatial(int32_t mesh_index, int elem_index) + : mesh_index_(mesh_index), elem_index_(elem_index) + {} + + //! Sample a position from the distribution + //! \param seed Pseudorandom number seed pointer + //! \return Sampled position + Position sample(uint64_t* seed) const override; + +private: + int32_t mesh_index_ {C_NONE}; //!< Index in global meshes array + int elem_index_; //! Index of mesh element +}; + +class MeshSource : public Source { +public: + // Constructors + explicit MeshSource(pugi::xml_node node); + + //! Sample from the external source distribution + //! \param[inout] seed Pseudorandom seed pointer + //! \return Sampled site + SourceSite sample(uint64_t* seed) const override; + + // Properties + double strength() const override { return space_->total_strength(); } + + // Accessors + const unique_ptr& source(int32_t i) const + { + return sources_.size() == 1 ? sources_[0] : sources_[i]; + } + +private: + // Data members + unique_ptr space_; //!< Mesh spatial + vector> sources_; //!< Source distributions +}; //============================================================================== // Functions diff --git a/include/openmc/span.h b/include/openmc/span.h new file mode 100644 index 0000000000..723bccd76e --- /dev/null +++ b/include/openmc/span.h @@ -0,0 +1,237 @@ +#ifndef OPENMC_SPAN_H +#define OPENMC_SPAN_H +#include // for std::size_t, std::ptrdiff_t +#include // for std::begin, std::end +#include // for std::out_of_range +#include + +#include "openmc/vector.h" + +namespace openmc { + +template +class span { +public: + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = T*; + using const_iterator = const T*; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + /** + * @brief Default constructor for an empty span. + */ + span() noexcept : data_(nullptr), size_(0) {} + + /** + * @brief Constructs a span from a pointer and size. + * @param ptr Pointer to the first element. + * @param count Number of elements in the span. + */ + span(pointer ptr, size_type count) : data_(ptr), size_(count) {} + + /** + * @brief Constructs a span from two pointers marking the span range. + * @param first Pointer to the first element. + * @param last Pointer past the last element. + * @throws std::out_of_range if last < first. + */ + span(pointer first, pointer last) : data_(first), size_(last - first) + { + if (last < first) { + throw std::out_of_range("span: last pointer is before first pointer"); + } + } + + /** + * @brief Constructs a span from a non-const std::vector. + * @param vec Reference to the vector to create a span from. + */ + span(std::vector& vec) : data_(vec.data()), size_(vec.size()) {} + + /** + * @brief Constructs a span from a const std::vector. + * + * This is handling the semantics that a span is used + * for read-only access into a vector. + * @param vec Reference to the const vector to create a span from. + */ + template::value>> + span(const std::vector>& vec) + : data_(vec.data()), size_(vec.size()) + {} + + /** + * @brief Constructs a read-only span from a non-const span. + */ + template::value>> + span(const span>& other) noexcept + : data_(other.data()), size_(other.size()) + {} + + /** + * @brief Access an element without bounds checking. + * @param index Index of the element to access. + * @return Reference to the accessed element. + */ + reference operator[](size_type index) { return data_[index]; } + + /** + * @brief Access an element without bounds checking (const version). + * @param index Index of the element to access. + * @return Const reference to the accessed element. + */ + const_reference operator[](size_type index) const { return data_[index]; } + + /** + * @brief Access an element with bounds checking. + * @param index Index of the element to access. + * @return Reference to the accessed element. + * @throws std::out_of_range if index is out of range. + */ + reference at(size_type index) + { + if (index >= size_) { + throw std::out_of_range("span: index out of range"); + } + return data_[index]; + } + + /** + * @brief Access an element with bounds checking (const version). + * @param index Index of the element to access. + * @return Const reference to the accessed element. + * @throws std::out_of_range if index is out of range. + */ + const_reference at(size_type index) const + { + if (index >= size_) { + throw std::out_of_range("span: index out of range"); + } + return data_[index]; + } + + /** + * @brief Get a pointer to the underlying data. + * @return Pointer to the data, or nullptr if the span is empty. + */ + pointer data() noexcept { return data_; } + + /** + * @brief Get a const pointer to the underlying data. + * @return Const pointer to the data, or nullptr if the span is empty. + */ + const_pointer data() const noexcept { return data_; } + + /** + * @brief Get the number of elements in the span. + * @return The size of the span. + */ + size_type size() const noexcept { return size_; } + + /** + * @brief Check if the span is empty. + * @return True if the span is empty, false otherwise. + */ + bool empty() const noexcept { return size_ == 0; } + + /** + * @brief Get an iterator to the beginning of the span. + * @return Iterator pointing to the first element. + */ + iterator begin() noexcept { return data_; } + + /** + * @brief Get a const iterator to the beginning of the span. + * @return Const iterator pointing to the first element. + */ + const_iterator begin() const noexcept { return data_; } + + /** + * @brief Get a const iterator to the beginning of the span. + * @return Const iterator pointing to the first element. + */ + const_iterator cbegin() const noexcept { return data_; } + + /** + * @brief Get an iterator to the end of the span. + * @return Iterator pointing past the last element. + */ + iterator end() noexcept { return data_ + size_; } + + /** + * @brief Get a const iterator to the end of the span. + * @return Const iterator pointing past the last element. + */ + const_iterator end() const noexcept { return data_ + size_; } + + /** + * @brief Get a const iterator to the end of the span. + * @return Const iterator pointing past the last element. + */ + const_iterator cend() const noexcept { return data_ + size_; } + + /** + * @brief Access the first element. + * @return Reference to the first element. + * @throws std::out_of_range if the span is empty. + */ + reference front() + { + if (empty()) { + throw std::out_of_range("span::front(): span is empty"); + } + return data_[0]; + } + + /** + * @brief Access the first element (const version). + * @return Const reference to the first element. + * @throws std::out_of_range if the span is empty. + */ + const_reference front() const + { + if (empty()) { + throw std::out_of_range("span::front(): span is empty"); + } + return data_[0]; + } + + /** + * @brief Access the last element. + * @return Reference to the last element. + * @throws std::out_of_range if the span is empty. + */ + reference back() + { + if (empty()) { + throw std::out_of_range("span::back(): span is empty"); + } + return data_[size_ - 1]; + } + + /** + * @brief Access the last element (const version). + * @return Const reference to the last element. + * @throws std::out_of_range if the span is empty. + */ + const_reference back() const + { + if (empty()) { + throw std::out_of_range("span::back(): span is empty"); + } + return data_[size_ - 1]; + } + +private: + pointer data_; + size_type size_; +}; + +} // namespace openmc +#endif // OPENMC_SPAN_H diff --git a/include/openmc/state_point.h b/include/openmc/state_point.h index 5ada2bf88d..fb1aaf7b98 100644 --- a/include/openmc/state_point.h +++ b/include/openmc/state_point.h @@ -2,19 +2,51 @@ #define OPENMC_STATE_POINT_H #include +#include #include "hdf5.h" #include "openmc/capi.h" #include "openmc/particle.h" +#include "openmc/shared_array.h" +#include "openmc/span.h" #include "openmc/vector.h" namespace openmc { void load_state_point(); -vector calculate_surf_source_size(); -void write_source_point(const char* filename, bool surf_source_bank = false); -void write_source_bank(hid_t group_id, bool surf_source_bank); + +// By passing in a filename, source bank, and list of source indices +// on each MPI rank, this writes an HDF5 file which contains that +// information which can later be read in by read_source_bank +// (defined below). If you're writing code to write out a new kind +// of particle bank, this function is the one you want to use! +// +// For example, this is used to write both the surface source sites +// or fission source sites for eigenvalue continuation runs. +// +// This function ends up calling write_source_bank, and is responsible +// for opening the file to be written to and controlling whether the +// write is done in parallel (if compiled with parallel HDF5). +// +// bank_index is an exclusive parallel scan of the source_bank.size() +// values on each rank, used to create global indexing. This vector +// can be created by calling calculate_parallel_index_vector on +// source_bank.size() if such a vector is not already available. +// +// The source_bank variable is used as work space if MPI is used, +// so it cannot be given as a const span. +void write_h5_source_point(const char* filename, span source_bank, + const vector& bank_index); + +void write_source_point(std::string, span source_bank, + const vector& bank_index, bool use_mcpl); + +// This appends a source bank specification to an HDF5 file +// that's already open. It is used internally by write_source_point. +void write_source_bank(hid_t group_id, span source_bank, + const vector& bank_index); + void read_source_bank( hid_t group_id, vector& sites, bool distribute); void write_tally_results_nr(hid_t file_id); diff --git a/include/openmc/string_utils.h b/include/openmc/string_utils.h index 6b4c69d48b..2e8b0d14f3 100644 --- a/include/openmc/string_utils.h +++ b/include/openmc/string_utils.h @@ -1,6 +1,7 @@ #ifndef OPENMC_STRING_UTILS_H #define OPENMC_STRING_UTILS_H +#include #include #include "openmc/vector.h" @@ -15,7 +16,7 @@ std::string to_element(const std::string& name); void to_lower(std::string& str); -int word_count(std::string const& str); +int word_count(const std::string& str); vector split(const std::string& in); @@ -23,5 +24,20 @@ bool ends_with(const std::string& value, const std::string& ending); bool starts_with(const std::string& value, const std::string& beginning); +template +inline std::string concatenate(const T& values, const std::string& del = ", ") +{ + if (values.size() == 0) + return ""; + + std::stringstream oss; + auto it = values.begin(); + oss << *it++; + while (it != values.end()) { + oss << del << *it++; + } + return oss.str(); +} + } // namespace openmc #endif // OPENMC_STRING_UTILS_H diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 785e1a7ec1..839bdf4383 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -9,6 +9,7 @@ #include "pugixml.hpp" #include "openmc/boundary_condition.h" +#include "openmc/bounding_box.h" #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr #include "openmc/particle.h" @@ -28,65 +29,15 @@ extern std::unordered_map surface_map; extern vector> surfaces; } // namespace model -//============================================================================== -//! Coordinates for an axis-aligned cuboid that bounds a geometric object. -//============================================================================== - -struct BoundingBox { - double xmin = -INFTY; - double xmax = INFTY; - double ymin = -INFTY; - double ymax = INFTY; - double zmin = -INFTY; - double zmax = INFTY; - - inline BoundingBox operator&(const BoundingBox& other) - { - BoundingBox result = *this; - return result &= other; - } - - inline BoundingBox operator|(const BoundingBox& other) - { - BoundingBox result = *this; - return result |= other; - } - - // intersect operator - inline BoundingBox& operator&=(const BoundingBox& other) - { - xmin = std::max(xmin, other.xmin); - xmax = std::min(xmax, other.xmax); - ymin = std::max(ymin, other.ymin); - ymax = std::min(ymax, other.ymax); - zmin = std::max(zmin, other.zmin); - zmax = std::min(zmax, other.zmax); - return *this; - } - - // union operator - inline BoundingBox& operator|=(const BoundingBox& other) - { - xmin = std::min(xmin, other.xmin); - xmax = std::max(xmax, other.xmax); - ymin = std::min(ymin, other.ymin); - ymax = std::max(ymax, other.ymax); - zmin = std::min(zmin, other.zmin); - zmax = std::max(zmax, other.zmax); - return *this; - } -}; - //============================================================================== //! A geometry primitive used to define regions of 3D space. //============================================================================== class Surface { public: - int id_; //!< Unique ID - std::string name_; //!< User-defined name - std::shared_ptr bc_ {nullptr}; //!< Boundary condition - GeometryType geom_type_; //!< Geometry type indicator (CSG or DAGMC) + int id_; //!< Unique ID + std::string name_; //!< User-defined name + unique_ptr bc_; //!< Boundary condition bool surf_source_ {false}; //!< Activate source banking for the surface? int triso_base_index_; int triso_particle_index_ = -1; @@ -108,9 +59,10 @@ public: //! Determine the direction of a ray reflected from the surface. //! \param[in] r The point at which the ray is incident. //! \param[in] u Incident direction of the ray - //! \param[inout] p Pointer to the particle + //! \param[inout] p Pointer to the particle. Only DAGMC uses this. //! \return Outgoing direction of the ray - virtual Direction reflect(Position r, Direction u, Particle* p) const; + virtual Direction reflect( + Position r, Direction u, GeometryState* p = nullptr) const; virtual Direction diffuse_reflect( Position r, Direction u, uint64_t* seed) const; @@ -129,7 +81,11 @@ public: //! exactly on the surface. virtual double distance(Position r, Direction u, bool coincident) const = 0; - virtual bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const { return {}; }; + virtual bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const + { + return {}; + }; virtual void connect_to_triso_base(int triso_index, std::string key) {}; virtual vector get_center() const { return {}; }; virtual double get_radius() const { return {}; }; @@ -146,14 +102,14 @@ public: //! Get the BoundingBox for this surface. virtual BoundingBox bounding_box(bool /*pos_side*/) const { return {}; } -protected: - virtual void to_hdf5_inner(hid_t group_id) const = 0; -}; - -class CSGSurface : public Surface { -public: - explicit CSGSurface(pugi::xml_node surf_node); - CSGSurface(); + /* Must specify if this is a CSG or DAGMC-type surface. Only + * the DAGMC surface should return the DAG type geometry, so + * by default, this returns the CSG. The main difference is that + * if the geom_type is found to be DAG in the geometry handling code, + * some DAGMC-specific operations get carried out like resetting + * the particle's intersection history when necessary. + */ + virtual GeometryType geom_type() const { return GeometryType::CSG; } protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; @@ -165,15 +121,16 @@ protected: //! The plane is described by the equation \f$x - x_0 = 0\f$ //============================================================================== -class SurfaceXPlane : public CSGSurface { +class SurfaceXPlane : public Surface { public: explicit SurfaceXPlane(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; - BoundingBox bounding_box(bool pos_side) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double x0_; }; @@ -184,15 +141,16 @@ public: //! The plane is described by the equation \f$y - y_0 = 0\f$ //============================================================================== -class SurfaceYPlane : public CSGSurface { +class SurfaceYPlane : public Surface { public: explicit SurfaceYPlane(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; - BoundingBox bounding_box(bool pos_side) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double y0_; }; @@ -203,15 +161,16 @@ public: //! The plane is described by the equation \f$z - z_0 = 0\f$ //============================================================================== -class SurfaceZPlane : public CSGSurface { +class SurfaceZPlane : public Surface { public: explicit SurfaceZPlane(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; - BoundingBox bounding_box(bool pos_side) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double z0_; }; @@ -222,14 +181,15 @@ public: //! The plane is described by the equation \f$A x + B y + C z - D = 0\f$ //============================================================================== -class SurfacePlane : public CSGSurface { +class SurfacePlane : public Surface { public: explicit SurfacePlane(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + void to_hdf5_inner(hid_t group_id) const override; double A_, B_, C_, D_; }; @@ -241,15 +201,16 @@ public: //! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceXCylinder : public CSGSurface { +class SurfaceXCylinder : public Surface { public: explicit SurfaceXCylinder(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - BoundingBox bounding_box(bool pos_side) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double y0_, z0_, radius_; }; @@ -261,15 +222,16 @@ public: //! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceYCylinder : public CSGSurface { +class SurfaceYCylinder : public Surface { public: explicit SurfaceYCylinder(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - BoundingBox bounding_box(bool pos_side) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double x0_, z0_, radius_; }; @@ -281,15 +243,16 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceZCylinder : public CSGSurface { +class SurfaceZCylinder : public Surface { public: explicit SurfaceZCylinder(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - BoundingBox bounding_box(bool pos_side) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; double x0_, y0_, radius_; }; @@ -301,21 +264,22 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 - R^2 = 0\f$ //============================================================================== -class SurfaceSphere : public CSGSurface { +class SurfaceSphere : public Surface { public: explicit SurfaceSphere(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; - vector get_center() const; - double get_radius() const; - void connect_to_triso_base(int triso_index, std::string key); - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - BoundingBox bounding_box(bool pos_side) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; + BoundingBox bounding_box(bool pos_side) const override; + vector get_center() const override; + double get_radius() const override; + void connect_to_triso_base(int triso_index, std::string key) override; double x0_, y0_, z0_, radius_; - //int triso_base_index_ = -1; + // int triso_base_index_ = -1; }; //============================================================================== @@ -325,14 +289,15 @@ public: //! \f$(y - y_0)^2 + (z - z_0)^2 - R^2 (x - x_0)^2 = 0\f$ //============================================================================== -class SurfaceXCone : public CSGSurface { +class SurfaceXCone : public Surface { public: explicit SurfaceXCone(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, radius_sq_; }; @@ -344,14 +309,15 @@ public: //! \f$(x - x_0)^2 + (z - z_0)^2 - R^2 (y - y_0)^2 = 0\f$ //============================================================================== -class SurfaceYCone : public CSGSurface { +class SurfaceYCone : public Surface { public: explicit SurfaceYCone(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, radius_sq_; }; @@ -363,14 +329,15 @@ public: //! \f$(x - x_0)^2 + (y - y_0)^2 - R^2 (z - z_0)^2 = 0\f$ //============================================================================== -class SurfaceZCone : public CSGSurface { +class SurfaceZCone : public Surface { public: explicit SurfaceZCone(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, radius_sq_; }; @@ -382,14 +349,15 @@ public: //! 0\f$ //============================================================================== -class SurfaceQuadric : public CSGSurface { +class SurfaceQuadric : public Surface { public: explicit SurfaceQuadric(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; @@ -401,14 +369,15 @@ public: //! \f$(x-x_0)^2/B^2 + (\sqrt{(y-y_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceXTorus : public CSGSurface { +class SurfaceXTorus : public Surface { public: explicit SurfaceXTorus(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, A_, B_, C_; }; @@ -419,14 +388,15 @@ public: //! \f$(y-y_0)^2/B^2 + (\sqrt{(x-x_0)^2 + (z-z_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceYTorus : public CSGSurface { +class SurfaceYTorus : public Surface { public: explicit SurfaceYTorus(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - void to_hdf5_inner(hid_t group_id) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, A_, B_, C_; }; @@ -437,14 +407,15 @@ public: //! \f$(z-z_0)^2/B^2 + (\sqrt{(x-x_0)^2 + (y-y_0)^2} - A)^2/C^2 -1 \f$ //============================================================================== -class SurfaceZTorus : public CSGSurface { +class SurfaceZTorus : public Surface { public: explicit SurfaceZTorus(pugi::xml_node surf_node); - double evaluate(Position r) const; - double distance(Position r, Direction u, bool coincident) const; - Direction normal(Position r) const; - bool triso_in_mesh(vector mesh_center, vector lattice_pitch) const; - void to_hdf5_inner(hid_t group_id) const; + double evaluate(Position r) const override; + double distance(Position r, Direction u, bool coincident) const override; + Direction normal(Position r) const override; + void to_hdf5_inner(hid_t group_id) const override; + bool triso_in_mesh( + vector mesh_center, vector lattice_pitch) const override; double x0_, y0_, z0_, A_, B_, C_; }; diff --git a/include/openmc/tallies/filter.h b/include/openmc/tallies/filter.h index 2eecfe0e4e..65098597a5 100644 --- a/include/openmc/tallies/filter.h +++ b/include/openmc/tallies/filter.h @@ -6,7 +6,6 @@ #include #include "pugixml.hpp" -#include #include "openmc/constants.h" #include "openmc/hdf5_interface.h" @@ -17,6 +16,40 @@ namespace openmc { +enum class FilterType { + AZIMUTHAL, + CELLBORN, + CELLFROM, + CELL, + CELL_INSTANCE, + COLLISION, + DELAYED_GROUP, + DISTRIBCELL, + ENERGY_FUNCTION, + ENERGY, + ENERGY_OUT, + LEGENDRE, + MATERIAL, + MATERIALFROM, + MESH, + MESHBORN, + MESH_MATERIAL, + MESH_SURFACE, + MU, + MUSURFACE, + PARENT_NUCLIDE, + PARTICLE, + POLAR, + SPHERICAL_HARMONICS, + SPATIAL_LEGENDRE, + SURFACE, + TIME, + UNIVERSE, + WEIGHT, + ZERNIKE, + ZERNIKE_RADIAL +}; + //============================================================================== //! Modifies tally score events. //============================================================================== @@ -58,7 +91,8 @@ public: //---------------------------------------------------------------------------- // Methods - virtual std::string type() const = 0; + virtual std::string type_str() const = 0; + virtual FilterType type() const = 0; //! Matches a tally event to a set of filter bins and weights. //! @@ -72,7 +106,7 @@ public: //! Writes data describing this filter to an HDF5 statepoint group. virtual void to_statepoint(hid_t filter_group) const { - write_dataset(filter_group, "type", type()); + write_dataset(filter_group, "type", type_str()); write_dataset(filter_group, "n_bins", n_bins_); } @@ -98,7 +132,7 @@ public: //! \return Number of bins int n_bins() const { return n_bins_; } - gsl::index index() const { return index_; } + int64_t index() const { return index_; } //---------------------------------------------------------------------------- // Data members @@ -108,7 +142,7 @@ protected: private: int32_t id_ {C_NONE}; - gsl::index index_; + int64_t index_; }; //============================================================================== @@ -128,5 +162,24 @@ extern vector> tally_filters; //! Make sure index corresponds to a valid filter int verify_filter(int32_t index); +//============================================================================== +// Filter implementation +//============================================================================== + +template +T* Filter::create(int32_t id) +{ + static_assert(std::is_base_of::value, + "Type specified is not derived from openmc::Filter"); + // Create filter and add to filters vector + auto filter = make_unique(); + auto ptr_out = filter.get(); + model::tally_filters.emplace_back(std::move(filter)); + // Assign ID + model::tally_filters.back()->set_id(id); + + return ptr_out; +} + } // namespace openmc #endif // OPENMC_TALLIES_FILTER_H diff --git a/include/openmc/tallies/filter_azimuthal.h b/include/openmc/tallies/filter_azimuthal.h index 2272b500ab..4853c55459 100644 --- a/include/openmc/tallies/filter_azimuthal.h +++ b/include/openmc/tallies/filter_azimuthal.h @@ -4,8 +4,7 @@ #include "openmc/vector.h" #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" namespace openmc { @@ -24,7 +23,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "azimuthal"; } + std::string type_str() const override { return "azimuthal"; } + FilterType type() const override { return FilterType::AZIMUTHAL; } void from_xml(pugi::xml_node node) override; @@ -38,7 +38,7 @@ public: //---------------------------------------------------------------------------- // Accessors - void set_bins(gsl::span bins); + void set_bins(span bins); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_cell.h b/include/openmc/tallies/filter_cell.h index 46d89811d3..ac6539466f 100644 --- a/include/openmc/tallies/filter_cell.h +++ b/include/openmc/tallies/filter_cell.h @@ -4,8 +4,7 @@ #include #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -25,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "cell"; } + std::string type_str() const override { return "cell"; } + FilterType type() const override { return FilterType::CELL; } void from_xml(pugi::xml_node node) override; @@ -41,7 +41,7 @@ public: const vector& cells() const { return cells_; } - void set_cells(gsl::span cells); + void set_cells(span cells); protected: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_cell_instance.h b/include/openmc/tallies/filter_cell_instance.h index 4de3fcd292..69499765b2 100644 --- a/include/openmc/tallies/filter_cell_instance.h +++ b/include/openmc/tallies/filter_cell_instance.h @@ -4,9 +4,8 @@ #include #include -#include - #include "openmc/cell.h" +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -22,13 +21,14 @@ public: // Constructors, destructors CellInstanceFilter() = default; - CellInstanceFilter(gsl::span instances); + CellInstanceFilter(span instances); ~CellInstanceFilter() = default; //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "cellinstance"; } + std::string type_str() const override { return "cellinstance"; } + FilterType type() const override { return FilterType::CELL_INSTANCE; } void from_xml(pugi::xml_node node) override; @@ -46,7 +46,7 @@ public: const std::unordered_set& cells() const { return cells_; } - void set_cell_instances(gsl::span instances); + void set_cell_instances(span instances); private: //---------------------------------------------------------------------------- @@ -59,7 +59,7 @@ private: std::unordered_set cells_; //! A map from cell/instance indices to filter bin indices. - std::unordered_map map_; + std::unordered_map map_; //! Indicates if filter uses only material-filled cells bool material_cells_only_; diff --git a/include/openmc/tallies/filter_cellborn.h b/include/openmc/tallies/filter_cellborn.h index 282854020a..417aedeced 100644 --- a/include/openmc/tallies/filter_cellborn.h +++ b/include/openmc/tallies/filter_cellborn.h @@ -11,12 +11,13 @@ namespace openmc { //! Specifies which cell the particle was born in. //============================================================================== -class CellbornFilter : public CellFilter { +class CellBornFilter : public CellFilter { public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "cellborn"; } + std::string type_str() const override { return "cellborn"; } + FilterType type() const override { return FilterType::CELLBORN; } void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const override; diff --git a/include/openmc/tallies/filter_cellfrom.h b/include/openmc/tallies/filter_cellfrom.h index 47abd03538..61ff50b055 100644 --- a/include/openmc/tallies/filter_cellfrom.h +++ b/include/openmc/tallies/filter_cellfrom.h @@ -16,7 +16,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "cellfrom"; } + std::string type_str() const override { return "cellfrom"; } + FilterType type() const override { return FilterType::CELLFROM; } void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const override; diff --git a/include/openmc/tallies/filter_collision.h b/include/openmc/tallies/filter_collision.h index 3724b06cfe..7d42a5ddd3 100644 --- a/include/openmc/tallies/filter_collision.h +++ b/include/openmc/tallies/filter_collision.h @@ -1,9 +1,9 @@ #ifndef OPENMC_TALLIES_FILTER_COLLISIONS_H #define OPENMC_TALLIES_FILTER_COLLISIONS_H -#include #include +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -23,7 +23,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "collision"; } + std::string type_str() const override { return "collision"; } + FilterType type() const override { return FilterType::COLLISION; } void from_xml(pugi::xml_node node) override; @@ -38,7 +39,7 @@ public: // Accessors const vector& bins() const { return bins_; } - void set_bins(gsl::span bins); + void set_bins(span bins); protected: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_delayedgroup.h b/include/openmc/tallies/filter_delayedgroup.h index 72ffa1db5a..7d11447ab1 100644 --- a/include/openmc/tallies/filter_delayedgroup.h +++ b/include/openmc/tallies/filter_delayedgroup.h @@ -1,8 +1,7 @@ #ifndef OPENMC_TALLIES_FILTER_DELAYEDGROUP_H #define OPENMC_TALLIES_FILTER_DELAYEDGROUP_H -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -25,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "delayedgroup"; } + std::string type_str() const override { return "delayedgroup"; } + FilterType type() const override { return FilterType::DELAYED_GROUP; } void from_xml(pugi::xml_node node) override; @@ -41,7 +41,7 @@ public: const vector& groups() const { return groups_; } - void set_groups(gsl::span groups); + void set_groups(span groups); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_distribcell.h b/include/openmc/tallies/filter_distribcell.h index d72ae022fd..b5cdcce84c 100644 --- a/include/openmc/tallies/filter_distribcell.h +++ b/include/openmc/tallies/filter_distribcell.h @@ -21,7 +21,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "distribcell"; } + std::string type_str() const override { return "distribcell"; } + FilterType type() const override { return FilterType::DISTRIBCELL; } void from_xml(pugi::xml_node node) override; diff --git a/include/openmc/tallies/filter_energy.h b/include/openmc/tallies/filter_energy.h index 000aaa28b9..cf8a8aa0f5 100644 --- a/include/openmc/tallies/filter_energy.h +++ b/include/openmc/tallies/filter_energy.h @@ -1,8 +1,7 @@ #ifndef OPENMC_TALLIES_FILTER_ENERGY_H #define OPENMC_TALLIES_FILTER_ENERGY_H -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -22,7 +21,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "energy"; } + std::string type_str() const override { return "energy"; } + FilterType type() const override { return FilterType::ENERGY; } void from_xml(pugi::xml_node node) override; @@ -37,7 +37,7 @@ public: // Accessors const vector& bins() const { return bins_; } - void set_bins(gsl::span bins); + void set_bins(span bins); bool matches_transport_groups() const { return matches_transport_groups_; } @@ -63,7 +63,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "energyout"; } + std::string type_str() const override { return "energyout"; } + FilterType type() const override { return FilterType::ENERGY_OUT; } void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const override; diff --git a/include/openmc/tallies/filter_energyfunc.h b/include/openmc/tallies/filter_energyfunc.h index c066dfa17f..e03c23dda7 100644 --- a/include/openmc/tallies/filter_energyfunc.h +++ b/include/openmc/tallies/filter_energyfunc.h @@ -1,6 +1,8 @@ #ifndef OPENMC_TALLIES_FILTER_ENERGYFUNC_H #define OPENMC_TALLIES_FILTER_ENERGYFUNC_H +#include "openmc/constants.h" +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -23,7 +25,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "energyfunction"; } + std::string type_str() const override { return "energyfunction"; } + FilterType type() const override { return FilterType::ENERGY_FUNCTION; } void from_xml(pugi::xml_node node) override; @@ -39,7 +42,9 @@ public: const vector& energy() const { return energy_; } const vector& y() const { return y_; } - void set_data(gsl::span energy, gsl::span y); + Interpolation interpolation() const { return interpolation_; } + void set_data(span energy, span y); + void set_interpolation(const std::string& interpolation); private: //---------------------------------------------------------------------------- @@ -50,6 +55,9 @@ private: //! Interpolant values. vector y_; + + //! Interpolation scheme + Interpolation interpolation_ {Interpolation::lin_lin}; }; } // namespace openmc diff --git a/include/openmc/tallies/filter_legendre.h b/include/openmc/tallies/filter_legendre.h index b1fac37c86..839fd77bfe 100644 --- a/include/openmc/tallies/filter_legendre.h +++ b/include/openmc/tallies/filter_legendre.h @@ -21,7 +21,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "legendre"; } + std::string type_str() const override { return "legendre"; } + FilterType type() const override { return FilterType::LEGENDRE; } void from_xml(pugi::xml_node node) override; diff --git a/include/openmc/tallies/filter_material.h b/include/openmc/tallies/filter_material.h index f58fc99385..ccfe5403d5 100644 --- a/include/openmc/tallies/filter_material.h +++ b/include/openmc/tallies/filter_material.h @@ -4,8 +4,7 @@ #include #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -25,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "material"; } + std::string type_str() const override { return "material"; } + FilterType type() const override { return FilterType::MATERIAL; } void from_xml(pugi::xml_node node) override; @@ -43,9 +43,9 @@ public: const vector& materials() const { return materials_; } - void set_materials(gsl::span materials); + void set_materials(span materials); -private: +protected: //---------------------------------------------------------------------------- // Data members diff --git a/include/openmc/tallies/filter_materialfrom.h b/include/openmc/tallies/filter_materialfrom.h new file mode 100644 index 0000000000..52039852a5 --- /dev/null +++ b/include/openmc/tallies/filter_materialfrom.h @@ -0,0 +1,29 @@ +#ifndef OPENMC_TALLIES_FILTER_MATERIALFROM_H +#define OPENMC_TALLIES_FILTER_MATERIALFROM_H + +#include + +#include "openmc/tallies/filter_material.h" + +namespace openmc { + +//============================================================================== +//! Specifies which material particles exit when crossing a surface. +//============================================================================== + +class MaterialFromFilter : public MaterialFilter { +public: + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "materialfrom"; } + FilterType type() const override { return FilterType::MATERIALFROM; } + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; + + std::string text_label(int bin) const override; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_MATERIALFROM_H diff --git a/include/openmc/tallies/filter_mesh.h b/include/openmc/tallies/filter_mesh.h index ef055b1a2f..4c9460d2eb 100644 --- a/include/openmc/tallies/filter_mesh.h +++ b/include/openmc/tallies/filter_mesh.h @@ -9,9 +9,9 @@ namespace openmc { //============================================================================== -//! Indexes the location of particle events to a regular mesh. For tracklength -//! tallies, it will produce multiple valid bins and the bin weight will -//! correspond to the fraction of the track length that lies in that bin. +//! Indexes the location of particle events to a mesh. For tracklength tallies, +//! it will produce multiple valid bins and the bin weight will correspond to +//! the fraction of the track length that lies in that bin. //============================================================================== class MeshFilter : public Filter { @@ -24,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "mesh"; } + std::string type_str() const override { return "mesh"; } + FilterType type() const override { return FilterType::MESH; } void from_xml(pugi::xml_node node) override; @@ -54,9 +55,9 @@ protected: //---------------------------------------------------------------------------- // Data members - int32_t mesh_; - bool translated_ {false}; - Position translation_ {0.0, 0.0, 0.0}; + int32_t mesh_; //!< Index of the mesh + bool translated_ {false}; //!< Whether or not the filter is translated + Position translation_ {0.0, 0.0, 0.0}; //!< Filter translation }; } // namespace openmc diff --git a/include/openmc/tallies/filter_meshborn.h b/include/openmc/tallies/filter_meshborn.h new file mode 100644 index 0000000000..8ab7a8c766 --- /dev/null +++ b/include/openmc/tallies/filter_meshborn.h @@ -0,0 +1,26 @@ +#ifndef OPENMC_TALLIES_FILTER_MESHBORN_H +#define OPENMC_TALLIES_FILTER_MESHBORN_H + +#include + +#include "openmc/position.h" +#include "openmc/tallies/filter_mesh.h" + +namespace openmc { + +class MeshBornFilter : public MeshFilter { +public: + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "meshborn"; } + FilterType type() const override { return FilterType::MESHBORN; } + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; + + std::string text_label(int bin) const override; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_MESHBORN_H diff --git a/include/openmc/tallies/filter_meshmaterial.h b/include/openmc/tallies/filter_meshmaterial.h new file mode 100644 index 0000000000..42a4edcf08 --- /dev/null +++ b/include/openmc/tallies/filter_meshmaterial.h @@ -0,0 +1,114 @@ +#ifndef OPENMC_TALLIES_FILTER_MESHMATERIAL_H +#define OPENMC_TALLIES_FILTER_MESHMATERIAL_H + +#include +#include +#include +#include + +#include "openmc/position.h" +#include "openmc/random_ray/source_region.h" +#include "openmc/span.h" +#include "openmc/tallies/filter.h" +#include "openmc/vector.h" + +namespace openmc { + +//============================================================================== +//! Helper structs that define a combination of a mesh element index and a +//! material index and a functor for hashing to place in an unordered_map +//============================================================================== + +struct ElementMat { + //! Check for equality + bool operator==(const ElementMat& other) const + { + return index_element == other.index_element && index_mat == other.index_mat; + } + + int32_t index_element; + int32_t index_mat; +}; + +struct ElementMatHash { + std::size_t operator()(const ElementMat& k) const + { + size_t seed = 0; + hash_combine(seed, k.index_element); + hash_combine(seed, k.index_mat); + return seed; + } +}; + +//============================================================================== +//! Indexes the location of particle events to combinations of mesh element +//! index and material. For tracklength tallies, it will produce multiple valid +//! bins and the bin weight will correspond to the fraction of the track length +//! that lies in that bin. +//============================================================================== + +class MeshMaterialFilter : public Filter { +public: + //---------------------------------------------------------------------------- + // Constructors, destructors + + ~MeshMaterialFilter() = default; + + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "meshmaterial"; } + FilterType type() const override { return FilterType::MESH_MATERIAL; } + + void from_xml(pugi::xml_node node) override; + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; + + void to_statepoint(hid_t filter_group) const override; + + std::string text_label(int bin) const override; + + //---------------------------------------------------------------------------- + // Accessors + + int32_t mesh() const { return mesh_; } + + void set_mesh(int32_t mesh); + + //! Set the bins based on a flat vector of alternating element index and + //! material IDs + void set_bins(span bins); + + //! Set the bins based on a vector of (element, material index) pairs + void set_bins(vector&& bins); + + virtual void set_translation(const Position& translation); + + virtual void set_translation(const double translation[3]); + + virtual const Position& translation() const { return translation_; } + + virtual bool translated() const { return translated_; } + +private: + //---------------------------------------------------------------------------- + // Data members + + int32_t mesh_; //!< Index of the mesh + bool translated_ {false}; //!< Whether or not the filter is translated + Position translation_ {0.0, 0.0, 0.0}; //!< Filter translation + + //! The indices of the mesh element-material combinations binned by this + //! filter. + vector bins_; + + //! The set of materials used in this filter + std::unordered_set materials_; + + //! A map from mesh element-material indices to filter bin indices. + std::unordered_map map_; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_MESHMATERIAL_H diff --git a/include/openmc/tallies/filter_meshsurface.h b/include/openmc/tallies/filter_meshsurface.h index 28f4e265f8..195995c699 100644 --- a/include/openmc/tallies/filter_meshsurface.h +++ b/include/openmc/tallies/filter_meshsurface.h @@ -10,7 +10,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "meshsurface"; } + std::string type_str() const override { return "meshsurface"; } + FilterType type() const override { return FilterType::MESH_SURFACE; } void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const override; diff --git a/include/openmc/tallies/filter_mu.h b/include/openmc/tallies/filter_mu.h index 5299f6dd48..d6e7f1798e 100644 --- a/include/openmc/tallies/filter_mu.h +++ b/include/openmc/tallies/filter_mu.h @@ -1,8 +1,7 @@ #ifndef OPENMC_TALLIES_FILTER_MU_H #define OPENMC_TALLIES_FILTER_MU_H -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -23,7 +22,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "mu"; } + std::string type_str() const override { return "mu"; } + FilterType type() const override { return FilterType::MU; } void from_xml(pugi::xml_node node) override; @@ -37,9 +37,9 @@ public: //---------------------------------------------------------------------------- // Accessors - void set_bins(gsl::span bins); + void set_bins(span bins); -private: +protected: //---------------------------------------------------------------------------- // Data members diff --git a/include/openmc/tallies/filter_musurface.h b/include/openmc/tallies/filter_musurface.h new file mode 100644 index 0000000000..fa6816836d --- /dev/null +++ b/include/openmc/tallies/filter_musurface.h @@ -0,0 +1,32 @@ +#ifndef OPENMC_TALLIES_FILTER_MU_SURFACE_H +#define OPENMC_TALLIES_FILTER_MU_SURFACE_H + +#include "openmc/tallies/filter_mu.h" +#include "openmc/vector.h" + +namespace openmc { + +//============================================================================== +//! Bins the incoming-outgoing direction cosine. This is only used for surface +//! crossings. +//============================================================================== + +class MuSurfaceFilter : public MuFilter { +public: + //---------------------------------------------------------------------------- + // Constructors, destructors + + ~MuSurfaceFilter() = default; + + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "musurface"; } + FilterType type() const override { return FilterType::MUSURFACE; } + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_MU_SURFACE_H diff --git a/include/openmc/tallies/filter_parent_nuclide.h b/include/openmc/tallies/filter_parent_nuclide.h new file mode 100644 index 0000000000..53f8a5fa41 --- /dev/null +++ b/include/openmc/tallies/filter_parent_nuclide.h @@ -0,0 +1,56 @@ +#ifndef OPENMC_TALLIES_FILTER_PARENT_NUCLIDE_H +#define OPENMC_TALLIES_FILTER_PARENT_NUCLIDE_H + +#include +#include + +#include "openmc/span.h" +#include "openmc/tallies/filter.h" +#include "openmc/vector.h" + +namespace openmc { + +//============================================================================== +//! Bins events by parent nuclide (for decay photons) +//============================================================================== + +class ParentNuclideFilter : public Filter { +public: + //---------------------------------------------------------------------------- + // Constructors, destructors + + ~ParentNuclideFilter() = default; + + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "parentnuclide"; } + FilterType type() const override { return FilterType::PARENT_NUCLIDE; } + + void from_xml(pugi::xml_node node) override; + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; + + void to_statepoint(hid_t filter_group) const override; + + std::string text_label(int bin) const override; + + //---------------------------------------------------------------------------- + // Accessors + + const vector& bins() const { return bins_; } + void set_bins(span bins); + +protected: + //---------------------------------------------------------------------------- + // Data members + + vector bins_; + vector nuclides_; + + std::unordered_map map_; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_PARENT_NUCLIDE_H diff --git a/include/openmc/tallies/filter_particle.h b/include/openmc/tallies/filter_particle.h index cd4c5d413e..863a6d282f 100644 --- a/include/openmc/tallies/filter_particle.h +++ b/include/openmc/tallies/filter_particle.h @@ -2,6 +2,7 @@ #define OPENMC_TALLIES_FILTER_PARTICLE_H #include "openmc/particle.h" +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -21,7 +22,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "particle"; } + std::string type_str() const override { return "particle"; } + FilterType type() const override { return FilterType::PARTICLE; } void from_xml(pugi::xml_node node) override; @@ -37,7 +39,7 @@ public: const vector& particles() const { return particles_; } - void set_particles(gsl::span particles); + void set_particles(span particles); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_polar.h b/include/openmc/tallies/filter_polar.h index e06aca1e04..c7c73c89f5 100644 --- a/include/openmc/tallies/filter_polar.h +++ b/include/openmc/tallies/filter_polar.h @@ -3,8 +3,7 @@ #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -24,7 +23,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "polar"; } + std::string type_str() const override { return "polar"; } + FilterType type() const override { return FilterType::POLAR; } void from_xml(pugi::xml_node node) override; @@ -38,7 +38,7 @@ public: //---------------------------------------------------------------------------- // Accessors - void set_bins(gsl::span bins); + void set_bins(span bins); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_sph_harm.h b/include/openmc/tallies/filter_sph_harm.h index 5f5bf84f2e..a6904c3010 100644 --- a/include/openmc/tallies/filter_sph_harm.h +++ b/include/openmc/tallies/filter_sph_harm.h @@ -3,8 +3,6 @@ #include -#include - #include "openmc/tallies/filter.h" namespace openmc { @@ -25,7 +23,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "sphericalharmonics"; } + std::string type_str() const override { return "sphericalharmonics"; } + FilterType type() const override { return FilterType::SPHERICAL_HARMONICS; } void from_xml(pugi::xml_node node) override; @@ -45,7 +44,7 @@ public: SphericalHarmonicsCosine cosine() const { return cosine_; } - void set_cosine(gsl::cstring_span cosine); + void set_cosine(const std::string& cosine); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_sptl_legendre.h b/include/openmc/tallies/filter_sptl_legendre.h index d6ac246683..b6c380e9b8 100644 --- a/include/openmc/tallies/filter_sptl_legendre.h +++ b/include/openmc/tallies/filter_sptl_legendre.h @@ -23,7 +23,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "spatiallegendre"; } + std::string type_str() const override { return "spatiallegendre"; } + FilterType type() const override { return FilterType::SPATIAL_LEGENDRE; } void from_xml(pugi::xml_node node) override; diff --git a/include/openmc/tallies/filter_surface.h b/include/openmc/tallies/filter_surface.h index 358963fde8..e78243f5ff 100644 --- a/include/openmc/tallies/filter_surface.h +++ b/include/openmc/tallies/filter_surface.h @@ -4,8 +4,7 @@ #include #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -25,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "surface"; } + std::string type_str() const override { return "surface"; } + FilterType type() const override { return FilterType::SURFACE; } void from_xml(pugi::xml_node node) override; @@ -39,7 +39,7 @@ public: //---------------------------------------------------------------------------- // Accessors - void set_surfaces(gsl::span surfaces); + void set_surfaces(span surfaces); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_time.h b/include/openmc/tallies/filter_time.h index c66481c578..3ce557abda 100644 --- a/include/openmc/tallies/filter_time.h +++ b/include/openmc/tallies/filter_time.h @@ -1,8 +1,7 @@ #ifndef OPENMC_TALLIES_FILTER_TIME_H #define OPENMC_TALLIES_FILTER_TIME_H -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -22,7 +21,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "time"; } + std::string type_str() const override { return "time"; } + FilterType type() const override { return FilterType::TIME; } void from_xml(pugi::xml_node node) override; @@ -37,7 +37,7 @@ public: // Accessors const vector& bins() const { return bins_; } - void set_bins(gsl::span bins); + void set_bins(span bins); protected: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_universe.h b/include/openmc/tallies/filter_universe.h index fde0b63979..461434ec44 100644 --- a/include/openmc/tallies/filter_universe.h +++ b/include/openmc/tallies/filter_universe.h @@ -4,8 +4,7 @@ #include #include -#include - +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/vector.h" @@ -25,7 +24,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "universe"; } + std::string type_str() const override { return "universe"; } + FilterType type() const override { return FilterType::UNIVERSE; } void from_xml(pugi::xml_node node) override; @@ -39,7 +39,7 @@ public: //---------------------------------------------------------------------------- // Accessors - void set_universes(gsl::span universes); + void set_universes(span universes); private: //---------------------------------------------------------------------------- diff --git a/include/openmc/tallies/filter_weight.h b/include/openmc/tallies/filter_weight.h new file mode 100644 index 0000000000..1fe9d75d3e --- /dev/null +++ b/include/openmc/tallies/filter_weight.h @@ -0,0 +1,51 @@ +#ifndef OPENMC_TALLIES_FILTER_WEIGHT_H +#define OPENMC_TALLIES_FILTER_WEIGHT_H + +#include + +#include "openmc/span.h" +#include "openmc/tallies/filter.h" +#include "openmc/vector.h" + +namespace openmc { + +//============================================================================== +//! Bins the weights of the particles. +//============================================================================== + +class WeightFilter : public Filter { +public: + //---------------------------------------------------------------------------- + // Constructors, destructors + + ~WeightFilter() = default; + + //---------------------------------------------------------------------------- + // Methods + + std::string type_str() const override { return "weight"; } + FilterType type() const override { return FilterType::WEIGHT; } + + void from_xml(pugi::xml_node node) override; + + void get_all_bins(const Particle& p, TallyEstimator estimator, + FilterMatch& match) const override; + + void to_statepoint(hid_t filter_group) const override; + + std::string text_label(int bin) const override; + + //---------------------------------------------------------------------------- + // Accessors + + const vector& bins() const { return bins_; } + void set_bins(span bins); + +protected: + //---------------------------------------------------------------------------- + // Data members + vector bins_; +}; + +} // namespace openmc +#endif // OPENMC_TALLIES_FILTER_WEIGHT_H diff --git a/include/openmc/tallies/filter_zernike.h b/include/openmc/tallies/filter_zernike.h index 72c47e654e..b6d9c91e6d 100644 --- a/include/openmc/tallies/filter_zernike.h +++ b/include/openmc/tallies/filter_zernike.h @@ -21,7 +21,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "zernike"; } + std::string type_str() const override { return "zernike"; } + FilterType type() const override { return FilterType::ZERNIKE; } void from_xml(pugi::xml_node node) override; @@ -72,7 +73,8 @@ public: //---------------------------------------------------------------------------- // Methods - std::string type() const override { return "zernikeradial"; } + std::string type_str() const override { return "zernikeradial"; } + FilterType type() const override { return FilterType::ZERNIKE_RADIAL; } void get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const override; diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 13b8317ee4..3beeb9d5ac 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -3,6 +3,7 @@ #include "openmc/constants.h" #include "openmc/memory.h" // for unique_ptr +#include "openmc/span.h" #include "openmc/tallies/filter.h" #include "openmc/tallies/trigger.h" #include "openmc/vector.h" @@ -10,7 +11,6 @@ #include "pugixml.hpp" #include "xtensor/xfixed.hpp" #include "xtensor/xtensor.hpp" -#include #include #include @@ -35,23 +35,65 @@ public: void set_id(int32_t id); + int id() const { return id_; } + void set_active(bool active) { active_ = active; } + void set_multiply_density(bool value) { multiply_density_ = value; } + void set_writable(bool writable) { writable_ = writable; } void set_scores(pugi::xml_node node); void set_scores(const vector& scores); + std::vector scores() const; + + int32_t n_scores() const { return scores_.size(); } + void set_nuclides(pugi::xml_node node); void set_nuclides(const vector& nuclides); + const xt::xtensor& results() const { return results_; } + + //! returns vector of indices corresponding to the tally this is called on const vector& filters() const { return filters_; } + //! returns a vector of filter types for the tally + std::vector filter_types() const; + + //! returns a mapping of filter types to index into the tally's filters + std::unordered_map filter_indices() const; + + //! \brief Returns the tally filter at index i int32_t filters(int i) const { return filters_[i]; } - void set_filters(gsl::span filters); + //! \brief Return a const pointer to a filter instance based on type. Always + //! returns the first matching filter type + template + const T* get_filter() const + { + const T* out; + for (auto filter_idx : filters_) { + if ((out = dynamic_cast(model::tally_filters[filter_idx].get()))) + return out; + } + return nullptr; + } + + template + const T* get_filter(int idx) const + { + if (const T* out = dynamic_cast(model::tally_filters[filters_.at(idx)])) + return out; + return nullptr; + } + + //! \brief Check if this tally has a specified type of filter + bool has_filter(FilterType filter_type) const; + + void set_filters(span filters); //! Given already-set filters, set the stride lengths void set_strides(); @@ -60,12 +102,14 @@ public: int32_t n_filter_bins() const { return n_filter_bins_; } + bool multiply_density() const { return multiply_density_; } + bool writable() const { return writable_; } //---------------------------------------------------------------------------- // Other methods. - void add_filter(Filter* filter) { set_filters({&filter, 1}); } + void add_filter(Filter* filter); void init_triggers(pugi::xml_node node); @@ -75,6 +119,12 @@ public: void accumulate(); + //! return the index of a score specified by name + int score_index(const std::string& score) const; + + //! Tally results reshaped according to filter sizes + xt::xarray get_reshaped_data() const; + //! A string representing the i-th score on this tally std::string score_name(int score_idx) const; @@ -104,9 +154,6 @@ public: //! Index of each nuclide to be tallied. -1 indicates total material. vector nuclides_ {-1}; - //! True if this tally has a bin for every nuclide in the problem - bool all_nuclides_ {false}; - //! Results for each bin -- the first dimension of the array is for the //! combination of filters (e.g. specific cell, specific energy group, etc.) //! and the second dimension of the array is for scores (e.g. flux, total @@ -140,7 +187,10 @@ private: int32_t n_filter_bins_ {0}; - gsl::index index_; + //! Whether to multiply by atom density for reaction rates + bool multiply_density_ {true}; + + int64_t index_; }; //============================================================================== @@ -153,9 +203,14 @@ extern vector> tallies; extern vector active_tallies; extern vector active_analog_tallies; extern vector active_tracklength_tallies; +extern vector active_timed_tracklength_tallies; extern vector active_collision_tallies; extern vector active_meshsurf_tallies; extern vector active_surface_tallies; +extern vector active_pulse_height_tallies; +extern vector pulse_height_cells; +extern vector time_grid; + } // namespace model namespace simulation { @@ -179,10 +234,21 @@ extern double global_tally_leakage; //! Read tally specification from tallies.xml void read_tallies_xml(); +//! Read tally specification from an XML node +//! \param[in] root node of tallies XML element +void read_tallies_xml(pugi::xml_node root); + //! \brief Accumulate the sum of the contributions from each history within the //! batch to a new random variable void accumulate_tallies(); +//! Determine distance to next time boundary +// +//! \param time Current time of particle +//! \param speed Speed of particle +//! \return Distance to next time boundary (or INFTY if none) +double distance_to_time_boundary(double time, double speed); + //! Determine which tallies should be active void setup_active_tallies(); diff --git a/include/openmc/tallies/tally_scoring.h b/include/openmc/tallies/tally_scoring.h index 85d0fc58f3..c3ab779e6a 100644 --- a/include/openmc/tallies/tally_scoring.h +++ b/include/openmc/tallies/tally_scoring.h @@ -91,12 +91,29 @@ void score_analog_tally_mg(Particle& p); //! \param distance The distance in [cm] traveled by the particle void score_tracklength_tally(Particle& p, double distance); +//! Score time filtered tallies using a tracklength estimate of the flux. +// +//! This is triggered at every event (surface crossing, lattice crossing, or +//! collision) and thus cannot be done for tallies that require post-collision +//! information. +// +//! \param p The particle being tracked +//! \param total_distance The distance in [cm] traveled by the particle +void score_timed_tracklength_tally(Particle& p, double total_distance); + //! Score surface or mesh-surface tallies for particle currents. // //! \param p The particle being tracked -//! \param tallies A vector of tallies to score to +//! \param tallies A vector of the indices of the tallies to score to void score_surface_tally(Particle& p, const vector& tallies); +//! Score the pulse-height tally +//! This is triggered at the end of every particle history +// +//! \param p The particle being tracked +//! \param tallies A vector of the indices of the tallies to score to +void score_pulse_height_tally(Particle& p, const vector& tallies); + } // namespace openmc #endif // OPENMC_TALLIES_TALLY_SCORING_H diff --git a/include/openmc/tallies/trigger.h b/include/openmc/tallies/trigger.h index 9fe159b9d9..7feed5e8ad 100644 --- a/include/openmc/tallies/trigger.h +++ b/include/openmc/tallies/trigger.h @@ -23,6 +23,7 @@ enum class TriggerMetric { struct Trigger { TriggerMetric metric; //!< The type of uncertainty (e.g. std dev) measured double threshold; //!< Uncertainty value below which trigger is satisfied + bool ignore_zeros; //!< Whether to allow zero tally bins to be ignored int score_index; //!< Index of the relevant score in the tally's arrays }; diff --git a/include/openmc/timer.h b/include/openmc/timer.h index 62b97883f4..d928aad456 100644 --- a/include/openmc/timer.h +++ b/include/openmc/timer.h @@ -31,6 +31,7 @@ extern Timer time_event_advance_particle; extern Timer time_event_surface_crossing; extern Timer time_event_collision; extern Timer time_event_death; +extern Timer time_update_src; } // namespace simulation diff --git a/include/openmc/track_output.h b/include/openmc/track_output.h index fc65a2eaab..2380fe4405 100644 --- a/include/openmc/track_output.h +++ b/include/openmc/track_output.h @@ -9,8 +9,31 @@ namespace openmc { // Non-member functions //============================================================================== +//! Open HDF5 track file for writing and create track datatype +void open_track_file(); + +//! Close HDF5 resources for track file +void close_track_file(); + +//! Determine whether a given particle should collect/write track information +// +//! \param[in] p Current particle +//! \return Whether to collect/write track information +bool check_track_criteria(const Particle& p); + +//! Create a new track state history for a primary/secondary particle +// +//! \param[in] p Current particle void add_particle_track(Particle& p); + +//! Store particle's current state +// +//! \param[in] p Current particle void write_particle_track(Particle& p); + +//! Write full particle state history to HDF5 track file +// +//! \param[in] p Current particle void finalize_particle_track(Particle& p); } // namespace openmc diff --git a/include/openmc/universe.h b/include/openmc/universe.h new file mode 100644 index 0000000000..796f9e6508 --- /dev/null +++ b/include/openmc/universe.h @@ -0,0 +1,84 @@ +#ifndef OPENMC_UNIVERSE_H +#define OPENMC_UNIVERSE_H + +#include "openmc/bounding_box.h" +#include "openmc/cell.h" + +namespace openmc { + +#ifdef OPENMC_DAGMC_ENABLED +class DAGUniverse; +#endif + +class GeometryState; +class Universe; +class UniversePartitioner; + +namespace model { + +extern std::unordered_map universe_map; +extern vector> universes; + +} // namespace model + +//============================================================================== +//! A geometry primitive that fills all space and contains cells. +//============================================================================== + +class Universe { +public: + int32_t id_; //!< Unique ID + vector cells_; //!< Cells within this universe + int filled_with_triso_base_ = -1; //!< ID of cell filled with virtual lattice + int32_t n_instances_; //!< Number of instances of this universe + + //! \brief Write universe information to an HDF5 group. + //! \param group_id An HDF5 group id. + virtual void to_hdf5(hid_t group_id) const; + + virtual bool find_cell(GeometryState& p) const; + + virtual bool find_cell_in_virtual_lattice(GeometryState& p) const; + + BoundingBox bounding_box() const; + + /* By default, universes are CSG universes. The DAGMC + * universe overrides standard behaviors, and in the future, + * other things might too. + */ + virtual GeometryType geom_type() const { return GeometryType::CSG; } + + unique_ptr partitioner_; +}; + +//============================================================================== +//! Speeds up geometry searches by grouping cells in a search tree. +// +//! Currently this object only works with universes that are divided up by a +//! bunch of z-planes. It could be generalized to other planes, cylinders, +//! and spheres. +//============================================================================== + +class UniversePartitioner { +public: + explicit UniversePartitioner(const Universe& univ); + + //! Return the list of cells that could contain the given coordinates. + const vector& get_cells(Position r, Direction u) const; + +private: + //! A sorted vector of indices to surfaces that partition the universe + vector surfs_; + + //! Vectors listing the indices of the cells that lie within each partition + // + //! There are n+1 partitions with n surfaces. `partitions_.front()` gives the + //! cells that lie on the negative side of `surfs_.front()`. + //! `partitions_.back()` gives the cells that lie on the positive side of + //! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched + //! between `surfs_[i-1]` and `surfs_[i]`. + vector> partitions_; +}; + +} // namespace openmc +#endif // OPENMC_UNIVERSE_H diff --git a/include/openmc/version.h.in b/include/openmc/version.h.in index 98f431288f..6dfc7c7dd6 100644 --- a/include/openmc/version.h.in +++ b/include/openmc/version.h.in @@ -1,3 +1,6 @@ +#ifndef OPENMC_VERSION_H +#define OPENMC_VERSION_H + #include "openmc/array.h" namespace openmc { @@ -6,9 +9,13 @@ namespace openmc { // clang-format off constexpr int VERSION_MAJOR {@OPENMC_VERSION_MAJOR@}; constexpr int VERSION_MINOR {@OPENMC_VERSION_MINOR@}; -constexpr int VERSION_RELEASE {@OPENMC_VERSION_RELEASE@}; -constexpr bool VERSION_DEV {false}; +constexpr int VERSION_RELEASE {@OPENMC_VERSION_PATCH@}; +constexpr bool VERSION_DEV {@OPENMC_DEV_STATE@}; +constexpr const char* VERSION_COMMIT_COUNT = "@OPENMC_COMMIT_COUNT@"; +constexpr const char* VERSION_COMMIT_HASH = "@OPENMC_COMMIT_HASH@"; constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; // clang-format on } // namespace openmc + +#endif // OPENMC_VERSION_H diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index db96f250f9..fa8d3d65ec 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -1,16 +1,22 @@ #ifndef OPENMC_VOLUME_CALC_H #define OPENMC_VOLUME_CALC_H +#include // for find +#include +#include +#include + #include "openmc/array.h" +#include "openmc/openmp_interface.h" #include "openmc/position.h" #include "openmc/tallies/trigger.h" #include "openmc/vector.h" #include "pugixml.hpp" #include "xtensor/xtensor.hpp" - -#include -#include +#ifdef _OPENMP +#include +#endif namespace openmc { @@ -33,6 +39,8 @@ public: // Constructors VolumeCalculation(pugi::xml_node node); + VolumeCalculation() = default; + // Methods //! \brief Stochastically determine the volume of a set of domains along with @@ -69,7 +77,8 @@ private: //! \param[in] i_material Index in global materials vector //! \param[in,out] indices Vector of material indices //! \param[in,out] hits Number of hits corresponding to each material - void check_hit(int i_material, vector& indices, vector& hits) const; + void check_hit( + int i_material, vector& indices, vector& hits) const; }; //============================================================================== @@ -84,6 +93,35 @@ extern vector volume_calcs; // Non-member functions //============================================================================== +//! Reduce vector of indices and hits from each thread to a single copy +// +//! \param[in] local_indices Indices specific to each thread +//! \param[in] local_hits Hit count specific to each thread +//! \param[out] indices Reduced vector of indices +//! \param[out] hits Reduced vector of hits +template +void reduce_indices_hits(const vector& local_indices, + const vector& local_hits, vector& indices, vector& hits) +{ + const int n_threads = num_threads(); + +#pragma omp for ordered schedule(static, 1) + for (int i = 0; i < n_threads; ++i) { +#pragma omp ordered + for (int j = 0; j < local_indices.size(); ++j) { + // Check if this material has been added to the master list and if + // so, accumulate the number of hits + auto it = std::find(indices.begin(), indices.end(), local_indices[j]); + if (it == indices.end()) { + indices.push_back(local_indices[j]); + hits.push_back(local_hits[j]); + } else { + hits[it - indices.begin()] += local_hits[j]; + } + } + } +} + void free_memory_volume(); } // namespace openmc diff --git a/include/openmc/weight_windows.h b/include/openmc/weight_windows.h index c2ef29ae8f..7638155228 100644 --- a/include/openmc/weight_windows.h +++ b/include/openmc/weight_windows.h @@ -11,10 +11,14 @@ #include "openmc/memory.h" #include "openmc/mesh.h" #include "openmc/particle.h" +#include "openmc/span.h" +#include "openmc/tallies/tally.h" #include "openmc/vector.h" namespace openmc { +enum class WeightWindowUpdateMethod { MAGIC, FW_CADIS }; + //============================================================================== // Constants //============================================================================== @@ -37,11 +41,13 @@ void free_memory_weight_windows(); //============================================================================== class WeightWindows; +class WeightWindowsGenerator; namespace variance_reduction { extern std::unordered_map ww_map; extern vector> weight_windows; +extern vector> weight_windows_generators; } // namespace variance_reduction @@ -55,7 +61,7 @@ struct WeightWindow { double max_lb_ratio {1}; double survival_weight {0.5}; double weight_cutoff {DEFAULT_WEIGHT_CUTOFF}; - int max_split {1}; + int max_split {10}; //! Whether the weight window is in a valid state bool is_valid() const { return lower_weight >= 0.0; } @@ -65,6 +71,7 @@ struct WeightWindow { { lower_weight *= factor; upper_weight *= factor; + survival_weight *= factor; } }; @@ -74,15 +81,55 @@ struct WeightWindow { class WeightWindows { public: + //---------------------------------------------------------------------------- // Constructors - WeightWindows(); + WeightWindows(int32_t id = -1); WeightWindows(pugi::xml_node node); + ~WeightWindows(); + static WeightWindows* create(int32_t id = -1); + static WeightWindows* from_hdf5( + hid_t wws_group, const std::string& group_name); + //---------------------------------------------------------------------------- // Methods +private: + template + void check_bounds(const T& lower, const T& upper) const; + template + void check_bounds(const T& lower) const; + + void check_tally_update_compatibility(const Tally* tally); + +public: //! Set the weight window ID void set_id(int32_t id = -1); + void set_energy_bounds(span bounds); + + void set_mesh(const std::unique_ptr& mesh); + + void set_mesh(const Mesh* mesh); + + void set_mesh(int32_t mesh_idx); + + //! Ready the weight window class for use + void set_defaults(); + + //! Ensure the weight window lower bounds are properly allocated + void allocate_ww_bounds(); + + //! Update weight window boundaries using tally results + //! \param[in] tally Pointer to the tally whose results will be used to + //! update weight windows \param[in] value String representing the type of + //! value to use for weight window generation (one of "mean" or "rel_err") + //! \param[in] threshold Relative error threshold. Results over this + //! threshold will be ignored \param[in] ratio Ratio of upper to lower + //! weight window bounds + void update_weights(const Tally* tally, const std::string& value = "mean", + double threshold = 1.0, double ratio = 5.0, + WeightWindowUpdateMethod method = WeightWindowUpdateMethod::MAGIC); + // NOTE: This is unused for now but may be used in the future //! Write weight window settings to an HDF5 file //! \param[in] group HDF5 group to write to @@ -92,23 +139,106 @@ public: //! \param[in] p Particle to get weight window for WeightWindow get_weight_window(const Particle& p) const; + std::array bounds_size() const; + + const vector& energy_bounds() const { return energy_bounds_; } + + void set_bounds(const xt::xtensor& lower_ww_bounds, + const xt::xtensor& upper_bounds); + + void set_bounds(const xt::xtensor& lower_bounds, double ratio); + + void set_bounds( + span lower_bounds, span upper_bounds); + + void set_bounds(span lower_bounds, double ratio); + + void set_particle_type(ParticleType p_type); + + double survival_ratio() const { return survival_ratio_; } + + double& survival_ratio() { return survival_ratio_; } + + double max_lower_bound_ratio() const { return max_lb_ratio_; } + + double& max_lower_bound_ratio() { return max_lb_ratio_; } + + int max_split() const { return max_split_; } + + int& max_split() { return max_split_; } + + double weight_cutoff() const { return weight_cutoff_; } + + double& weight_cutoff() { return weight_cutoff_; } + + //---------------------------------------------------------------------------- // Accessors int32_t id() const { return id_; } - const Mesh& mesh() const { return *model::meshes[mesh_idx_]; } + int32_t& id() { return id_; } + + int32_t index() const { return index_; } + + vector& energy_bounds() { return energy_bounds_; } + + const std::unique_ptr& mesh() const { return model::meshes[mesh_idx_]; } + + const xt::xtensor& lower_ww_bounds() const { return lower_ww_; } + xt::xtensor& lower_ww_bounds() { return lower_ww_; } + + const xt::xtensor& upper_ww_bounds() const { return upper_ww_; } + xt::xtensor& upper_ww_bounds() { return upper_ww_; } + + ParticleType particle_type() const { return particle_type_; } private: + //---------------------------------------------------------------------------- // Data members - int32_t id_; //!< Unique ID - ParticleType particle_type_; //!< Particle type to apply weight windows to - vector energy_bins_; //!< Energy bins [eV] - vector lower_ww_; //!< Lower weight window bounds - vector upper_ww_; //!< Upper weight window bounds - double survival_ratio_ {3.0}; //!< Survival weight ratio + int32_t id_; //!< Unique ID + int64_t index_; //!< Index into weight windows vector + ParticleType particle_type_ { + ParticleType::neutron}; //!< Particle type to apply weight windows to + vector energy_bounds_; //!< Energy boundaries [eV] + xt::xtensor lower_ww_; //!< Lower weight window bounds (shape: + //!< energy_bins, mesh_bins (k, j, i)) + xt::xtensor + upper_ww_; //!< Upper weight window bounds (shape: energy_bins, mesh_bins) + double survival_ratio_ {3.0}; //!< Survival weight ratio double max_lb_ratio_ {1.0}; //!< Maximum lower bound to particle weight ratio double weight_cutoff_ {DEFAULT_WEIGHT_CUTOFF}; //!< Weight cutoff - int max_split_ {10}; //!< Maximum value for particle splitting - int32_t mesh_idx_; //!< index in meshes vector + int max_split_ {10}; //!< Maximum value for particle splitting + int32_t mesh_idx_ {-1}; //!< Index in meshes vector }; +class WeightWindowsGenerator { +public: + // Constructors + WeightWindowsGenerator(pugi::xml_node node); + + // Methods + void update() const; + + //! Create the tally used for weight window generation + void create_tally(); + + // Data members + int32_t tally_idx_; //!< Index of the tally used to update the weight windows + int32_t ww_idx_; //!< Index of the weight windows object being generated + WeightWindowUpdateMethod method_; //!< Method used to update weight window. + int32_t max_realizations_; //!< Maximum number of tally realizations + int32_t update_interval_; //!< Determines how often updates occur + bool on_the_fly_; //!< Whether or not to keep tally results between batches or + //!< realizations + + // MAGIC update parameters + std::string tally_value_ { + "mean"}; // get_node_xarray( return xt::adapt(v, shape); } +std::vector get_node_position_array( + pugi::xml_node node, const char* name, bool lowercase = false); + +Position get_node_position( + pugi::xml_node node, const char* name, bool lowercase = false); + } // namespace openmc #endif // OPENMC_XML_INTERFACE_H diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index 460dbd3588..7826a759a1 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -8,7 +8,10 @@ sections are available. .SH SYNOPSIS \fBopenmc\fR [\fIoptions\fR] [\fIpath\fR] .PP -It is assumed that if no +.I path +specifies either the path to a single model XML file containing the full model +or a directory containing either a model.xml file or a set of individual XML +files (settings.xml, materials.xml, geometry.xml). It is assumed that if no .I path is specified, the XML input files are present in the current directory. .SH OPTIONS @@ -34,7 +37,7 @@ Restart a previous run from a state point or a particle restart file named Use \fIN\fP OpenMP threads. .TP .B "\-t\fR, \fP\-\-track" -Write tracks for all particles. +Write tracks for all particles (up to max_tracks). .TP .B "\-v\fR, \fP\-\-version" Show version information. @@ -46,6 +49,9 @@ The behavior of .B openmc is affected by the following environment variables. .TP +.B OPENMC_CHAIN_FILE +Indicates the path to a depletion chain XML file. +.TP .B OPENMC_CROSS_SECTIONS Indicates the default path to the cross_sections.xml summary file that is used to locate HDF5 format cross section libraries if the user has not specified the @@ -57,7 +63,7 @@ Indicates the default path to an HDF5 file that contains multi-group cross section libraries if the user has not specified the tag in .I materials.xml\fP. .SH LICENSE -Copyright \(co 2011-2022 Massachusetts Institute of Technology, UChicago +Copyright \(co 2011-2025 Massachusetts Institute of Technology, UChicago Argonne LLC, and OpenMC contributors. .PP Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/openmc/__init__.py b/openmc/__init__.py index d7bcdffff9..bb972b4e6a 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -1,4 +1,6 @@ +import importlib.metadata from openmc.arithmetic import * +from openmc.bounding_box import * from openmc.cell import * from openmc.checkvalue import * from openmc.mesh import * @@ -10,11 +12,12 @@ from openmc.material import * from openmc.plots import * from openmc.region import * from openmc.volume import * -from openmc.source import * from openmc.weight_windows import * -from openmc.settings import * from openmc.surface import * from openmc.universe import * +from openmc.dagmc import * +from openmc.source import * +from openmc.settings import * from openmc.lattice import * from openmc.filter import * from openmc.filter_expansion import * @@ -30,10 +33,13 @@ from openmc.mixin import * from openmc.plotter import * from openmc.search import * from openmc.polynomial import * -from . import examples +from openmc.tracks import * +from .config import * # Import a few names from the model module -from openmc.model import rectangular_prism, hexagonal_prism, Model +from openmc.model import Model + +from . import examples -__version__ = '0.13.0' +__version__ = importlib.metadata.version("openmc") diff --git a/openmc/_xml.py b/openmc/_xml.py index 32679fd89e..758d805255 100644 --- a/openmc/_xml.py +++ b/openmc/_xml.py @@ -1,22 +1,40 @@ -def clean_indentation(element, level=0, spaces_per_level=2): - """ - copy and paste from https://effbot.org/zone/element-lib.htm#prettyprint - it basically walks your tree and adds spaces and newlines so the tree is - printed in a nice way +def clean_indentation(element, level=0, spaces_per_level=2, trailing_indent=True): + """Set indentation of XML element and its sub-elements. + Copied and pasted from https://effbot.org/zone/element-lib.htm#prettyprint. + It walks your tree and adds spaces and newlines so the tree is + printed in a nice way. + + Parameters + ---------- + level : int + Indentation level for the element passed in (default 0) + spaces_per_level : int + Number of spaces per indentation level (default 2) + trailing_indent : bool + Whether or not to add indentation after closing the element + """ i = "\n" + level*spaces_per_level*" " + # ensure there's always some tail for the element passed in + if not element.tail: + element.tail = "" + if len(element): if not element.text or not element.text.strip(): element.text = i + spaces_per_level*" " - if not element.tail or not element.tail.strip(): + if trailing_indent and (not element.tail or not element.tail.strip()): element.tail = i for sub_element in element: + # `trailing_indent` is intentionally not forwarded to the recursive + # call. Any child element of the topmost element should add + # indentation at the end to ensure its parent's indentation is + # correct. clean_indentation(sub_element, level+1, spaces_per_level) if not sub_element.tail or not sub_element.tail.strip(): sub_element.tail = i else: - if level and (not element.tail or not element.tail.strip()): + if trailing_indent and level and (not element.tail or not element.tail.strip()): element.tail = i @@ -25,7 +43,7 @@ def get_text(elem, name, default=None): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element Element from which to search name : str Name of attribute/subelement @@ -45,19 +63,24 @@ def get_text(elem, name, default=None): return child.text if child is not None else default -def reorder_attributes(root): - """Sort attributes in XML to preserve pre-Python 3.8 behavior + +def get_elem_list(elem, name, dtype=int): + """Helper function to get a list of values from an elem Parameters ---------- - root : xml.etree.ElementTree.Element - Root element + elem : lxml.etree._Element + XML element that should contain a tuple + name : str + Name of the subelement to obtain tuple from + dtype : data-type + The type of each element in the tuple + Returns + ------- + list of dtype + Data read from the list """ - for el in root.iter(): - attrib = el.attrib - if len(attrib) > 1: - # adjust attribute order, e.g. by sorting - attribs = sorted(attrib.items()) - attrib.clear() - attrib.update(attribs) + text = get_text(elem, name) + if text is not None: + return [dtype(x) for x in text.split()] diff --git a/openmc/arithmetic.py b/openmc/arithmetic.py index 618c7c4a1a..92c42284c0 100644 --- a/openmc/arithmetic.py +++ b/openmc/arithmetic.py @@ -10,10 +10,10 @@ from .filter import _FILTER_TYPES # Acceptable tally arithmetic binary operations -_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^'] +_TALLY_ARITHMETIC_OPS = {'+', '-', '*', '/', '^'} # Acceptable tally aggregation operations -_TALLY_AGGREGATE_OPS = ['sum', 'avg'] +_TALLY_AGGREGATE_OPS = {'sum', 'avg'} class CrossScore: @@ -54,33 +54,32 @@ class CrossScore: return str(other) == str(self) def __repr__(self): - return '({} {} {})'.format(self.left_score, self.binary_op, - self.right_score) + return f'({self.left_score} {self.binary_op} {self.right_score})' @property def left_score(self): return self._left_score - @property - def right_score(self): - return self._right_score - - @property - def binary_op(self): - return self._binary_op - @left_score.setter def left_score(self, left_score): cv.check_type('left_score', left_score, (str, CrossScore, AggregateScore)) self._left_score = left_score + @property + def right_score(self): + return self._right_score + @right_score.setter def right_score(self, right_score): cv.check_type('right_score', right_score, (str, CrossScore, AggregateScore)) self._right_score = right_score + @property + def binary_op(self): + return self._binary_op + @binary_op.setter def binary_op(self, binary_op): cv.check_type('binary_op', binary_op, str) @@ -94,9 +93,9 @@ class CrossNuclide: Parameters ---------- - left_nuclide : openmc.Nuclide or CrossNuclide + left_nuclide : str or CrossNuclide The left nuclide in the outer product - right_nuclide : openmc.Nuclide or CrossNuclide + right_nuclide : str or CrossNuclide The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -104,9 +103,9 @@ class CrossNuclide: Attributes ---------- - left_nuclide : openmc.Nuclide or CrossNuclide + left_nuclide : str or CrossNuclide The left nuclide in the outer product - right_nuclide : openmc.Nuclide or CrossNuclide + right_nuclide : str or CrossNuclide The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -132,55 +131,36 @@ class CrossNuclide: def left_nuclide(self): return self._left_nuclide + @left_nuclide.setter + def left_nuclide(self, left_nuclide): + cv.check_type('left_nuclide', left_nuclide, + (str, CrossNuclide, AggregateNuclide)) + self._left_nuclide = left_nuclide + @property def right_nuclide(self): return self._right_nuclide - @property - def binary_op(self): - return self._binary_op - - @property - def name(self): - - string = '' - - # If the Summary was linked, the left nuclide is a Nuclide object - if isinstance(self.left_nuclide, openmc.Nuclide): - string += '(' + self.left_nuclide.name - # If the Summary was not linked, the left nuclide is the ZAID - else: - string += '(' + str(self.left_nuclide) - - string += ' ' + self.binary_op + ' ' - - # If the Summary was linked, the right nuclide is a Nuclide object - if isinstance(self.right_nuclide, openmc.Nuclide): - string += self.right_nuclide.name + ')' - # If the Summary was not linked, the right nuclide is the ZAID - else: - string += str(self.right_nuclide) + ')' - - return string - - @left_nuclide.setter - def left_nuclide(self, left_nuclide): - cv.check_type('left_nuclide', left_nuclide, - (openmc.Nuclide, CrossNuclide, AggregateNuclide)) - self._left_nuclide = left_nuclide - @right_nuclide.setter def right_nuclide(self, right_nuclide): cv.check_type('right_nuclide', right_nuclide, - (openmc.Nuclide, CrossNuclide, AggregateNuclide)) + (str, CrossNuclide, AggregateNuclide)) self._right_nuclide = right_nuclide + @property + def binary_op(self): + return self._binary_op + @binary_op.setter def binary_op(self, binary_op): cv.check_type('binary_op', binary_op, str) cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) self._binary_op = binary_op + @property + def name(self): + return f'({self.left_nuclide} {self.binary_op} {self.right_nuclide})' + class CrossFilter: """A special-purpose filter used to encapsulate all combinations of two @@ -188,9 +168,9 @@ class CrossFilter: Parameters ---------- - left_filter : Filter or CrossFilter + left_filter : openmc.Filter or CrossFilter The left filter in the outer product - right_filter : Filter or CrossFilter + right_filter : openmc.Filter or CrossFilter The right filter in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -200,9 +180,9 @@ class CrossFilter: ---------- type : str The type of the crossfilter (e.g., 'energy / energy') - left_filter : Filter or CrossFilter + left_filter : openmc.Filter or CrossFilter The left filter in the outer product - right_filter : Filter or CrossFilter + right_filter : openmc.Filter or CrossFilter The right filter in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to @@ -241,19 +221,37 @@ class CrossFilter: def left_filter(self): return self._left_filter + @left_filter.setter + def left_filter(self, left_filter): + cv.check_type('left_filter', left_filter, + (openmc.Filter, CrossFilter, AggregateFilter)) + self._left_filter = left_filter + @property def right_filter(self): return self._right_filter + @right_filter.setter + def right_filter(self, right_filter): + cv.check_type('right_filter', right_filter, + (openmc.Filter, CrossFilter, AggregateFilter)) + self._right_filter = right_filter + @property def binary_op(self): return self._binary_op + @binary_op.setter + def binary_op(self, binary_op): + cv.check_type('binary_op', binary_op, str) + cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) + self._binary_op = binary_op + @property def type(self): left_type = self.left_filter.type right_type = self.right_filter.type - return '({} {} {})'.format(left_type, self.binary_op, right_type) + return f'({left_type} {self.binary_op} {right_type})' @property def bins(self): @@ -266,24 +264,6 @@ class CrossFilter: else: return 0 - @left_filter.setter - def left_filter(self, left_filter): - cv.check_type('left_filter', left_filter, - (openmc.Filter, CrossFilter, AggregateFilter)) - self._left_filter = left_filter - - @right_filter.setter - def right_filter(self, right_filter): - cv.check_type('right_filter', right_filter, - (openmc.Filter, CrossFilter, AggregateFilter)) - self._right_filter = right_filter - - @binary_op.setter - def binary_op(self, binary_op): - cv.check_type('binary_op', binary_op, str) - cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS) - self._binary_op = binary_op - def get_bin_index(self, filter_bin): """Returns the index in the CrossFilter for some bin. @@ -412,10 +392,21 @@ class AggregateScore: def scores(self): return self._scores + @scores.setter + def scores(self, scores): + cv.check_iterable_type('scores', scores, str) + self._scores = scores + @property def aggregate_op(self): return self._aggregate_op + @aggregate_op.setter + def aggregate_op(self, aggregate_op): + cv.check_type('aggregate_op', aggregate_op, (str, CrossScore)) + cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) + self._aggregate_op = aggregate_op + @property def name(self): @@ -423,17 +414,6 @@ class AggregateScore: string = '(' + ', '.join(self.scores) + ')' return string - @scores.setter - def scores(self, scores): - cv.check_iterable_type('scores', scores, str) - self._scores = scores - - @aggregate_op.setter - def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, (str, CrossScore)) - cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) - self._aggregate_op = aggregate_op - class AggregateNuclide: """A special-purpose tally nuclide used to encapsulate an aggregate of a @@ -441,7 +421,7 @@ class AggregateNuclide: Parameters ---------- - nuclides : Iterable of str or openmc.Nuclide or CrossNuclide + nuclides : Iterable of str or CrossNuclide The nuclides included in the aggregation aggregate_op : str The tally aggregation operator (e.g., 'sum', 'avg', etc.) used @@ -449,7 +429,7 @@ class AggregateNuclide: Attributes ---------- - nuclides : Iterable of str or openmc.Nuclide or CrossNuclide + nuclides : Iterable of str or CrossNuclide The nuclides included in the aggregation aggregate_op : str The tally aggregation operator (e.g., 'sum', 'avg', etc.) used @@ -474,42 +454,33 @@ class AggregateNuclide: return str(other) == str(self) def __repr__(self): - - # Append each nuclide in the aggregate to the string - string = f'{self.aggregate_op}(' - names = [nuclide.name if isinstance(nuclide, openmc.Nuclide) - else str(nuclide) for nuclide in self.nuclides] - string += ', '.join(map(str, names)) + ')' - return string + return f'{self.aggregate_op}{self.name}' @property def nuclides(self): return self._nuclides - @property - def aggregate_op(self): - return self._aggregate_op - - @property - def name(self): - - # Append each nuclide in the aggregate to the string - names = [nuclide.name if isinstance(nuclide, openmc.Nuclide) - else str(nuclide) for nuclide in self.nuclides] - string = '(' + ', '.join(map(str, names)) + ')' - return string - @nuclides.setter def nuclides(self, nuclides): cv.check_iterable_type('nuclides', nuclides, (str, CrossNuclide)) self._nuclides = nuclides + @property + def aggregate_op(self): + return self._aggregate_op + @aggregate_op.setter def aggregate_op(self, aggregate_op): cv.check_type('aggregate_op', aggregate_op, str) cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) self._aggregate_op = aggregate_op + @property + def name(self): + # Append each nuclide in the aggregate to the string + names = [str(nuclide) for nuclide in self.nuclides] + return '(' + ', '.join(map(str, names)) + ')' + class AggregateFilter: """A special-purpose tally filter used to encapsulate an aggregate of a @@ -517,7 +488,7 @@ class AggregateFilter: Parameters ---------- - aggregate_filter : Filter or CrossFilter + aggregate_filter : openmc.Filter or CrossFilter The filter included in the aggregation bins : Iterable of tuple The filter bins included in the aggregation @@ -529,7 +500,7 @@ class AggregateFilter: ---------- type : str The type of the aggregatefilter (e.g., 'sum(energy)', 'sum(cell)') - aggregate_filter : filter + aggregate_filter : openmc.Filter The filter included in the aggregation aggregate_op : str The tally aggregation operator (e.g., 'sum', 'avg', etc.) used @@ -588,22 +559,26 @@ class AggregateFilter: def aggregate_filter(self): return self._aggregate_filter + @aggregate_filter.setter + def aggregate_filter(self, aggregate_filter): + cv.check_type('aggregate_filter', aggregate_filter, + (openmc.Filter, CrossFilter)) + self._aggregate_filter = aggregate_filter + @property def aggregate_op(self): return self._aggregate_op + @aggregate_op.setter + def aggregate_op(self, aggregate_op): + cv.check_type('aggregate_op', aggregate_op, str) + cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) + self._aggregate_op = aggregate_op + @property def type(self): return self._type - @property - def bins(self): - return self._bins - - @property - def num_bins(self): - return len(self.bins) if self.aggregate_filter else 0 - @type.setter def type(self, filter_type): if filter_type not in _FILTER_TYPES: @@ -613,22 +588,22 @@ class AggregateFilter: self._type = filter_type - @aggregate_filter.setter - def aggregate_filter(self, aggregate_filter): - cv.check_type('aggregate_filter', aggregate_filter, - (openmc.Filter, CrossFilter)) - self._aggregate_filter = aggregate_filter + @property + def bins(self): + return self._bins @bins.setter def bins(self, bins): cv.check_iterable_type('bins', bins, Iterable) self._bins = list(map(tuple, bins)) - @aggregate_op.setter - def aggregate_op(self, aggregate_op): - cv.check_type('aggregate_op', aggregate_op, str) - cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS) - self._aggregate_op = aggregate_op + @property + def num_bins(self): + return len(self.bins) if self.aggregate_filter else 0 + + @property + def shape(self): + return (self.num_bins,) def get_bin_index(self, filter_bin): """Returns the index in the AggregateFilter for some bin. diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py new file mode 100644 index 0000000000..f0dc06a4a0 --- /dev/null +++ b/openmc/bounding_box.py @@ -0,0 +1,202 @@ +from __future__ import annotations +from collections.abc import Iterable + +import numpy as np + +from .checkvalue import check_length + + +class BoundingBox: + """Axis-aligned bounding box. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + lower_left : iterable of float + The x, y, z coordinates of the lower left corner of the bounding box in [cm] + upper_right : iterable of float + The x, y, z coordinates of the upper right corner of the bounding box in [cm] + + Attributes + ---------- + center : numpy.ndarray + x, y, z coordinates of the center of the bounding box in [cm] + lower_left : numpy.ndarray + The x, y, z coordinates of the lower left corner of the bounding box in [cm] + upper_right : numpy.ndarray + The x, y, z coordinates of the upper right corner of the bounding box in [cm] + volume : float + The volume of the bounding box in [cm^3] + extent : dict + A dictionary of basis as keys and the extent (left, right, bottom, top) + as values. Intended use in Matplotlib plots when setting extent + width : iterable of float + The width of the x, y and z axis in [cm] + """ + + def __init__(self, lower_left: Iterable[float], upper_right: Iterable[float]): + check_length("lower_left", lower_left, 3, 3) + check_length("upper_right", upper_right, 3, 3) + self._bounds = np.asarray([lower_left, upper_right], dtype=float) + + def __repr__(self) -> str: + return "BoundingBox(lower_left={}, upper_right={})".format( + tuple(float(x) for x in self.lower_left), + tuple(float(x) for x in self.upper_right)) + + def __getitem__(self, key) -> np.ndarray: + return self._bounds[key] + + def __len__(self): + return 2 + + def __setitem__(self, key, val): + self._bounds[key] = val + + def __iand__(self, other: BoundingBox) -> BoundingBox: + """Updates the box be the intersection of itself and another box + + Parameters + ---------- + other : BoundingBox + The box used to resize this box + + Returns + ------- + An updated bounding box + """ + self.lower_left = np.maximum(self.lower_left, other.lower_left) + self.upper_right = np.minimum(self.upper_right, other.upper_right) + return self + + def __and__(self, other: BoundingBox) -> BoundingBox: + new = BoundingBox(*self) + new &= other + return new + + def __ior__(self, other: BoundingBox) -> BoundingBox: + """Updates the box be the union of itself and another box + + Parameters + ---------- + other : BoundingBox + The box used to resize this box + + Returns + ------- + An updated bounding box + """ + self.lower_left = np.minimum(self.lower_left, other.lower_left) + self.upper_right = np.maximum(self.upper_right, other.upper_right) + return self + + def __or__(self, other: BoundingBox) -> BoundingBox: + new = BoundingBox(*self) + new |= other + return new + + def __contains__(self, other): + """Check whether or not a point or another bounding box is in the bounding box. + + For another bounding box to be in the parent it must lie fully inside of it. + """ + # test for a single point + if isinstance(other, (tuple, list, np.ndarray)): + point = other + check_length("Point", point, 3, 3) + return all(point > self.lower_left) and all(point < self.upper_right) + elif isinstance(other, BoundingBox): + return all([p in self for p in [other.lower_left, other.upper_right]]) + else: + raise TypeError( + f"Unable to determine if {other} is in the bounding box." + f" Expected a tuple or a bounding box, but {type(other)} given" + ) + + @property + def center(self) -> np.ndarray: + return (self[0] + self[1]) / 2 + + @property + def lower_left(self) -> np.ndarray: + return self[0] + + @lower_left.setter + def lower_left(self, llc): + check_length('lower_left', llc, 3, 3) + self[0] = llc + + @property + def upper_right(self) -> np.ndarray: + return self[1] + + @upper_right.setter + def upper_right(self, urc): + check_length('upper_right', urc, 3, 3) + self[1] = urc + + @property + def volume(self) -> float: + return np.abs(np.prod(self[1] - self[0])) + + @property + def extent(self): + return { + "xy": ( + self.lower_left[0], + self.upper_right[0], + self.lower_left[1], + self.upper_right[1], + ), + "xz": ( + self.lower_left[0], + self.upper_right[0], + self.lower_left[2], + self.upper_right[2], + ), + "yz": ( + self.lower_left[1], + self.upper_right[1], + self.lower_left[2], + self.upper_right[2], + ), + } + + @property + def width(self): + return self.upper_right - self.lower_left + + def expand(self, padding_distance: float, inplace: bool = False) -> BoundingBox: + """Returns an expanded bounding box + + Parameters + ---------- + padding_distance : float + The distance to enlarge the bounding box by + inplace : bool + Whether or not to return a new BoundingBox instance or to modify the + current BoundingBox object. + + Returns + ------- + An expanded bounding box + """ + if inplace: + self[0] -= padding_distance + self[1] += padding_distance + return self + else: + return BoundingBox(self[0] - padding_distance, self[1] + padding_distance) + + @classmethod + def infinite(cls) -> BoundingBox: + """Create an infinite box. Useful as a starting point for determining + geometry bounds. + + Returns + ------- + An infinitely large bounding box. + """ + infs = np.full((3,), np.inf) + return cls(-infs, infs) diff --git a/openmc/cell.py b/openmc/cell.py index 52ff271418..ebabcf7620 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -1,19 +1,19 @@ -from collections import OrderedDict from collections.abc import Iterable -from copy import deepcopy from math import cos, sin, pi from numbers import Real -from xml.etree import ElementTree as ET +import lxml.etree as ET import numpy as np from uncertainties import UFloat import openmc import openmc.checkvalue as cv -from ._xml import get_text +from ._xml import get_elem_list, get_text from .mixin import IDManagerMixin +from .plots import add_plot_params from .region import Region, Complement from .surface import Halfspace +from .bounding_box import BoundingBox class Cell(IDManagerMixin): @@ -72,6 +72,10 @@ class Cell(IDManagerMixin): temperature : float or iterable of float Temperature of the cell in Kelvin. Multiple temperatures can be given to give each distributed cell instance a unique temperature. + density : float or iterable of float + Density of the cell in [g/cm3]. Multiple densities can be given to give + each distributed cell instance a unique density. Densities set here will + override the density set on materials used to fill the cell. translation : Iterable of float If the cell is filled with a universe, this array specifies a vector that is used to translate (shift) the universe. @@ -86,12 +90,14 @@ class Cell(IDManagerMixin): calculated in a stochastic volume calculation and added via the :meth:`Cell.add_volume_information` method. For 'distribmat' cells it is the total volume of all instances. - atoms : collections.OrderedDict + atoms : dict Mapping of nuclides to the total number of atoms for each nuclide present in the cell, or in all of its instances for a 'distribmat' fill. For example, {'U235': 1.0e22, 'U238': 5.0e22, ...}. .. versionadded:: 0.12 + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the cell """ @@ -107,6 +113,7 @@ class Cell(IDManagerMixin): self._rotation = None self._rotation_matrix = None self._temperature = None + self._density = None self._translation = None self._paths = None self._num_instances = None @@ -144,6 +151,7 @@ class Cell(IDManagerMixin): if self.fill_type == 'material': string += '\t{0: <15}=\t{1}\n'.format('Temperature', self.temperature) + string += '\t{0: <15}=\t{1}\n'.format('Density', self.density) string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation) string += '{: <16}=\t{}\n'.format('\tVolume', self.volume) @@ -153,10 +161,37 @@ class Cell(IDManagerMixin): def name(self): return self._name + @name.setter + def name(self, name): + if name is not None: + cv.check_type('cell name', name, str) + self._name = name + else: + self._name = '' + @property def fill(self): return self._fill + @fill.setter + def fill(self, fill): + if fill is not None: + if isinstance(fill, Iterable): + for i, f in enumerate(fill): + if f is not None: + cv.check_type('cell.fill[i]', f, openmc.Material) + + elif not isinstance(fill, (openmc.Material, openmc.Lattice, + openmc.UniverseBase)): + msg = (f'Unable to set Cell ID="{self._id}" to use a ' + f'non-Material or Universe fill "{fill}"') + raise ValueError(msg) + self._fill = fill + + # Info about atom content can now be invalid + # (since fill has just changed) + self._atoms = None + @property def fill_type(self): if isinstance(self.fill, openmc.Material): @@ -174,10 +209,37 @@ class Cell(IDManagerMixin): def region(self): return self._region + @region.setter + def region(self, region): + if region is not None: + cv.check_type('cell region', region, Region) + self._region = region + @property def rotation(self): return self._rotation + @rotation.setter + def rotation(self, rotation): + cv.check_length('cell rotation', rotation, 3) + self._rotation = np.asarray(rotation) + + # Save rotation matrix -- the reason we do this instead of having it be + # automatically calculated when the rotation_matrix property is accessed + # is so that plotting on a rotated geometry can be done faster. + if self._rotation.ndim == 2: + # User specified rotation matrix directly + self._rotation_matrix = self._rotation + else: + phi, theta, psi = self.rotation*(-pi/180.) + c3, s3 = cos(phi), sin(phi) + c2, s2 = cos(theta), sin(theta) + c1, s1 = cos(psi), sin(psi) + self._rotation_matrix = np.array([ + [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], + [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], + [-s2, c2*s3, c2*c3]]) + @property def rotation_matrix(self): return self._rotation_matrix @@ -186,14 +248,76 @@ class Cell(IDManagerMixin): def temperature(self): return self._temperature + @temperature.setter + def temperature(self, temperature): + # Make sure temperatures are positive + cv.check_type('cell temperature', temperature, (Iterable, Real), none_ok=True) + if isinstance(temperature, Iterable): + cv.check_type('cell temperature', temperature, Iterable, Real) + for T in temperature: + cv.check_greater_than('cell temperature', T, 0.0, True) + elif isinstance(temperature, Real): + cv.check_greater_than('cell temperature', temperature, 0.0, True) + + # If this cell is filled with a universe or lattice, propagate + # temperatures to all cells contained. Otherwise, simply assign it. + if self.fill_type in ('universe', 'lattice'): + for c in self.get_all_cells().values(): + if c.fill_type == 'material': + c._temperature = temperature + else: + self._temperature = temperature + + @property + def density(self): + return self._density + + @density.setter + def density(self, density): + # Make sure densities are greater than zero + cv.check_type('cell density', density, (Iterable, Real), none_ok=True) + if isinstance(density, Iterable): + cv.check_type('cell density', density, Iterable, Real) + for rho in density: + cv.check_greater_than('cell density', rho, 0.0, True) + elif isinstance(density, Real): + cv.check_greater_than('cell density', density, 0.0, True) + + # If this cell is filled with a universe or lattice, propagate + # densities to all cells contained. Otherwise, simply assign it. + if self.fill_type in ('universe', 'lattice'): + for c in self.get_all_cells().values(): + if c.fill_type == 'material': + c._density = density + else: + self._density = density + @property def translation(self): return self._translation + @translation.setter + def translation(self, translation): + cv.check_type('cell translation', translation, Iterable, Real) + cv.check_length('cell translation', translation, 3) + self._translation = np.asarray(translation) + @property def volume(self): return self._volume + @volume.setter + def volume(self, volume): + if volume is not None: + cv.check_type('cell volume', volume, (Real, UFloat)) + cv.check_greater_than('cell volume', volume, 0.0, equality=True) + + self._volume = volume + + # Info about atom content can now be invalid + # (since volume has just changed) + self._atoms = None + @property def atoms(self): if self._atoms is None: @@ -219,22 +343,22 @@ class Cell(IDManagerMixin): self._atoms = self._fill.get_nuclide_atom_densities() # Convert to total number of atoms - for key, nuclide in self._atoms.items(): - atom = nuclide[1] * self._volume * 1.0e+24 + for key, atom_per_bcm in self._atoms.items(): + atom = atom_per_bcm * self._volume * 1.0e+24 self._atoms[key] = atom elif self.fill_type == 'distribmat': # Assumes that volume is total volume of all instances # Also assumes that all instances have the same volume partial_volume = self.volume / len(self.fill) - self._atoms = OrderedDict() + self._atoms = {} for mat in self.fill: - for key, nuclide in mat.get_nuclide_atom_densities().items(): + for key, atom_per_bcm in mat.get_nuclide_atom_densities().items(): # To account for overlap of nuclides between distribmat # we need to append new atoms to any existing value # hence it is necessary to ask for default. atom = self._atoms.setdefault(key, 0) - atom += nuclide[1] * partial_volume * 1.0e+24 + atom += atom_per_bcm * partial_volume * 1.0e+24 self._atoms[key] = atom else: @@ -255,8 +379,7 @@ class Cell(IDManagerMixin): if self.region is not None: return self.region.bounding_box else: - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() @property def num_instances(self): @@ -266,98 +389,6 @@ class Cell(IDManagerMixin): 'Geometry.determine_paths() method.') return self._num_instances - @name.setter - def name(self, name): - if name is not None: - cv.check_type('cell name', name, str) - self._name = name - else: - self._name = '' - - @fill.setter - def fill(self, fill): - if fill is not None: - if isinstance(fill, Iterable): - for i, f in enumerate(fill): - if f is not None: - cv.check_type('cell.fill[i]', f, openmc.Material) - - elif not isinstance(fill, (openmc.Material, openmc.Lattice, - openmc.UniverseBase)): - msg = (f'Unable to set Cell ID="{self._id}" to use a ' - f'non-Material or Universe fill "{fill}"') - raise ValueError(msg) - self._fill = fill - - # Info about atom content can now be invalid - # (since fill has just changed) - self._atoms = None - - @rotation.setter - def rotation(self, rotation): - cv.check_length('cell rotation', rotation, 3) - self._rotation = np.asarray(rotation) - - # Save rotation matrix -- the reason we do this instead of having it be - # automatically calculated when the rotation_matrix property is accessed - # is so that plotting on a rotated geometry can be done faster. - if self._rotation.ndim == 2: - # User specified rotation matrix directly - self._rotation_matrix = self._rotation - else: - phi, theta, psi = self.rotation*(-pi/180.) - c3, s3 = cos(phi), sin(phi) - c2, s2 = cos(theta), sin(theta) - c1, s1 = cos(psi), sin(psi) - self._rotation_matrix = np.array([ - [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], - [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], - [-s2, c2*s3, c2*c3]]) - - @translation.setter - def translation(self, translation): - cv.check_type('cell translation', translation, Iterable, Real) - cv.check_length('cell translation', translation, 3) - self._translation = np.asarray(translation) - - @temperature.setter - def temperature(self, temperature): - # Make sure temperatures are positive - cv.check_type('cell temperature', temperature, (Iterable, Real)) - if isinstance(temperature, Iterable): - cv.check_type('cell temperature', temperature, Iterable, Real) - for T in temperature: - cv.check_greater_than('cell temperature', T, 0.0, True) - else: - cv.check_greater_than('cell temperature', temperature, 0.0, True) - - # If this cell is filled with a universe or lattice, propagate - # temperatures to all cells contained. Otherwise, simply assign it. - if self.fill_type in ('universe', 'lattice'): - for c in self.get_all_cells().values(): - if c.fill_type == 'material': - c._temperature = temperature - else: - self._temperature = temperature - - @region.setter - def region(self, region): - if region is not None: - cv.check_type('cell region', region, Region) - self._region = region - - @volume.setter - def volume(self, volume): - if volume is not None: - cv.check_type('cell volume', volume, (Real, UFloat)) - cv.check_greater_than('cell volume', volume, 0.0, equality=True) - - self._volume = volume - - # Info about atom content can now be invalid - # (since volume has just changed) - self._atoms = None - def add_volume_information(self, volume_calc): """Add volume information to a cell. @@ -392,13 +423,13 @@ class Cell(IDManagerMixin): Returns ------- - nuclides : collections.OrderedDict + nuclides : dict Dictionary whose keys are nuclide names and values are 2-tuples of (nuclide, density) """ - nuclides = OrderedDict() + nuclides = {} if self.fill_type == 'material': nuclides.update(self.fill.get_nuclide_densities()) @@ -408,9 +439,8 @@ class Cell(IDManagerMixin): if self._atoms is not None: volume = self.volume for name, atoms in self._atoms.items(): - nuclide = openmc.Nuclide(name) density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm - nuclides[name] = (nuclide, density) + nuclides[name] = (name, density) else: raise RuntimeError( 'Volume information is needed to calculate microscopic ' @@ -426,20 +456,18 @@ class Cell(IDManagerMixin): Returns ------- - cells : collections.orderedDict + cells : dict Dictionary whose keys are cell IDs and values are :class:`Cell` instances """ + if memo is None: + memo = set() + elif self in memo: + return {} + memo.add(self) - cells = OrderedDict() - - if memo and self in memo: - return cells - - if memo is not None: - memo.add(self) - + cells = {} if self.fill_type in ('universe', 'lattice'): cells.update(self.fill.get_all_cells(memo)) @@ -450,12 +478,12 @@ class Cell(IDManagerMixin): Returns ------- - materials : collections.OrderedDict + materials : dict Dictionary whose keys are material IDs and values are :class:`Material` instances """ - materials = OrderedDict() + materials = {} if self.fill_type == 'material': materials[self.fill.id] = self.fill elif self.fill_type == 'distribmat': @@ -470,29 +498,33 @@ class Cell(IDManagerMixin): return materials - def get_all_universes(self): + def get_all_universes(self, memo=None): """Return all universes that are contained within this one if any of its cells are filled with a universe or lattice. Returns ------- - universes : collections.OrderedDict + universes : dict Dictionary whose keys are universe IDs and values are :class:`Universe` instances """ + if memo is None: + memo = set() + if self in memo: + return {} + memo.add(self) - universes = OrderedDict() - + universes = {} if self.fill_type == 'universe': universes[self.fill.id] = self.fill - universes.update(self.fill.get_all_universes()) + universes.update(self.fill.get_all_universes(memo)) elif self.fill_type == 'lattice': - universes.update(self.fill.get_all_universes()) + universes.update(self.fill.get_all_universes(memo)) return universes - def clone(self, clone_materials=True, clone_regions=True, memo=None): + def clone(self, clone_materials=True, clone_regions=True, memo=None): """Create a copy of this cell with a new unique ID, and clones the cell's region and fill. @@ -524,8 +556,16 @@ class Cell(IDManagerMixin): paths = self._paths self._paths = None - clone = deepcopy(self) - clone.id = None + clone = openmc.Cell(name=self.name) + clone.volume = self.volume + if self.temperature is not None: + clone.temperature = self.temperature + if self.density is not None: + clone.density = self.density + if self.translation is not None: + clone.translation = self.translation + if self.rotation is not None: + clone.rotation = self.rotation clone._num_instances = None # Restore paths on original instance @@ -557,12 +597,25 @@ class Cell(IDManagerMixin): return memo[self] + @add_plot_params + def plot(self, *args, **kwargs): + """Display a slice plot of the cell. + + .. versionadded:: 0.14.0 + """ + # Create dummy universe but preserve used_ids + next_id = openmc.UniverseBase.next_id + u = openmc.Universe(cells=[self]) + openmc.UniverseBase.used_ids.remove(u.id) + openmc.UniverseBase.next_id = next_id + return u.plot(*args, **kwargs) + def create_xml_subelement(self, xml_element, memo=None): """Add the cell's xml representation to an incoming xml element Parameters ---------- - xml_element : xml.etree.ElementTree.Element + xml_element : lxml.etree._Element XML element to be added to memo : set or None @@ -618,10 +671,11 @@ class Cell(IDManagerMixin): # thus far. def create_surface_elements(node, element, memo=None): if isinstance(node, Halfspace): - if memo and node.surface in memo: + if memo is None: + memo = set() + elif node.surface in memo: return - if memo is not None: - memo.add(node.surface) + memo.add(node.surface) xml_element.append(node.surface.to_xml_element()) elif isinstance(node, Complement): @@ -640,12 +694,21 @@ class Cell(IDManagerMixin): else: element.set("temperature", str(self.temperature)) + if self.density is not None: + if isinstance(self.density, Iterable): + element.set("density", ' '.join(str(t) for t in self.density)) + else: + element.set("density", str(self.density)) + if self.translation is not None: element.set("translation", ' '.join(map(str, self.translation))) if self.rotation is not None: element.set("rotation", ' '.join(map(str, self.rotation.ravel()))) + if self.volume is not None: + element.set("volume", str(self.volume)) + return element @classmethod @@ -654,13 +717,13 @@ class Cell(IDManagerMixin): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element `` element surfaces : dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances materials : dict - Dictionary mapping material IDs to :class:`openmc.Material` - instances (defined in :math:`openmc.Geometry.from_xml`) + Dictionary mapping material ID strings to :class:`openmc.Material` + instances (defined in :meth:`openmc.Geometry.from_xml`) get_universe : function Function returning universe (defined in :meth:`openmc.Geometry.from_xml`) @@ -676,9 +739,8 @@ class Cell(IDManagerMixin): c = cls(cell_id, name) # Assign material/distributed materials or fill - mat_text = get_text(elem, 'material') - if mat_text is not None: - mat_ids = mat_text.split() + mat_ids = get_elem_list(elem, 'material', str) + if mat_ids is not None: if len(mat_ids) > 1: c.fill = [materials[i] for i in mat_ids] else: @@ -693,16 +755,24 @@ class Cell(IDManagerMixin): c.region = Region.from_expression(region, surfaces) # Check for other attributes - t = get_text(elem, 'temperature') - if t is not None: - if ' ' in t: - c.temperature = [float(t_i) for t_i in t.split()] + temperature = get_elem_list(elem, 'temperature', float) + if temperature is not None: + if len(temperature) > 1: + c.temperature = temperature else: - c.temperature = float(t) - for key in ('temperature', 'rotation', 'translation'): - value = get_text(elem, key) - if value is not None: - setattr(c, key, [float(x) for x in value.split()]) + c.temperature = temperature[0] + density = get_elem_list(elem, 'density', float) + if density is not None: + c.density = density if len(density) > 1 else density[0] + v = get_text(elem, 'volume') + if v is not None: + c.volume = float(v) + for key in ('temperature', 'density', 'rotation', 'translation'): + values = get_elem_list(elem, key, float) + if values is not None: + if key == 'rotation' and len(values) == 9: + values = np.array(values).reshape(3, 3) + setattr(c, key, values) # Add this cell to appropriate universe univ_id = int(get_text(elem, 'universe', 0)) diff --git a/openmc/checkvalue.py b/openmc/checkvalue.py index d50b3fcaec..4fa205b14f 100644 --- a/openmc/checkvalue.py +++ b/openmc/checkvalue.py @@ -1,8 +1,12 @@ import copy +import os from collections.abc import Iterable import numpy as np +# Type for arguments that accept file paths +PathLike = str | os.PathLike + def check_type(name, value, expected_type, expected_iter_type=None, *, none_ok=False): """Ensure that an object is of an expected type. Optionally, if the object is @@ -166,6 +170,29 @@ def check_length(name, value, length_min, length_max=None): raise ValueError(msg) +def check_increasing(name: str, value, equality: bool = False): + """Ensure that a list's elements are strictly or loosely increasing. + + Parameters + ---------- + name : str + Description of value being checked + value : iterable + Object to check if increasing + equality : bool, optional + Whether equality is allowed. Defaults to False. + + """ + if equality: + if not np.all(np.diff(value) >= 0.0): + raise ValueError(f'Unable to set "{name}" to "{value}" since its ' + 'elements must be increasing.') + elif not equality: + if not np.all(np.diff(value) > 0.0): + raise ValueError(f'Unable to set "{name}" to "{value}" since its ' + 'elements must be strictly increasing.') + + def check_value(name, value, accepted_values): """Ensure that an object's value is contained in a set of acceptable values. diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 54c43f3334..eff6a151fb 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -135,7 +135,7 @@ class CMFDMesh: return outstr def _get_repr(self, list_var, label): - outstr = "\t{:<11} = ".format(label) + outstr = f"\t{label:<11} = " if list(list_var): outstr += ", ".join(str(i) for i in list_var) return outstr @@ -242,11 +242,11 @@ class CMFDMesh: check_length('CMFD mesh grid', grid, grid_length) for i in range(grid_length): - check_type('CMFD mesh {}-grid'.format(dims[i]), grid[i], Iterable, + check_type(f'CMFD mesh {dims[i]}-grid', grid[i], Iterable, Real) - check_greater_than('CMFD mesh {}-grid length'.format(dims[i]), + check_greater_than(f'CMFD mesh {dims[i]}-grid length', len(grid[i]), 1) - self._grid = np.array(grid) + self._grid = [np.array(g) for g in grid] self._display_mesh_warning('rectilinear', 'CMFD mesh grid') def _display_mesh_warning(self, mesh_type, variable_label): @@ -612,7 +612,7 @@ class CMFDRun: for key, value in display.items(): check_value('display key', key, ('balance', 'entropy', 'dominance', 'source')) - check_type("display['{}']".format(key), value, bool) + check_type(f"display['{key}']", value, bool) self._display[key] = value @downscatter.setter @@ -928,7 +928,7 @@ class CMFDRun: with h5py.File(filename, 'a') as f: if 'cmfd' not in f: if openmc.lib.settings.verbosity >= 5: - print(' Writing CMFD data to {}...'.format(filename)) + print(f' Writing CMFD data to {filename}...') sys.stdout.flush() cmfd_group = f.create_group("cmfd") cmfd_group.attrs['cmfd_on'] = self._cmfd_on @@ -982,14 +982,16 @@ class CMFDRun: temp_data = np.ones(len(loss_row)) temp_loss = sparse.csr_matrix((temp_data, (loss_row, loss_col)), shape=(n, n)) + temp_loss.sort_indices() # Pass coremap as 1-d array of 32-bit integers coremap = np.swapaxes(self._coremap, 0, 2).flatten().astype(np.int32) - args = temp_loss.indptr, len(temp_loss.indptr), \ - temp_loss.indices, len(temp_loss.indices), n, \ + return openmc.lib._dll.openmc_initialize_linsolver( + temp_loss.indptr.astype(np.int32), len(temp_loss.indptr), + temp_loss.indices.astype(np.int32), len(temp_loss.indices), n, self._spectral, coremap, self._use_all_threads - return openmc.lib._dll.openmc_initialize_linsolver(*args) + ) def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" @@ -1074,13 +1076,12 @@ class CMFDRun: # Get acceleration map, otherwise set all regions to be accelerated if self._mesh.map is not None: check_length('CMFD coremap', self._mesh.map, - np.product(self._indices[0:3])) + np.prod(self._indices[:3])) if openmc.lib.master(): self._coremap = np.array(self._mesh.map) else: if openmc.lib.master(): - self._coremap = np.ones((np.product(self._indices[0:3])), - dtype=int) + self._coremap = np.ones(np.prod(self._indices[:3]), dtype=int) # Check CMFD tallies accummulated before feedback turned on if self._feedback and self._solver_begin < self._tally_begin: @@ -1133,12 +1134,12 @@ class CMFDRun: with h5py.File(filename, 'r') as f: if 'cmfd' not in f: raise OpenMCError('Could not find CMFD parameters in ', - 'file {}'.format(filename)) + f'file {filename}') else: # Overwrite CMFD values from statepoint if (openmc.lib.master() and openmc.lib.settings.verbosity >= 5): - print(' Loading CMFD data from {}...'.format(filename)) + print(f' Loading CMFD data from {filename}...') sys.stdout.flush() cmfd_group = f['cmfd'] @@ -1309,9 +1310,6 @@ class CMFDRun: Whether or not to run an adjoint calculation """ - # Check for physical adjoint - physical_adjoint = adjoint and self._adjoint_type == 'physical' - # Start timer for build time_start_buildcmfd = time.time() @@ -1382,9 +1380,7 @@ class CMFDRun: """ # Write each element in vector to file - with open(base_filename+'.dat', 'w') as fh: - for val in vector: - fh.write('{:0.8f}\n'.format(val)) + np.savetxt(f'{base_filename}.dat', vector, fmt='%.8f') # Save as numpy format np.save(base_filename, vector) @@ -1413,8 +1409,7 @@ class CMFDRun: # Get all data entries for particular row in matrix data = matrix.data[matrix.indptr[row]:matrix.indptr[row+1]] for i in range(len(cols)): - fh.write('{:3d}, {:3d}, {:0.8f}\n'.format( - row, cols[i], data[i])) + fh.write(f'{row:3d}, {cols[i]:3d}, {data[i]:0.8f}\n') # Save matrix in scipy format sparse.save_npz(base_filename, matrix) @@ -1435,9 +1430,6 @@ class CMFDRun: # indices of the actual problem so that cmfd_flux can be multiplied by # nfissxs - # Calculate volume - vol = np.product(self._hxyz, axis=3) - # Reshape phi by number of groups phi = self._phi.reshape((n, ng)) @@ -1594,6 +1586,7 @@ class CMFDRun: loss_row = self._loss_row loss_col = self._loss_col loss = sparse.csr_matrix((data, (loss_row, loss_col)), shape=(n, n)) + loss.sort_indices() return loss def _build_prod_matrix(self, adjoint): @@ -1620,6 +1613,7 @@ class CMFDRun: prod_row = self._prod_row prod_col = self._prod_col prod = sparse.csr_matrix((data, (prod_row, prod_col)), shape=(n, n)) + prod.sort_indices() return prod def _execute_power_iter(self, loss, prod): @@ -2151,7 +2145,6 @@ class CMFDRun: is_accel = self._coremap != _CMFD_NOACCEL # Logical for determining whether a zero flux "albedo" b.c. should be # applied - is_zero_flux_alb = abs(self._albedo - _ZERO_FLUX) < _TINY_BIT x_inds, y_inds, z_inds = np.indices((nx, ny, nz)) # Define slice equivalent to is_accel[0,:,:] @@ -2317,8 +2310,8 @@ class CMFDRun: constant_values=_CMFD_NOACCEL)[:,:,1:] # Create empty row and column vectors to store for loss matrix - row = np.array([]) - col = np.array([]) + row = np.array([], dtype=int) + col = np.array([], dtype=int) # Store all indices used to populate production and loss matrix is_accel = self._coremap != _CMFD_NOACCEL @@ -2728,8 +2721,6 @@ class CMFDRun: # Define flux in each cell cell_flux = self._flux / dxdydz - # Extract indices of coremap that are accelerated - is_accel = self._coremap != _CMFD_NOACCEL # Define dhat at left surface for all mesh cells on left boundary boundary = self._first_x_accel diff --git a/openmc/config.py b/openmc/config.py new file mode 100644 index 0000000000..23d8e23a7b --- /dev/null +++ b/openmc/config.py @@ -0,0 +1,218 @@ +"""Module for handling global configuration in OpenMC. + +This module exports a single object, `config`, that can be used to control +various settings, primarily paths to data files. It acts like a dictionary but +with special behaviors. + +Examples +-------- +>>> import openmc +>>> openmc.config['cross_sections'] = '/path/to/my/cross_sections.xml' +>>> print(openmc.config) +{'resolve_paths': True, 'cross_sections': PosixPath('/path/to/my/cross_sections.xml')} + +""" +from collections.abc import MutableMapping +from contextlib import contextmanager +import os +from pathlib import Path +import warnings +from typing import Any, Dict, Iterator + +from openmc.data import DataLibrary +from openmc.data.decay import _DECAY_ENERGY, _DECAY_PHOTON_ENERGY + +__all__ = ["config"] + + +class _Config(MutableMapping): + """A configuration dictionary for OpenMC with special handling for path-like values. + + This class enforces valid configuration keys and synchronizes path-related + settings with their corresponding environment variables. + + Attributes + ---------- + cross_sections : pathlib.Path + Path to a cross_sections.xml file. Also sets/unsets the + OPENMC_CROSS_SECTIONS environment variable. + mg_cross_sections : pathlib.Path + Path to a multi-group cross_sections.h5 file. Also sets/unsets + the OPENMC_MG_CROSS_SECTIONS environment variable. + chain_file : pathlib.Path + Path to a depletion chain XML file. Also sets/unsets the + OPENMC_CHAIN_FILE environment variable. Setting or deleting this + clears internal decay data caches. + resolve_paths : bool + If True (default), all paths assigned are resolved to absolute + paths. If False, paths are stored as they are provided. + + """ + _PATH_KEYS: Dict[str, str] = { + 'cross_sections': 'OPENMC_CROSS_SECTIONS', + 'mg_cross_sections': 'OPENMC_MG_CROSS_SECTIONS', + 'chain_file': 'OPENMC_CHAIN_FILE' + } + + def __init__(self, data: dict = ()): + self._mapping: Dict[str, Any] = {'resolve_paths': True} + self.update(data) + + def __getitem__(self, key: str) -> Any: + return self._mapping[key] + + def __delitem__(self, key: str): + """Delete a configuration key. + + This also deletes the corresponding environment variable if the key is a + path-like key, and clears decay data caches if 'chain_file' is deleted. + 'resolve_paths' cannot be deleted. + + """ + if key == 'resolve_paths': + raise KeyError("'resolve_paths' cannot be deleted.") + del self._mapping[key] + if key in self._PATH_KEYS: + env_var = self._PATH_KEYS[key] + if env_var in os.environ: + del os.environ[env_var] + if key == 'chain_file': + _DECAY_PHOTON_ENERGY.clear() + _DECAY_ENERGY.clear() + + def __setitem__(self, key: str, value: Any): + """Set a configuration key and its corresponding value. + + For path-like keys, this method performs several actions: + 1. Resolves the path to an absolute path if `resolve_paths` is True. + 2. Stores the `pathlib.Path` object. + 3. Sets the corresponding environment variable (e.g., OPENMC_CROSS_SECTIONS). + 4. For 'chain_file', clears internal decay data caches. + 5. Issues a `UserWarning` if the final path does not exist. + + """ + if key in self._PATH_KEYS: + p = Path(value) + # Use .get() for robustness, defaulting to True + if self._mapping.get('resolve_paths', True): + stored_path = p.resolve(strict=False) + else: + stored_path = p + + self._mapping[key] = stored_path + os.environ[self._PATH_KEYS[key]] = str(stored_path) + + if key == 'chain_file': + _DECAY_PHOTON_ENERGY.clear() + _DECAY_ENERGY.clear() + + if not stored_path.exists(): + warnings.warn(f"Path '{stored_path}' does not exist.", UserWarning) + + elif key == 'resolve_paths': + if not isinstance(value, bool): + raise TypeError("'resolve_paths' must be a boolean.") + self._mapping[key] = value + else: + valid_keys = list(self._PATH_KEYS.keys()) + ['resolve_paths'] + raise KeyError( + f"Unrecognized config key: {key}. Acceptable keys are: " + f"{', '.join(repr(k) for k in valid_keys)}." + ) + + def __iter__(self) -> Iterator[str]: + return iter(self._mapping) + + def __len__(self) -> int: + return len(self._mapping) + + def __repr__(self) -> str: + return repr(self._mapping) + + def clear(self): + """Clear all configuration keys except for 'resolve_paths'. + + This ensures that the path resolution behavior is not accidentally reset + when clearing the configuration. + + """ + # Create a copy of keys to iterate over for safe deletion + keys_to_delete = [k for k in self._mapping if k != 'resolve_paths'] + for key in keys_to_delete: + del self[key] + + @contextmanager + def patch(self, key: str, value: Any): + """Context manager to temporarily change a configuration value. + + After the `with` block, the configuration is restored to its original + state. + + Parameters + ---------- + key : str + The key of the configuration value to change. + value + The new temporary value. + + Examples + -------- + >>> openmc.config['cross_sections'] = 'endf71.xml' + >>> with openmc.config.patch('cross_sections', 'fendl32.xml'): + ... # Code in this block sees the new value + ... print(f"Inside with block: {openmc.config['cross_sections']}") + >>> # Outside the block, the value is restored + >>> print(f"Outside with block: {openmc.config['cross_sections']}") + Inside with block: fendl32.xml + Outside with block: endf71.xml + + """ + previous_value = self.get(key) + self[key] = value + try: + yield + finally: + if previous_value is None: + del self[key] + else: + self[key] = previous_value + + +def _default_config(**kwargs) -> _Config: + """Create a configuration initialized from environment variables. + + This function checks for OPENMC_CROSS_SECTIONS, OPENMC_MG_CROSS_SECTIONS, + and OPENMC_CHAIN_FILE environment variables. It also has logic to find + a chain file within a `cross_sections.xml` file if one is not + explicitly set. + + Returns + ------- + _Config + A new configuration object. + + """ + config = _Config(kwargs) + for key,var in _Config._PATH_KEYS.items(): + if var in os.environ: + config[key] = os.environ[var] + + chain_file = config.get("chain_file") + xs_path = config.get("cross_sections") + if chain_file is None and xs_path is not None and xs_path.exists(): + try: + data = DataLibrary.from_xml(xs_path) + except Exception: + # Let this pass silently if cross_sections.xml can't be parsed + # or if a dependency like lxml is not available. + pass + else: + for lib in reversed(data.libraries): + if lib['type'] == 'depletion_chain': + config['chain_file'] = xs_path.parent / lib['path'] + break + return config + + +# Global configuration dictionary for OpenMC settings. +config = _default_config() diff --git a/openmc/dagmc.py b/openmc/dagmc.py new file mode 100644 index 0000000000..d1265be268 --- /dev/null +++ b/openmc/dagmc.py @@ -0,0 +1,632 @@ +from collections.abc import Iterable, Mapping +from numbers import Integral + +import h5py +import lxml.etree as ET +import numpy as np +import warnings + +import openmc +import openmc.checkvalue as cv +from ._xml import get_elem_list, get_text +from .checkvalue import check_type, check_value +from .surface import _BOUNDARY_TYPES +from .bounding_box import BoundingBox +from .utility_funcs import input_path +from .plots import add_plot_params + + +class DAGMCUniverse(openmc.UniverseBase): + """A reference to a DAGMC file to be used in the model. + + .. versionadded:: 0.13.0 + + Parameters + ---------- + filename : str + Path to the DAGMC file used to represent this universe. + universe_id : int, optional + Unique identifier of the universe. If not specified, an identifier will + automatically be assigned. + name : str, optional + Name of the universe. If not specified, the name is the empty string. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between OpenMC and UWUW materials (False) + material_overrides : dict, optional + A dictionary of material overrides. The keys are material name strings + and the values are Iterables of openmc.Material objects. If a material + name is found in the DAGMC file, the material will be replaced with the + openmc.Material object in the value. + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + filename : str + Path to the DAGMC file used to represent this universe. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between OpenMC and UWUW materials (False) + bounding_box : openmc.BoundingBox + Lower-left and upper-right coordinates of an axis-aligned bounding box + of the universe. + + .. versionadded:: 0.13.1 + material_names : list of str + Return a sorted list of materials names that are contained within the + DAGMC h5m file. This is useful when naming openmc.Material() objects as + each material name present in the DAGMC h5m file must have a matching + openmc.Material() with the same name. + + .. versionadded:: 0.13.2 + n_cells : int + The number of cells in the DAGMC model. This is the number of cells at + runtime and accounts for the implicit complement whether or not is it + present in the DAGMC file. + + .. versionadded:: 0.13.2 + n_surfaces : int + The number of surfaces in the model. + + .. versionadded:: 0.13.2 + material_overrides : dict + A dictionary of material overrides. Keys are cell IDs; values are + iterables of :class:`openmc.Material` objects. The material assignment + of each DAGMC cell ID key will be replaced with the + :class:`~openmc.Material` object in the value. If the value contains + multiple :class:`~openmc.Material` objects, each Material in the list + will be assigned to the corresponding instance of the cell. + + .. versionadded:: 0.15.1 + """ + + def __init__(self, + filename: cv.PathLike, + universe_id=None, + name='', + auto_geom_ids=False, + auto_mat_ids=False, + material_overrides=None): + super().__init__(universe_id, name) + # Initialize class attributes + self.filename = filename + self.auto_geom_ids = auto_geom_ids + self.auto_mat_ids = auto_mat_ids + self._material_overrides = {} + if material_overrides is not None: + self.material_overrides = material_overrides + + def __repr__(self): + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') + string += '{: <16}=\t{}\n'.format('\tFile', self.filename) + return string + + @property + def bounding_box(self): + with h5py.File(self.filename) as dagmc_file: + coords = dagmc_file['tstt']['nodes']['coordinates'][()] + lower_left_corner = coords.min(axis=0) + upper_right_corner = coords.max(axis=0) + return openmc.BoundingBox(lower_left_corner, upper_right_corner) + + @property + def filename(self): + return self._filename + + @filename.setter + def filename(self, val: cv.PathLike): + cv.check_type('DAGMC filename', val, cv.PathLike) + self._filename = input_path(val) + + @property + def material_overrides(self): + return self._material_overrides + + @material_overrides.setter + def material_overrides(self, val): + cv.check_type('material overrides', val, Mapping) + for key, value in val.items(): + self.add_material_override(key, value) + + def replace_material_assignment(self, material_name: str, material: openmc.Material): + """Replace a material assignment within the DAGMC universe. + + Replace the material assignment of all cells filled with a material in + the DAGMC universe. The universe must be synchronized in an initialized + Model (see :meth:`~openmc.DAGMCUniverse.sync_dagmc_cells`) before + calling this method. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + material_name : str + Material name to replace + material : openmc.Material + Material to replace the material_name with + + """ + if material_name not in self.material_names: + raise ValueError( + f"No material with name '{material_name}' found in the DAGMC universe") + + if not self.cells: + raise RuntimeError("This DAGMC universe has not been synchronized " + "on an initialized Model.") + + for cell in self.cells.values(): + if cell.fill is None: + continue + if isinstance(cell.fill, openmc.Iterable): + cell.fill = list(map(lambda x: material if x.name == material_name else x, cell.fill)) + else: + cell.fill = material if cell.fill.name == material_name else cell.fill + + def add_material_override(self, key, overrides=None): + """Add a material override to the universe. + + .. versionadded:: 0.15 + + Parameters + ---------- + key : openmc.DAGMCCell or int + Cell object or ID of the Cell to override + value : openmc.Material or Iterable of openmc.Material + Material(s) to be applied to the Cell passed as the key + + """ + # Ensure that they key is a valid type + if not isinstance(key, (int, openmc.DAGMCCell)): + raise ValueError("Unrecognized key type. " + "Must be an integer or openmc.DAGMCCell object") + + # Ensure that overrides is an iterable of openmc.Material + overrides = overrides if isinstance(overrides, openmc.Iterable) else [overrides] + cv.check_iterable_type('material objects', overrides, (openmc.Material, type(None))) + + # if a DAGMCCell is passed, redcue the key to the ID of the cell + if isinstance(key, openmc.DAGMCCell): + key = key.id + + if key not in self.cells: + raise ValueError(f"Cell ID '{key}' not found in DAGMC universe") + + self._material_overrides[key] = overrides + + @property + def auto_geom_ids(self): + return self._auto_geom_ids + + @auto_geom_ids.setter + def auto_geom_ids(self, val): + cv.check_type('DAGMC automatic geometry ids', val, bool) + self._auto_geom_ids = val + + @property + def auto_mat_ids(self): + return self._auto_mat_ids + + @auto_mat_ids.setter + def auto_mat_ids(self, val): + cv.check_type('DAGMC automatic material ids', val, bool) + self._auto_mat_ids = val + + @property + def material_names(self): + dagmc_file_contents = h5py.File(self.filename) + material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( + 'values') + material_tags_ascii = [] + for tag in material_tags_hex: + candidate_tag = tag.tobytes().decode().replace('\x00', '') + # tags might be for temperature or reflective surfaces + if candidate_tag.startswith('mat:'): + # if name ends with _comp remove it, it is not parsed + if candidate_tag.endswith('_comp'): + candidate_tag = candidate_tag[:-5] + # removes first 4 characters as openmc.Material name should be + # set without the 'mat:' part of the tag + material_tags_ascii.append(candidate_tag[4:]) + + return sorted(set(material_tags_ascii)) + + def _n_geom_elements(self, geom_type): + """ + Helper function for retrieving the number geometric entities in a DAGMC + file + + Parameters + ---------- + geom_type : str + The type of geometric entity to count. One of {'Volume', 'Surface'}. Returns + the runtime number of voumes in the DAGMC model (includes implicit complement). + + Returns + ------- + int + Number of geometry elements of the specified type + """ + cv.check_value('geometry type', geom_type, ('volume', 'surface')) + + def decode_str_tag(tag_val): + return tag_val.tobytes().decode().replace('\x00', '') + + with h5py.File(self.filename) as dagmc_file: + category_data = dagmc_file['tstt/tags/CATEGORY/values'] + category_strs = map(decode_str_tag, category_data) + n = sum([v == geom_type.capitalize() for v in category_strs]) + + # check for presence of an implicit complement in the file and + # increment the number of cells if it doesn't exist + if geom_type == 'volume': + name_data = dagmc_file['tstt/tags/NAME/values'] + name_strs = map(decode_str_tag, name_data) + if not sum(['impl_complement' in n for n in name_strs]): + n += 1 + return n + + @property + def n_cells(self): + return self._n_geom_elements('volume') + + @property + def n_surfaces(self): + return self._n_geom_elements('surface') + + def create_xml_subelement(self, xml_element, memo=None): + if memo is None: + memo = set() + + if self in memo: + return + + memo.add(self) + + # Ensure that the material overrides are up-to-date + for cell in self.cells.values(): + if cell.fill is None: + continue + self.add_material_override(cell, cell.fill) + + # Set xml element values + dagmc_element = ET.Element('dagmc_universe') + dagmc_element.set('id', str(self.id)) + + if self.auto_geom_ids: + dagmc_element.set('auto_geom_ids', 'true') + if self.auto_mat_ids: + dagmc_element.set('auto_mat_ids', 'true') + dagmc_element.set('filename', str(self.filename)) + if self._material_overrides: + mat_element = ET.Element('material_overrides') + for key in self._material_overrides: + cell_overrides = ET.Element('cell_override') + cell_overrides.set("id", str(key)) + material_element = ET.Element('material_ids') + material_element.text = ' '.join( + str(t.id) for t in self._material_overrides[key]) + cell_overrides.append(material_element) + mat_element.append(cell_overrides) + dagmc_element.append(mat_element) + xml_element.append(dagmc_element) + + def bounding_region( + self, + bounded_type: str = 'box', + boundary_type: str = 'vacuum', + starting_id: int = 10000, + padding_distance: float = 0. + ): + """Creates a either a spherical or box shaped bounding region around + the DAGMC geometry. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + bounded_type : str + The type of bounding surface(s) to use when constructing the region. + Options include a single spherical surface (sphere) or a rectangle + made from six planes (box). + boundary_type : str + Boundary condition that defines the behavior for particles hitting + the surface. Defaults to vacuum boundary condition. Passed into the + surface construction. + starting_id : int + Starting ID of the surface(s) used in the region. For bounded_type + 'box', the next 5 IDs will also be used. Defaults to 10000 to reduce + the chance of an overlap of surface IDs with the DAGMC geometry. + padding_distance : float + Distance between the bounding region surfaces and the minimal + bounding box. Allows for the region to be larger than the DAGMC + geometry. + + Returns + ------- + openmc.Region + Region instance + """ + + check_type('boundary type', boundary_type, str) + check_value('boundary type', boundary_type, _BOUNDARY_TYPES) + check_type('starting surface id', starting_id, Integral) + check_type('bounded type', bounded_type, str) + check_value('bounded type', bounded_type, ('box', 'sphere')) + + bbox = self.bounding_box.expand(padding_distance, True) + + if bounded_type == 'sphere': + radius = np.linalg.norm(bbox.upper_right - bbox.center) + bounding_surface = openmc.Sphere( + surface_id=starting_id, + x0=bbox.center[0], + y0=bbox.center[1], + z0=bbox.center[2], + boundary_type=boundary_type, + r=radius, + ) + + return -bounding_surface + + if bounded_type == 'box': + # defines plane surfaces for all six faces of the bounding box + lower_x = openmc.XPlane(bbox[0][0], surface_id=starting_id) + upper_x = openmc.XPlane(bbox[1][0], surface_id=starting_id+1) + lower_y = openmc.YPlane(bbox[0][1], surface_id=starting_id+2) + upper_y = openmc.YPlane(bbox[1][1], surface_id=starting_id+3) + lower_z = openmc.ZPlane(bbox[0][2], surface_id=starting_id+4) + upper_z = openmc.ZPlane(bbox[1][2], surface_id=starting_id+5) + + region = +lower_x & -upper_x & +lower_y & -upper_y & +lower_z & -upper_z + + for surface in region.get_surfaces().values(): + surface.boundary_type = boundary_type + + return region + + def bounded_universe(self, bounding_cell_id=10000, **kwargs): + """Returns an openmc.Universe filled with this DAGMCUniverse and bounded + with a cell. Defaults to a box cell with a vacuum surface however this + can be changed using the kwargs which are passed directly to + DAGMCUniverse.bounding_region(). + + Parameters + ---------- + bounding_cell_id : int + The cell ID number to use for the bounding cell, defaults to 10000 to reduce + the chance of overlapping ID numbers with the DAGMC geometry. + + Returns + ------- + openmc.Universe + Universe instance + """ + bounding_cell = openmc.Cell( + fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) + return openmc.Universe(cells=[bounding_cell]) + + @classmethod + def from_hdf5(cls, group): + """Create DAGMC universe from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(group.name.split('/')[-1].lstrip('universe ')) + fname = group['filename'][()].decode() + name = group['name'][()].decode() if 'name' in group else None + + out = cls(fname, universe_id=id, name=name) + + out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) + out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) + + return out + + @classmethod + def from_xml_element(cls, elem, mats = None): + """Generate DAGMC universe from XML element + + Parameters + ---------- + elem : lxml.etree._Element + `` element + mats : dict + Dictionary mapping material ID strings to :class:`openmc.Material` + instances (defined in :meth:`openmc.Geometry.from_xml`) + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(get_text(elem, 'id')) + fname = get_text(elem, 'filename') + + out = cls(fname, universe_id=id) + + name = get_text(elem, 'name') + if name is not None: + out.name = name + + out.auto_geom_ids = bool(get_text(elem, "auto_geom_ids")) + out.auto_mat_ids = bool(get_text(elem, "auto_mat_ids")) + + el_mat_override = elem.find('material_overrides') + if el_mat_override is not None: + if mats is None: + raise ValueError("Material overrides found in DAGMC universe " + "but no materials were provided to populate " + "the mapping.") + out._material_overrides = {} + for elem in el_mat_override.findall('cell_override'): + cell_id = int(get_text(elem, 'id')) + mat_ids = get_elem_list(elem, "material_ids", str) or [] + mat_objs = [mats[mat_id] for mat_id in mat_ids] + out._material_overrides[cell_id] = mat_objs + + return out + + def _partial_deepcopy(self): + """Clone all of the openmc.DAGMCUniverse object's attributes except for + its cells, as they are copied within the clone function. This should + only to be used within the openmc.UniverseBase.clone() context. + """ + clone = openmc.DAGMCUniverse(name=self.name, filename=self.filename) + clone.volume = self.volume + clone.auto_geom_ids = self.auto_geom_ids + clone.auto_mat_ids = self.auto_mat_ids + return clone + + def add_cell(self, cell): + """Add a cell to the universe. + + Parameters + ---------- + cell : openmc.DAGMCCell + Cell to add + + """ + if not isinstance(cell, openmc.DAGMCCell): + msg = f'Unable to add a DAGMCCell to DAGMCUniverse ' \ + f'ID="{self._id}" since "{cell}" is not a DAGMCCell' + raise TypeError(msg) + + cell_id = cell.id + + if cell_id not in self._cells: + self._cells[cell_id] = cell + + def remove_cell(self, cell): + """Remove a cell from the universe. + + Parameters + ---------- + cell : openmc.Cell + Cell to remove + + """ + + if not isinstance(cell, openmc.DAGMCCell): + msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ + f'since "{cell}" is not a Cell' + raise TypeError(msg) + + # If the Cell is in the Universe's list of Cells, delete it + self._cells.pop(cell.id, None) + + def sync_dagmc_cells(self, mats: Iterable[openmc.Material]): + """Synchronize DAGMC cell information between Python and C API + + .. versionadded:: 0.15.1 + + Parameters + ---------- + mats : iterable of openmc.Material + Iterable of materials to assign to the DAGMC cells + + """ + import openmc.lib + if not openmc.lib.is_initialized: + raise RuntimeError("This universe must be part of an openmc.Model " + "initialized via Model.init_lib before calling " + "this method.") + + dagmc_cell_ids = openmc.lib.dagmc.dagmc_universe_cell_ids(self.id) + if len(dagmc_cell_ids) != self.n_cells: + raise ValueError( + f"Number of cells in DAGMC universe {self.id} does not match " + f"the number of cells in the Python universe." + ) + + mats_per_id = {mat.id: mat for mat in mats} + for dag_cell_id in dagmc_cell_ids: + dag_cell = openmc.lib.cells[dag_cell_id] + if isinstance(dag_cell.fill, Iterable): + fill = [mats_per_id[mat.id] for mat in dag_cell.fill if mat] + else: + fill = mats_per_id[dag_cell.fill.id] if dag_cell.fill else None + self.add_cell(openmc.DAGMCCell(cell_id=dag_cell_id, fill=fill)) + + @add_plot_params + def plot(self, *args, **kwargs): + """Display a slice plot of the DAGMCUniverse. + """ + return openmc.Geometry(self).plot(*args, **kwargs) + + +class DAGMCCell(openmc.Cell): + """A cell class for DAGMC-based geometries. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + cell_id : int or None, optional + Unique identifier for the cell. If None, an identifier will be + automatically assigned. + name : str, optional + Name of the cell. + fill : openmc.Material or None, optional + Material filling the cell. If None, the cell is filled with vacuum. + + Attributes + ---------- + DAG_parent_universe : int + The parent universe of the cell. + + """ + def __init__(self, cell_id=None, name='', fill=None): + super().__init__(cell_id, name, fill, None) + + @property + def DAG_parent_universe(self): + """Get the parent universe of the cell.""" + return self._parent_universe + + @DAG_parent_universe.setter + def DAG_parent_universe(self, universe): + """Set the parent universe of the cell.""" + self._parent_universe = universe.id + + def bounding_box(self): + return BoundingBox.infinite() + + def get_all_cells(self, memo=None): + return {} + + def get_all_universes(self, memo=None): + return {} + + def clone(self, clone_materials=True, clone_regions=True, memo=None): + warnings.warn("clone is not available for cells in a DAGMC universe") + return self + + def plot(self, *args, **kwargs): + raise TypeError("plot is not available for DAGMC cells.") + + def create_xml_subelement(self, xml_element, memo=None): + raise TypeError("create_xml_subelement is not available for DAGMC cells.") + + @classmethod + def from_xml_element(cls, elem, surfaces, materials, get_universe): + raise TypeError("from_xml_element is not available for DAGMC cells.") diff --git a/openmc/data/_endf.pyx b/openmc/data/_endf.pyx deleted file mode 100644 index 991ee015b2..0000000000 --- a/openmc/data/_endf.pyx +++ /dev/null @@ -1,8 +0,0 @@ -# cython: c_string_type=str, c_string_encoding=ascii - -cdef extern from "endf.c": - double cfloat_endf(const char* buffer, int n) - -def float_endf(s): - cdef const char* c_string = s - return cfloat_endf(c_string, len(s)) diff --git a/openmc/data/ace.py b/openmc/data/ace.py index 97be6f6275..6ccb76c922 100644 --- a/openmc/data/ace.py +++ b/openmc/data/ace.py @@ -9,13 +9,12 @@ unresolved resonance region, and tabulated data in the fast region. After the ENDF data has been reconstructed and Doppler-broadened, the ACER module generates ACE-format cross sections. -.. _MCNP: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/ -.. _NJOY: http://t2.lanl.gov/codes.shtml -.. _ENDF: http://www.nndc.bnl.gov/endf +.. _MCNP: https://mcnp.lanl.gov/ +.. _NJOY: https://www.njoy21.io/ +.. _ENDF: https://www.nndc.bnl.gov/endf-library/ """ -from collections import OrderedDict import enum from pathlib import Path import struct @@ -24,7 +23,7 @@ import numpy as np import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -from .data import ATOMIC_SYMBOL, gnd_name, EV_PER_MEV, K_BOLTZMANN +from .data import ATOMIC_SYMBOL, gnds_name, EV_PER_MEV, K_BOLTZMANN from .endf import ENDF_FLOAT_RE @@ -88,7 +87,7 @@ def get_metadata(zaid, metastable_scheme='nndc'): # Determine name element = ATOMIC_SYMBOL[Z] - name = gnd_name(Z, mass_number, metastable) + name = gnds_name(Z, mass_number, metastable) return (name, element, Z, mass_number, metastable) @@ -144,7 +143,7 @@ def ascii_to_binary(ascii_file, binary_file): # that XSS will start at the second record nxs = [int(x) for x in ' '.join(lines[idx + 6:idx + 8]).split()] jxs = [int(x) for x in ' '.join(lines[idx + 8:idx + 12]).split()] - binary_file.write(struct.pack(str('=16i32i{}x'.format(record_length - 500)), + binary_file.write(struct.pack(str(f'=16i32i{record_length - 500}x'), *(nxs + jxs))) # Read/write XSS array. Null bytes are added to form a complete record @@ -153,8 +152,7 @@ def ascii_to_binary(ascii_file, binary_file): start = idx + _ACE_HEADER_SIZE xss = np.fromstring(' '.join(lines[start:start + n_lines]), sep=' ') extra_bytes = record_length - ((len(xss)*8 - 1) % record_length + 1) - binary_file.write(struct.pack(str('={}d{}x'.format( - nxs[0], extra_bytes)), *xss)) + binary_file.write(struct.pack(str(f'={nxs[0]}d{extra_bytes}x'), *xss)) # Advance to next table in file idx += _ACE_HEADER_SIZE + n_lines @@ -185,8 +183,7 @@ def get_table(filename, name=None): if lib.tables: return lib.tables[0] else: - raise ValueError('Could not find ACE table with name: {}' - .format(name)) + raise ValueError(f'Could not find ACE table with name: {name}') # The beginning of an ASCII ACE file consists of 12 lines that include the name, @@ -296,14 +293,14 @@ class Library(EqualityMixin): if verbose: kelvin = round(temperature * EV_PER_MEV / K_BOLTZMANN) - print("Loading nuclide {} at {} K".format(name, kelvin)) + print(f"Loading nuclide {name} at {kelvin} K") # Read JXS jxs = list(struct.unpack(str('=32i'), ace_file.read(128))) # Read XSS ace_file.seek(start_position + recl_length) - xss = list(struct.unpack(str('={}d'.format(length)), + xss = list(struct.unpack(str(f'={length}d'), ace_file.read(length*8))) # Insert zeros at beginning of NXS, JXS, and XSS arrays so that the @@ -394,7 +391,7 @@ class Library(EqualityMixin): if verbose: kelvin = round(temperature * EV_PER_MEV / K_BOLTZMANN) - print("Loading nuclide {} at {} K".format(name, kelvin)) + print(f"Loading nuclide {name} at {kelvin} K") # Insert zeros at beginning of NXS, JXS, and XSS arrays so that the # indexing will be the same as Fortran. This makes it easier to @@ -456,8 +453,7 @@ class TableType(enum.Enum): for member in cls: if suffix.endswith(member.value): return member - raise ValueError("Suffix '{}' has no corresponding ACE table type." - .format(suffix)) + raise ValueError(f"Suffix '{suffix}' has no corresponding ACE table type.") class Table(EqualityMixin): @@ -508,7 +504,7 @@ class Table(EqualityMixin): return TableType.from_suffix(xs[-1]) def __repr__(self): - return "".format(self.name) + return f"" def get_libraries_from_xsdir(path): @@ -544,7 +540,7 @@ def get_libraries_from_xsdir(path): # Create list of ACE libraries -- we use an ordered dictionary while # building to get O(1) membership checks while retaining insertion order - libraries = OrderedDict() + libraries = {} for line in lines: words = line.split() if len(words) < 3: @@ -573,11 +569,11 @@ def get_libraries_from_xsdata(path): List of paths to ACE libraries """ xsdata = Path(path) - with open(xsdata, 'r') as xsdata: + with open(xsdata, 'r') as xsdata_file: # As in get_libraries_from_xsdir, we use a dict for O(1) membership # check while retaining insertion order - libraries = OrderedDict() - for line in xsdata: + libraries = {} + for line in xsdata_file: words = line.split() if len(words) >= 9: lib = (xsdata.parent / words[8]).resolve() diff --git a/openmc/data/angle_distribution.py b/openmc/data/angle_distribution.py index 4d058bcb7c..e59ffa0c73 100644 --- a/openmc/data/angle_distribution.py +++ b/openmc/data/angle_distribution.py @@ -42,16 +42,16 @@ class AngleDistribution(EqualityMixin): def energy(self): return self._energy - @property - def mu(self): - return self._mu - @energy.setter def energy(self, energy): cv.check_type('angle distribution incoming energy', energy, Iterable, Real) self._energy = energy + @property + def mu(self): + return self._mu + @mu.setter def mu(self, mu): cv.check_type('angle distribution scattering cosines', mu, @@ -235,7 +235,6 @@ class AngleDistribution(EqualityMixin): items = get_cont_record(file_obj) li = items[2] nk = items[4] - center_of_mass = (items[3] == 2) # Check for obsolete energy transformation matrix. If present, just skip # it and keep reading @@ -259,7 +258,6 @@ class AngleDistribution(EqualityMixin): mu = [] for i in range(n_energy): items, al = get_list_record(file_obj) - temperature = items[0] energy[i] = items[1] coefficients = np.asarray([1.0] + al) mu.append(Legendre(coefficients)) @@ -273,7 +271,6 @@ class AngleDistribution(EqualityMixin): mu = [] for i in range(n_energy): params, f = get_tab1_record(file_obj) - temperature = params[0] energy[i] = params[1] if f.n_regions > 1: raise NotImplementedError('Angular distribution with multiple ' @@ -289,7 +286,6 @@ class AngleDistribution(EqualityMixin): mu = [] for i in range(n_energy_legendre): items, al = get_list_record(file_obj) - temperature = items[0] energy_legendre[i] = items[1] coefficients = np.asarray([1.0] + al) mu.append(Legendre(coefficients)) @@ -300,7 +296,6 @@ class AngleDistribution(EqualityMixin): energy_tabulated = np.zeros(n_energy_tabulated) for i in range(n_energy_tabulated): params, f = get_tab1_record(file_obj) - temperature = params[0] energy_tabulated[i] = params[1] if f.n_regions > 1: raise NotImplementedError('Angular distribution with multiple ' diff --git a/openmc/data/angle_energy.py b/openmc/data/angle_energy.py index 6009dc748a..71ca47587d 100644 --- a/openmc/data/angle_energy.py +++ b/openmc/data/angle_energy.py @@ -44,6 +44,8 @@ class AngleEnergy(EqualityMixin, ABC): return openmc.data.IncoherentInelasticAEDiscrete.from_hdf5(group) elif dist_type == 'incoherent_inelastic': return openmc.data.IncoherentInelasticAE.from_hdf5(group) + elif dist_type == 'mixed_elastic': + return openmc.data.MixedElasticAE.from_hdf5(group) @staticmethod def from_ace(ace, location_dist, location_start, rx=None): @@ -110,7 +112,6 @@ class AngleEnergy(EqualityMixin, ABC): distribution = openmc.data.NBodyPhaseSpace.from_ace( ace, idx, rx.q_value) else: - raise ValueError("Unsupported ACE secondary energy " - "distribution law {}".format(law)) + raise ValueError(f"Unsupported ACE secondary energy distribution law {law}") return distribution diff --git a/openmc/data/correlated.py b/openmc/data/correlated.py index 1aa4c20c8c..2ff095a5c4 100644 --- a/openmc/data/correlated.py +++ b/openmc/data/correlated.py @@ -58,46 +58,46 @@ class CorrelatedAngleEnergy(AngleEnergy): def breakpoints(self): return self._breakpoints - @property - def interpolation(self): - return self._interpolation - - @property - def energy(self): - return self._energy - - @property - def energy_out(self): - return self._energy_out - - @property - def mu(self): - return self._mu - @breakpoints.setter def breakpoints(self, breakpoints): cv.check_type('correlated angle-energy breakpoints', breakpoints, Iterable, Integral) self._breakpoints = breakpoints + @property + def interpolation(self): + return self._interpolation + @interpolation.setter def interpolation(self, interpolation): cv.check_type('correlated angle-energy interpolation', interpolation, Iterable, Integral) self._interpolation = interpolation + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('correlated angle-energy incoming energy', energy, Iterable, Real) self._energy = energy + @property + def energy_out(self): + return self._energy_out + @energy_out.setter def energy_out(self, energy_out): cv.check_type('correlated angle-energy outgoing energy', energy_out, Iterable, Univariate) self._energy_out = energy_out + @property + def mu(self): + return self._mu + @mu.setter def mu(self, mu): cv.check_iterable_type('correlated angle-energy outgoing cosine', @@ -113,7 +113,7 @@ class CorrelatedAngleEnergy(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_(self._name) + group.attrs['type'] = np.bytes_(self._name) dset = group.create_dataset('energy', data=self.energy) dset.attrs['interpolation'] = np.vstack((self.breakpoints, diff --git a/openmc/data/data.py b/openmc/data/data.py index a1d0ee1dce..5ecadd37be 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -1,10 +1,11 @@ import itertools -from math import sqrt +import json import os import re +from pathlib import Path +from math import sqrt, log from warnings import warn - # Isotopic abundances from Meija J, Coplen T B, et al, "Isotopic compositions # of the elements 2013 (IUPAC Technical Report)", Pure. Appl. Chem. 88 (3), # pp. 293-306 (2013). The "representative isotopic abundance" values from @@ -93,7 +94,7 @@ NATURAL_ABUNDANCE = { 'Yb174': 0.32025, 'Yb176': 0.12995, 'Lu175': 0.97401, 'Lu176': 0.02599, 'Hf174': 0.0016, 'Hf176': 0.0526, 'Hf177': 0.186, 'Hf178': 0.2728, 'Hf179': 0.1362, - 'Hf180': 0.3508, 'Ta180': 0.0001201, 'Ta181': 0.9998799, + 'Hf180': 0.3508, 'Ta180_m1': 0.0001201, 'Ta181': 0.9998799, 'W180': 0.0012, 'W182': 0.265, 'W183': 0.1431, 'W184': 0.3064, 'W186': 0.2843, 'Re185': 0.374, 'Re187': 0.626, 'Os184': 0.0002, 'Os186': 0.0159, @@ -177,6 +178,93 @@ ATOMIC_SYMBOL = {0: 'n', 1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 118: 'Og'} ATOMIC_NUMBER = {value: key for key, value in ATOMIC_SYMBOL.items()} +DADZ = { + '(n,2nd)': (-3, -1), + '(n,2n)': (-1, 0), + '(n,3n)': (-2, 0), + '(n,na)': (-4, -2), + '(n,n3a)': (-12, -6), + '(n,2na)': (-5, -2), + '(n,3na)': (-6, -2), + '(n,np)': (-1, -1), + '(n,n2a)': (-8, -4), + '(n,2n2a)': (-9, -4), + '(n,nd)': (-2, -1), + '(n,nt)': (-3, -1), + '(n,n3He)': (-3, -2), + '(n,nd2a)': (-10, -5), + '(n,nt2a)': (-11, -5), + '(n,4n)': (-3, 0), + '(n,2np)': (-2, -1), + '(n,3np)': (-3, -1), + '(n,n2p)': (-2, -2), + '(n,npa)': (-5, -3), + '(n,gamma)': (1, 0), + '(n,p)': (0, -1), + '(n,d)': (-1, -1), + '(n,t)': (-2, -1), + '(n,3He)': (-2, -2), + '(n,a)': (-3, -2), + '(n,2a)': (-7, -4), + '(n,3a)': (-11, -6), + '(n,2p)': (-1, -2), + '(n,pa)': (-4, -3), + '(n,t2a)': (-10, -5), + '(n,d2a)': (-9, -5), + '(n,pd)': (-2, -2), + '(n,pt)': (-3, -2), + '(n,da)': (-5, -3), + '(n,5n)': (-4, 0), + '(n,6n)': (-5, 0), + '(n,2nt)': (-4, -1), + '(n,ta)': (-6, -3), + '(n,4np)': (-4, -1), + '(n,3nd)': (-4, -1), + '(n,nda)': (-6, -3), + '(n,2npa)': (-6, -3), + '(n,7n)': (-6, 0), + '(n,8n)': (-7, 0), + '(n,5np)': (-5, -1), + '(n,6np)': (-6, -1), + '(n,7np)': (-7, -1), + '(n,4na)': (-7, -2), + '(n,5na)': (-8, -2), + '(n,6na)': (-9, -2), + '(n,7na)': (-10, -2), + '(n,4nd)': (-5, -1), + '(n,5nd)': (-6, -1), + '(n,6nd)': (-7, -1), + '(n,3nt)': (-5, -1), + '(n,4nt)': (-6, -1), + '(n,5nt)': (-7, -1), + '(n,6nt)': (-8, -1), + '(n,2n3He)': (-4, -2), + '(n,3n3He)': (-5, -2), + '(n,4n3He)': (-6, -2), + '(n,3n2p)': (-4, -2), + '(n,3n2a)': (-10, -4), + '(n,3npa)': (-7, -3), + '(n,dt)': (-4, -2), + '(n,npd)': (-3, -2), + '(n,npt)': (-4, -2), + '(n,ndt)': (-5, -2), + '(n,np3He)': (-4, -3), + '(n,nd3He)': (-5, -3), + '(n,nt3He)': (-6, -3), + '(n,nta)': (-7, -3), + '(n,2n2p)': (-3, -2), + '(n,p3He)': (-4, -3), + '(n,d3He)': (-5, -3), + '(n,3Hea)': (-6, -4), + '(n,4n2p)': (-5, -2), + '(n,4n2a)': (-11, -4), + '(n,4npa)': (-8, -3), + '(n,3p)': (-2, -3), + '(n,n3p)': (-3, -3), + '(n,3n2pa)': (-8, -4), + '(n,5n2p)': (-6, -2), +} + # Values here are from the Committee on Data for Science and Technology # (CODATA) 2018 recommendation (https://physics.nist.gov/cuu/Constants/). @@ -194,17 +282,20 @@ AVOGADRO = 6.02214076e23 NEUTRON_MASS = 1.00866491595 # Used in atomic_mass function as a cache -_ATOMIC_MASS = {} +_ATOMIC_MASS: dict[str, float] = {} -# Regex for GND nuclide names (used in zam function) -_GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') +# Regex for GNDS nuclide names (used in zam function) +_GNDS_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)') +# Used in half_life function as a cache +_HALF_LIFE: dict[str, float] = {} +_LOG_TWO = log(2.0) def atomic_mass(isotope): """Return atomic mass of isotope in atomic mass units. - Atomic mass data comes from the `Atomic Mass Evaluation 2016 - `_. + Atomic mass data comes from the `Atomic Mass Evaluation 2020 + `_. Parameters ---------- @@ -219,21 +310,21 @@ def atomic_mass(isotope): """ if not _ATOMIC_MASS: - # Load data from AME2016 file - mass_file = os.path.join(os.path.dirname(__file__), 'mass16.txt') + # Load data from AME2020 file + mass_file = os.path.join(os.path.dirname(__file__), 'mass_1.mas20.txt') with open(mass_file, 'r') as ame: - # Read lines in file starting at line 40 - for line in itertools.islice(ame, 39, None): - name = '{}{}'.format(line[20:22].strip(), int(line[16:19])) - mass = float(line[96:99]) + 1e-6*float( - line[100:106] + '.' + line[107:112]) + # Read lines in file starting at line 37 + for line in itertools.islice(ame, 36, None): + name = f'{line[20:22].strip()}{int(line[16:19])}' + mass = float(line[106:109]) + 1e-6*float( + line[110:116] + '.' + line[117:123]) _ATOMIC_MASS[name.lower()] = mass # For isotopes found in some libraries that represent all natural # isotopes of their element (e.g. C0), calculate the atomic mass as - # the sum of the atomic mass times the natural abudance of the isotopes + # the sum of the atomic mass times the natural abundance of the isotopes # that make up the element. - for element in ['C', 'Zn', 'Pt', 'Os', 'Tl']: + for element in ['C', 'Zn', 'Pt', 'Os', 'Tl', 'V']: isotope_zero = element.lower() + '0' _ATOMIC_MASS[isotope_zero] = 0. for iso, abundance in isotopes(element): @@ -269,8 +360,63 @@ def atomic_weight(element): if weight > 0.: return weight else: - raise ValueError("No naturally-occurring isotopes for element '{}'." - .format(element)) + raise ValueError(f"No naturally-occurring isotopes for element '{element}'.") + + +def half_life(isotope): + """Return half-life of isotope in seconds or None if isotope is stable + + Half-life values are from the `ENDF/B-VIII.0 decay sublibrary + `_. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + isotope : str + Name of isotope, e.g., 'Pu239' + + Returns + ------- + float + Half-life of isotope in [s] + + """ + global _HALF_LIFE + if not _HALF_LIFE: + # Load ENDF/B-VIII.0 data from JSON file + half_life_path = Path(__file__).with_name('half_life.json') + _HALF_LIFE = json.loads(half_life_path.read_text()) + + return _HALF_LIFE.get(isotope.lower()) + + +def decay_constant(isotope): + """Return decay constant of isotope in [s^-1] + + Decay constants are based on half-life values from the + :func:`~openmc.data.half_life` function. When the isotope is stable, a decay + constant of zero is returned. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + isotope : str + Name of isotope, e.g., 'Pu239' + + Returns + ------- + float + Decay constant of isotope in [s^-1] + + See also + -------- + openmc.data.half_life + + """ + t = half_life(isotope) + return _LOG_TWO / t if t else 0.0 def water_density(temperature, pressure=0.1013): @@ -377,8 +523,11 @@ def water_density(temperature, pressure=0.1013): return coeff / pi / gamma1_pi -def gnd_name(Z, A, m=0): - """Return nuclide name using GND convention +def gnds_name(Z, A, m=0): + """Return nuclide name using GNDS convention + + .. versionchanged:: 0.14.0 + Function name changed from ``gnd_name`` to ``gnds_name`` Parameters ---------- @@ -392,16 +541,26 @@ def gnd_name(Z, A, m=0): Returns ------- str - Nuclide name in GND convention, e.g., 'Am242_m1' + Nuclide name in GNDS convention, e.g., 'Am242_m1' """ if m > 0: - return '{}{}_m{}'.format(ATOMIC_SYMBOL[Z], A, m) + return f'{ATOMIC_SYMBOL[Z]}{A}_m{m}' + return f'{ATOMIC_SYMBOL[Z]}{A}' + + + +def _get_element_symbol(element: str) -> str: + if len(element) > 2: + symbol = ELEMENT_SYMBOL.get(element.lower()) + if symbol is None: + raise ValueError(f'Element name "{element}" not recognized') + return symbol else: - return '{}{}'.format(ATOMIC_SYMBOL[Z], A) + return element -def isotopes(element): +def isotopes(element: str) -> list[tuple[str, float]]: """Return naturally occurring isotopes and their abundances .. versionadded:: 0.12.1 @@ -422,16 +581,11 @@ def isotopes(element): If the element name is not recognized """ - # Convert name to symbol if needed - if len(element) > 2: - symbol = ELEMENT_SYMBOL.get(element.lower()) - if symbol is None: - raise ValueError('Element name "{}" not recognised'.format(element)) - element = symbol + element = _get_element_symbol(element) # Get the nuclides present in nature result = [] - for kv in sorted(NATURAL_ABUNDANCE.items()): + for kv in NATURAL_ABUNDANCE.items(): if re.match(r'{}\d+'.format(element), kv[0]): result.append(kv) @@ -444,7 +598,7 @@ def zam(name): Parameters ---------- name : str - Name of nuclide using GND convention, e.g., 'Am242_m1' + Name of nuclide using GNDS convention, e.g., 'Am242_m1' Returns ------- @@ -453,14 +607,13 @@ def zam(name): """ try: - symbol, A, state = _GND_NAME_RE.match(name).groups() + symbol, A, state = _GNDS_NAME_RE.fullmatch(name).groups() except AttributeError: - raise ValueError("'{}' does not appear to be a nuclide name in GND " - "format".format(name)) + raise ValueError(f"'{name}' does not appear to be a nuclide name in " + "GNDS format") if symbol not in ATOMIC_NUMBER: - raise ValueError("'{}' is not a recognized element symbol" - .format(symbol)) + raise ValueError(f"'{symbol}' is not a recognized element symbol") metastable = int(state[2:]) if state else 0 return (ATOMIC_NUMBER[symbol], int(A), metastable) diff --git a/openmc/data/decay.py b/openmc/data/decay.py index a0402b47db..c8a0bb5e7e 100644 --- a/openmc/data/decay.py +++ b/openmc/data/decay.py @@ -1,4 +1,5 @@ from collections.abc import Iterable +from functools import cached_property from io import StringIO from math import log import re @@ -7,9 +8,13 @@ from warnings import warn import numpy as np from uncertainties import ufloat, UFloat +import openmc import openmc.checkvalue as cv +from openmc.exceptions import DataError from openmc.mixin import EqualityMixin +from openmc.stats import Discrete, Tabular, Univariate, combine_distributions from .data import ATOMIC_SYMBOL, ATOMIC_NUMBER +from .function import INTERPOLATION_SCHEME from .endf import Evaluation, get_head_record, get_list_record, get_tab1_record @@ -123,7 +128,7 @@ class FissionProductYields(EqualityMixin): isomeric_state = int(values[4*j + 1]) name = ATOMIC_SYMBOL[Z] + str(A) if isomeric_state > 0: - name += '_m{}'.format(isomeric_state) + name += f'_m{isomeric_state}' yield_j = ufloat(values[4*j + 2], values[4*j + 3]) yields[name] = yield_j @@ -139,7 +144,7 @@ class FissionProductYields(EqualityMixin): # Assign basic nuclide properties self.nuclide = { - 'name': ev.gnd_name, + 'name': ev.gnds_name, 'atomic_number': ev.target['atomic_number'], 'mass_number': ev.target['mass_number'], 'isomeric_state': ev.target['isomeric_state'] @@ -223,6 +228,18 @@ class DecayMode(EqualityMixin): def branching_ratio(self): return self._branching_ratio + @branching_ratio.setter + def branching_ratio(self, branching_ratio): + cv.check_type('branching ratio', branching_ratio, UFloat) + cv.check_greater_than('branching ratio', + branching_ratio.nominal_value, 0.0, True) + if branching_ratio.nominal_value == 0.0: + warn('Decay mode {} of parent {} has a zero branching ratio.' + .format(self.modes, self.parent)) + cv.check_greater_than('branching ratio uncertainty', + branching_ratio.std_dev, 0.0, True) + self._branching_ratio = branching_ratio + @property def daughter(self): # Determine atomic number and mass number of parent @@ -240,33 +257,22 @@ class DecayMode(EqualityMixin): Z += delta_Z if self._daughter_state > 0: - return '{}{}_m{}'.format(ATOMIC_SYMBOL[Z], A, self._daughter_state) + return f'{ATOMIC_SYMBOL[Z]}{A}_m{self._daughter_state}' else: - return '{}{}'.format(ATOMIC_SYMBOL[Z], A) - - @property - def energy(self): - return self._energy - - @property - def modes(self): - return self._modes + return f'{ATOMIC_SYMBOL[Z]}{A}' @property def parent(self): return self._parent - @branching_ratio.setter - def branching_ratio(self, branching_ratio): - cv.check_type('branching ratio', branching_ratio, UFloat) - cv.check_greater_than('branching ratio', - branching_ratio.nominal_value, 0.0, True) - if branching_ratio.nominal_value == 0.0: - warn('Decay mode {} of parent {} has a zero branching ratio.' - .format(self.modes, self.parent)) - cv.check_greater_than('branching ratio uncertainty', - branching_ratio.std_dev, 0.0, True) - self._branching_ratio = branching_ratio + @parent.setter + def parent(self, parent): + cv.check_type('parent nuclide', parent, str) + self._parent = parent + + @property + def energy(self): + return self._energy @energy.setter def energy(self, energy): @@ -276,16 +282,15 @@ class DecayMode(EqualityMixin): energy.std_dev, 0.0, True) self._energy = energy + @property + def modes(self): + return self._modes + @modes.setter def modes(self, modes): cv.check_type('decay modes', modes, Iterable, str) self._modes = modes - @parent.setter - def parent(self, parent): - cv.check_type('parent nuclide', parent, str) - self._parent = parent - class Decay(EqualityMixin): """Radioactive decay data. @@ -314,6 +319,12 @@ class Decay(EqualityMixin): 'excited_state', 'mass', 'stable', 'spin', and 'parity'. spectra : dict Resulting radiation spectra for each radiation type. + sources : dict + Radioactive decay source distributions represented as a dictionary + mapping particle types (e.g., 'photon') to instances of + :class:`openmc.stats.Univariate`. + + .. versionadded:: 0.13.1 """ def __init__(self, ev_or_filename): @@ -338,10 +349,9 @@ class Decay(EqualityMixin): self.nuclide['mass_number'] = A self.nuclide['isomeric_state'] = metastable if metastable > 0: - self.nuclide['name'] = '{}{}_m{}'.format(ATOMIC_SYMBOL[Z], A, - metastable) + self.nuclide['name'] = f'{ATOMIC_SYMBOL[Z]}{A}_m{metastable}' else: - self.nuclide['name'] = '{}{}'.format(ATOMIC_SYMBOL[Z], A) + self.nuclide['name'] = f'{ATOMIC_SYMBOL[Z]}{A}' self.nuclide['mass'] = items[1] # AWR self.nuclide['excited_state'] = items[2] # State of the original nuclide self.nuclide['stable'] = (items[4] == 1) # Nucleus stability flag @@ -442,7 +452,7 @@ class Decay(EqualityMixin): # Read continuous spectrum ci = {} params, ci['probability'] = get_tab1_record(file_obj) - ci['type'] = get_decay_modes(params[0]) + ci['from_mode'] = get_decay_modes(params[0]) # Read covariance (Ek, Fk) table LCOV = params[3] @@ -465,11 +475,10 @@ class Decay(EqualityMixin): @property def decay_constant(self): - if hasattr(self.half_life, 'n'): - return log(2.)/self.half_life - else: - mu, sigma = self.half_life - return ufloat(log(2.)/mu, log(2.)/mu**2*sigma) + if self.half_life.n == 0.0: + name = self.nuclide['name'] + raise ValueError(f"{name} is listed as unstable but has a zero half-life.") + return log(2.)/self.half_life @property def decay_energy(self): @@ -496,3 +505,157 @@ class Decay(EqualityMixin): """ return cls(ev_or_filename) + + @cached_property + def sources(self): + """Radioactive decay source distributions""" + sources = {} + name = self.nuclide['name'] + decay_constant = self.decay_constant.n + for particle, spectra in self.spectra.items(): + # Set particle type based on 'particle' above + particle_type = { + 'gamma': 'photon', + 'beta-': 'electron', + 'ec/beta+': 'positron', + 'alpha': 'alpha', + 'n': 'neutron', + 'sf': 'fragment', + 'p': 'proton', + 'e-': 'electron', + 'xray': 'photon', + 'anti-neutrino': 'anti-neutrino', + 'neutrino': 'neutrino', + }[particle] + + if particle_type not in sources: + sources[particle_type] = [] + + # Create distribution for discrete + if spectra['continuous_flag'] in ('discrete', 'both'): + energies = [] + intensities = [] + for discrete_data in spectra['discrete']: + energies.append(discrete_data['energy'].n) + intensities.append(discrete_data['intensity'].n) + energies = np.array(energies) + intensity = spectra['discrete_normalization'].n + rates = decay_constant * intensity * np.array(intensities) + dist_discrete = Discrete(energies, rates) + sources[particle_type].append(dist_discrete) + + # Create distribution for continuous + if spectra['continuous_flag'] in ('continuous', 'both'): + f = spectra['continuous']['probability'] + if len(f.interpolation) > 1: + raise NotImplementedError("Multiple interpolation regions: {name}, {particle}") + interpolation = INTERPOLATION_SCHEME[f.interpolation[0]] + if interpolation not in ('histogram', 'linear-linear'): + warn( + f"Continuous spectra with {interpolation} interpolation " + f"({name}, {particle}) encountered.") + + intensity = spectra['continuous_normalization'].n + rates = decay_constant * intensity * f.y + dist_continuous = Tabular(f.x, rates, interpolation) + sources[particle_type].append(dist_continuous) + + # Combine discrete distributions + merged_sources = {} + for particle_type, dist_list in sources.items(): + merged_sources[particle_type] = combine_distributions( + dist_list, [1.0]*len(dist_list)) + + return merged_sources + + +_DECAY_PHOTON_ENERGY = {} + + +def decay_photon_energy(nuclide: str) -> Univariate | None: + """Get photon energy distribution resulting from the decay of a nuclide + + This function relies on data stored in a depletion chain. Before calling it + for the first time, you need to ensure that a depletion chain has been + specified in openmc.config['chain_file']. + + .. versionadded:: 0.13.2 + + Parameters + ---------- + nuclide : str + Name of nuclide, e.g., 'Co58' + + Returns + ------- + openmc.stats.Univariate or None + Distribution of energies in [eV] of photons emitted from decay, or None + if no photon source exists. Note that the probabilities represent + intensities, given as [Bq/atom] (in other words, decay constants). + """ + if not _DECAY_PHOTON_ENERGY: + chain_file = openmc.config.get('chain_file') + if chain_file is None: + raise DataError( + "A depletion chain file must be specified with " + "openmc.config['chain_file'] in order to load decay data." + ) + + from openmc.deplete import Chain + chain = Chain.from_xml(chain_file) + for nuc in chain.nuclides: + if 'photon' in nuc.sources: + _DECAY_PHOTON_ENERGY[nuc.name] = nuc.sources['photon'] + + # If the chain file contained no sources at all, warn the user + if not _DECAY_PHOTON_ENERGY: + warn(f"Chain file '{chain_file}' does not have any decay photon " + "sources listed.") + + return _DECAY_PHOTON_ENERGY.get(nuclide) + + +_DECAY_ENERGY = {} + + +def decay_energy(nuclide: str): + """Get decay energy value resulting from the decay of a nuclide + + This function relies on data stored in a depletion chain. Before calling it + for the first time, you need to ensure that a depletion chain has been + specified in openmc.config['chain_file']. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + nuclide : str + Name of nuclide, e.g., 'H3' + + Returns + ------- + float + Decay energy of nuclide in [eV]. If the nuclide is stable, a value of + 0.0 is returned. + """ + if not _DECAY_ENERGY: + chain_file = openmc.config.get('chain_file') + if chain_file is None: + raise DataError( + "A depletion chain file must be specified with " + "openmc.config['chain_file'] in order to load decay data." + ) + + from openmc.deplete import Chain + chain = Chain.from_xml(chain_file) + for nuc in chain.nuclides: + if nuc.decay_energy: + _DECAY_ENERGY[nuc.name] = nuc.decay_energy + + # If the chain file contained no decay energy, warn the user + if not _DECAY_ENERGY: + warn(f"Chain file '{chain_file}' does not have any decay energy.") + + return _DECAY_ENERGY.get(nuclide, 0.0) + + diff --git a/openmc/data/effective_dose/dose.py b/openmc/data/effective_dose/dose.py index 6ceb2c09e0..d49043b0a6 100644 --- a/openmc/data/effective_dose/dose.py +++ b/openmc/data/effective_dose/dose.py @@ -2,40 +2,61 @@ from pathlib import Path import numpy as np -_FILES = ( - ('electron', 'electrons.txt'), - ('helium', 'helium_ions.txt'), - ('mu-', 'negative_muons.txt'), - ('pi-', 'negative_pions.txt'), - ('neutron', 'neutrons.txt'), - ('photon', 'photons.txt'), - ('photon kerma', 'photons_kerma.txt'), - ('mu+', 'positive_muons.txt'), - ('pi+', 'positive_pions.txt'), - ('positron', 'positrons.txt'), - ('proton', 'protons.txt') -) +import openmc.checkvalue as cv -_DOSE_ICRP116 = {} +_FILES = { + ('icrp74', 'neutron'): Path('icrp74') / 'neutrons.txt', + ('icrp74', 'photon'): Path('icrp74') / 'photons.txt', + ('icrp116', 'electron'): Path('icrp116') / 'electrons.txt', + ('icrp116', 'helium'): Path('icrp116') / 'helium_ions.txt', + ('icrp116', 'mu-'): Path('icrp116') / 'negative_muons.txt', + ('icrp116', 'pi-'): Path('icrp116') / 'negative_pions.txt', + ('icrp116', 'neutron'): Path('icrp116') / 'neutrons.txt', + ('icrp116', 'photon'): Path('icrp116') / 'photons.txt', + ('icrp116', 'photon kerma'): Path('icrp116') / 'photons_kerma.txt', + ('icrp116', 'mu+'): Path('icrp116') / 'positive_muons.txt', + ('icrp116', 'pi+'): Path('icrp116') / 'positive_pions.txt', + ('icrp116', 'positron'): Path('icrp116') / 'positrons.txt', + ('icrp116', 'proton'): Path('icrp116') / 'protons.txt', +} + +_DOSE_TABLES = {} -def _load_dose_icrp116(): - """Load effective dose tables from text files""" - for particle, filename in _FILES: - path = Path(__file__).parent / filename - data = np.loadtxt(path, skiprows=3) - data[:, 0] *= 1e6 # Change energies to eV - _DOSE_ICRP116[particle] = data +def _load_dose_icrp(data_source: str, particle: str): + """Load effective dose tables from text files. + + Parameters + ---------- + data_source : {'icrp74', 'icrp116'} + The dose conversion data source to use + particle : {'neutron', 'photon', 'photon kerma', 'electron', 'positron'} + Incident particle + + """ + path = Path(__file__).parent / _FILES[data_source, particle] + data = np.loadtxt(path, skiprows=3, encoding='utf-8') + data[:, 0] *= 1e6 # Change energies to eV + _DOSE_TABLES[data_source, particle] = data -def dose_coefficients(particle, geometry='AP'): - """Return effective dose conversion coefficients from ICRP-116 +def dose_coefficients(particle, geometry='AP', data_source='icrp116'): + """Return effective dose conversion coefficients. - This function provides fluence (and air kerma) to effective dose conversion - coefficients for various types of external exposures based on values in - `ICRP Publication 116 `_. - Corrected values found in a correigendum are used rather than the values in - theoriginal report. + This function provides fluence (and air kerma) to effective or ambient dose + (H*(10)) conversion coefficients for various types of external exposures + based on values in ICRP publications. Corrected values found in a + corrigendum are used rather than the values in the original report. + Available libraries include `ICRP Publication 74 + ` and `ICRP Publication 116 + `. + + For ICRP 74 data, the photon effective dose per fluence is determined by + multiplying the air kerma per fluence values (Table A.1) by the effective + dose per air kerma (Table A.17). The neutron effective dose per fluence is + found in Table A.41. For ICRP 116 data, the photon effective dose per + fluence is found in Table A.1 and the neutron effective dose per fluence is + found in Table A.5. Parameters ---------- @@ -44,6 +65,8 @@ def dose_coefficients(particle, geometry='AP'): geometry : {'AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO'} Irradiation geometry assumed. Refer to ICRP-116 (Section 3.2) for the meaning of the options here. + data_source : {'icrp74', 'icrp116'} + The data source for the effective dose conversion coefficients. Returns ------- @@ -54,19 +77,29 @@ def dose_coefficients(particle, geometry='AP'): 'photon kerma', the coefficients are given in [Sv/Gy]. """ - if not _DOSE_ICRP116: - _load_dose_icrp116() + + cv.check_value('geometry', geometry, {'AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO'}) + cv.check_value('data_source', data_source, {'icrp74', 'icrp116'}) + + if (data_source, particle) not in _FILES: + available_particles = sorted({p for (ds, p) in _FILES if ds == data_source}) + msg = ( + f"'{particle}' has no dose data in data source {data_source}. " + f"Available particles for {data_source} are: {available_particles}" + ) + raise ValueError(msg) + elif (data_source, particle) not in _DOSE_TABLES: + _load_dose_icrp(data_source, particle) # Get all data for selected particle - data = _DOSE_ICRP116.get(particle) - if data is None: - raise ValueError("{} has no effective dose data".format(particle)) + data = _DOSE_TABLES[data_source, particle] # Determine index for selected geometry if particle in ('neutron', 'photon', 'proton', 'photon kerma'): - index = ('AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO').index(geometry) + columns = ('AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO') else: - index = ('AP', 'PA', 'ISO').index(geometry) + columns = ('AP', 'PA', 'ISO') + index = columns.index(geometry) # Pull out energy and dose from table energy = data[:, 0].copy() diff --git a/openmc/data/effective_dose/electrons.txt b/openmc/data/effective_dose/icrp116/electrons.txt similarity index 100% rename from openmc/data/effective_dose/electrons.txt rename to openmc/data/effective_dose/icrp116/electrons.txt diff --git a/openmc/data/effective_dose/helium_ions.txt b/openmc/data/effective_dose/icrp116/helium_ions.txt similarity index 100% rename from openmc/data/effective_dose/helium_ions.txt rename to openmc/data/effective_dose/icrp116/helium_ions.txt diff --git a/openmc/data/effective_dose/negative_muons.txt b/openmc/data/effective_dose/icrp116/negative_muons.txt similarity index 100% rename from openmc/data/effective_dose/negative_muons.txt rename to openmc/data/effective_dose/icrp116/negative_muons.txt diff --git a/openmc/data/effective_dose/negative_pions.txt b/openmc/data/effective_dose/icrp116/negative_pions.txt similarity index 100% rename from openmc/data/effective_dose/negative_pions.txt rename to openmc/data/effective_dose/icrp116/negative_pions.txt diff --git a/openmc/data/effective_dose/neutrons.txt b/openmc/data/effective_dose/icrp116/neutrons.txt similarity index 100% rename from openmc/data/effective_dose/neutrons.txt rename to openmc/data/effective_dose/icrp116/neutrons.txt diff --git a/openmc/data/effective_dose/photons.txt b/openmc/data/effective_dose/icrp116/photons.txt similarity index 100% rename from openmc/data/effective_dose/photons.txt rename to openmc/data/effective_dose/icrp116/photons.txt diff --git a/openmc/data/effective_dose/photons_kerma.txt b/openmc/data/effective_dose/icrp116/photons_kerma.txt similarity index 100% rename from openmc/data/effective_dose/photons_kerma.txt rename to openmc/data/effective_dose/icrp116/photons_kerma.txt diff --git a/openmc/data/effective_dose/positive_muons.txt b/openmc/data/effective_dose/icrp116/positive_muons.txt similarity index 100% rename from openmc/data/effective_dose/positive_muons.txt rename to openmc/data/effective_dose/icrp116/positive_muons.txt diff --git a/openmc/data/effective_dose/positive_pions.txt b/openmc/data/effective_dose/icrp116/positive_pions.txt similarity index 100% rename from openmc/data/effective_dose/positive_pions.txt rename to openmc/data/effective_dose/icrp116/positive_pions.txt diff --git a/openmc/data/effective_dose/positrons.txt b/openmc/data/effective_dose/icrp116/positrons.txt similarity index 100% rename from openmc/data/effective_dose/positrons.txt rename to openmc/data/effective_dose/icrp116/positrons.txt diff --git a/openmc/data/effective_dose/protons.txt b/openmc/data/effective_dose/icrp116/protons.txt similarity index 100% rename from openmc/data/effective_dose/protons.txt rename to openmc/data/effective_dose/icrp116/protons.txt diff --git a/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py b/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py new file mode 100644 index 0000000000..f8e970137e --- /dev/null +++ b/openmc/data/effective_dose/icrp74/generate_photon_effective_dose.py @@ -0,0 +1,69 @@ +from prettytable import PrettyTable +import numpy as np + +# Data from Table A.1 (air kerma per fluence) +energy_a1 = np.array([ + 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.08, 0.1, 0.15, 0.2, + 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0 +]) +air_kerma = np.array([7.43, 3.12, 1.68, 0.721, 0.429, 0.323, 0.289, 0.307, 0.371, 0.599, 0.856, 1.38, + 1.89, 2.38, 2.84, 3.69, 4.47, 6.14, 7.55, 9.96, 12.1, 14.1, 16.1, 20.1, 24.0]) + +# Data from Table A.17 (effective dose per air kerma) +energy_a17 = np.array([ + 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.15, 0.2, 0.3, + 0.4, 0.5, 0.6, 0.8, 1.0, 2.0, 4.0, 6.0, 8.0, 10.0 +]) +dose_per_airkerma = { + 'AP': np.array([ + 0.00653, 0.0402, 0.122, 0.416, 0.788, 1.106, 1.308, 1.407, 1.433, 1.394, + 1.256, 1.173, 1.093, 1.056, 1.036, 1.024, 1.010, 1.003, 0.992, 0.993, + 0.993, 0.991, 0.990 + ]), + 'PA': np.array([ + 0.00248, 0.00586, 0.0181, 0.128, 0.370, 0.640, 0.846, 0.966, 1.019, + 1.030, 0.959, 0.915, 0.880, 0.871, 0.869, 0.870, 0.875, 0.880, 0.901, + 0.918, 0.924, 0.927, 0.929 + ]), + 'RLAT': np.array([ + 0.00172, 0.00549, 0.0151, 0.0782, 0.205, 0.345, 0.455, 0.522, 0.554, + 0.571, 0.551, 0.549, 0.557, 0.570, 0.585, 0.600, 0.628, 0.651, 0.728, + 0.796, 0.827, 0.846, 0.860 + ]), + 'LLAT': np.array([ + 0.00172, 0.00549, 0.0155, 0.0904, 0.241, 0.405, 0.528, 0.598, 0.628, + 0.641, 0.620, 0.615, 0.615, 0.623, 0.635, 0.648, 0.670, 0.691, 0.757, + 0.813, 0.836, 0.850, 0.859 + ]), + 'ROT': np.array([ + 0.00326, 0.0153, 0.0462, 0.191, 0.426, 0.661, 0.828, 0.924, 0.961, + 0.960, 0.892, 0.854, 0.824, 0.814, 0.812, 0.814, 0.821, 0.831, 0.871, + 0.909, 0.925, 0.934, 0.941 + ]), + 'ISO': np.array([ + 0.00271, 0.0123, 0.0362, 0.143, 0.326, 0.511, 0.642, 0.720, 0.749, + 0.748, 0.700, 0.679, 0.664, 0.667, 0.675, 0.684, 0.703, 0.719, 0.774, + 0.824, 0.846, 0.859, 0.868 + ]) +} + +# Interpolate air kerma onto energy grid for Table A.17 +air_kerma = np.interp(energy_a17, energy_a1, air_kerma) + +# Compute effective dose per fluence +dose_per_fluence = { + geometry: air_kerma * dose_per_airkerma + for geometry, dose_per_airkerma in dose_per_airkerma.items() +} + +# Create table +table = PrettyTable() +table.field_names = ['Energy (MeV)', 'AP', 'PA', 'LLAT', 'RLAT', 'ROT', 'ISO'] +table.float_format = '.7' +for i, energy in enumerate(energy_a17): + row = [energy] + for geometry in table.field_names[1:]: + row.append(dose_per_fluence[geometry][i]) + table.add_row(row) +print('Photons: Effective dose per fluence, in units of pSv cm², for monoenergetic particles incident in various geometries.\n') +print(table.get_string(border=False)) diff --git a/openmc/data/effective_dose/icrp74/neutrons.txt b/openmc/data/effective_dose/icrp74/neutrons.txt new file mode 100644 index 0000000000..14aab48bd1 --- /dev/null +++ b/openmc/data/effective_dose/icrp74/neutrons.txt @@ -0,0 +1,50 @@ +Neutrons: Effective dose per fluence, in units of pSv cm², for monoenergetic particles incident in various geometries. + +Energy (MeV) AP PA LLAT RLAT ROT ISO +1.00E-09 5.24 3.52 1.68 1.36 2.99 2.4 +1.00E-08 6.55 4.39 2.04 1.7 3.72 2.89 +2.50E-08 7.6 5.16 2.31 1.99 4.4 3.3 +1.00E-07 9.95 6.77 2.86 2.58 5.75 4.13 +2.00E-07 11.2 7.63 3.21 2.92 6.43 4.59 +5.00E-07 12.8 8.76 3.72 3.35 7.27 5.2 +1.00E-06 13.8 9.55 4.12 3.67 7.84 5.63 +2.00E-06 14.5 10.2 4.39 3.89 8.31 5.96 +5.00E-06 15 10.7 4.66 4.08 8.72 6.28 +1.00E-05 15.1 11 4.8 4.16 8.9 6.44 +2.00E-05 15.1 11.1 4.89 4.2 8.92 6.51 +5.00E-05 14.8 11.1 4.95 4.19 8.82 6.51 +1.00E-04 14.6 11 4.95 4.15 8.69 6.45 +2.00E-04 14.4 10.9 4.92 4.1 8.56 6.32 +5.00E-04 14.2 10.7 4.86 4.03 8.4 6.14 +1.00E-03 14.2 10.7 4.84 4 8.34 6.04 +2.00E-03 14.4 10.8 4.87 4 8.39 6.05 +5.00E-03 15.7 11.6 5.25 4.29 9.06 6.52 +1.00E-02 18.3 13.5 6.14 5.02 10.6 7.7 +2.00E-02 23.8 17.3 7.95 6.48 13.8 10.2 +3.00E-02 29 21 9.74 7.93 16.9 12.7 +5.00E-02 38.5 27.6 13.1 10.6 22.7 17.3 +7.00E-02 47.2 33.5 16.1 13.1 27.8 21.5 +1.00E-01 59.8 41.3 20.1 16.4 34.8 27.2 +1.50E-01 80.2 52.2 25.5 21.2 45.4 35.2 +2.00E-01 99 61.5 30.3 25.6 54.8 42.4 +3.00E-01 133 77.1 38.6 33.4 71.6 54.7 +5.00E-01 188 103 53.2 46.8 99.4 75 +7.00E-01 231 124 66.6 58.3 123 92.8 +9.00E-01 267 144 79.6 69.1 144 108 +1 282 154 86 74.5 154 116 +1.2 310 175 99.8 85.8 173 130 +2 383 247 153 129 234 178 +3 432 308 195 171 283 220 +4 458 345 224 198 315 250 +5 474 366 244 217 335 272 +6 483 380 261 232 348 282 +7 490 391 274 244 358 290 +8 494 399 285 253 366 297 +9 497 406 294 261 373 303 +1.00E+01 499 412 302 268 378 309 +1.20E+01 499 422 315 278 385 322 +1.40E+01 496 429 324 286 390 333 +1.50E+01 494 431 328 290 391 338 +1.60E+01 491 433 331 293 393 342 +1.80E+01 486 435 335 299 394 345 +2.00E+01 480 436 338 305 395 343 diff --git a/openmc/data/effective_dose/icrp74/photons.txt b/openmc/data/effective_dose/icrp74/photons.txt new file mode 100644 index 0000000000..1ce3d67e03 --- /dev/null +++ b/openmc/data/effective_dose/icrp74/photons.txt @@ -0,0 +1,26 @@ +Photons: Effective dose per fluence, in units of pSv cm², for monoenergetic particles incident in various geometries. + + Energy (MeV) AP PA LLAT RLAT ROT ISO + 0.0100000 0.0485179 0.0184264 0.0127796 0.0127796 0.0242218 0.0201353 + 0.0150000 0.1254240 0.0182832 0.0171288 0.0171288 0.0477360 0.0383760 + 0.0200000 0.2049600 0.0304080 0.0260400 0.0253680 0.0776160 0.0608160 + 0.0300000 0.2999360 0.0922880 0.0651784 0.0563822 0.1377110 0.1031030 + 0.0400000 0.3380520 0.1587300 0.1033890 0.0879450 0.1827540 0.1398540 + 0.0500000 0.3572380 0.2067200 0.1308150 0.1114350 0.2135030 0.1650530 + 0.0600000 0.3780120 0.2444940 0.1525920 0.1314950 0.2392920 0.1855380 + 0.0700000 0.4192860 0.2878680 0.1782040 0.1555560 0.2753520 0.2145600 + 0.0800000 0.4399310 0.3128330 0.1927960 0.1700780 0.2950270 0.2299430 + 0.1000000 0.5171740 0.3821300 0.2378110 0.2118410 0.3561600 0.2775080 + 0.1500000 0.7523440 0.5744410 0.3713800 0.3300490 0.5343080 0.4193000 + 0.2000000 1.0040880 0.7832400 0.5264400 0.4699440 0.7310240 0.5812240 + 0.3000000 1.5083400 1.2144000 0.8487000 0.7686600 1.1371200 0.9163200 + 0.4000000 1.9958400 1.6461900 1.1774700 1.0773000 1.5384600 1.2606300 + 0.5000000 2.4656800 2.0682200 1.5113000 1.3923000 1.9325600 1.6065000 + 0.6000000 2.9081600 2.4708000 1.8403200 1.7040000 2.3117600 1.9425600 + 0.8000000 3.7269000 3.2287500 2.4723000 2.3173200 3.0294900 2.5940700 + 1.0000000 4.4834100 3.9336000 3.0887700 2.9099700 3.7145700 3.2139300 + 2.0000000 7.4896000 6.8025500 5.7153500 5.4964000 6.5760500 5.8437000 + 4.0000000 12.0153000 11.1078000 9.8373000 9.6316000 10.9989000 9.9704000 + 6.0000000 15.9873000 14.8764000 13.4596000 13.3147000 14.8925000 13.6206000 + 8.0000000 19.9191000 18.6327000 17.0850000 17.0046000 18.7734000 17.2659000 + 10.0000000 23.7600000 22.2960000 20.6160000 20.6400000 22.5840000 20.8320000 diff --git a/openmc/data/endf.c b/openmc/data/endf.c deleted file mode 100644 index 936fd3bbbe..0000000000 --- a/openmc/data/endf.c +++ /dev/null @@ -1,57 +0,0 @@ -#include - -//! Convert string representation of a floating point number into a double -// -//! This function handles converting floating point numbers from an ENDF 11 -//! character field into a double, covering all the corner cases. Floating point -//! numbers are allowed to contain whitespace (which is ignored). Also, in -//! exponential notation, it allows the 'e' to be omitted. A field containing -//! only whitespace is to be interpreted as a zero. -// -//! \param buffer character input from an ENDF file -//! \param n Length of character input -//! \return Floating point number - -double cfloat_endf(const char* buffer, int n) -{ - char arr[12]; // 11 characters plus a null terminator - int j = 0; // current position in arr - int found_significand = 0; - int found_exponent = 0; - - // limit n to 11 characters - n = n > 11 ? 11 : n; - - int i; - for (i = 0; i < n; ++i) { - char c = buffer[i]; - - // Skip whitespace characters - if (c == ' ') continue; - - if (found_significand) { - if (!found_exponent) { - if (c == '+' || c == '-') { - // In the case that we encounter +/- and we haven't yet encountered - // e/E, we manually add it - arr[j++] = 'e'; - found_exponent = 1; - - } else if (c == 'e' || c == 'E' || c == 'd' || c == 'D') { - arr[j++] = 'e'; - found_exponent = 1; - continue; - } - } - } else if (c == '.' || (c >= '0' && c <= '9')) { - found_significand = 1; - } - - // Copy character - arr[j++] = c; - } - - // Done copying. Add null terminator and convert to double - arr[j] = '\0'; - return atof(arr); -} diff --git a/openmc/data/endf.py b/openmc/data/endf.py index f9b9d06941..eca3744693 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -1,9 +1,9 @@ """Module for parsing and manipulating data from ENDF evaluations. -All the classes and functions in this module are based on document -ENDF-102 titled "Data Formats and Procedures for the Evaluated Nuclear -Data File ENDF-6". The latest version from June 2009 can be found at -http://www-nds.iaea.org/ndspub/documents/endf/endf102/endf102.pdf +All the classes and functions in this module are based on document ENDF-102 +titled "Data Formats and Procedures for the Evaluated Nuclear Data File ENDF-6". +The version from September 2023 can be found at +https://www.nndc.bnl.gov/endfdocs/ENDF-102-2023.pdf """ import io @@ -12,13 +12,9 @@ import re import numpy as np -from .data import gnd_name +from .data import gnds_name from .function import Tabulated1D -try: - from ._endf import float_endf - _CYTHON = True -except ImportError: - _CYTHON = False +from endf.records import float_endf _LIBRARY = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF', @@ -91,10 +87,6 @@ def py_float_endf(s): return float(ENDF_FLOAT_RE.sub(r'\1e\2\3', s)) -if not _CYTHON: - float_endf = py_float_endf - - def int_endf(s): """Convert string of integer number in ENDF to int. @@ -449,8 +441,7 @@ class Evaluation: def __repr__(self): name = self.target['zsymam'].replace(' ', '') - return '<{} for {} {}>'.format(self.info['sublibrary'], name, - self.info['library']) + return f"<{self.info['sublibrary']} for {name} {self.info['library']}>" def _read_header(self): file_obj = io.StringIO(self.section[1, 451]) @@ -520,10 +511,10 @@ class Evaluation: self.reaction_list.append((mf, mt, nc, mod)) @property - def gnd_name(self): - return gnd_name(self.target['atomic_number'], - self.target['mass_number'], - self.target['isomeric_state']) + def gnds_name(self): + return gnds_name(self.target['atomic_number'], + self.target['mass_number'], + self.target['isomeric_state']) class Tabulated2D: @@ -531,7 +522,7 @@ class Tabulated2D: This is a dummy class that is not really used other than to store the interpolation information for a two-dimensional function. Once we refactor - to adopt GND-like data containers, this will probably be removed or + to adopt GNDS-like data containers, this will probably be removed or extended. Parameters diff --git a/openmc/data/energy_distribution.py b/openmc/data/energy_distribution.py index 3b6d325eff..069ab1b9be 100644 --- a/openmc/data/energy_distribution.py +++ b/openmc/data/energy_distribution.py @@ -53,8 +53,7 @@ class EnergyDistribution(EqualityMixin, ABC): elif energy_type == 'continuous': return ContinuousTabular.from_hdf5(group) else: - raise ValueError("Unknown energy distribution type: {}" - .format(energy_type)) + raise ValueError(f"Unknown energy distribution type: {energy_type}") @staticmethod def from_endf(file_obj, params): @@ -253,15 +252,15 @@ class MaxwellEnergy(EnergyDistribution): def theta(self): return self._theta - @property - def u(self): - return self._u - @theta.setter def theta(self, theta): cv.check_type('Maxwell theta', theta, Tabulated1D) self._theta = theta + @property + def u(self): + return self._u + @u.setter def u(self, u): cv.check_type('Maxwell restriction energy', u, Real) @@ -277,7 +276,7 @@ class MaxwellEnergy(EnergyDistribution): """ - group.attrs['type'] = np.string_('maxwell') + group.attrs['type'] = np.bytes_('maxwell') group.attrs['u'] = self.u self.theta.to_hdf5(group, 'theta') @@ -386,15 +385,15 @@ class Evaporation(EnergyDistribution): def theta(self): return self._theta - @property - def u(self): - return self._u - @theta.setter def theta(self, theta): cv.check_type('Evaporation theta', theta, Tabulated1D) self._theta = theta + @property + def u(self): + return self._u + @u.setter def u(self, u): cv.check_type('Evaporation restriction energy', u, Real) @@ -410,7 +409,7 @@ class Evaporation(EnergyDistribution): """ - group.attrs['type'] = np.string_('evaporation') + group.attrs['type'] = np.bytes_('evaporation') group.attrs['u'] = self.u self.theta.to_hdf5(group, 'theta') @@ -523,24 +522,24 @@ class WattEnergy(EnergyDistribution): def a(self): return self._a - @property - def b(self): - return self._b - - @property - def u(self): - return self._u - @a.setter def a(self, a): cv.check_type('Watt a', a, Tabulated1D) self._a = a + @property + def b(self): + return self._b + @b.setter def b(self, b): cv.check_type('Watt b', b, Tabulated1D) self._b = b + @property + def u(self): + return self._u + @u.setter def u(self, u): cv.check_type('Watt restriction energy', u, Real) @@ -556,7 +555,7 @@ class WattEnergy(EnergyDistribution): """ - group.attrs['type'] = np.string_('watt') + group.attrs['type'] = np.bytes_('watt') group.attrs['u'] = self.u self.a.to_hdf5(group, 'a') self.b.to_hdf5(group, 'b') @@ -691,14 +690,6 @@ class MadlandNix(EnergyDistribution): def efl(self): return self._efl - @property - def efh(self): - return self._efh - - @property - def tm(self): - return self._tm - @efl.setter def efl(self, efl): name = 'Madland-Nix light fragment energy' @@ -706,6 +697,10 @@ class MadlandNix(EnergyDistribution): cv.check_greater_than(name, efl, 0.) self._efl = efl + @property + def efh(self): + return self._efh + @efh.setter def efh(self, efh): name = 'Madland-Nix heavy fragment energy' @@ -713,6 +708,10 @@ class MadlandNix(EnergyDistribution): cv.check_greater_than(name, efh, 0.) self._efh = efh + @property + def tm(self): + return self._tm + @tm.setter def tm(self, tm): cv.check_type('Madland-Nix maximum temperature', tm, Tabulated1D) @@ -728,7 +727,7 @@ class MadlandNix(EnergyDistribution): """ - group.attrs['type'] = np.string_('madland-nix') + group.attrs['type'] = np.bytes_('madland-nix') group.attrs['efl'] = self.efl group.attrs['efh'] = self.efh self.tm.to_hdf5(group) @@ -778,7 +777,6 @@ class MadlandNix(EnergyDistribution): return cls(efl, efh, tm) - class DiscretePhoton(EnergyDistribution): """Discrete photon energy distribution @@ -814,24 +812,24 @@ class DiscretePhoton(EnergyDistribution): def primary_flag(self): return self._primary_flag - @property - def energy(self): - return self._energy - - @property - def atomic_weight_ratio(self): - return self._atomic_weight_ratio - @primary_flag.setter def primary_flag(self, primary_flag): cv.check_type('discrete photon primary_flag', primary_flag, Integral) self._primary_flag = primary_flag + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('discrete photon energy', energy, Real) self._energy = energy + @property + def atomic_weight_ratio(self): + return self._atomic_weight_ratio + @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): cv.check_type('atomic weight ratio', atomic_weight_ratio, Real) @@ -847,7 +845,7 @@ class DiscretePhoton(EnergyDistribution): """ - group.attrs['type'] = np.string_('discrete_photon') + group.attrs['type'] = np.bytes_('discrete_photon') group.attrs['primary_flag'] = self.primary_flag group.attrs['energy'] = self.energy group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio @@ -922,15 +920,15 @@ class LevelInelastic(EnergyDistribution): def threshold(self): return self._threshold - @property - def mass_ratio(self): - return self._mass_ratio - @threshold.setter def threshold(self, threshold): cv.check_type('level inelastic threhsold', threshold, Real) self._threshold = threshold + @property + def mass_ratio(self): + return self._mass_ratio + @mass_ratio.setter def mass_ratio(self, mass_ratio): cv.check_type('level inelastic mass ratio', mass_ratio, Real) @@ -946,7 +944,7 @@ class LevelInelastic(EnergyDistribution): """ - group.attrs['type'] = np.string_('level') + group.attrs['type'] = np.bytes_('level') group.attrs['threshold'] = self.threshold group.attrs['mass_ratio'] = self.mass_ratio @@ -1029,36 +1027,36 @@ class ContinuousTabular(EnergyDistribution): def breakpoints(self): return self._breakpoints - @property - def interpolation(self): - return self._interpolation - - @property - def energy(self): - return self._energy - - @property - def energy_out(self): - return self._energy_out - @breakpoints.setter def breakpoints(self, breakpoints): cv.check_type('continuous tabular breakpoints', breakpoints, Iterable, Integral) self._breakpoints = breakpoints + @property + def interpolation(self): + return self._interpolation + @interpolation.setter def interpolation(self, interpolation): cv.check_type('continuous tabular interpolation', interpolation, Iterable, Integral) self._interpolation = interpolation + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('continuous tabular incoming energy', energy, Iterable, Real) self._energy = energy + @property + def energy_out(self): + return self._energy_out + @energy_out.setter def energy_out(self, energy_out): cv.check_type('continuous tabular outgoing energy', energy_out, @@ -1075,7 +1073,7 @@ class ContinuousTabular(EnergyDistribution): """ - group.attrs['type'] = np.string_('continuous') + group.attrs['type'] = np.bytes_('continuous') dset = group.create_dataset('energy', data=self.energy) dset.attrs['interpolation'] = np.vstack((self.breakpoints, diff --git a/openmc/data/fission_energy.py b/openmc/data/fission_energy.py index bdce84ff79..3c7998ee21 100644 --- a/openmc/data/fission_energy.py +++ b/openmc/data/fission_energy.py @@ -44,7 +44,7 @@ class FissionEnergyRelease(EqualityMixin): ---------- [1] D. G. Madland, "Total prompt energy release in the neutron-induced fission of ^235U, ^238U, and ^239Pu", Nuclear Physics A 772:113--137 (2006). - + Attributes ---------- @@ -100,30 +100,65 @@ class FissionEnergyRelease(EqualityMixin): def fragments(self): return self._fragments + @fragments.setter + def fragments(self, energy_release): + cv.check_type('fragments', energy_release, Callable) + self._fragments = energy_release + @property def prompt_neutrons(self): return self._prompt_neutrons + @prompt_neutrons.setter + def prompt_neutrons(self, energy_release): + cv.check_type('prompt_neutrons', energy_release, Callable) + self._prompt_neutrons = energy_release + @property def delayed_neutrons(self): return self._delayed_neutrons + @delayed_neutrons.setter + def delayed_neutrons(self, energy_release): + cv.check_type('delayed_neutrons', energy_release, Callable) + self._delayed_neutrons = energy_release + @property def prompt_photons(self): return self._prompt_photons + @prompt_photons.setter + def prompt_photons(self, energy_release): + cv.check_type('prompt_photons', energy_release, Callable) + self._prompt_photons = energy_release + @property def delayed_photons(self): return self._delayed_photons + @delayed_photons.setter + def delayed_photons(self, energy_release): + cv.check_type('delayed_photons', energy_release, Callable) + self._delayed_photons = energy_release + @property def betas(self): return self._betas + @betas.setter + def betas(self, energy_release): + cv.check_type('betas', energy_release, Callable) + self._betas = energy_release + @property def neutrinos(self): return self._neutrinos + @neutrinos.setter + def neutrinos(self, energy_release): + cv.check_type('neutrinos', energy_release, Callable) + self._neutrinos = energy_release + @property def recoverable(self): components = ['fragments', 'prompt_neutrons', 'delayed_neutrons', @@ -154,41 +189,6 @@ class FissionEnergyRelease(EqualityMixin): # Use a polynomial to subtract incident energy. return sum_functions([self.total, Polynomial((0.0, -1.0))]) - @fragments.setter - def fragments(self, energy_release): - cv.check_type('fragments', energy_release, Callable) - self._fragments = energy_release - - @prompt_neutrons.setter - def prompt_neutrons(self, energy_release): - cv.check_type('prompt_neutrons', energy_release, Callable) - self._prompt_neutrons = energy_release - - @delayed_neutrons.setter - def delayed_neutrons(self, energy_release): - cv.check_type('delayed_neutrons', energy_release, Callable) - self._delayed_neutrons = energy_release - - @prompt_photons.setter - def prompt_photons(self, energy_release): - cv.check_type('prompt_photons', energy_release, Callable) - self._prompt_photons = energy_release - - @delayed_photons.setter - def delayed_photons(self, energy_release): - cv.check_type('delayed_photons', energy_release, Callable) - self._delayed_photons = energy_release - - @betas.setter - def betas(self, energy_release): - cv.check_type('betas', energy_release, Callable) - self._betas = energy_release - - @neutrinos.setter - def neutrinos(self, energy_release): - cv.check_type('neutrinos', energy_release, Callable) - self._neutrinos = energy_release - @classmethod def from_endf(cls, ev, incident_neutron): """Generate fission energy release data from an ENDF file. diff --git a/openmc/data/function.py b/openmc/data/function.py index b5aa2117d3..c5914f513d 100644 --- a/openmc/data/function.py +++ b/openmc/data/function.py @@ -255,18 +255,38 @@ class Tabulated1D(Function1D): def x(self): return self._x + @x.setter + def x(self, x): + cv.check_type('x values', x, Iterable, Real) + self._x = x + @property def y(self): return self._y + @y.setter + def y(self, y): + cv.check_type('y values', y, Iterable, Real) + self._y = y + @property def breakpoints(self): return self._breakpoints + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_type('breakpoints', breakpoints, Iterable, Integral) + self._breakpoints = breakpoints + @property def interpolation(self): return self._interpolation + @interpolation.setter + def interpolation(self, interpolation): + cv.check_type('interpolation', interpolation, Iterable, Integral) + self._interpolation = interpolation + @property def n_pairs(self): return len(self.x) @@ -275,26 +295,6 @@ class Tabulated1D(Function1D): def n_regions(self): return len(self.breakpoints) - @x.setter - def x(self, x): - cv.check_type('x values', x, Iterable, Real) - self._x = x - - @y.setter - def y(self, y): - cv.check_type('y values', y, Iterable, Real) - self._y = y - - @breakpoints.setter - def breakpoints(self, breakpoints): - cv.check_type('breakpoints', breakpoints, Iterable, Integral) - self._breakpoints = breakpoints - - @interpolation.setter - def interpolation(self, interpolation): - cv.check_type('interpolation', interpolation, Iterable, Integral) - self._interpolation = interpolation - def integral(self): """Integral of the tabulated function over its tabulated range. @@ -364,7 +364,7 @@ class Tabulated1D(Function1D): """ dataset = group.create_dataset(name, data=np.vstack( [self.x, self.y])) - dataset.attrs['type'] = np.string_(type(self).__name__) + dataset.attrs['type'] = np.bytes_(type(self).__name__) dataset.attrs['breakpoints'] = self.breakpoints dataset.attrs['interpolation'] = self.interpolation @@ -460,7 +460,7 @@ class Polynomial(np.polynomial.Polynomial, Function1D): """ dataset = group.create_dataset(name, data=self.coef) - dataset.attrs['type'] = np.string_(type(self).__name__) + dataset.attrs['type'] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -544,7 +544,7 @@ class Combination(EqualityMixin): self._operations = operations -class Sum(EqualityMixin): +class Sum(Function1D): """Sum of multiple functions. This class allows you to create a callable object which represents the sum @@ -578,6 +578,49 @@ class Sum(EqualityMixin): cv.check_type('functions', functions, Iterable, Callable) self._functions = functions + def to_hdf5(self, group, name='xy'): + """Write sum of functions to an HDF5 group + + .. versionadded:: 0.13.1 + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + name : str + Name of the dataset to create + + """ + sum_group = group.create_group(name) + sum_group.attrs['type'] = np.bytes_(type(self).__name__) + sum_group.attrs['n'] = len(self.functions) + for i, f in enumerate(self.functions): + f.to_hdf5(sum_group, f'func_{i+1}') + + @classmethod + def from_hdf5(cls, group): + """Generate sum of functions from an HDF5 group + + .. versionadded:: 0.13.1 + + Parameters + ---------- + group : h5py.Group + Group to read from + + Returns + ------- + openmc.data.Sum + Functions read from the group + + """ + n = group.attrs['n'] + functions = [ + Function1D.from_hdf5(group[f'func_{i+1}']) + for i in range(n) + ] + return cls(functions) + class Regions1D(EqualityMixin): r"""Piecewise composition of multiple functions. @@ -621,15 +664,15 @@ class Regions1D(EqualityMixin): def functions(self): return self._functions - @property - def breakpoints(self): - return self._breakpoints - @functions.setter def functions(self, functions): cv.check_type('functions', functions, Iterable, Callable) self._functions = functions + @property + def breakpoints(self): + return self._breakpoints + @breakpoints.setter def breakpoints(self, breakpoints): cv.check_iterable_type('breakpoints', breakpoints, Real) @@ -665,50 +708,28 @@ class ResonancesWithBackground(EqualityMixin): self.background = background self.mt = mt - def __call__(self, x): - # Get background cross section - xs = self.background(x) - - for r in self.resonances: - if not isinstance(r, openmc.data.resonance._RESOLVED): - continue - - if isinstance(x, Iterable): - # Determine which energies are within resolved resonance range - within = (r.energy_min <= x) & (x <= r.energy_max) - - # Get resonance cross sections and add to background - resonant_xs = r.reconstruct(x[within]) - xs[within] += resonant_xs[self.mt] - else: - if r.energy_min <= x <= r.energy_max: - resonant_xs = r.reconstruct(x) - xs += resonant_xs[self.mt] - - return xs - @property def background(self): return self._background - @property - def mt(self): - return self._mt - - @property - def resonances(self): - return self._resonances - @background.setter def background(self, background): cv.check_type('background cross section', background, Callable) self._background = background + @property + def mt(self): + return self._mt + @mt.setter def mt(self, mt): cv.check_type('MT value', mt, Integral) self._mt = mt + @property + def resonances(self): + return self._resonances + @resonances.setter def resonances(self, resonances): cv.check_type('resolved resonance parameters', resonances, diff --git a/openmc/data/half_life.json b/openmc/data/half_life.json new file mode 100644 index 0000000000..4f670918b5 --- /dev/null +++ b/openmc/data/half_life.json @@ -0,0 +1,3563 @@ +{ + "h3": 388789600.0, + "h4": 9.90652e-23, + "h5": 7.99473e-23, + "h6": 2.84812e-22, + "h7": 2.3e-23, + "he5": 7.595e-22, + "he6": 0.8067, + "he7": 3.038e-21, + "he8": 0.1191, + "he9": 7e-21, + "he10": 1.519e-21, + "li4": 7.55721e-23, + "li5": 3.06868e-22, + "li8": 0.838, + "li9": 0.1783, + "li10": 2e-21, + "li11": 0.00859, + "li12": 1e-08, + "be5": 1e-09, + "be6": 4.95326e-21, + "be7": 4598208.0, + "be8": 8.18132e-17, + "be10": 47652000000000.0, + "be11": 13.81, + "be12": 0.0213, + "be13": 2.7e-21, + "be14": 0.00435, + "be15": 2e-07, + "be16": 2e-07, + "b6": 1e-09, + "b7": 3.255e-22, + "b8": 0.77, + "b9": 8.43888e-19, + "b12": 0.0202, + "b13": 0.01736, + "b14": 0.0125, + "b15": 0.00993, + "b16": 1.9e-10, + "b17": 0.00508, + "b18": 2.6e-08, + "b19": 0.00292, + "c8": 1.9813e-21, + "c9": 0.1265, + "c10": 19.29, + "c11": 1223.1, + "c14": 179878000000.0, + "c15": 2.449, + "c16": 0.747, + "c17": 0.193, + "c18": 0.092, + "c19": 0.049, + "c20": 0.0145, + "c21": 3e-08, + "c22": 0.0062, + "n10": 2e-22, + "n11": 3.12742e-22, + "n12": 0.011, + "n13": 597.9, + "n16": 7.13, + "n17": 4.171, + "n18": 0.624, + "n19": 0.271, + "n20": 0.13, + "n21": 0.085, + "n22": 0.024, + "n23": 0.0145, + "n24": 5.2e-08, + "n25": 2.6e-07, + "o12": 1.13925e-21, + "o13": 0.00858, + "o14": 70.606, + "o15": 122.24, + "o19": 26.88, + "o20": 13.51, + "o21": 3.42, + "o22": 2.25, + "o23": 0.0905, + "o24": 0.065, + "o25": 5e-08, + "o26": 4e-08, + "o27": 2.6e-07, + "o28": 1e-07, + "f14": 5.0007e-22, + "f15": 4.557e-22, + "f16": 1.13925e-20, + "f17": 64.49, + "f18": 6586.2, + "f20": 11.163, + "f21": 4.158, + "f22": 4.23, + "f23": 2.23, + "f24": 0.39, + "f25": 0.05, + "f26": 0.0096, + "f27": 0.005, + "f28": 4e-08, + "f29": 0.0025, + "f30": 2.6e-07, + "f31": 2.5e-07, + "ne16": 3.73524e-21, + "ne17": 0.1092, + "ne18": 1.672, + "ne19": 17.22, + "ne23": 37.24, + "ne24": 202.8, + "ne25": 0.602, + "ne26": 0.197, + "ne27": 0.032, + "ne28": 0.0189, + "ne29": 0.0148, + "ne30": 0.0073, + "ne31": 0.0034, + "ne32": 0.0035, + "ne33": 1.8e-07, + "ne34": 6e-08, + "na18": 1.3e-21, + "na19": 4e-08, + "na20": 0.4479, + "na21": 22.49, + "na22": 82134970.0, + "na24": 53989.2, + "na24_m1": 0.02018, + "na25": 59.1, + "na26": 1.077, + "na27": 0.301, + "na28": 0.0305, + "na29": 0.0449, + "na30": 0.048, + "na31": 0.017, + "na32": 0.0132, + "na33": 0.008, + "na34": 0.0055, + "na35": 0.0015, + "na36": 1.8e-07, + "na37": 6e-08, + "mg19": 4e-12, + "mg20": 0.0908, + "mg21": 0.122, + "mg22": 3.8755, + "mg23": 11.317, + "mg27": 567.48, + "mg28": 75294.0, + "mg29": 1.3, + "mg30": 0.335, + "mg31": 0.232, + "mg32": 0.086, + "mg33": 0.0905, + "mg34": 0.02, + "mg35": 0.07, + "mg36": 0.0039, + "mg37": 2.6e-07, + "mg38": 2.6e-07, + "mg39": 1.8e-07, + "mg40": 1.7e-07, + "al21": 3.5e-08, + "al22": 0.059, + "al23": 0.47, + "al24": 2.053, + "al24_m1": 0.13, + "al25": 7.183, + "al26": 22626800000000.0, + "al26_m1": 6.3452, + "al28": 134.484, + "al29": 393.6, + "al30": 3.62, + "al31": 0.644, + "al32": 0.033, + "al33": 0.0417, + "al34": 0.042, + "al35": 0.0386, + "al36": 0.09, + "al37": 0.0107, + "al38": 0.0076, + "al39": 7.6e-06, + "al40": 2.6e-07, + "al41": 2.6e-07, + "al42": 1.7e-07, + "si22": 0.029, + "si23": 0.0423, + "si24": 0.14, + "si25": 0.22, + "si26": 2.234, + "si27": 4.16, + "si31": 9438.0, + "si32": 4828310000.0, + "si33": 6.11, + "si34": 2.77, + "si35": 0.78, + "si36": 0.45, + "si37": 0.09, + "si38": 1e-06, + "si39": 0.0475, + "si40": 0.033, + "si41": 0.02, + "si42": 0.0125, + "si43": 6e-08, + "si44": 3.6e-07, + "p24": 0.0074, + "p25": 3e-08, + "p26": 0.0437, + "p27": 0.26, + "p28": 0.2703, + "p29": 4.142, + "p30": 149.88, + "p32": 1232323.0, + "p33": 2189376.0, + "p34": 12.43, + "p35": 47.3, + "p36": 5.6, + "p37": 2.31, + "p38": 0.64, + "p39": 0.28, + "p40": 0.125, + "p41": 0.1, + "p42": 0.0485, + "p43": 0.0365, + "p44": 0.0185, + "p45": 2e-07, + "p46": 2e-07, + "s26": 0.01, + "s27": 0.0155, + "s28": 0.125, + "s29": 0.187, + "s30": 1.178, + "s31": 2.572, + "s35": 7560864.0, + "s37": 303.0, + "s38": 10218.0, + "s39": 11.5, + "s40": 8.8, + "s41": 1.99, + "s42": 1.013, + "s43": 0.28, + "s44": 0.1, + "s45": 0.068, + "s46": 0.05, + "s48": 2e-07, + "s49": 2e-07, + "cl28": 0.0017, + "cl29": 2e-08, + "cl30": 3e-08, + "cl31": 0.15, + "cl32": 0.298, + "cl33": 2.511, + "cl34": 1.5264, + "cl34_m1": 1920.0, + "cl36": 9498840000000.0, + "cl38": 2233.8, + "cl38_m1": 0.715, + "cl39": 3336.0, + "cl40": 81.0, + "cl41": 38.4, + "cl42": 6.8, + "cl43": 3.13, + "cl44": 0.56, + "cl45": 0.413, + "cl46": 0.232, + "cl47": 0.101, + "cl48": 2e-07, + "cl49": 1.7e-07, + "cl50": 0.02, + "cl51": 2e-07, + "ar30": 2e-08, + "ar31": 0.0151, + "ar32": 0.098, + "ar33": 0.173, + "ar34": 0.8445, + "ar35": 1.775, + "ar37": 3027456.0, + "ar39": 8488990000.0, + "ar41": 6576.6, + "ar42": 1038250000.0, + "ar43": 322.2, + "ar44": 712.2, + "ar45": 21.48, + "ar46": 8.4, + "ar47": 1.23, + "ar48": 0.475, + "ar49": 0.17, + "ar50": 0.085, + "ar51": 2e-07, + "ar52": 0.01, + "ar53": 0.003, + "k32": 0.0033, + "k33": 2.5e-08, + "k34": 2.5e-08, + "k35": 0.178, + "k36": 0.342, + "k37": 1.226, + "k38": 458.16, + "k38_m1": 0.924, + "k40": 3.93839e16, + "k42": 44496.0, + "k43": 80280.0, + "k44": 1327.8, + "k45": 1068.6, + "k46": 105.0, + "k47": 17.5, + "k48": 6.8, + "k49": 1.26, + "k50": 0.472, + "k51": 0.365, + "k52": 0.105, + "k53": 0.03, + "k54": 0.01, + "k55": 0.003, + "ca34": 3.5e-08, + "ca35": 0.0257, + "ca36": 0.102, + "ca37": 0.1811, + "ca38": 0.44, + "ca39": 0.8596, + "ca41": 3218880000000.0, + "ca45": 14049500.0, + "ca47": 391910.4, + "ca48": 7.25824e26, + "ca49": 523.08, + "ca50": 13.9, + "ca51": 10.0, + "ca52": 4.6, + "ca53": 0.09, + "ca54": 0.086, + "ca55": 0.022, + "ca56": 0.01, + "ca57": 0.005, + "sc36": 0.0088, + "sc37": 0.056, + "sc38": 0.0423, + "sc39": 3e-07, + "sc40": 0.1823, + "sc41": 0.5963, + "sc42": 0.6808, + "sc42_m1": 62.0, + "sc43": 14007.6, + "sc44": 14292.0, + "sc44_m1": 210996.0, + "sc45_m1": 0.318, + "sc46": 7239456.0, + "sc46_m1": 18.75, + "sc47": 289370.9, + "sc48": 157212.0, + "sc49": 3430.8, + "sc50": 102.5, + "sc50_m1": 0.35, + "sc51": 12.4, + "sc52": 8.2, + "sc53": 3.0, + "sc54": 0.36, + "sc55": 0.105, + "sc56": 0.06, + "sc57": 0.013, + "sc58": 0.012, + "sc59": 0.01, + "sc60": 0.003, + "ti38": 1.2e-07, + "ti39": 0.032, + "ti40": 0.0533, + "ti41": 0.0804, + "ti42": 0.199, + "ti43": 0.509, + "ti44": 1893460000.0, + "ti45": 11088.0, + "ti51": 345.6, + "ti52": 102.0, + "ti53": 32.7, + "ti54": 1.5, + "ti55": 1.3, + "ti56": 0.2, + "ti57": 0.06, + "ti58": 0.059, + "ti59": 0.03, + "ti60": 0.022, + "ti61": 3e-07, + "ti62": 0.01, + "ti63": 0.003, + "v40": 0.0077, + "v41": 0.0244, + "v42": 5.5e-08, + "v43": 0.8, + "v44": 0.111, + "v44_m1": 0.15, + "v45": 0.547, + "v46": 0.4225, + "v46_m1": 0.00102, + "v47": 1956.0, + "v48": 1380110.0, + "v49": 28512000.0, + "v50": 4.41806e24, + "v52": 224.58, + "v53": 92.58, + "v54": 49.8, + "v55": 6.54, + "v56": 0.216, + "v57": 0.35, + "v58": 0.185, + "v59": 0.075, + "v60": 0.068, + "v61": 0.047, + "v62": 1.5e-07, + "v63": 0.017, + "v64": 0.019, + "v65": 0.01, + "cr42": 0.014, + "cr43": 0.0216, + "cr44": 0.0535, + "cr45": 0.0609, + "cr46": 0.26, + "cr47": 0.5, + "cr48": 77616.0, + "cr49": 2538.0, + "cr51": 2393366.0, + "cr55": 209.82, + "cr56": 356.4, + "cr57": 21.1, + "cr58": 7.0, + "cr59": 0.46, + "cr60": 0.49, + "cr61": 0.27, + "cr62": 0.19, + "cr63": 0.129, + "cr64": 0.043, + "cr65": 0.027, + "cr66": 0.01, + "cr67": 0.05, + "mn44": 1.05e-07, + "mn45": 7e-08, + "mn46": 0.0345, + "mn47": 0.1, + "mn48": 0.1581, + "mn49": 0.382, + "mn50": 0.28319, + "mn50_m1": 105.0, + "mn51": 2772.0, + "mn52": 483062.4, + "mn52_m1": 1266.0, + "mn53": 116763000000000.0, + "mn54": 26961120.0, + "mn56": 9284.04, + "mn57": 85.4, + "mn58": 3.0, + "mn58_m1": 65.4, + "mn59": 4.59, + "mn60": 51.0, + "mn60_m1": 1.77, + "mn61": 0.67, + "mn62": 0.671, + "mn62_m1": 0.092, + "mn63": 0.29, + "mn64": 0.09, + "mn65": 0.092, + "mn66": 0.064, + "mn67": 0.047, + "mn68": 0.028, + "mn69": 0.014, + "fe45": 0.00203, + "fe46": 0.013, + "fe47": 0.0219, + "fe48": 0.044, + "fe49": 0.0647, + "fe50": 0.155, + "fe51": 0.305, + "fe52": 29790.0, + "fe52_m1": 45.9, + "fe53": 510.6, + "fe53_m1": 152.4, + "fe55": 86594050.0, + "fe59": 3844368.0, + "fe60": 47336400000000.0, + "fe61": 358.8, + "fe62": 68.0, + "fe63": 6.1, + "fe64": 2.0, + "fe65": 0.81, + "fe65_m1": 1.12, + "fe66": 0.44, + "fe67": 0.416, + "fe68": 0.187, + "fe69": 0.109, + "fe70": 0.094, + "fe71": 0.028, + "fe72": 1.5e-07, + "co49": 3.5e-08, + "co50": 0.044, + "co51": 2e-07, + "co52": 0.115, + "co53": 0.24, + "co53_m1": 0.247, + "co54": 0.19328, + "co54_m1": 88.8, + "co55": 63108.0, + "co56": 6672931.0, + "co57": 23478340.0, + "co58": 6122304.0, + "co58_m1": 32760.0, + "co60": 166344200.0, + "co60_m1": 628.02, + "co61": 5940.0, + "co62": 90.0, + "co62_m1": 834.6, + "co63": 27.4, + "co64": 0.3, + "co65": 1.16, + "co66": 0.2, + "co67": 0.425, + "co68": 0.199, + "co68_m1": 1.6, + "co69": 0.22, + "co70": 0.119, + "co70_m1": 0.5, + "co71": 0.079, + "co72": 0.0599, + "co73": 0.041, + "co74": 0.03, + "co75": 0.034, + "ni48": 0.0021, + "ni49": 0.0075, + "ni50": 0.012, + "ni51": 2e-07, + "ni52": 0.038, + "ni53": 0.045, + "ni54": 0.104, + "ni55": 0.2047, + "ni56": 524880.0, + "ni57": 128160.0, + "ni59": 2398380000000.0, + "ni63": 3193630000.0, + "ni65": 9061.884, + "ni66": 196560.0, + "ni67": 21.0, + "ni68": 29.0, + "ni69": 11.4, + "ni69_m1": 3.5, + "ni70": 6.0, + "ni71": 2.56, + "ni72": 1.57, + "ni73": 0.84, + "ni74": 0.68, + "ni75": 0.6, + "ni76": 0.238, + "ni77": 0.061, + "ni78": 0.11, + "cu52": 0.0069, + "cu53": 3e-07, + "cu54": 7.5e-08, + "cu55": 0.04, + "cu56": 0.094, + "cu57": 0.1963, + "cu58": 3.204, + "cu59": 81.5, + "cu60": 1422.0, + "cu61": 11998.8, + "cu62": 580.38, + "cu64": 45723.6, + "cu66": 307.2, + "cu67": 222588.0, + "cu68": 31.1, + "cu68_m1": 225.0, + "cu69": 171.0, + "cu70": 44.5, + "cu70_m1": 33.0, + "cu70_m2": 6.6, + "cu71": 19.5, + "cu72": 6.63, + "cu73": 4.2, + "cu74": 1.75, + "cu75": 1.224, + "cu76": 0.653, + "cu76_m1": 1.27, + "cu77": 0.469, + "cu78": 0.335, + "cu79": 0.188, + "cu80": 0.17, + "cu81": 0.028, + "zn54": 0.0037, + "zn55": 0.02, + "zn56": 5e-07, + "zn57": 0.038, + "zn58": 0.084, + "zn59": 0.182, + "zn60": 142.8, + "zn61": 89.1, + "zn61_m1": 0.43, + "zn61_m2": 0.14, + "zn62": 33336.0, + "zn63": 2308.2, + "zn65": 21075550.0, + "zn69": 3384.0, + "zn69_m1": 49536.0, + "zn71": 147.0, + "zn71_m1": 14256.0, + "zn72": 167400.0, + "zn73": 23.5, + "zn73_m1": 5.8, + "zn73_m2": 0.013, + "zn74": 95.6, + "zn75": 10.2, + "zn76": 5.7, + "zn77": 2.08, + "zn77_m1": 1.05, + "zn78": 1.47, + "zn79": 0.995, + "zn80": 0.54, + "zn81": 0.32, + "zn82": 0.052, + "zn83": 0.043, + "ga56": 0.0059, + "ga57": 0.0123, + "ga58": 0.0152, + "ga59": 0.0418, + "ga60": 0.07, + "ga61": 0.168, + "ga62": 0.11612, + "ga63": 32.4, + "ga64": 157.62, + "ga65": 912.0, + "ga66": 34164.0, + "ga67": 281810.9, + "ga68": 4062.6, + "ga70": 1268.4, + "ga72": 50760.0, + "ga72_m1": 0.03968, + "ga73": 17496.0, + "ga74": 487.2, + "ga74_m1": 9.5, + "ga75": 126.0, + "ga76": 32.6, + "ga77": 13.2, + "ga78": 5.09, + "ga79": 2.847, + "ga80": 1.676, + "ga81": 1.217, + "ga82": 0.599, + "ga83": 0.3081, + "ga84": 0.085, + "ga85": 0.048, + "ga86": 0.029, + "ge58": 0.0152, + "ge59": 0.0418, + "ge60": 0.03, + "ge61": 0.039, + "ge62": 1.5e-07, + "ge63": 0.142, + "ge64": 63.7, + "ge65": 30.9, + "ge66": 8136.0, + "ge67": 1134.0, + "ge68": 23410080.0, + "ge69": 140580.0, + "ge71": 987552.0, + "ge71_m1": 0.02041, + "ge73_m1": 0.499, + "ge75": 4966.8, + "ge75_m1": 47.7, + "ge77": 40680.0, + "ge77_m1": 52.9, + "ge78": 5280.0, + "ge79": 18.98, + "ge79_m1": 39.0, + "ge80": 29.5, + "ge81": 7.6, + "ge81_m1": 7.6, + "ge82": 4.55, + "ge83": 1.85, + "ge84": 0.954, + "ge85": 0.535, + "ge86": 0.095, + "ge87": 0.14, + "ge88": 0.066, + "ge89": 0.039, + "as60": 0.0083, + "as61": 0.0166, + "as62": 0.0259, + "as63": 0.0921, + "as64": 0.036, + "as65": 0.128, + "as66": 0.09579, + "as67": 42.5, + "as68": 151.6, + "as69": 913.8, + "as70": 3156.0, + "as71": 235080.0, + "as72": 93600.0, + "as73": 6937920.0, + "as74": 1535328.0, + "as75_m1": 0.01762, + "as76": 94464.0, + "as77": 139788.0, + "as78": 5442.0, + "as79": 540.6, + "as80": 15.2, + "as81": 33.3, + "as82": 19.1, + "as82_m1": 13.6, + "as83": 13.4, + "as84": 4.2, + "as85": 2.021, + "as86": 0.945, + "as87": 0.56, + "as88": 0.112, + "as89": 0.059, + "as90": 0.043, + "as91": 0.044, + "as92": 0.027, + "se65": 0.05, + "se66": 0.033, + "se67": 0.136, + "se68": 35.5, + "se69": 27.4, + "se70": 2466.0, + "se71": 284.4, + "se72": 725760.0, + "se73": 25740.0, + "se73_m1": 2388.0, + "se75": 10349860.0, + "se77_m1": 17.36, + "se79": 9309490000000.0, + "se79_m1": 235.2, + "se81": 1107.0, + "se81_m1": 3436.8, + "se83": 1338.0, + "se83_m1": 70.1, + "se84": 195.6, + "se85": 31.7, + "se86": 14.3, + "se87": 5.5, + "se88": 1.53, + "se89": 0.41, + "se90": 0.161, + "se91": 0.27, + "se92": 0.093, + "se93": 0.062, + "se94": 0.059, + "br67": 0.0443, + "br68": 1.2e-06, + "br69": 2.4e-08, + "br70": 0.0791, + "br70_m1": 2.2, + "br71": 21.4, + "br72": 78.6, + "br72_m1": 10.6, + "br73": 204.0, + "br74": 1524.0, + "br74_m1": 2760.0, + "br75": 5802.0, + "br76": 58320.0, + "br76_m1": 1.31, + "br77": 205329.6, + "br77_m1": 256.8, + "br78": 387.0, + "br79_m1": 4.86, + "br80": 1060.8, + "br80_m1": 15913.8, + "br82": 127015.2, + "br82_m1": 367.8, + "br83": 8640.0, + "br84": 1905.6, + "br84_m1": 360.0, + "br85": 174.0, + "br86": 55.0, + "br87": 55.65, + "br88": 16.29, + "br89": 4.4, + "br90": 1.92, + "br91": 0.541, + "br92": 0.343, + "br93": 0.102, + "br94": 0.07, + "br95": 0.066, + "br96": 0.042, + "br97": 0.04, + "kr69": 0.032, + "kr70": 0.052, + "kr71": 0.1, + "kr72": 17.1, + "kr73": 27.3, + "kr74": 690.0, + "kr75": 257.4, + "kr76": 53280.0, + "kr77": 4464.0, + "kr79": 126144.0, + "kr79_m1": 50.0, + "kr81": 7226690000000.0, + "kr81_m1": 13.1, + "kr83_m1": 6588.0, + "kr85": 339433500.0, + "kr85_m1": 16128.0, + "kr87": 4578.0, + "kr88": 10224.0, + "kr89": 189.0, + "kr90": 32.32, + "kr91": 8.57, + "kr92": 1.84, + "kr93": 1.286, + "kr94": 0.212, + "kr95": 0.114, + "kr96": 0.08, + "kr97": 0.063, + "kr98": 0.046, + "kr99": 0.027, + "kr100": 0.007, + "rb71": 1e-09, + "rb72": 1.2e-06, + "rb73": 3e-08, + "rb74": 0.064776, + "rb75": 19.0, + "rb76": 36.5, + "rb77": 226.2, + "rb78": 1059.6, + "rb78_m1": 344.4, + "rb79": 1374.0, + "rb80": 34.0, + "rb81": 16459.2, + "rb81_m1": 1830.0, + "rb82": 75.45, + "rb82_m1": 23299.2, + "rb83": 7447680.0, + "rb84": 2835648.0, + "rb84_m1": 1215.6, + "rb86": 1609718.0, + "rb86_m1": 61.02, + "rb87": 1.51792e18, + "rb88": 1066.38, + "rb89": 909.0, + "rb90": 158.0, + "rb90_m1": 258.0, + "rb91": 58.4, + "rb92": 4.492, + "rb93": 5.84, + "rb94": 2.702, + "rb95": 0.3777, + "rb96": 0.203, + "rb97": 0.1691, + "rb98": 0.114, + "rb98_m1": 0.096, + "rb99": 0.054, + "rb100": 0.051, + "rb101": 0.032, + "rb102": 0.037, + "sr73": 0.025, + "sr74": 1.2e-06, + "sr75": 0.088, + "sr76": 7.89, + "sr77": 9.0, + "sr78": 150.0, + "sr79": 135.0, + "sr80": 6378.0, + "sr81": 1338.0, + "sr82": 2190240.0, + "sr83": 116676.0, + "sr83_m1": 4.95, + "sr85": 5602176.0, + "sr85_m1": 4057.8, + "sr87_m1": 10134.0, + "sr89": 4365792.0, + "sr90": 908543300.0, + "sr91": 34668.0, + "sr92": 9756.0, + "sr93": 445.38, + "sr94": 75.3, + "sr95": 23.9, + "sr96": 1.07, + "sr97": 0.429, + "sr98": 0.653, + "sr99": 0.27, + "sr100": 0.202, + "sr101": 0.118, + "sr102": 0.069, + "sr103": 0.068, + "sr104": 0.043, + "sr105": 0.0556, + "y76": 2e-07, + "y77": 0.062, + "y78": 0.05, + "y78_m1": 5.7, + "y79": 14.8, + "y80": 30.1, + "y80_m1": 4.8, + "y81": 70.4, + "y82": 8.3, + "y83": 424.8, + "y83_m1": 171.0, + "y84": 2370.0, + "y84_m1": 4.6, + "y85": 9648.0, + "y85_m1": 17496.0, + "y86": 53064.0, + "y86_m1": 2880.0, + "y87": 287280.0, + "y87_m1": 48132.0, + "y88": 9212486.0, + "y88_m1": 0.000301, + "y88_m2": 0.01397, + "y89_m1": 15.663, + "y90": 230400.0, + "y90_m1": 11484.0, + "y91": 5055264.0, + "y91_m1": 2982.6, + "y92": 12744.0, + "y93": 36648.0, + "y93_m1": 0.82, + "y94": 1122.0, + "y95": 618.0, + "y96": 5.34, + "y96_m1": 9.6, + "y97": 3.75, + "y97_m1": 1.17, + "y97_m2": 0.142, + "y98": 0.548, + "y98_m1": 2.0, + "y99": 1.47, + "y100": 0.735, + "y100_m1": 0.94, + "y101": 0.45, + "y102": 0.36, + "y102_m1": 0.298, + "y103": 0.23, + "y104": 0.18, + "y105": 0.088, + "y106": 0.066, + "y107": 0.03, + "y108": 0.048, + "zr78": 2e-07, + "zr79": 0.056, + "zr80": 4.6, + "zr81": 5.5, + "zr82": 32.0, + "zr83": 41.6, + "zr84": 1554.0, + "zr85": 471.6, + "zr85_m1": 10.9, + "zr86": 59400.0, + "zr87": 6048.0, + "zr87_m1": 14.0, + "zr88": 7205760.0, + "zr89": 282276.0, + "zr89_m1": 249.66, + "zr90_m1": 0.8092, + "zr93": 48283100000000.0, + "zr95": 5532365.0, + "zr96": 6.31152e26, + "zr97": 60296.4, + "zr98": 30.7, + "zr99": 2.1, + "zr100": 7.1, + "zr101": 2.3, + "zr102": 2.9, + "zr103": 1.3, + "zr104": 1.2, + "zr105": 0.6, + "zr106": 0.27, + "zr107": 0.15, + "zr108": 0.08, + "zr109": 0.117, + "zr110": 0.098, + "nb81": 0.8, + "nb82": 0.05, + "nb83": 4.1, + "nb84": 9.8, + "nb85": 20.9, + "nb85_m1": 3.3, + "nb86": 88.0, + "nb87": 225.0, + "nb87_m1": 156.0, + "nb88": 873.0, + "nb88_m1": 466.8, + "nb89": 7308.0, + "nb89_m1": 3960.0, + "nb90": 52560.0, + "nb90_m1": 18.81, + "nb90_m2": 0.00619, + "nb91": 21459200000.0, + "nb91_m1": 5258304.0, + "nb92": 1095050000000000.0, + "nb92_m1": 876960.0, + "nb93_m1": 509024100.0, + "nb94": 640619000000.0, + "nb94_m1": 375.78, + "nb95": 3023222.0, + "nb95_m1": 311904.0, + "nb96": 84060.0, + "nb97": 4326.0, + "nb97_m1": 58.7, + "nb98": 2.86, + "nb98_m1": 3078.0, + "nb99": 15.0, + "nb99_m1": 150.0, + "nb100": 1.5, + "nb100_m1": 2.99, + "nb101": 7.1, + "nb102": 4.3, + "nb102_m1": 1.3, + "nb103": 1.5, + "nb104": 4.9, + "nb104_m1": 0.94, + "nb105": 2.95, + "nb106": 0.93, + "nb107": 0.3, + "nb108": 0.193, + "nb109": 0.19, + "nb110": 0.17, + "nb111": 0.08, + "nb112": 0.069, + "nb113": 0.03, + "mo83": 0.0195, + "mo84": 3.8, + "mo85": 3.2, + "mo86": 19.6, + "mo87": 14.02, + "mo88": 480.0, + "mo89": 126.6, + "mo89_m1": 0.19, + "mo90": 20412.0, + "mo91": 929.4, + "mo91_m1": 64.6, + "mo93": 126230000000.0, + "mo93_m1": 24660.0, + "mo99": 237513.6, + "mo100": 2.3037e26, + "mo101": 876.6, + "mo102": 678.0, + "mo103": 67.5, + "mo104": 60.0, + "mo105": 35.6, + "mo106": 8.73, + "mo107": 3.5, + "mo108": 1.09, + "mo109": 0.53, + "mo110": 0.3, + "mo111": 0.2, + "mo112": 0.287, + "mo113": 0.1, + "mo114": 0.08, + "mo115": 0.092, + "tc85": 0.5, + "tc86": 0.054, + "tc86_m1": 1.1e-06, + "tc87": 2.2, + "tc88": 5.8, + "tc88_m1": 6.4, + "tc89": 12.8, + "tc89_m1": 12.9, + "tc90": 8.7, + "tc90_m1": 49.2, + "tc91": 188.4, + "tc91_m1": 198.0, + "tc92": 255.0, + "tc93": 9900.0, + "tc93_m1": 2610.0, + "tc94": 17580.0, + "tc94_m1": 3120.0, + "tc95": 72000.0, + "tc95_m1": 5270400.0, + "tc96": 369792.0, + "tc96_m1": 3090.0, + "tc97": 132857000000000.0, + "tc97_m1": 7862400.0, + "tc98": 132542000000000.0, + "tc99": 6661810000000.0, + "tc99_m1": 21624.12, + "tc100": 15.46, + "tc101": 852.0, + "tc102": 5.28, + "tc102_m1": 261.0, + "tc103": 54.2, + "tc104": 1098.0, + "tc105": 456.0, + "tc106": 35.6, + "tc107": 21.2, + "tc108": 5.17, + "tc109": 0.86, + "tc110": 0.92, + "tc111": 0.29, + "tc112": 0.28, + "tc113": 0.16, + "tc114": 0.15, + "tc115": 0.073, + "tc116": 0.09, + "tc117": 0.04, + "tc118": 0.066, + "ru87": 1.5e-06, + "ru88": 1.25, + "ru89": 1.5, + "ru90": 11.7, + "ru91": 7.9, + "ru91_m1": 7.6, + "ru92": 219.0, + "ru93": 59.7, + "ru93_m1": 10.8, + "ru94": 3108.0, + "ru95": 5914.8, + "ru97": 244512.0, + "ru103": 3390941.0, + "ru103_m1": 0.00169, + "ru105": 15984.0, + "ru106": 32123520.0, + "ru107": 225.0, + "ru108": 273.0, + "ru109": 34.5, + "ru110": 11.6, + "ru111": 2.12, + "ru112": 1.75, + "ru113": 0.8, + "ru113_m1": 0.51, + "ru114": 0.52, + "ru115": 0.74, + "ru116": 0.204, + "ru117": 0.142, + "ru118": 0.123, + "ru119": 0.162, + "ru120": 0.149, + "rh89": 1.5e-06, + "rh90": 0.0145, + "rh90_m1": 1.05, + "rh91": 1.47, + "rh92": 4.66, + "rh93": 11.9, + "rh94": 70.6, + "rh94_m1": 25.8, + "rh95": 301.2, + "rh95_m1": 117.6, + "rh96": 594.0, + "rh96_m1": 90.6, + "rh97": 1842.0, + "rh97_m1": 2772.0, + "rh98": 523.2, + "rh98_m1": 216.0, + "rh99": 1391040.0, + "rh99_m1": 16920.0, + "rh100": 74880.0, + "rh100_m1": 276.0, + "rh101": 104140100.0, + "rh101_m1": 374976.0, + "rh102": 17910720.0, + "rh102_m1": 118088500.0, + "rh103_m1": 3366.84, + "rh104": 42.3, + "rh104_m1": 260.4, + "rh105": 127296.0, + "rh105_m1": 40.0, + "rh106": 30.07, + "rh106_m1": 7860.0, + "rh107": 1302.0, + "rh108": 16.8, + "rh108_m1": 360.0, + "rh109": 80.0, + "rh110": 3.2, + "rh110_m1": 28.5, + "rh111": 11.0, + "rh112": 2.1, + "rh112_m1": 6.73, + "rh113": 2.8, + "rh114": 1.85, + "rh115": 0.99, + "rh116": 0.68, + "rh116_m1": 0.57, + "rh117": 0.44, + "rh118": 0.266, + "rh119": 0.171, + "rh120": 0.136, + "rh121": 0.151, + "rh122": 0.108, + "rh123": 0.0489, + "pd91": 1e-06, + "pd92": 0.8, + "pd93": 1.3, + "pd94": 9.0, + "pd95": 10.0, + "pd95_m1": 13.3, + "pd96": 122.0, + "pd97": 186.0, + "pd98": 1062.0, + "pd99": 1284.0, + "pd100": 313632.0, + "pd101": 30492.0, + "pd103": 1468022.0, + "pd107": 205124000000000.0, + "pd107_m1": 21.3, + "pd109": 49324.32, + "pd109_m1": 281.4, + "pd111": 1404.0, + "pd111_m1": 19800.0, + "pd112": 75708.0, + "pd113": 93.0, + "pd113_m1": 0.3, + "pd114": 145.2, + "pd115": 25.0, + "pd115_m1": 50.0, + "pd116": 11.8, + "pd117": 4.3, + "pd117_m1": 0.0191, + "pd118": 1.9, + "pd119": 0.92, + "pd120": 0.5, + "pd121": 0.285, + "pd122": 0.175, + "pd123": 0.244, + "pd124": 0.038, + "pd125": 0.3987, + "pd126": 0.2499, + "ag93": 1.5e-06, + "ag94": 0.035, + "ag94_m1": 0.55, + "ag94_m2": 0.4, + "ag95": 2.0, + "ag95_m1": 0.5, + "ag95_m2": 0.016, + "ag95_m3": 0.04, + "ag96": 4.4, + "ag96_m1": 6.9, + "ag97": 25.5, + "ag98": 47.5, + "ag99": 124.0, + "ag99_m1": 10.5, + "ag100": 120.6, + "ag100_m1": 134.4, + "ag101": 666.0, + "ag101_m1": 3.1, + "ag102": 774.0, + "ag102_m1": 462.0, + "ag103": 3942.0, + "ag103_m1": 5.7, + "ag104": 4152.0, + "ag104_m1": 2010.0, + "ag105": 3567456.0, + "ag105_m1": 433.8, + "ag106": 1437.6, + "ag106_m1": 715392.0, + "ag107_m1": 44.3, + "ag108": 142.92, + "ag108_m1": 13822200000.0, + "ag109_m1": 39.6, + "ag110": 24.6, + "ag110_m1": 21579260.0, + "ag111": 643680.0, + "ag111_m1": 64.8, + "ag112": 11268.0, + "ag113": 19332.0, + "ag113_m1": 68.7, + "ag114": 4.6, + "ag114_m1": 0.0015, + "ag115": 1200.0, + "ag115_m1": 18.0, + "ag116": 237.0, + "ag116_m1": 20.0, + "ag116_m2": 9.3, + "ag117": 72.8, + "ag117_m1": 5.34, + "ag118": 3.76, + "ag118_m1": 2.0, + "ag119": 2.1, + "ag119_m1": 6.0, + "ag120": 1.23, + "ag120_m1": 0.32, + "ag121": 0.78, + "ag122": 0.529, + "ag122_m1": 0.2, + "ag123": 0.3, + "ag124": 0.172, + "ag125": 0.166, + "ag126": 0.107, + "ag127": 0.109, + "ag128": 0.058, + "ag129": 0.046, + "ag130": 0.05, + "cd95": 0.005, + "cd96": 1.0, + "cd97": 2.8, + "cd98": 9.2, + "cd99": 16.0, + "cd100": 49.1, + "cd101": 81.6, + "cd102": 330.0, + "cd103": 438.0, + "cd104": 3462.0, + "cd105": 3330.0, + "cd107": 23400.0, + "cd109": 39864960.0, + "cd111_m1": 2912.4, + "cd113": 2.53723e23, + "cd113_m1": 444962200.0, + "cd115": 192456.0, + "cd115_m1": 3849984.0, + "cd116": 9.78286e26, + "cd117": 8964.0, + "cd117_m1": 12096.0, + "cd118": 3018.0, + "cd119": 161.4, + "cd119_m1": 132.0, + "cd120": 50.8, + "cd121": 13.5, + "cd121_m1": 8.3, + "cd122": 5.24, + "cd123": 2.1, + "cd123_m1": 1.82, + "cd124": 1.25, + "cd125": 0.68, + "cd125_m1": 0.48, + "cd126": 0.515, + "cd127": 0.37, + "cd128": 0.28, + "cd129": 0.27, + "cd130": 0.162, + "cd131": 0.068, + "cd132": 0.097, + "in97": 0.005, + "in98": 0.0425, + "in98_m1": 1.6, + "in99": 3.05, + "in100": 5.9, + "in101": 15.1, + "in102": 23.3, + "in103": 65.0, + "in103_m1": 34.0, + "in104": 108.0, + "in104_m1": 15.7, + "in105": 304.2, + "in105_m1": 48.0, + "in106": 372.0, + "in106_m1": 312.0, + "in107": 1944.0, + "in107_m1": 50.4, + "in108": 3480.0, + "in108_m1": 2376.0, + "in109": 15001.2, + "in109_m1": 80.4, + "in109_m2": 0.209, + "in110": 17640.0, + "in110_m1": 4146.0, + "in111": 242326.1, + "in111_m1": 462.0, + "in112": 898.2, + "in112_m1": 1233.6, + "in113_m1": 5968.56, + "in114": 71.9, + "in114_m1": 4277664.0, + "in114_m2": 0.0431, + "in115": 1.39169e22, + "in115_m1": 16149.6, + "in116": 14.1, + "in116_m1": 3257.4, + "in116_m2": 2.18, + "in117": 2592.0, + "in117_m1": 6972.0, + "in118": 5.0, + "in118_m1": 267.0, + "in118_m2": 8.5, + "in119": 144.0, + "in119_m1": 1080.0, + "in120": 3.08, + "in120_m1": 46.2, + "in120_m2": 47.3, + "in121": 23.1, + "in121_m1": 232.8, + "in122": 1.5, + "in122_m1": 10.3, + "in122_m2": 10.8, + "in123": 6.17, + "in123_m1": 47.4, + "in124": 3.12, + "in124_m1": 3.7, + "in125": 2.36, + "in125_m1": 12.2, + "in126": 1.53, + "in126_m1": 1.64, + "in127": 1.09, + "in127_m1": 3.67, + "in128": 0.84, + "in128_m1": 0.72, + "in129": 0.61, + "in129_m1": 1.23, + "in130": 0.29, + "in130_m1": 0.54, + "in130_m2": 0.54, + "in131": 0.28, + "in131_m1": 0.35, + "in131_m2": 0.32, + "in132": 0.207, + "in133": 0.165, + "in133_m1": 0.18, + "in134": 0.14, + "in135": 0.092, + "sn99": 0.005, + "sn100": 0.86, + "sn101": 1.7, + "sn102": 4.5, + "sn103": 7.0, + "sn104": 20.8, + "sn105": 34.0, + "sn106": 115.0, + "sn107": 174.0, + "sn108": 618.0, + "sn109": 1080.0, + "sn110": 14796.0, + "sn111": 2118.0, + "sn113": 9943776.0, + "sn113_m1": 1284.0, + "sn117_m1": 1175040.0, + "sn119_m1": 25315200.0, + "sn121": 97308.0, + "sn121_m1": 1385380000.0, + "sn123": 11162880.0, + "sn123_m1": 2403.6, + "sn125": 832896.0, + "sn125_m1": 571.2, + "sn126": 7258250000000.0, + "sn127": 7560.0, + "sn127_m1": 247.8, + "sn128": 3544.2, + "sn128_m1": 6.5, + "sn129": 133.8, + "sn129_m1": 414.0, + "sn130": 223.2, + "sn130_m1": 102.0, + "sn131": 56.0, + "sn131_m1": 58.4, + "sn132": 39.7, + "sn133": 1.46, + "sn134": 1.05, + "sn135": 0.53, + "sn136": 0.25, + "sn137": 0.19, + "sb103": 1.5e-06, + "sb104": 0.46, + "sb105": 1.22, + "sb106": 0.6, + "sb107": 4.0, + "sb108": 7.4, + "sb109": 17.0, + "sb110": 23.0, + "sb111": 75.0, + "sb112": 51.4, + "sb113": 400.2, + "sb114": 209.4, + "sb115": 1926.0, + "sb116": 948.0, + "sb116_m1": 3618.0, + "sb117": 10080.0, + "sb118": 216.0, + "sb118_m1": 18000.0, + "sb119": 137484.0, + "sb119_m1": 0.85, + "sb120": 953.4, + "sb120_m1": 497664.0, + "sb122": 235336.3, + "sb122_m1": 251.46, + "sb124": 5201280.0, + "sb124_m1": 93.0, + "sb124_m2": 1212.0, + "sb125": 87053530.0, + "sb126": 1067040.0, + "sb126_m1": 1149.0, + "sb126_m2": 11.0, + "sb127": 332640.0, + "sb128": 32436.0, + "sb128_m1": 624.0, + "sb129": 15840.0, + "sb129_m1": 1062.0, + "sb130": 2370.0, + "sb130_m1": 378.0, + "sb131": 1381.8, + "sb132": 167.4, + "sb132_m1": 246.0, + "sb133": 150.0, + "sb134": 0.78, + "sb134_m1": 10.07, + "sb135": 1.679, + "sb136": 0.923, + "sb137": 0.45, + "sb138": 0.168, + "sb139": 0.127, + "te105": 6.2e-07, + "te106": 6e-05, + "te107": 0.0031, + "te108": 2.1, + "te109": 4.6, + "te110": 18.6, + "te111": 19.3, + "te112": 120.0, + "te113": 102.0, + "te114": 912.0, + "te115": 348.0, + "te115_m1": 402.0, + "te116": 8964.0, + "te117": 3720.0, + "te117_m1": 0.103, + "te118": 518400.0, + "te119": 57780.0, + "te119_m1": 406080.0, + "te121": 1656288.0, + "te121_m1": 14186880.0, + "te123_m1": 10298880.0, + "te125_m1": 4959360.0, + "te127": 33660.0, + "te127_m1": 9417600.0, + "te128": 2.77707e26, + "te129": 4176.0, + "te129_m1": 2903040.0, + "te131": 1500.0, + "te131_m1": 119700.0, + "te132": 276825.6, + "te133": 750.0, + "te133_m1": 3324.0, + "te134": 2508.0, + "te135": 19.0, + "te136": 17.5, + "te137": 2.49, + "te138": 1.4, + "te139": 0.347, + "te140": 0.304, + "te141": 0.213, + "te142": 0.2, + "i108": 0.036, + "i109": 0.000103, + "i110": 0.65, + "i111": 2.5, + "i112": 3.42, + "i113": 6.6, + "i114": 2.1, + "i114_m1": 6.2, + "i115": 78.0, + "i116": 2.91, + "i117": 133.2, + "i118": 822.0, + "i118_m1": 510.0, + "i119": 1146.0, + "i120": 4896.0, + "i120_m1": 3180.0, + "i121": 7632.0, + "i122": 217.8, + "i123": 47604.24, + "i124": 360806.4, + "i125": 5132160.0, + "i126": 1117152.0, + "i128": 1499.4, + "i129": 495454000000000.0, + "i130": 44496.0, + "i130_m1": 530.4, + "i131": 693377.3, + "i132": 8262.0, + "i132_m1": 4993.2, + "i133": 74880.0, + "i133_m1": 9.0, + "i134": 3150.0, + "i134_m1": 211.2, + "i135": 23652.0, + "i136": 83.4, + "i136_m1": 46.9, + "i137": 24.5, + "i138": 6.23, + "i139": 2.28, + "i140": 0.86, + "i141": 0.43, + "i142": 0.222, + "i143": 0.296, + "i144": 0.194, + "i145": 0.127, + "xe110": 0.093, + "xe111": 0.81, + "xe112": 2.7, + "xe113": 2.74, + "xe114": 10.0, + "xe115": 18.0, + "xe116": 59.0, + "xe117": 61.0, + "xe118": 228.0, + "xe119": 348.0, + "xe120": 2400.0, + "xe121": 2406.0, + "xe122": 72360.0, + "xe123": 7488.0, + "xe125": 60840.0, + "xe125_m1": 56.9, + "xe127": 3144960.0, + "xe127_m1": 69.2, + "xe129_m1": 767232.0, + "xe131_m1": 1022976.0, + "xe132_m1": 0.00839, + "xe133": 452995.2, + "xe133_m1": 189216.0, + "xe134_m1": 0.29, + "xe135": 32904.0, + "xe135_m1": 917.4, + "xe137": 229.08, + "xe138": 844.8, + "xe139": 39.68, + "xe140": 13.6, + "xe141": 1.73, + "xe142": 1.23, + "xe143": 0.3, + "xe144": 1.15, + "xe145": 0.188, + "xe146": 0.369, + "xe147": 0.1, + "cs112": 0.0005, + "cs113": 1.67e-05, + "cs114": 0.57, + "cs115": 1.4, + "cs116": 0.7, + "cs116_m1": 3.85, + "cs117": 8.4, + "cs117_m1": 6.5, + "cs118": 14.0, + "cs118_m1": 17.0, + "cs119": 43.0, + "cs119_m1": 30.4, + "cs120": 61.3, + "cs120_m1": 57.0, + "cs121": 155.0, + "cs121_m1": 122.0, + "cs122": 21.18, + "cs122_m1": 0.36, + "cs122_m2": 222.0, + "cs123": 352.8, + "cs123_m1": 1.64, + "cs124": 30.8, + "cs124_m1": 6.3, + "cs125": 2802.0, + "cs125_m1": 0.0009, + "cs126": 98.4, + "cs127": 22500.0, + "cs128": 217.2, + "cs129": 115416.0, + "cs130": 1752.6, + "cs130_m1": 207.6, + "cs131": 837129.6, + "cs132": 559872.0, + "cs134": 65172760.0, + "cs134_m1": 10483.2, + "cs135": 72582500000000.0, + "cs135_m1": 3180.0, + "cs136": 1137024.0, + "cs136_m1": 19.0, + "cs137": 949252600.0, + "cs138": 2004.6, + "cs138_m1": 174.6, + "cs139": 556.2, + "cs140": 63.7, + "cs141": 24.84, + "cs142": 1.684, + "cs143": 1.791, + "cs144": 0.994, + "cs144_m1": 1.0, + "cs145": 0.587, + "cs146": 0.321, + "cs147": 0.23, + "cs148": 0.146, + "cs149": 0.05, + "cs150": 0.05, + "cs151": 0.05, + "ba114": 0.43, + "ba115": 0.45, + "ba116": 1.3, + "ba117": 1.75, + "ba118": 5.5, + "ba119": 5.4, + "ba120": 24.0, + "ba121": 29.7, + "ba122": 117.0, + "ba123": 162.0, + "ba124": 660.0, + "ba125": 210.0, + "ba126": 6000.0, + "ba127": 762.0, + "ba127_m1": 1.9, + "ba128": 209952.0, + "ba129": 8028.0, + "ba129_m1": 7776.0, + "ba130_m1": 0.0094, + "ba131": 993600.0, + "ba131_m1": 876.0, + "ba133": 331862400.0, + "ba133_m1": 140040.0, + "ba135_m1": 103320.0, + "ba136_m1": 0.3084, + "ba137_m1": 153.12, + "ba139": 4983.6, + "ba140": 1101833.0, + "ba141": 1096.2, + "ba142": 636.0, + "ba143": 14.5, + "ba144": 11.5, + "ba145": 4.31, + "ba146": 2.22, + "ba147": 0.894, + "ba148": 0.612, + "ba149": 0.344, + "ba150": 0.3, + "ba151": 0.259, + "ba152": 0.228, + "ba153": 0.158, + "la117": 0.0235, + "la117_m1": 0.01, + "la118": 1.0, + "la119": 2.0, + "la120": 2.8, + "la121": 5.3, + "la122": 8.6, + "la123": 17.0, + "la124": 29.21, + "la124_m1": 21.0, + "la125": 64.8, + "la125_m1": 0.4, + "la126": 54.0, + "la126_m1": 50.0, + "la127": 306.0, + "la127_m1": 222.0, + "la128": 310.8, + "la128_m1": 60.0, + "la129": 696.0, + "la129_m1": 0.56, + "la130": 522.0, + "la131": 3540.0, + "la132": 17280.0, + "la132_m1": 1458.0, + "la133": 14083.2, + "la134": 387.0, + "la135": 70200.0, + "la136": 592.2, + "la136_m1": 0.114, + "la137": 1893460000000.0, + "la138": 3.21888e18, + "la140": 145026.7, + "la141": 14112.0, + "la142": 5466.0, + "la143": 852.0, + "la144": 40.8, + "la145": 24.8, + "la146": 6.27, + "la146_m1": 10.0, + "la147": 4.06, + "la148": 1.26, + "la149": 1.05, + "la150": 0.86, + "la151": 0.778, + "la152": 0.451, + "la153": 0.342, + "la154": 0.228, + "la155": 0.184, + "ce119": 0.2, + "ce120": 0.25, + "ce121": 1.1, + "ce122": 2.73, + "ce123": 3.8, + "ce124": 6.0, + "ce125": 10.2, + "ce126": 51.0, + "ce127": 31.0, + "ce127_m1": 28.6, + "ce128": 235.8, + "ce129": 210.0, + "ce130": 1374.0, + "ce131": 618.0, + "ce131_m1": 324.0, + "ce132": 12636.0, + "ce132_m1": 0.0094, + "ce133": 5820.0, + "ce133_m1": 17640.0, + "ce134": 273024.0, + "ce135": 63720.0, + "ce135_m1": 20.0, + "ce137": 32400.0, + "ce137_m1": 123840.0, + "ce138_m1": 0.00865, + "ce139": 11892180.0, + "ce139_m1": 54.8, + "ce141": 2808691.0, + "ce143": 118940.4, + "ce144": 24616220.0, + "ce145": 180.6, + "ce146": 811.2, + "ce147": 56.4, + "ce148": 56.0, + "ce149": 5.3, + "ce150": 4.0, + "ce151": 1.76, + "ce152": 1.4, + "ce153": 0.979, + "ce154": 0.775, + "ce155": 0.471, + "ce156": 0.369, + "ce157": 0.2428, + "pr121": 1.4, + "pr122": 0.5, + "pr123": 0.8, + "pr124": 1.2, + "pr125": 3.3, + "pr126": 3.14, + "pr127": 4.2, + "pr128": 2.85, + "pr129": 32.0, + "pr130": 40.0, + "pr131": 90.6, + "pr131_m1": 5.73, + "pr132": 96.0, + "pr133": 390.0, + "pr134": 1020.0, + "pr134_m1": 660.0, + "pr135": 1440.0, + "pr136": 786.0, + "pr137": 4608.0, + "pr138": 87.0, + "pr138_m1": 7632.0, + "pr139": 15876.0, + "pr140": 203.4, + "pr142": 68832.0, + "pr142_m1": 876.0, + "pr143": 1172448.0, + "pr144": 1036.8, + "pr144_m1": 432.0, + "pr145": 21542.4, + "pr146": 1449.0, + "pr147": 804.0, + "pr148": 137.4, + "pr148_m1": 120.6, + "pr149": 135.6, + "pr150": 6.19, + "pr151": 18.9, + "pr152": 3.63, + "pr153": 4.28, + "pr154": 2.3, + "pr155": 0.852, + "pr156": 0.733, + "pr157": 0.598, + "pr158": 0.1342, + "pr159": 0.1055, + "nd124": 0.5, + "nd125": 0.6, + "nd126": 2e-07, + "nd127": 1.8, + "nd128": 5.0, + "nd129": 4.9, + "nd130": 21.0, + "nd131": 26.0, + "nd132": 94.0, + "nd133": 70.0, + "nd133_m1": 70.0, + "nd134": 510.0, + "nd135": 744.0, + "nd135_m1": 330.0, + "nd136": 3039.0, + "nd137": 2310.0, + "nd137_m1": 1.6, + "nd138": 18144.0, + "nd139": 1782.0, + "nd139_m1": 19800.0, + "nd140": 291168.0, + "nd141": 8964.0, + "nd141_m1": 62.0, + "nd144": 7.22669e22, + "nd147": 948672.0, + "nd149": 6220.8, + "nd150": 2.49305e26, + "nd151": 746.4, + "nd152": 684.0, + "nd153": 31.6, + "nd154": 25.9, + "nd155": 8.9, + "nd156": 5.49, + "nd157": 1.906, + "nd158": 1.331, + "nd159": 0.773, + "nd160": 0.5883, + "nd161": 0.4884, + "pm126": 0.5, + "pm127": 1.0, + "pm128": 1.0, + "pm129": 2.4, + "pm130": 2.6, + "pm131": 6.3, + "pm132": 6.2, + "pm133": 15.0, + "pm133_m1": 8.8, + "pm134": 5.0, + "pm134_m1": 22.0, + "pm135": 49.0, + "pm135_m1": 45.0, + "pm136": 47.0, + "pm136_m1": 107.0, + "pm137": 144.0, + "pm138": 10.0, + "pm138_m1": 194.4, + "pm139": 249.0, + "pm139_m1": 0.18, + "pm140": 357.0, + "pm140_m1": 357.0, + "pm141": 1254.0, + "pm142": 40.5, + "pm142_m1": 0.002, + "pm143": 22896000.0, + "pm144": 31363200.0, + "pm145": 558569500.0, + "pm146": 174513500.0, + "pm147": 82788210.0, + "pm148": 463795.2, + "pm148_m1": 3567456.0, + "pm149": 191088.0, + "pm150": 9648.0, + "pm151": 102240.0, + "pm152": 247.2, + "pm152_m1": 451.2, + "pm152_m2": 828.0, + "pm153": 315.0, + "pm154": 103.8, + "pm154_m1": 160.8, + "pm155": 41.5, + "pm156": 26.7, + "pm157": 10.56, + "pm158": 4.8, + "pm159": 1.47, + "pm160": 1.561, + "pm161": 1.065, + "pm162": 0.2679, + "pm163": 0.2, + "sm128": 0.5, + "sm129": 0.55, + "sm130": 1.0, + "sm131": 1.2, + "sm132": 4.0, + "sm133": 3.7, + "sm134": 9.5, + "sm135": 10.3, + "sm136": 47.0, + "sm137": 45.0, + "sm138": 186.0, + "sm139": 154.2, + "sm139_m1": 10.7, + "sm140": 889.2, + "sm141": 612.0, + "sm141_m1": 1356.0, + "sm142": 4349.4, + "sm143": 525.0, + "sm143_m1": 66.0, + "sm143_m2": 0.03, + "sm145": 29376000.0, + "sm146": 3250430000000000.0, + "sm147": 3.34511e18, + "sm148": 2.20903e23, + "sm151": 2840184000.0, + "sm153": 167400.0, + "sm153_m1": 0.0106, + "sm155": 1338.0, + "sm156": 33840.0, + "sm157": 482.0, + "sm158": 318.0, + "sm159": 11.37, + "sm160": 9.6, + "sm161": 4.8, + "sm162": 2.4, + "sm163": 1.748, + "sm164": 1.226, + "sm165": 0.764, + "eu130": 0.001, + "eu131": 0.0178, + "eu132": 0.1, + "eu133": 1.0, + "eu134": 0.5, + "eu135": 1.5, + "eu136": 3.8, + "eu136_m1": 3.3, + "eu136_m2": 3.8, + "eu137": 11.0, + "eu138": 12.1, + "eu139": 17.9, + "eu140": 1.51, + "eu140_m1": 0.125, + "eu141": 40.7, + "eu141_m1": 2.7, + "eu142": 2.34, + "eu142_m1": 73.38, + "eu143": 155.4, + "eu144": 10.2, + "eu145": 512352.0, + "eu146": 396576.0, + "eu147": 2082240.0, + "eu148": 4708800.0, + "eu149": 8043840.0, + "eu150": 1164480000.0, + "eu150_m1": 46080.0, + "eu152": 427195200.0, + "eu152_m1": 33521.76, + "eu152_m2": 5760.0, + "eu154": 271426900.0, + "eu154_m1": 2760.0, + "eu155": 149993300.0, + "eu156": 1312416.0, + "eu157": 54648.0, + "eu158": 2754.0, + "eu159": 1086.0, + "eu160": 38.0, + "eu161": 26.0, + "eu162": 10.6, + "eu163": 7.7, + "eu164": 2.844, + "eu165": 2.3, + "eu166": 0.4, + "eu167": 0.2, + "gd134": 0.4, + "gd135": 1.1, + "gd136": 2e-05, + "gd137": 2.2, + "gd138": 4.7, + "gd139": 5.8, + "gd139_m1": 4.8, + "gd140": 15.8, + "gd141": 14.0, + "gd141_m1": 24.5, + "gd142": 70.2, + "gd143": 39.0, + "gd143_m1": 110.0, + "gd144": 268.2, + "gd145": 1380.0, + "gd145_m1": 85.0, + "gd146": 4170528.0, + "gd147": 137016.0, + "gd148": 2354197000.0, + "gd149": 801792.0, + "gd150": 56488100000000.0, + "gd151": 10713600.0, + "gd152": 3.40822e21, + "gd153": 20770560.0, + "gd155_m1": 0.03197, + "gd159": 66524.4, + "gd161": 219.6, + "gd162": 504.0, + "gd163": 68.0, + "gd164": 45.0, + "gd165": 10.3, + "gd166": 4.8, + "gd167": 3.0, + "gd168": 0.3, + "gd169": 1.0, + "tb135": 0.995, + "tb136": 0.2, + "tb137": 0.6, + "tb138": 2e-07, + "tb139": 1.6, + "tb140": 2.4, + "tb141": 3.5, + "tb141_m1": 7.9, + "tb142": 0.597, + "tb142_m1": 0.303, + "tb143": 12.0, + "tb143_m1": 21.0, + "tb144": 1.0, + "tb144_m1": 4.25, + "tb145": 30.9, + "tb145_m1": 30.9, + "tb146": 8.0, + "tb146_m1": 23.0, + "tb146_m2": 0.00118, + "tb147": 5904.0, + "tb147_m1": 109.8, + "tb148": 3600.0, + "tb148_m1": 132.0, + "tb149": 14824.8, + "tb149_m1": 249.6, + "tb150": 12528.0, + "tb150_m1": 348.0, + "tb151": 63392.4, + "tb151_m1": 25.0, + "tb152": 63000.0, + "tb152_m1": 252.0, + "tb153": 202176.0, + "tb154": 77400.0, + "tb154_m1": 33840.0, + "tb154_m2": 81720.0, + "tb155": 459648.0, + "tb156": 462240.0, + "tb156_m1": 87840.0, + "tb156_m2": 19080.0, + "tb157": 2240590000.0, + "tb158": 5680368000.0, + "tb158_m1": 10.7, + "tb160": 6246720.0, + "tb161": 596678.4, + "tb162": 456.0, + "tb163": 1170.0, + "tb164": 180.0, + "tb165": 126.6, + "tb166": 25.1, + "tb167": 19.4, + "tb168": 8.2, + "tb169": 2.0, + "tb170": 3.0, + "tb171": 0.5, + "dy138": 0.2, + "dy139": 0.6, + "dy140": 0.84, + "dy141": 0.9, + "dy142": 2.3, + "dy143": 3.2, + "dy143_m1": 3.0, + "dy144": 9.1, + "dy145": 6.0, + "dy145_m1": 14.1, + "dy146": 29.0, + "dy146_m1": 0.15, + "dy147": 40.0, + "dy147_m1": 55.7, + "dy148": 198.0, + "dy149": 252.0, + "dy149_m1": 0.49, + "dy150": 430.2, + "dy151": 1074.0, + "dy152": 8568.0, + "dy153": 23040.0, + "dy154": 94672800000000.0, + "dy155": 35640.0, + "dy157": 29304.0, + "dy157_m1": 0.0216, + "dy159": 12476160.0, + "dy165": 8402.4, + "dy165_m1": 75.42, + "dy166": 293760.0, + "dy167": 372.0, + "dy168": 522.0, + "dy169": 39.0, + "dy170": 30.0, + "dy171": 6.0, + "dy172": 3.0, + "dy173": 2.0, + "ho140": 0.006, + "ho141": 0.0041, + "ho142": 0.4, + "ho143": 2e-07, + "ho144": 0.7, + "ho145": 2.4, + "ho146": 3.6, + "ho147": 5.8, + "ho148": 2.2, + "ho148_m1": 9.59, + "ho148_m2": 0.00236, + "ho149": 21.1, + "ho149_m1": 56.0, + "ho150": 72.0, + "ho150_m1": 23.3, + "ho151": 35.2, + "ho151_m1": 47.2, + "ho152": 161.8, + "ho152_m1": 50.0, + "ho153": 120.6, + "ho153_m1": 558.0, + "ho154": 705.6, + "ho154_m1": 186.0, + "ho155": 2880.0, + "ho155_m1": 0.00088, + "ho156": 3360.0, + "ho156_m1": 9.5, + "ho156_m2": 468.0, + "ho157": 756.0, + "ho158": 678.0, + "ho158_m1": 1680.0, + "ho158_m2": 1278.0, + "ho159": 1983.0, + "ho159_m1": 8.3, + "ho160": 1536.0, + "ho160_m1": 18072.0, + "ho160_m2": 3.0, + "ho161": 8928.0, + "ho161_m1": 6.76, + "ho162": 900.0, + "ho162_m1": 4020.0, + "ho163": 144218000000.0, + "ho163_m1": 1.09, + "ho164": 1740.0, + "ho164_m1": 2250.0, + "ho166": 96480.0, + "ho166_m1": 37869100000.0, + "ho167": 11160.0, + "ho168": 179.4, + "ho168_m1": 132.0, + "ho169": 283.2, + "ho170": 165.6, + "ho170_m1": 43.0, + "ho171": 53.0, + "ho172": 25.0, + "ho173": 10.0, + "ho174": 8.0, + "ho175": 5.0, + "er143": 0.2, + "er144": 2e-07, + "er146": 1.7, + "er147": 2.5, + "er147_m1": 2.5, + "er148": 4.6, + "er149": 4.0, + "er149_m1": 8.9, + "er150": 18.5, + "er151": 23.5, + "er151_m1": 0.58, + "er152": 10.3, + "er153": 37.1, + "er154": 223.8, + "er155": 318.0, + "er156": 1170.0, + "er157": 1119.0, + "er157_m1": 0.076, + "er158": 8244.0, + "er159": 2160.0, + "er160": 102888.0, + "er161": 11556.0, + "er163": 4500.0, + "er165": 37296.0, + "er167_m1": 2.269, + "er169": 811468.8, + "er171": 27057.6, + "er172": 177480.0, + "er173": 84.0, + "er174": 192.0, + "er175": 72.0, + "er176": 20.0, + "er177": 3.0, + "tm145": 3.1e-06, + "tm146": 0.08, + "tm146_m1": 0.2, + "tm147": 0.58, + "tm148": 0.7, + "tm149": 0.9, + "tm150": 2.2, + "tm150_m1": 0.0052, + "tm151": 4.17, + "tm151_m1": 4.51e-07, + "tm152": 8.0, + "tm152_m1": 5.2, + "tm153": 1.48, + "tm153_m1": 2.5, + "tm154": 8.1, + "tm154_m1": 3.3, + "tm155": 21.6, + "tm155_m1": 45.0, + "tm156": 83.8, + "tm157": 217.8, + "tm158": 238.8, + "tm159": 547.8, + "tm160": 564.0, + "tm160_m1": 74.5, + "tm161": 1812.0, + "tm162": 1302.0, + "tm162_m1": 24.3, + "tm163": 6516.0, + "tm164": 120.0, + "tm164_m1": 306.0, + "tm165": 108216.0, + "tm166": 27720.0, + "tm166_m1": 0.34, + "tm167": 799200.0, + "tm168": 8043840.0, + "tm170": 11111040.0, + "tm171": 60590590.0, + "tm172": 228960.0, + "tm173": 29664.0, + "tm174": 324.0, + "tm175": 912.0, + "tm176": 114.0, + "tm177": 90.0, + "tm178": 30.0, + "tm179": 20.0, + "yb148": 0.25, + "yb149": 0.7, + "yb150": 2e-07, + "yb151": 1.6, + "yb151_m1": 1.6, + "yb152": 3.04, + "yb153": 4.2, + "yb154": 0.409, + "yb155": 1.793, + "yb156": 26.1, + "yb157": 38.6, + "yb158": 89.4, + "yb159": 100.2, + "yb160": 288.0, + "yb161": 252.0, + "yb162": 1132.2, + "yb163": 663.0, + "yb164": 4548.0, + "yb165": 594.0, + "yb166": 204120.0, + "yb167": 1050.0, + "yb169": 2766355.0, + "yb169_m1": 46.0, + "yb171_m1": 0.00525, + "yb175": 361584.0, + "yb175_m1": 0.0682, + "yb176_m1": 11.4, + "yb177": 6879.6, + "yb177_m1": 6.41, + "yb178": 4440.0, + "yb179": 480.0, + "yb180": 144.0, + "yb181": 60.0, + "lu150": 0.043, + "lu151": 0.0806, + "lu152": 0.7, + "lu153": 0.9, + "lu153_m1": 1.0, + "lu154": 2.0, + "lu154_m1": 1.12, + "lu155": 0.068, + "lu155_m1": 0.138, + "lu155_m2": 0.00269, + "lu156": 0.494, + "lu156_m1": 0.198, + "lu157": 6.8, + "lu157_m1": 4.79, + "lu158": 10.6, + "lu159": 12.1, + "lu160": 36.1, + "lu160_m1": 40.0, + "lu161": 77.0, + "lu161_m1": 0.0073, + "lu162": 82.2, + "lu162_m1": 90.0, + "lu162_m2": 114.0, + "lu163": 238.2, + "lu164": 188.4, + "lu165": 644.4, + "lu166": 159.0, + "lu166_m1": 84.6, + "lu166_m2": 127.2, + "lu167": 3090.0, + "lu167_m1": 60.0, + "lu168": 330.0, + "lu168_m1": 402.0, + "lu169": 122616.0, + "lu169_m1": 160.0, + "lu170": 173836.8, + "lu170_m1": 0.67, + "lu171": 711936.0, + "lu171_m1": 79.0, + "lu172": 578880.0, + "lu172_m1": 222.0, + "lu173": 43233910.0, + "lu174": 104455700.0, + "lu174_m1": 12268800.0, + "lu176": 1.18657e18, + "lu176_m1": 13086.0, + "lu177": 574300.8, + "lu177_m1": 13862020.0, + "lu177_m2": 390.0, + "lu178": 1704.0, + "lu178_m1": 1386.0, + "lu179": 16524.0, + "lu179_m1": 0.0031, + "lu180": 342.0, + "lu180_m1": 0.001, + "lu181": 210.0, + "lu182": 120.0, + "lu183": 58.0, + "lu184": 20.0, + "hf153": 6e-06, + "hf154": 2.0, + "hf155": 0.89, + "hf156": 0.023, + "hf157": 0.11, + "hf158": 2.85, + "hf159": 5.6, + "hf160": 13.6, + "hf161": 18.2, + "hf162": 39.4, + "hf163": 40.0, + "hf164": 111.0, + "hf165": 76.0, + "hf166": 406.2, + "hf167": 123.0, + "hf168": 1557.0, + "hf169": 194.4, + "hf170": 57636.0, + "hf171": 43560.0, + "hf171_m1": 29.5, + "hf172": 59012710.0, + "hf173": 84960.0, + "hf174": 6.31152e22, + "hf175": 6048000.0, + "hf177_m1": 1.09, + "hf177_m2": 3084.0, + "hf178_m1": 4.0, + "hf178_m2": 978285600.0, + "hf179_m1": 18.67, + "hf179_m2": 2164320.0, + "hf180_m1": 19800.0, + "hf181": 3662496.0, + "hf182": 280863000000000.0, + "hf182_m1": 3690.0, + "hf183": 3841.2, + "hf184": 14832.0, + "hf184_m1": 48.0, + "hf185": 210.0, + "hf186": 156.0, + "hf187": 30.0, + "hf188": 20.0, + "ta155": 0.0031, + "ta156": 0.144, + "ta156_m1": 0.36, + "ta157": 0.0101, + "ta157_m1": 0.0043, + "ta157_m2": 0.0017, + "ta158": 0.055, + "ta158_m1": 0.0367, + "ta159": 0.83, + "ta159_m1": 0.515, + "ta160": 1.55, + "ta160_m1": 1.7, + "ta161": 2.89, + "ta162": 3.57, + "ta163": 10.6, + "ta164": 14.2, + "ta165": 31.0, + "ta166": 34.4, + "ta167": 80.0, + "ta168": 120.0, + "ta169": 294.0, + "ta170": 405.6, + "ta171": 1398.0, + "ta172": 2208.0, + "ta173": 11304.0, + "ta174": 4104.0, + "ta175": 37800.0, + "ta176": 29124.0, + "ta176_m1": 0.0011, + "ta176_m2": 0.00097, + "ta177": 203616.0, + "ta178": 558.6, + "ta178_m1": 8496.0, + "ta178_m2": 0.058, + "ta179": 57434830.0, + "ta179_m1": 0.009, + "ta179_m2": 0.0541, + "ta180": 29354.4, + "ta182": 9913536.0, + "ta182_m1": 0.283, + "ta182_m2": 950.4, + "ta183": 440640.0, + "ta184": 31320.0, + "ta185": 2964.0, + "ta185_m1": 0.002, + "ta186": 630.0, + "ta187": 120.0, + "ta188": 20.0, + "ta189": 3.0, + "ta190": 0.3, + "w158": 0.00125, + "w159": 0.0073, + "w160": 0.091, + "w161": 0.409, + "w162": 1.36, + "w163": 2.8, + "w164": 6.3, + "w165": 5.1, + "w166": 19.2, + "w167": 19.9, + "w168": 53.0, + "w169": 74.0, + "w170": 145.2, + "w171": 142.8, + "w172": 396.0, + "w173": 456.0, + "w174": 1992.0, + "w175": 2112.0, + "w176": 9000.0, + "w177": 7920.0, + "w178": 1866240.0, + "w179": 2223.0, + "w179_m1": 384.0, + "w180": 5.68037e25, + "w180_m1": 0.00547, + "w181": 10471680.0, + "w183_m1": 5.2, + "w185": 6488640.0, + "w185_m1": 100.2, + "w186": 5.36e27, + "w186_m1": 0.003, + "w187": 86400.0, + "w188": 6028992.0, + "w189": 642.0, + "w190": 1800.0, + "w190_m1": 0.0031, + "w191": 20.0, + "w192": 10.0, + "re160": 0.00085, + "re161": 0.00037, + "re161_m1": 0.0156, + "re162": 0.107, + "re162_m1": 0.077, + "re163": 0.39, + "re163_m1": 0.214, + "re164": 0.53, + "re164_m1": 0.87, + "re165": 1.0, + "re165_m1": 2.1, + "re166": 2.25, + "re167": 5.9, + "re167_m1": 3.4, + "re168": 4.4, + "re169": 8.1, + "re169_m1": 15.1, + "re170": 9.2, + "re171": 15.2, + "re172": 55.0, + "re172_m1": 15.0, + "re173": 118.8, + "re174": 144.0, + "re175": 353.4, + "re176": 318.0, + "re177": 840.0, + "re178": 792.0, + "re179": 1170.0, + "re180": 146.4, + "re181": 71640.0, + "re182": 230400.0, + "re182_m1": 45720.0, + "re183": 6048000.0, + "re183_m1": 0.00104, + "re184": 3058560.0, + "re184_m1": 14601600.0, + "re186": 321261.1, + "re186_m1": 6311520000000.0, + "re187": 1.36644e18, + "re188": 61214.4, + "re188_m1": 1115.4, + "re189": 87480.0, + "re190": 186.0, + "re190_m1": 11520.0, + "re191": 588.0, + "re192": 16.0, + "re193": 51.9, + "re194": 1.0, + "os162": 0.00205, + "os163": 0.0055, + "os164": 0.021, + "os165": 0.071, + "os166": 0.199, + "os167": 0.81, + "os168": 2.1, + "os169": 3.43, + "os170": 7.37, + "os171": 8.3, + "os172": 19.2, + "os173": 22.4, + "os174": 44.0, + "os175": 84.0, + "os176": 216.0, + "os177": 180.0, + "os178": 300.0, + "os179": 390.0, + "os180": 1290.0, + "os181": 6300.0, + "os181_m1": 162.0, + "os182": 78624.0, + "os183": 46800.0, + "os183_m1": 35640.0, + "os185": 8087040.0, + "os186": 6.31152e22, + "os189_m1": 20916.0, + "os190_m1": 594.0, + "os191": 1330560.0, + "os191_m1": 47160.0, + "os192_m1": 5.9, + "os193": 108396.0, + "os194": 189345600.0, + "os195": 540.0, + "os196": 2094.0, + "ir164": 0.14, + "ir164_m1": 9.5e-05, + "ir165": 1e-06, + "ir166": 0.0105, + "ir166_m1": 0.0151, + "ir167": 0.0352, + "ir167_m1": 0.0257, + "ir168": 0.232, + "ir168_m1": 0.16, + "ir169": 0.64, + "ir169_m1": 0.281, + "ir170": 0.9, + "ir170_m1": 0.811, + "ir171": 3.5, + "ir171_m1": 1.4, + "ir172": 4.4, + "ir172_m1": 2.0, + "ir173": 9.0, + "ir173_m1": 2.2, + "ir174": 7.9, + "ir174_m1": 4.9, + "ir175": 9.0, + "ir176": 8.7, + "ir177": 30.0, + "ir178": 12.0, + "ir179": 79.0, + "ir180": 90.0, + "ir181": 294.0, + "ir182": 900.0, + "ir183": 3480.0, + "ir184": 11124.0, + "ir185": 51840.0, + "ir186": 59904.0, + "ir186_m1": 6840.0, + "ir187": 37800.0, + "ir187_m1": 0.0303, + "ir188": 149400.0, + "ir188_m1": 0.0042, + "ir189": 1140480.0, + "ir189_m1": 0.0133, + "ir189_m2": 0.0037, + "ir190": 1017792.0, + "ir190_m1": 4032.0, + "ir190_m2": 11113.2, + "ir191_m1": 4.899, + "ir191_m2": 5.5, + "ir192": 6378653.0, + "ir192_m1": 87.0, + "ir192_m2": 7605382000.0, + "ir193_m1": 909792.0, + "ir194": 69408.0, + "ir194_m1": 0.03185, + "ir194_m2": 14774400.0, + "ir195": 9000.0, + "ir195_m1": 13680.0, + "ir196": 52.0, + "ir196_m1": 5040.0, + "ir197": 348.0, + "ir197_m1": 534.0, + "ir198": 8.0, + "ir199": 6.5, + "pt166": 0.0003, + "pt167": 0.00078, + "pt168": 0.002, + "pt169": 0.007, + "pt170": 0.014, + "pt171": 0.051, + "pt172": 0.096, + "pt173": 0.382, + "pt174": 0.889, + "pt175": 2.53, + "pt176": 6.33, + "pt177": 10.6, + "pt178": 21.1, + "pt179": 21.2, + "pt180": 56.0, + "pt181": 52.0, + "pt182": 180.0, + "pt183": 390.0, + "pt183_m1": 43.0, + "pt184": 1038.0, + "pt184_m1": 0.00101, + "pt185": 4254.0, + "pt185_m1": 1980.0, + "pt186": 7488.0, + "pt187": 8460.0, + "pt188": 881280.0, + "pt189": 39132.0, + "pt190": 2.05124e19, + "pt191": 242092.8, + "pt193": 1577880000.0, + "pt193_m1": 374112.0, + "pt195_m1": 346464.0, + "pt197": 71609.4, + "pt197_m1": 5724.6, + "pt199": 1848.0, + "pt199_m1": 13.6, + "pt200": 45360.0, + "pt201": 150.0, + "pt202": 158400.0, + "au169": 0.00015, + "au170": 0.000291, + "au170_m1": 0.00062, + "au171": 1.9e-05, + "au171_m1": 0.00102, + "au172": 0.0047, + "au173": 0.025, + "au173_m1": 0.014, + "au174": 0.139, + "au174_m1": 0.1629, + "au175": 0.1, + "au175_m1": 0.156, + "au176": 1.05, + "au176_m1": 1.36, + "au177": 1.462, + "au177_m1": 1.18, + "au178": 2.6, + "au179": 3.3, + "au180": 8.1, + "au181": 13.7, + "au182": 15.6, + "au183": 42.8, + "au184": 20.6, + "au184_m1": 47.6, + "au185": 255.0, + "au186": 642.0, + "au187": 504.0, + "au187_m1": 2.3, + "au188": 530.4, + "au189": 1722.0, + "au189_m1": 275.4, + "au190": 2568.0, + "au190_m1": 0.125, + "au191": 11448.0, + "au191_m1": 0.92, + "au192": 17784.0, + "au192_m1": 0.029, + "au192_m2": 0.16, + "au193": 63540.0, + "au193_m1": 3.9, + "au194": 136872.0, + "au194_m1": 0.6, + "au194_m2": 0.42, + "au195": 16078870.0, + "au195_m1": 30.5, + "au196": 532820.2, + "au196_m1": 8.1, + "au196_m2": 34560.0, + "au197_m1": 7.73, + "au198": 232822.1, + "au198_m1": 196300.8, + "au199": 271209.6, + "au200": 2904.0, + "au200_m1": 67320.0, + "au201": 1560.0, + "au202": 28.4, + "au203": 60.0, + "au204": 39.8, + "au205": 31.0, + "hg171": 6.9e-05, + "hg172": 0.000365, + "hg173": 0.0007, + "hg174": 0.0019, + "hg175": 0.0107, + "hg176": 0.0203, + "hg177": 0.1273, + "hg178": 0.269, + "hg179": 1.08, + "hg180": 2.58, + "hg181": 3.6, + "hg182": 10.83, + "hg183": 9.4, + "hg184": 30.9, + "hg185": 49.1, + "hg185_m1": 21.6, + "hg186": 82.8, + "hg187": 144.0, + "hg187_m1": 114.0, + "hg188": 195.0, + "hg189": 456.0, + "hg189_m1": 516.0, + "hg190": 1200.0, + "hg191": 2940.0, + "hg191_m1": 3048.0, + "hg192": 17460.0, + "hg193": 13680.0, + "hg193_m1": 42480.0, + "hg194": 14011600000.0, + "hg195": 37908.0, + "hg195_m1": 149760.0, + "hg197": 230904.0, + "hg197_m1": 85680.0, + "hg199_m1": 2560.2, + "hg203": 4025722.0, + "hg205": 308.4, + "hg205_m1": 0.00109, + "hg206": 499.2, + "hg207": 174.0, + "hg208": 2490.0, + "hg209": 36.5, + "hg210": 146.0, + "tl176": 0.006, + "tl177": 0.018, + "tl178": 0.06, + "tl179": 0.23, + "tl179_m1": 0.0017, + "tl180": 1.09, + "tl181": 3.2, + "tl181_m1": 0.0014, + "tl182": 3.1, + "tl183": 6.9, + "tl183_m1": 0.0533, + "tl184": 11.0, + "tl185": 19.5, + "tl185_m1": 1.93, + "tl186": 27.5, + "tl186_m1": 2.9, + "tl187": 51.0, + "tl187_m1": 15.6, + "tl188": 71.0, + "tl188_m1": 71.0, + "tl188_m2": 0.041, + "tl189": 138.0, + "tl189_m1": 84.0, + "tl190": 222.0, + "tl190_m1": 156.0, + "tl191": 1200.0, + "tl191_m1": 313.2, + "tl192": 576.0, + "tl192_m1": 648.0, + "tl193": 1296.0, + "tl193_m1": 126.6, + "tl194": 1980.0, + "tl194_m1": 1968.0, + "tl195": 4176.0, + "tl195_m1": 3.6, + "tl196": 6624.0, + "tl196_m1": 5076.0, + "tl197": 10224.0, + "tl197_m1": 0.54, + "tl198": 19080.0, + "tl198_m1": 6732.0, + "tl198_m2": 0.0321, + "tl199": 26712.0, + "tl199_m1": 0.0284, + "tl200": 93960.0, + "tl200_m1": 0.034, + "tl201": 262837.4, + "tl201_m1": 0.00201, + "tl202": 1063584.0, + "tl204": 119382400.0, + "tl206": 252.12, + "tl206_m1": 224.4, + "tl207": 286.2, + "tl207_m1": 1.33, + "tl208": 183.18, + "tl209": 132.0, + "tl210": 78.0, + "tl211": 60.0, + "tl212": 67.0, + "pb178": 0.00023, + "pb179": 0.003, + "pb180": 0.0045, + "pb181": 0.036, + "pb181_m1": 0.045, + "pb182": 0.0575, + "pb183": 0.535, + "pb183_m1": 0.415, + "pb184": 0.49, + "pb185": 6.3, + "pb185_m1": 4.3, + "pb186": 4.82, + "pb187": 15.2, + "pb187_m1": 18.3, + "pb188": 25.1, + "pb189": 39.0, + "pb189_m1": 50.0, + "pb190": 71.0, + "pb191": 79.8, + "pb191_m1": 130.8, + "pb192": 210.0, + "pb193": 120.0, + "pb193_m1": 348.0, + "pb194": 720.0, + "pb195": 900.0, + "pb195_m1": 900.0, + "pb196": 2220.0, + "pb197": 480.0, + "pb197_m1": 2580.0, + "pb198": 8640.0, + "pb199": 5400.0, + "pb199_m1": 732.0, + "pb200": 77400.0, + "pb201": 33588.0, + "pb201_m1": 60.8, + "pb202": 1656770000000.0, + "pb202_m1": 12744.0, + "pb203": 186912.0, + "pb203_m1": 6.21, + "pb203_m2": 0.48, + "pb204": 4.4e24, + "pb204_m1": 4015.8, + "pb205": 545946000000000.0, + "pb205_m1": 0.00555, + "pb207_m1": 0.806, + "pb209": 11710.8, + "pb210": 700578700.0, + "pb211": 2166.0, + "pb212": 38304.0, + "pb213": 612.0, + "pb214": 1608.0, + "pb215": 36.0, + "bi184": 0.013, + "bi184_m1": 0.0066, + "bi185": 5.8e-05, + "bi186": 0.015, + "bi186_m1": 0.0098, + "bi187": 0.032, + "bi187_m1": 0.00031, + "bi188": 0.06, + "bi188_m1": 0.265, + "bi189": 0.674, + "bi189_m1": 0.005, + "bi190": 6.3, + "bi190_m1": 6.2, + "bi191": 12.4, + "bi191_m1": 0.125, + "bi192": 34.6, + "bi192_m1": 39.6, + "bi193": 63.6, + "bi193_m1": 3.2, + "bi194": 95.0, + "bi194_m1": 125.0, + "bi194_m2": 115.0, + "bi195": 183.0, + "bi195_m1": 87.0, + "bi196": 308.0, + "bi196_m1": 0.6, + "bi196_m2": 240.0, + "bi197": 559.8, + "bi197_m1": 302.4, + "bi198": 618.0, + "bi198_m1": 696.0, + "bi198_m2": 7.7, + "bi199": 1620.0, + "bi199_m1": 1482.0, + "bi200": 2184.0, + "bi200_m1": 1860.0, + "bi200_m2": 0.4, + "bi201": 6180.0, + "bi201_m1": 3450.0, + "bi202": 6156.0, + "bi203": 42336.0, + "bi203_m1": 0.305, + "bi204": 40392.0, + "bi204_m1": 0.013, + "bi204_m2": 0.00107, + "bi205": 1322784.0, + "bi206": 539395.2, + "bi207": 995642300.0, + "bi208": 11613200000000.0, + "bi208_m1": 0.00258, + "bi209": 5.99594e26, + "bi210": 433036.8, + "bi210_m1": 95935100000000.0, + "bi211": 128.4, + "bi212": 3633.0, + "bi212_m1": 1500.0, + "bi212_m2": 420.0, + "bi213": 2735.4, + "bi214": 1194.0, + "bi215": 462.0, + "bi215_m1": 36.4, + "bi216": 135.0, + "bi217": 98.5, + "bi218": 33.0, + "po188": 0.000425, + "po189": 0.0035, + "po190": 0.00245, + "po191": 0.022, + "po191_m1": 0.093, + "po192": 0.0332, + "po193": 0.37, + "po193_m1": 0.373, + "po194": 0.392, + "po195": 4.64, + "po195_m1": 1.92, + "po196": 5.8, + "po197": 84.0, + "po197_m1": 32.0, + "po198": 106.2, + "po199": 328.2, + "po199_m1": 250.2, + "po200": 690.6, + "po201": 936.0, + "po201_m1": 537.6, + "po202": 2676.0, + "po203": 2202.0, + "po203_m1": 45.0, + "po204": 12708.0, + "po205": 6264.0, + "po205_m1": 0.000645, + "po205_m2": 0.0574, + "po206": 760320.0, + "po207": 20880.0, + "po207_m1": 2.79, + "po208": 91453920.0, + "po209": 3218880000.0, + "po210": 11955690.0, + "po211": 0.516, + "po211_m1": 25.2, + "po212": 2.99e-07, + "po212_m1": 45.1, + "po213": 4.2e-06, + "po214": 0.0001643, + "po215": 0.001781, + "po216": 0.145, + "po217": 1.53, + "po218": 185.88, + "po219": 3e-07, + "po220": 3e-07, + "at193": 0.0285, + "at194": 0.04, + "at194_m1": 0.25, + "at195": 0.328, + "at195_m1": 0.147, + "at196": 0.388, + "at196_m1": 1.1e-05, + "at197": 0.388, + "at197_m1": 2.0, + "at198": 4.2, + "at198_m1": 1.0, + "at199": 7.03, + "at200": 43.0, + "at200_m1": 47.0, + "at200_m2": 7.9, + "at201": 85.2, + "at202": 184.0, + "at202_m1": 182.0, + "at202_m2": 0.46, + "at203": 444.0, + "at204": 553.2, + "at204_m1": 0.108, + "at205": 1614.0, + "at206": 1836.0, + "at207": 6480.0, + "at208": 5868.0, + "at209": 19476.0, + "at210": 29160.0, + "at211": 25970.4, + "at212": 0.314, + "at212_m1": 0.119, + "at213": 1.25e-07, + "at214": 5.58e-07, + "at215": 0.0001, + "at216": 0.0003, + "at217": 0.0323, + "at218": 1.5, + "at219": 56.0, + "at220": 222.6, + "at221": 138.0, + "at222": 54.0, + "at223": 50.0, + "rn195": 0.0065, + "rn195_m1": 0.005, + "rn196": 0.0046, + "rn197": 0.066, + "rn197_m1": 0.021, + "rn198": 0.065, + "rn199": 0.59, + "rn199_m1": 0.31, + "rn200": 1.075, + "rn201": 7.0, + "rn201_m1": 3.8, + "rn202": 9.7, + "rn203": 44.0, + "rn203_m1": 26.9, + "rn204": 70.2, + "rn205": 170.0, + "rn206": 340.2, + "rn207": 555.0, + "rn208": 1461.0, + "rn209": 1728.0, + "rn210": 8640.0, + "rn211": 52560.0, + "rn212": 1434.0, + "rn213": 0.025, + "rn214": 2.7e-07, + "rn215": 2.3e-06, + "rn216": 4.5e-05, + "rn217": 0.00054, + "rn218": 0.035, + "rn219": 3.96, + "rn220": 55.6, + "rn221": 1542.0, + "rn222": 330350.4, + "rn223": 1458.0, + "rn224": 6420.0, + "rn225": 279.6, + "rn226": 444.0, + "rn227": 20.8, + "rn228": 65.0, + "fr199": 0.015, + "fr200": 0.049, + "fr201": 0.069, + "fr202": 0.3, + "fr202_m1": 0.29, + "fr203": 0.55, + "fr204": 1.7, + "fr204_m1": 2.6, + "fr204_m2": 1.0, + "fr205": 3.8, + "fr206": 16.0, + "fr206_m1": 16.0, + "fr206_m2": 0.7, + "fr207": 14.8, + "fr208": 59.1, + "fr209": 50.0, + "fr210": 190.8, + "fr211": 186.0, + "fr212": 1200.0, + "fr213": 34.82, + "fr214": 0.005, + "fr214_m1": 0.00335, + "fr215": 8.6e-08, + "fr216": 7e-07, + "fr217": 1.9e-05, + "fr218": 0.001, + "fr218_m1": 0.022, + "fr219": 0.02, + "fr220": 27.4, + "fr221": 294.0, + "fr222": 852.0, + "fr223": 1320.0, + "fr224": 199.8, + "fr225": 237.0, + "fr226": 49.0, + "fr227": 148.2, + "fr228": 38.0, + "fr229": 50.2, + "fr230": 19.1, + "fr231": 17.6, + "fr232": 5.5, + "ra202": 0.0275, + "ra203": 0.031, + "ra203_m1": 0.025, + "ra204": 0.0605, + "ra205": 0.22, + "ra205_m1": 0.18, + "ra206": 0.24, + "ra207": 1.3, + "ra207_m1": 0.055, + "ra208": 1.3, + "ra209": 4.6, + "ra210": 3.7, + "ra211": 13.0, + "ra212": 13.0, + "ra213": 163.8, + "ra213_m1": 0.0021, + "ra214": 2.46, + "ra215": 0.00155, + "ra216": 1.82e-07, + "ra217": 1.6e-06, + "ra218": 2.52e-05, + "ra219": 0.01, + "ra220": 0.018, + "ra221": 28.0, + "ra222": 36.17, + "ra223": 987552.0, + "ra224": 316224.0, + "ra225": 1287360.0, + "ra226": 50492200000.0, + "ra227": 2532.0, + "ra228": 181456200.0, + "ra229": 240.0, + "ra230": 5580.0, + "ra231": 103.0, + "ra232": 252.0, + "ra233": 30.0, + "ra234": 30.0, + "ac206": 0.024, + "ac206_m1": 0.0395, + "ac207": 0.027, + "ac208": 0.095, + "ac208_m1": 0.025, + "ac209": 0.098, + "ac210": 0.35, + "ac211": 0.21, + "ac212": 0.93, + "ac213": 0.8, + "ac214": 8.2, + "ac215": 0.17, + "ac216": 0.00044, + "ac216_m1": 0.000441, + "ac217": 6.9e-08, + "ac218": 1.08e-06, + "ac219": 1.18e-05, + "ac220": 0.0264, + "ac221": 0.052, + "ac222": 5.0, + "ac222_m1": 63.0, + "ac223": 126.0, + "ac224": 10008.0, + "ac225": 864000.0, + "ac226": 105732.0, + "ac227": 687072100.0, + "ac228": 22140.0, + "ac229": 3762.0, + "ac230": 122.0, + "ac231": 450.0, + "ac232": 119.0, + "ac233": 145.0, + "ac234": 44.0, + "ac235": 60.0, + "ac236": 120.0, + "th209": 0.0065, + "th210": 0.016, + "th211": 0.05, + "th212": 0.035, + "th213": 0.14, + "th214": 0.1, + "th215": 1.2, + "th216": 0.026, + "th217": 0.000251, + "th218": 1.17e-07, + "th219": 1.05e-06, + "th220": 9.7e-06, + "th221": 0.00173, + "th222": 0.002237, + "th223": 0.6, + "th224": 1.05, + "th225": 523.2, + "th226": 1834.2, + "th227": 1613952.0, + "th228": 60338130.0, + "th229": 231633000000.0, + "th230": 2378810000000.0, + "th231": 91872.0, + "th232": 4.43384e17, + "th233": 1338.0, + "th234": 2082240.0, + "th235": 426.0, + "th236": 2238.0, + "th237": 282.0, + "th238": 564.0, + "pa212": 0.0051, + "pa213": 0.0053, + "pa214": 0.017, + "pa215": 0.014, + "pa216": 0.16, + "pa217": 0.0036, + "pa217_m1": 0.0012, + "pa218": 0.000113, + "pa219": 5.3e-08, + "pa220": 7.8e-07, + "pa221": 5.9e-06, + "pa222": 0.0033, + "pa223": 0.0051, + "pa224": 0.79, + "pa225": 1.7, + "pa226": 108.0, + "pa227": 2298.0, + "pa228": 79200.0, + "pa229": 129600.0, + "pa230": 1503360.0, + "pa231": 1033830000000.0, + "pa232": 114048.0, + "pa233": 2330640.0, + "pa234": 24120.0, + "pa234_m1": 69.54, + "pa235": 1466.4, + "pa236": 546.0, + "pa237": 522.0, + "pa238": 136.2, + "pa239": 6480.0, + "pa240": 120.0, + "u217": 0.0235, + "u218": 0.000545, + "u219": 4.2e-05, + "u220": 6e-08, + "u222": 1.3e-06, + "u223": 1.8e-05, + "u224": 0.0009, + "u225": 0.084, + "u226": 0.35, + "u227": 66.0, + "u228": 546.0, + "u229": 3480.0, + "u230": 1797120.0, + "u231": 362880.0, + "u232": 2174319000.0, + "u233": 5023970000000.0, + "u234": 7747390000000.0, + "u235": 2.22102e16, + "u235_m1": 1560.0, + "u236": 739079000000000.0, + "u237": 583200.0, + "u238": 1.40999e17, + "u239": 1407.0, + "u240": 50760.0, + "u241": 300.0, + "u242": 1008.0, + "np225": 2e-06, + "np226": 0.035, + "np227": 0.51, + "np228": 61.4, + "np229": 240.0, + "np230": 276.0, + "np231": 2928.0, + "np232": 882.0, + "np233": 2172.0, + "np234": 380160.0, + "np235": 34231680.0, + "np236": 4828310000000.0, + "np236_m1": 81000.0, + "np237": 67659500000000.0, + "np238": 182908.8, + "np239": 203558.4, + "np240": 3714.0, + "np240_m1": 433.2, + "np241": 834.0, + "np242": 132.0, + "np242_m1": 330.0, + "np243": 111.0, + "np244": 137.4, + "pu228": 1.85, + "pu229": 112.0, + "pu230": 102.0, + "pu231": 516.0, + "pu232": 2028.0, + "pu233": 1254.0, + "pu234": 31680.0, + "pu235": 1518.0, + "pu236": 90191620.0, + "pu237": 3943296.0, + "pu237_m1": 0.18, + "pu238": 2767602000.0, + "pu239": 760854000000.0, + "pu240": 207049000000.0, + "pu241": 450958100.0, + "pu242": 11786800000000.0, + "pu243": 17841.6, + "pu244": 2559320000000000.0, + "pu245": 37800.0, + "pu246": 936576.0, + "pu247": 196128.0, + "am231": 10.0, + "am232": 79.0, + "am233": 192.0, + "am234": 139.2, + "am235": 618.0, + "am236": 216.0, + "am237": 4416.0, + "am238": 5880.0, + "am239": 42840.0, + "am240": 182880.0, + "am241": 13651800000.0, + "am242": 57672.0, + "am242_m1": 4449622000.0, + "am242_m2": 0.014, + "am243": 232580000000.0, + "am244": 36360.0, + "am244_m1": 1560.0, + "am245": 7380.0, + "am246": 2340.0, + "am246_m1": 1500.0, + "am247": 1380.0, + "am248": 600.0, + "am249": 120.0, + "cm233": 17.7, + "cm234": 51.0, + "cm235": 300.0, + "cm236": 1900.0, + "cm237": 1200.0, + "cm238": 8640.0, + "cm239": 10440.0, + "cm240": 2332800.0, + "cm241": 2833920.0, + "cm242": 14078020.0, + "cm243": 918326200.0, + "cm244": 571508100.0, + "cm244_m1": 5e-07, + "cm245": 268240000000.0, + "cm246": 150214000000.0, + "cm247": 492299000000000.0, + "cm248": 10982000000000.0, + "cm249": 3849.0, + "cm250": 261928000000.0, + "cm251": 1008.0, + "bk235": 20.0, + "bk237": 60.0, + "bk238": 144.0, + "bk240": 288.0, + "bk241": 276.0, + "bk242": 420.0, + "bk243": 16200.0, + "bk244": 15660.0, + "bk245": 426816.0, + "bk246": 155520.0, + "bk247": 43549500000.0, + "bk248": 283824000.0, + "bk248_m1": 85320.0, + "bk249": 27648000.0, + "bk250": 11563.2, + "bk251": 3336.0, + "bk253": 600.0, + "bk254": 120.0, + "cf237": 2.1, + "cf238": 0.021, + "cf239": 51.5, + "cf240": 57.6, + "cf241": 226.8, + "cf242": 222.0, + "cf243": 642.0, + "cf244": 1164.0, + "cf245": 2700.0, + "cf246": 128520.0, + "cf247": 11196.0, + "cf248": 28814400.0, + "cf249": 11076700000.0, + "cf250": 412773400.0, + "cf251": 28338700000.0, + "cf252": 83468070.0, + "cf253": 1538784.0, + "cf254": 5227200.0, + "cf255": 5100.0, + "cf256": 738.0, + "es240": 1.0, + "es241": 8.5, + "es242": 13.5, + "es243": 21.0, + "es244": 37.0, + "es245": 66.0, + "es246": 462.0, + "es247": 273.0, + "es247_m1": 54000000.0, + "es248": 1620.0, + "es249": 6132.0, + "es250": 30960.0, + "es250_m1": 7992.0, + "es251": 118800.0, + "es252": 40754880.0, + "es253": 1768608.0, + "es254": 23820480.0, + "es254_m1": 141479.9, + "es255": 3438720.0, + "es256": 1524.0, + "es256_m1": 27360.0, + "es257": 665280.0, + "es258": 180.0, + "fm242": 0.0008, + "fm243": 0.2, + "fm244": 0.0033, + "fm245": 4.2, + "fm246": 1.1, + "fm247": 29.0, + "fm248": 36.0, + "fm249": 156.0, + "fm250": 1800.0, + "fm250_m1": 1.8, + "fm251": 19080.0, + "fm252": 91404.0, + "fm253": 259200.0, + "fm254": 11664.0, + "fm255": 72252.0, + "fm256": 9456.0, + "fm257": 8683200.0, + "fm258": 0.00037, + "fm259": 1.5, + "fm260": 0.004, + "md245": 0.0009, + "md245_m1": 0.39, + "md246": 0.9, + "md247": 1.12, + "md247_m1": 0.26, + "md248": 7.0, + "md249": 24.0, + "md249_m1": 1.9, + "md250": 27.5, + "md251": 240.0, + "md252": 138.0, + "md253": 630.0, + "md254": 1680.0, + "md254_m1": 1680.0, + "md255": 1620.0, + "md256": 4620.0, + "md257": 19872.0, + "md258": 4449600.0, + "md258_m1": 3420.0, + "md259": 5760.0, + "md260": 2747520.0, + "md261": 2400.0, + "no250": 4.35e-06, + "no251": 0.8, + "no251_m1": 1.02, + "no252": 2.44, + "no253": 97.2, + "no254": 51.0, + "no254_m1": 0.28, + "no255": 186.0, + "no256": 2.91, + "no257": 25.0, + "no258": 0.0012, + "no259": 3480.0, + "no260": 0.106, + "no261": 160000.0, + "no262": 0.005, + "lr251": 1.341, + "lr252": 0.38, + "lr253": 0.575, + "lr253_m1": 1.535, + "lr254": 13.0, + "lr255": 22.0, + "lr255_m1": 2.53, + "lr256": 27.0, + "lr257": 0.646, + "lr258": 4.1, + "lr259": 6.2, + "lr260": 180.0, + "lr261": 2340.0, + "lr262": 14400.0, + "lr263": 18000.0, + "rf253": 5.15e-05, + "rf254": 2.3e-05, + "rf255": 1.68, + "rf256": 0.0064, + "rf257": 4.7, + "rf257_m1": 3.9, + "rf258": 0.012, + "rf259": 3.2, + "rf260": 0.021, + "rf261": 65.0, + "rf261_m1": 81.0, + "rf262": 2.3, + "rf263": 600.0, + "rf264": 3600.0, + "rf265": 1.0, + "db255": 1.7, + "db256": 1.7, + "db257": 1.52, + "db257_m1": 0.78, + "db258": 4.0, + "db258_m1": 20.0, + "db259": 0.51, + "db260": 1.52, + "db261": 1.8, + "db262": 35.0, + "db263": 28.5, + "db264": 180.0, + "db265": 900.0, + "sg258": 0.0032, + "sg259": 0.555, + "sg260": 0.0036, + "sg261": 0.23, + "sg262": 0.0079, + "sg263": 1.0, + "sg263_m1": 0.12, + "sg264": 0.045, + "sg265": 8.0, + "sg266": 25.0, + "sg269": 50.0, + "bh260": 0.0003, + "bh261": 0.013, + "bh262": 0.102, + "bh262_m1": 0.022, + "bh263": 0.0002, + "bh264": 0.66, + "bh265": 1.1, + "bh266": 5.4, + "bh267": 21.0, + "bh269": 50.0, + "hs263": 0.00355, + "hs264": 0.0008, + "hs265": 0.00205, + "hs265_m1": 0.0003, + "hs266": 0.00265, + "hs267": 0.0545, + "hs268": 1.2, + "hs269": 12.9, + "hs273": 50.0, + "mt265": 120.0, + "mt266": 0.0018, + "mt266_m1": 0.0017, + "mt267": 0.01, + "mt268": 0.0225, + "mt269": 0.05, + "mt270": 0.00605, + "mt271": 5.0, + "mt273": 20.0, + "ds267": 2.8e-06, + "ds268": 0.0001, + "ds269": 0.000268, + "ds270": 0.00015, + "ds270_m1": 0.009, + "ds271": 0.001705, + "ds271_m1": 0.0865, + "ds272": 1.0, + "ds273": 0.000225, + "ds279_m1": 0.19, + "rg272": 0.0041 +} diff --git a/openmc/data/kalbach_mann.py b/openmc/data/kalbach_mann.py index f4c914d909..d92bf9c213 100644 --- a/openmc/data/kalbach_mann.py +++ b/openmc/data/kalbach_mann.py @@ -5,6 +5,7 @@ from warnings import warn import numpy as np import openmc.checkvalue as cv +from openmc.mixin import EqualityMixin from openmc.stats import Tabular, Univariate, Discrete, Mixture from .function import Tabulated1D, INTERPOLATION_SCHEME from .angle_energy import AngleEnergy @@ -12,6 +13,242 @@ from .data import EV_PER_MEV from .endf import get_list_record, get_tab2_record +class _AtomicRepresentation(EqualityMixin): + """Atomic representation of an isotope or a particle. + + Parameters + ---------- + z : int + Number of protons (atomic number) + a : int + Number of nucleons (mass number) + + Raises + ------ + ValueError + When the number of protons (z) declared is higher than the number + of nucleons (a) + + Attributes + ---------- + z : int + Number of protons (atomic number) + a : int + Number of nucleons (mass number) + n : int + Number of neutrons + za : int + ZA identifier, 1000*Z + A, where Z is the atomic number and A the mass + number + + """ + def __init__(self, z, a): + # Sanity checks on values + cv.check_type('z', z, Integral) + cv.check_greater_than('z', z, 0, equality=True) + cv.check_type('a', a, Integral) + cv.check_greater_than('a', a, 0, equality=True) + if z > a: + raise ValueError(f"Number of protons ({z}) must be less than or " + f"equal to number of nucleons ({a}).") + + self._z = z + self._a = a + + def __add__(self, other): + """Add two _AtomicRepresentations""" + z = self.z + other.z + a = self.a + other.a + return _AtomicRepresentation(z=z, a=a) + + def __sub__(self, other): + """Substract two _AtomicRepresentations""" + z = self.z - other.z + a = self.a - other.a + return _AtomicRepresentation(z=z, a=a) + + @property + def a(self): + return self._a + + @property + def z(self): + return self._z + + @property + def n(self): + return self.a - self.z + + @property + def za(self): + return self.z * 1000 + self.a + + @classmethod + def from_za(cls, za): + """Instantiate an _AtomicRepresentation from a ZA identifier. + + Parameters + ---------- + za : int + ZA identifier, 1000*Z + A, where Z is the atomic number and A the + mass number + + Returns + ------- + _AtomicRepresentation + Atomic representation of the isotope/particle + + """ + z, a = divmod(za, 1000) + return cls(z, a) + + +def _separation_energy(compound, nucleus, particle): + """Calculates the separation energy as defined in ENDF-6 manual + BNL-203218-2018-INRE, Revision 215, File 6 description for LAW=1 + and LANG=2. This function can be used for the incident or emitted + particle of the following reaction: A + a -> C -> B + b + + Parameters + ---------- + compound : _AtomicRepresentation + Atomic representation of the compound (C) + nucleus : _AtomicRepresentation + Atomic representation of the nucleus (A or B) + particle : _AtomicRepresentation + Atomic representation of the particle (a or b) + + Returns + ------- + separation_energy : float + Separation energy in MeV + + """ + # Determine A, Z, and N for compound and nucleus + A_c = compound.a + Z_c = compound.z + N_c = compound.n + A_a = nucleus.a + Z_a = nucleus.z + N_a = nucleus.n + + # Determine breakup energy of incident particle (ENDF-6 Formats Manual, + # Appendix H, Table 3) in MeV + za_to_breaking_energy = { + 1: 0.0, + 1001: 0.0, + 1002: 2.224566, + 1003: 8.481798, + 2003: 7.718043, + 2004: 28.29566 + } + I_a = za_to_breaking_energy[particle.za] + + # Eq. 4 in in doi:10.1103/PhysRevC.37.2350 or ENDF-6 Formats Manual section + # 6.2.3.2 + return ( + 15.68 * (A_c - A_a) - + 28.07 * ((N_c - Z_c)**2 / A_c - (N_a - Z_a)**2 / A_a) - + 18.56 * (A_c**(2./3.) - A_a**(2./3.)) + + 33.22 * ((N_c - Z_c)**2 / A_c**(4./3.) - (N_a - Z_a)**2 / A_a**(4./3.)) - + 0.717 * (Z_c**2 / A_c**(1./3.) - Z_a**2 / A_a**(1./3.)) + + 1.211 * (Z_c**2 / A_c - Z_a**2 / A_a) - + I_a + ) + + +def kalbach_slope(energy_projectile, energy_emitted, za_projectile, + za_emitted, za_target): + """Returns Kalbach-Mann slope from calculations. + + The associated reaction is defined as: + A + a -> C -> B + b + + Where: + + - A is the targeted nucleus, + - a is the projectile, + - C is the compound, + - B is the residual nucleus, + - b is the emitted particle. + + The Kalbach-Mann slope calculation is done as defined in ENDF-6 manual + BNL-203218-2018-INRE, Revision 215, File 6 description for LAW=1 and + LANG=2. One exception to this, is that the entrance and emission channel + energies are not calculated with the AWR number, but approximated with + the number of mass instead. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + energy_projectile : float + Energy of the projectile in the laboratory system in eV + energy_emitted : float + Energy of the emitted particle in the center of mass system in eV + za_projectile : int + ZA identifier of the projectile + za_emitted : int + ZA identifier of the emitted particle + za_target : int + ZA identifier of the targeted nucleus + + Raises + ------ + NotImplementedError + When the projectile is not a neutron + + Returns + ------- + slope : float + Kalbach-Mann slope given with the same format as ACE file. + + """ + # TODO: develop for photons as projectile + # TODO: test for other particles than neutron + if za_projectile != 1: + raise NotImplementedError( + "Developed and tested for neutron projectile only." + ) + + # Special handling of elemental carbon + if za_emitted == 6000: + za_emitted = 6012 + if za_target == 6000: + za_target = 6012 + + projectile = _AtomicRepresentation.from_za(za_projectile) + emitted = _AtomicRepresentation.from_za(za_emitted) + target = _AtomicRepresentation.from_za(za_target) + compound = projectile + target + residual = compound - emitted + + # Calculate entrance and emission channel energy in MeV, defined in section + # 6.2.3.2 in the ENDF-6 Formats Manual + epsilon_a = energy_projectile * target.a / (target.a + projectile.a) / EV_PER_MEV + epsilon_b = energy_emitted * (residual.a + emitted.a) \ + / (residual.a * EV_PER_MEV) + + # Calculate separation energies using Eq. 4 in doi:10.1103/PhysRevC.37.2350 + # or ENDF-6 Formats Manual section 6.2.3.2 + s_a = _separation_energy(compound, target, projectile) + s_b = _separation_energy(compound, residual, emitted) + + # See Eq. 10 in doi:10.1103/PhysRevC.37.2350 or section 6.2.3.2 in the + # ENDF-6 Formats Manual + za_to_M = {1: 1.0, 1001: 1.0, 1002: 1.0, 2004: 0.0} + za_to_m = {1: 0.5, 1001: 1.0, 1002: 1.0, 1003: 1.0, 2003: 1.0, 2004: 2.0} + M = za_to_M[projectile.za] + m = za_to_m[emitted.za] + e_a = epsilon_a + s_a + e_b = epsilon_b + s_b + r_1 = min(e_a, 130.) + r_3 = min(e_a, 41.) + x_1 = r_1 * e_b / e_a + x_3 = r_3 * e_b / e_a + return 0.04 * x_1 + 1.8e-6 * x_1**3 + 6.7e-7 * M * m * x_3**4 + + class KalbachMann(AngleEnergy): """Kalbach-Mann distribution @@ -65,56 +302,56 @@ class KalbachMann(AngleEnergy): def breakpoints(self): return self._breakpoints - @property - def interpolation(self): - return self._interpolation - - @property - def energy(self): - return self._energy - - @property - def energy_out(self): - return self._energy_out - - @property - def precompound(self): - return self._precompound - - @property - def slope(self): - return self._slope - @breakpoints.setter def breakpoints(self, breakpoints): cv.check_type('Kalbach-Mann breakpoints', breakpoints, Iterable, Integral) self._breakpoints = breakpoints + @property + def interpolation(self): + return self._interpolation + @interpolation.setter def interpolation(self, interpolation): cv.check_type('Kalbach-Mann interpolation', interpolation, Iterable, Integral) self._interpolation = interpolation + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('Kalbach-Mann incoming energy', energy, Iterable, Real) self._energy = energy + @property + def energy_out(self): + return self._energy_out + @energy_out.setter def energy_out(self, energy_out): cv.check_type('Kalbach-Mann distributions', energy_out, Iterable, Univariate) self._energy_out = energy_out + @property + def precompound(self): + return self._precompound + @precompound.setter def precompound(self, precompound): cv.check_type('Kalbach-Mann precompound factor', precompound, Iterable, Tabulated1D) self._precompound = precompound + @property + def slope(self): + return self._slope + @slope.setter def slope(self, slope): cv.check_type('Kalbach-Mann slope', slope, Iterable, Tabulated1D) @@ -129,7 +366,7 @@ class KalbachMann(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('kalbach-mann') + group.attrs['type'] = np.bytes_('kalbach-mann') dset = group.create_dataset('energy', data=self.energy) dset.attrs['interpolation'] = np.vstack((self.breakpoints, @@ -319,7 +556,7 @@ class KalbachMann(AngleEnergy): n_energy_out = int(ace.xss[idx + 1]) data = ace.xss[idx + 2:idx + 2 + 5*n_energy_out].copy() data.shape = (5, n_energy_out) - data[0,:] *= EV_PER_MEV + data[0, :] *= EV_PER_MEV # Create continuous distribution eout_continuous = Tabular(data[0][n_discrete_lines:], @@ -352,13 +589,31 @@ class KalbachMann(AngleEnergy): return cls(breakpoints, interpolation, energy, energy_out, km_r, km_a) @classmethod - def from_endf(cls, file_obj): - """Generate Kalbach-Mann distribution from an ENDF evaluation + def from_endf(cls, file_obj, za_emitted, za_target, projectile_mass): + """Generate Kalbach-Mann distribution from an ENDF evaluation. + + If the projectile is a neutron, the slope is calculated when it is + not given explicitly. + + .. versionchanged:: 0.13.1 + Arguments changed to accommodate slope calculation Parameters ---------- file_obj : file-like object ENDF file positioned at the start of the Kalbach-Mann distribution + za_emitted : int + ZA identifier of the emitted particle + za_target : int + ZA identifier of the target + projectile_mass : float + Mass of the projectile + + Warns + ----- + UserWarning + If the mass of the projectile is not equal to 1 (other than + a neutron), the slope is not calculated and set to 0 if missing. Returns ------- @@ -374,6 +629,7 @@ class KalbachMann(AngleEnergy): energy_out = [] precompound = [] slope = [] + calculated_slope = [] for i in range(ne): items, values = get_list_record(file_obj) energy[i] = items[1] @@ -385,19 +641,46 @@ class KalbachMann(AngleEnergy): values.shape = (n_energy_out, n_angle + 2) # Outgoing energy distribution at the i-th incoming energy - eout_i = values[:,0] - eout_p_i = values[:,1] + eout_i = values[:, 0] + eout_p_i = values[:, 1] energy_out_i = Tabular(eout_i, eout_p_i, INTERPOLATION_SCHEME[lep]) energy_out.append(energy_out_i) - # Precompound and slope factors for Kalbach-Mann - r_i = values[:,2] + # Precompound factors for Kalbach-Mann + r_i = values[:, 2] + + # Slope factors for Kalbach-Mann if n_angle == 2: - a_i = values[:,3] + a_i = values[:, 3] + calculated_slope.append(False) else: - a_i = np.zeros_like(r_i) + # Check if the projectile is not a neutron + if not np.isclose(projectile_mass, 1.0, atol=1.0e-12, rtol=0.): + warn( + "Kalbach-Mann slope calculation is only available with " + "neutrons as projectile. Slope coefficients are set to 0." + ) + a_i = np.zeros_like(r_i) + calculated_slope.append(False) + + else: + # TODO: retrieve ZA of the projectile + za_projectile = 1 + a_i = [kalbach_slope(energy_projectile=energy[i], + energy_emitted=e, + za_projectile=za_projectile, + za_emitted=za_emitted, + za_target=za_target) + for e in eout_i] + calculated_slope.append(True) + precompound.append(Tabulated1D(eout_i, r_i)) slope.append(Tabulated1D(eout_i, a_i)) - return cls(tab2.breakpoints, tab2.interpolation, energy, - energy_out, precompound, slope) + km_distribution = cls(tab2.breakpoints, tab2.interpolation, energy, + energy_out, precompound, slope) + + # List of bool to indicate slope calculation by OpenMC + km_distribution._calculated_slope = calculated_slope + + return km_distribution diff --git a/openmc/data/laboratory.py b/openmc/data/laboratory.py index 87d9d29618..c20b459684 100644 --- a/openmc/data/laboratory.py +++ b/openmc/data/laboratory.py @@ -54,45 +54,46 @@ class LaboratoryAngleEnergy(AngleEnergy): def breakpoints(self): return self._breakpoints - @property - def interpolation(self): - return self._interpolation - @property - def energy(self): - return self._energy - - @property - def mu(self): - return self._mu - - @property - def energy_out(self): - return self._energy_out - @breakpoints.setter def breakpoints(self, breakpoints): cv.check_type('laboratory angle-energy breakpoints', breakpoints, Iterable, Integral) self._breakpoints = breakpoints + @property + def interpolation(self): + return self._interpolation + @interpolation.setter def interpolation(self, interpolation): cv.check_type('laboratory angle-energy interpolation', interpolation, Iterable, Integral) self._interpolation = interpolation + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('laboratory angle-energy incoming energy', energy, Iterable, Real) self._energy = energy + @property + def mu(self): + return self._mu + @mu.setter def mu(self, mu): cv.check_type('laboratory angle-energy outgoing cosine', mu, Iterable, Univariate) self._mu = mu + @property + def energy_out(self): + return self._energy_out + @energy_out.setter def energy_out(self, energy_out): cv.check_iterable_type('laboratory angle-energy outgoing energy', diff --git a/openmc/data/library.py b/openmc/data/library.py index bf937e57ad..b49757b0dc 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -1,27 +1,33 @@ import os -import xml.etree.ElementTree as ET import pathlib import h5py +import lxml.etree as ET -from openmc.mixin import EqualityMixin -from openmc._xml import clean_indentation, reorder_attributes +import openmc +from openmc._xml import get_elem_list, get_text, clean_indentation -class DataLibrary(EqualityMixin): +class DataLibrary(list): """Collection of cross section data libraries. - Attributes - ---------- - libraries : list of dict - List in which each item is a dictionary summarizing cross section data - from a single file. The dictionary has keys 'path', 'type', and - 'materials'. + This class behaves like a list where each item is a dictionary summarizing + cross section data from a single file. The dictionary has keys 'path', + 'type', and 'materials'. + + .. versionchanged:: 0.14.0 + This class now behaves like a list rather than requiring you to access + the list of libraries through a special attribute. """ def __init__(self): - self.libraries = [] + super().__init__() + + @property + def libraries(self): + # For backwards compatibility + return self def get_by_material(self, name, data_type='neutron'): """Return the library dictionary containing a given material. @@ -42,11 +48,26 @@ class DataLibrary(EqualityMixin): the dictionary has keys 'path', 'type', and 'materials'. """ - for library in self.libraries: + for library in self: if name in library['materials'] and data_type in library['type']: return library return None + def remove_by_material(self, name: str, data_type='neutron'): + """Remove the library dictionary containing a specific material + + Parameters + ---------- + name : str + Name of material, e.g. 'Am241' + data_type : str + Name of data type, e.g. 'neutron', 'photon', 'wmp', or 'thermal' + + """ + library = self.get_by_material(name, data_type) + if library is not None: + self.remove(library) + def register_file(self, filename): """Register a file with the data library. @@ -72,11 +93,10 @@ class DataLibrary(EqualityMixin): materials = list(h5file) else: raise ValueError( - "File type {} not supported by {}" - .format(path.name, self.__class__.__name__)) + f"File type {path.name} not supported by {self.__class__.__name__}") library = {'path': str(path), 'type': filetype, 'materials': materials} - self.libraries.append(library) + self.append(library) def export_to_xml(self, path='cross_sections.xml'): """Export cross section data library to an XML file. @@ -91,7 +111,7 @@ class DataLibrary(EqualityMixin): # Determine common directory for library paths common_dir = os.path.dirname(os.path.commonprefix( - [lib['path'] for lib in self.libraries])) + [lib['path'] for lib in self])) if common_dir == '': common_dir = '.' @@ -99,7 +119,7 @@ class DataLibrary(EqualityMixin): dir_element = ET.SubElement(root, "directory") dir_element.text = os.path.realpath(common_dir) - for library in self.libraries: + for library in self: if library['type'] == "depletion_chain": lib_element = ET.SubElement(root, "depletion_chain") else: @@ -112,7 +132,6 @@ class DataLibrary(EqualityMixin): clean_indentation(root) # Write XML file - reorder_attributes(root) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root) tree.write(str(path), xml_declaration=True, encoding='utf-8', method='xml') @@ -124,8 +143,8 @@ class DataLibrary(EqualityMixin): Parameters ---------- path : str, optional - Path to XML file to read. If not provided, the - :envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used. + Path to XML file to read. If not provided, + openmc.config['cross_sections'] will be used. Returns ------- @@ -136,15 +155,14 @@ class DataLibrary(EqualityMixin): data = cls() - # If path is None, get the cross sections from the - # OPENMC_CROSS_SECTIONS environment variable + # If path is None, get the cross sections from the global configuration if path is None: - path = os.environ.get('OPENMC_CROSS_SECTIONS') + path = openmc.config.get('cross_sections') - # Check to make sure there was an environmental variable. + # Check to make sure we picked up cross sections if path is None: - raise ValueError("Either path or OPENMC_CROSS_SECTIONS " - "environmental variable must be set") + raise ValueError("Either path or openmc.config['cross_sections'] " + "must be set") tree = ET.parse(path) root = tree.getroot() @@ -154,18 +172,17 @@ class DataLibrary(EqualityMixin): directory = os.path.dirname(path) for lib_element in root.findall('library'): - filename = os.path.join(directory, lib_element.attrib['path']) - filetype = lib_element.attrib['type'] - materials = lib_element.attrib['materials'].split() + filename = os.path.join(directory, get_text(lib_element, "path")) + filetype = get_text(lib_element, "type") + materials = get_elem_list(lib_element, "materials", str) or [] library = {'path': filename, 'type': filetype, 'materials': materials} data.libraries.append(library) # get depletion chain data - dep_node = root.find("depletion_chain") if dep_node is not None: - filename = os.path.join(directory, dep_node.attrib['path']) + filename = os.path.join(directory, get_text(dep_node, "path")) library = {'path': filename, 'type': 'depletion_chain', 'materials': []} data.libraries.append(library) diff --git a/openmc/data/mass16.txt b/openmc/data/mass16.txt deleted file mode 100644 index 4b479be459..0000000000 --- a/openmc/data/mass16.txt +++ /dev/null @@ -1,3475 +0,0 @@ -1 a0boogfu A T O M I C M A S S A D J U S T M E N T -0 DATE 1 Mar 2017 TIME 17:26 -0 ********************* A= 0 TO 295 - * file : mass16.txt * - ********************* - - This is one file out of a series of 3 files published in: - "The Ame2016 atomic mass evaluation (I)" by W.J.Huang, G.Audi, M.Wang, F.G.Kondev, S.Naimi and X.Xu - Chinese Physics C41 030002, March 2017. - "The Ame2016 atomic mass evaluation (II)" by M.Wang, G.Audi, F.G.Kondev, W.J.Huang, S.Naimi and X.Xu - Chinese Physics C41 030003, March 2017. - for files : mass16.txt : atomic masses - rct1-16.txt : react and sep energies, part 1 - rct2-16.txt : react and sep energies, part 2 - A fourth file is the "Rounded" version of the atomic mass table (the first file) - mass16round.txt : atomic masses "Rounded" version - - All files are 3436 lines long with 124 character per line. - Headers are 39 lines long. - Values in files 1, 2 and 3 are unrounded copy of the published ones - Values in file 4 are exact copy of the published ones - - col 1 : Fortran character control: 1 = page feed 0 = line feed - format : a1,i3,i5,i5,i5,1x,a3,a4,1x,f13.5,f11.5,f11.3,f9.3,1x,a2,f11.3,f9.3,1x,i3,1x,f12.5,f11.5 - cc NZ N Z A el o mass unc binding unc B beta unc atomic_mass unc - Warnings : this format is identical to the ones used in Ame2003 and Ame2012 - in particular "Mass Excess" and "Atomic Mass" values are given now, when necessary, - with 5 digits after decimal point. - decimal point is replaced by # for (non-experimental) estimated values. - * in place of value : not calculable - -....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+...10....+...11....+...12.... - - - MASS LIST - for analysis - -1N-Z N Z A EL O MASS EXCESS BINDING ENERGY/A BETA-DECAY ENERGY ATOMIC MASS - (keV) (keV) (keV) (micro-u) -0 1 1 0 1 n 8071.31713 0.00046 0.0 0.0 B- 782.347 0.000 1 008664.91582 0.00049 - -1 0 1 1 H 7288.97061 0.00009 0.0 0.0 B- * 1 007825.03224 0.00009 -0 0 1 1 2 H 13135.72176 0.00011 1112.283 0.000 B- * 2 014101.77811 0.00012 -0 1 2 1 3 H 14949.80993 0.00022 2827.265 0.000 B- 18.592 0.000 3 016049.28199 0.00023 - -1 1 2 3 He 14931.21793 0.00021 2572.680 0.000 B- -13736# 2000# 3 016029.32265 0.00022 - -3 0 3 3 Li -pp 28667# 2000# -2267# 667# B- * 3 030775# 2147# -0 2 3 1 4 H -n 24621.127 100.000 1720.449 25.000 B- 22196.211 100.000 4 026431.868 107.354 - 0 2 2 4 He 2424.91561 0.00006 7073.915 0.000 B- -22898.273 212.132 4 002603.25413 0.00006 - -2 1 3 4 Li -p 25323.189 212.132 1153.760 53.033 B- * 4 027185.562 227.733 -0 3 4 1 5 H -nn 32892.444 89.443 1336.359 17.889 B- 21661.211 91.652 5 035311.493 96.020 - 1 3 2 5 He -n 11231.233 20.000 5512.132 4.000 B- -447.654 53.852 5 012057.224 21.470 - -1 2 3 5 Li -p 11678.886 50.000 5266.132 10.000 B- -25460# 2003# 5 012537.800 53.677 - -3 1 4 5 Be x 37139# 2003# 18# 401# B- * 5 039870# 2150# -0 4 5 1 6 H -3n 41875.721 254.127 961.639 42.354 B- 24283.626 254.127 6 044955.437 272.816 - 2 4 2 6 He 17592.095 0.053 4878.519 0.009 B- 3505.216 0.053 6 018885.891 0.057 - 0 3 3 6 Li 14086.87895 0.00144 5332.331 0.000 B- -4288.154 5.448 6 015122.88742 0.00155 - -2 2 4 6 Be - 18375.033 5.448 4487.247 0.908 B- -28945# 2003# 6 019726.409 5.848 - -4 1 5 6 B x 47320# 2003# -467# 334# B- * 6 050800# 2150# -0 5 6 1 7 H -nn 49135# 1004# 940# 143# B- 23062# 1004# 7 052749# 1078# - 3 5 2 7 He -n 26073.126 7.559 4123.057 1.080 B- 11166.021 7.559 7 027990.654 8.115 - 1 4 3 7 Li 14907.10529 0.00423 5606.439 0.001 B- -861.893 0.071 7 016003.43666 0.00454 - -1 3 4 7 Be 15768.999 0.071 5371.548 0.010 B- -11907.551 25.150 7 016928.717 0.076 - -3 2 5 7 B p4n 27676.550 25.150 3558.705 3.593 B- * 7 029712.000 27.000 -0 4 6 2 8 He 31609.681 0.089 3924.520 0.011 B- 10663.878 0.100 8 033934.390 0.095 - 2 5 3 8 Li 20945.804 0.047 5159.712 0.006 B- 16004.133 0.059 8 022486.246 0.050 - 0 4 4 8 Be -a 4941.671 0.035 7062.435 0.004 B- -17979.896 1.000 8 005305.102 0.037 - -2 3 5 8 B 22921.567 1.000 4717.155 0.125 B- -12142.701 18.270 8 024607.316 1.073 - -4 2 6 8 C 35064.268 18.243 3101.524 2.280 B- * 8 037643.042 19.584 -0 5 7 2 9 He 40935.826 46.816 3349.037 5.202 B- 15980.924 46.817 9 043946.419 50.259 - 3 6 3 9 Li -3n 24954.902 0.186 5037.768 0.021 B- 13606.449 0.201 9 026790.191 0.200 - 1 5 4 9 Be 11348.453 0.077 6462.668 0.009 B- -1068.035 0.899 9 012183.066 0.082 - -1 4 5 9 B - 12416.488 0.903 6257.070 0.100 B- -16494.484 2.319 9 013329.649 0.969 - -3 3 6 9 C -pp 28910.972 2.137 4337.423 0.237 B- * 9 031037.207 2.293 -0 6 8 2 10 He -nn 49197.143 92.848 2995.134 9.285 B- 16144.519 93.715 10 052815.308 99.676 - 4 7 3 10 Li -n 33052.624 12.721 4531.351 1.272 B- 20445.136 12.722 10 035483.453 13.656 - 2 6 4 10 Be 12607.488 0.081 6497.630 0.008 B- 556.878 0.082 10 013534.695 0.086 - 0 5 5 10 B 12050.609 0.015 6475.083 0.002 B- -3648.062 0.069 10 012936.862 0.016 - -2 4 6 10 C 15698.672 0.070 6032.042 0.007 B- -23101.355 400.000 10 016853.218 0.075 - -4 3 7 10 N -- 38800.026 400.000 3643.672 40.000 B- * 10 041653.543 429.417 -0 5 8 3 11 Li x 40728.254 0.615 4155.381 0.056 B- 20551.087 0.659 11 043723.581 0.660 - 3 7 4 11 Be 20177.167 0.238 5952.540 0.022 B- 11509.460 0.238 11 021661.081 0.255 - 1 6 5 11 B 8667.707 0.012 6927.732 0.001 B- -1981.689 0.061 11 009305.166 0.013 - -1 5 6 11 C 10649.396 0.060 6676.456 0.005 B- -13654.163 46.154 11 011432.597 0.064 - -3 4 7 11 N -p 24303.559 46.154 5364.046 4.196 B- * 11 026090.945 49.548 -0 6 9 3 12 Li -n 49009.571 30.006 3791.600 2.501 B- 23931.812 30.067 12 052613.941 32.213 - 4 8 4 12 Be 25077.760 1.909 5720.722 0.159 B- 11708.363 2.321 12 026922.083 2.048 - 2 7 5 12 B 13369.397 1.321 6631.223 0.110 B- 13369.397 1.321 12 014352.638 1.418 - 0 6 6 12 C 0.0 0.0 7680.144 0.000 B- -17338.068 1.000 12 000000.0 0.0 - -2 5 7 12 N 17338.068 1.000 6170.109 0.083 B- -14576.544 24.021 12 018613.182 1.073 - -4 4 8 12 O -pp 31914.613 24.000 4890.202 2.000 B- * 12 034261.747 25.765 -0 7 10 3 13 Li -nn 56980.888 70.003 3507.630 5.385 B- 23321.812 70.739 13 061171.503 75.150 - 5 9 4 13 Be -n 33659.077 10.180 5241.435 0.783 B- 17097.130 10.230 13 036134.507 10.929 - 3 8 5 13 B -nn 16561.947 1.000 6496.419 0.077 B- 13436.938 1.000 13 017779.981 1.073 - 1 7 6 13 C 3125.00888 0.00021 7469.849 0.000 B- -2220.472 0.270 13 003354.83521 0.00023 - -1 6 7 13 N 5345.481 0.270 7238.863 0.021 B- -17769.951 9.530 13 005738.609 0.289 - -3 5 8 13 O +3n 23115.432 9.526 5811.763 0.733 B- * 13 024815.437 10.226 -0 6 10 4 14 Be x 39954.498 132.245 4993.897 9.446 B- 16290.813 133.936 14 042892.920 141.970 - 4 9 5 14 B 23663.685 21.213 6101.644 1.515 B- 20643.792 21.213 14 025404.012 22.773 - 2 8 6 14 C 3019.89278 0.00376 7520.319 0.000 B- 156.476 0.004 14 003241.98843 0.00403 - 0 7 7 14 N 2863.41672 0.00019 7475.614 0.000 B- -5144.364 0.025 14 003074.00446 0.00021 - -2 6 8 14 O 8007.781 0.025 7052.278 0.002 B- -23956.622 41.119 14 008596.706 0.027 - -4 5 9 14 F -p 31964.402 41.119 5285.208 2.937 B- * 14 034315.199 44.142 -0 7 11 4 15 Be -n 49825.815 165.797 4540.970 11.053 B- 20867.573 167.126 15 053490.215 177.990 - 5 10 5 15 B 28958.242 21.032 5879.985 1.402 B- 19085.098 21.047 15 031087.953 22.578 - 3 9 6 15 C -n 9873.144 0.800 7100.169 0.053 B- 9771.705 0.800 15 010599.256 0.858 - 1 8 7 15 N 101.43871 0.00060 7699.460 0.000 B- -2754.166 0.491 15 000108.89894 0.00065 - -1 7 8 15 O 2855.605 0.491 7463.692 0.033 B- -13711.146 14.009 15 003065.618 0.526 - -3 6 9 15 F -p 16566.751 14.000 6497.459 0.933 B- -23648.622 68.138 15 017785.139 15.029 - -5 5 10 15 Ne -pp 40215.373 66.684 4868.728 4.446 B- * 15 043172.980 71.588 -0 8 12 4 16 Be -nn 57447.132 165.797 4285.285 10.362 B- 20334.623 167.608 16 061672.036 177.990 - 6 11 5 16 B 37112.510 24.569 5507.302 1.536 B- 23418.378 24.828 16 039841.920 26.375 - 4 10 6 16 C -nn 13694.132 3.578 6922.054 0.224 B- 8010.225 4.254 16 014701.256 3.840 - 2 9 7 16 N -n 5683.907 2.301 7373.796 0.144 B- 10420.908 2.301 16 006101.925 2.470 - 0 8 8 16 O -4737.00135 0.00016 7976.206 0.000 B- -15417.254 8.321 15 994914.61960 0.00017 - -2 7 9 16 F - 10680.253 8.321 6963.731 0.520 B- -13306.523 22.106 16 011465.723 8.932 - -4 6 10 16 Ne -- 23986.776 20.480 6083.177 1.280 B- * 16 025750.864 21.986 -0 7 12 5 17 B x 43716.317 204.104 5269.667 12.006 B- 22684.419 204.841 17 046931.399 219.114 - 5 11 6 17 C 2p-n 21031.898 17.365 6558.024 1.021 B- 13161.820 22.946 17 022578.672 18.641 - 3 10 7 17 N +p 7870.079 15.000 7286.229 0.882 B- 8678.842 15.000 17 008448.877 16.103 - 1 9 8 17 O -808.76348 0.00066 7750.728 0.000 B- -2760.465 0.248 16 999131.75664 0.00070 - -1 8 9 17 F 1951.702 0.248 7542.328 0.015 B- -14548.746 0.432 17 002095.238 0.266 - -3 7 10 17 Ne 16500.447 0.354 6640.499 0.021 B- -18672.766 1001.356 17 017713.959 0.380 - -5 6 11 17 Na x 35173.214 1001.356 5496.080 58.903 B- * 17 037760.000 1075.000 -0 8 13 5 18 B -n 51792.634 204.165 4976.630 11.342 B- 26873.370 206.357 18 055601.682 219.180 - 6 12 6 18 C ++ 24919.264 30.000 6426.131 1.667 B- 11806.096 35.282 18 026751.932 32.206 - 4 11 7 18 N + 13113.168 18.570 7038.562 1.032 B- 13895.984 18.570 18 014077.565 19.935 - 2 10 8 18 O -782.81560 0.00071 7767.097 0.000 B- -1655.929 0.463 17 999159.61284 0.00076 - 0 9 9 18 F 873.113 0.463 7631.638 0.026 B- -4444.501 0.589 18 000937.325 0.497 - -2 8 10 18 Ne 5317.614 0.363 7341.257 0.020 B- -19720.374 93.882 18 005708.693 0.390 - -4 7 11 18 Na 25037.988 93.881 6202.217 5.216 B- * 18 026879.386 100.785 -0 9 14 5 19 B x 59770.244 525.363 4719.634 27.651 B- 27356.492 534.496 19 064166.000 564.000 - 7 13 6 19 C -n 32413.752 98.389 6118.273 5.178 B- 16557.471 99.748 19 034797.596 105.625 - 5 12 7 19 N p-2n 15856.282 16.404 6948.543 0.863 B- 12523.424 16.614 19 017022.419 17.610 - 3 11 8 19 O -n 3332.858 2.637 7566.495 0.139 B- 4820.302 2.637 19 003577.970 2.830 - 1 10 9 19 F -1487.44420 0.00086 7779.018 0.000 B- -3239.494 0.160 18 998403.16288 0.00093 - -1 9 10 19 Ne +3n 1752.050 0.160 7567.343 0.008 B- -11177.340 10.536 19 001880.903 0.171 - -3 8 11 19 Na 12929.390 10.535 6937.885 0.554 B- -18898.998 51.099 19 013880.272 11.309 - -5 7 12 19 Mg -pp 31828.389 50.001 5902.025 2.632 B- * 19 034169.182 53.678 -0 10 15 5 20 B x 68450# 800# 4453# 40# B- 30946# 833# 20 073484# 859# - 8 14 6 20 C x 37503.563 230.625 5961.435 11.531 B- 15737.067 243.746 20 040261.732 247.585 - 6 13 7 20 N x 21766.496 78.894 6709.171 3.945 B- 17970.324 78.899 20 023367.295 84.696 - 4 12 8 20 O -nn 3796.172 0.885 7568.570 0.044 B- 3813.635 0.885 20 004075.358 0.950 - 2 11 9 20 F -n -17.463 0.030 7720.134 0.002 B- 7024.467 0.030 19 999981.252 0.031 - 0 10 10 20 Ne -7041.93055 0.00157 8032.240 0.000 B- -13892.535 1.114 19 992440.17619 0.00168 - -2 9 11 20 Na 6850.604 1.114 7298.496 0.056 B- -10627.088 2.171 20 007354.426 1.195 - -4 8 12 20 Mg +t 17477.692 1.863 6728.025 0.093 B- * 20 018763.075 2.000 -0 11 16 5 21 B x 77330# 900# 4203# 43# B- 31687# 1079# 21 083017# 966# - 9 15 6 21 C x 45643# 596# 5674# 28# B- 20411# 611# 21 049000# 640# - 7 14 7 21 N x 25231.913 134.048 6609.015 6.383 B- 17169.878 134.584 21 027087.573 143.906 - 5 13 8 21 O -3n 8062.035 12.000 7389.374 0.571 B- 8109.640 12.134 21 008654.950 12.882 - 3 12 9 21 F -nn -47.605 1.800 7738.293 0.086 B- 5684.171 1.800 20 999948.894 1.932 - 1 11 10 21 Ne -5731.776 0.038 7971.713 0.002 B- -3547.145 0.090 20 993846.685 0.041 - -1 10 11 21 Na -2184.631 0.098 7765.547 0.005 B- -13088.480 0.761 20 997654.702 0.105 - -3 9 12 21 Mg x 10903.850 0.755 7105.031 0.036 B- -16086# 596# 21 011705.764 0.810 - -5 8 13 21 Al x 26990# 596# 6302# 28# B- * 21 028975# 640# -0 10 16 6 22 C -nn 53611.197 231.490 5421.077 10.522 B- 21846.396 311.063 22 057553.990 248.515 - 8 15 7 22 N x 31764.801 207.779 6378.534 9.445 B- 22481.768 215.435 22 034100.918 223.060 - 6 14 8 22 O -4n 9283.033 56.921 7364.871 2.587 B- 6489.660 58.256 22 009965.746 61.107 - 4 13 9 22 F + 2793.373 12.399 7624.295 0.564 B- 10818.092 12.399 22 002998.809 13.310 - 2 12 10 22 Ne -8024.719 0.018 8080.465 0.001 B- -2843.207 0.171 21 991385.109 0.018 - 0 11 11 22 Na -5181.511 0.171 7915.667 0.008 B- -4781.578 0.321 21 994437.418 0.183 - -2 10 12 22 Mg -399.933 0.313 7662.761 0.014 B- -18601# 401# 21 999570.654 0.335 - -4 9 13 22 Al x 18201# 401# 6782# 18# B- -15137# 643# 22 019540# 430# - -6 8 14 22 Si x 33338# 503# 6058# 23# B- * 22 035790# 540# -0 11 17 6 23 C x 64171# 997# 5077# 43# B- 27450# 1082# 23 068890# 1070# - 9 16 7 23 N x 36720.425 420.570 6236.671 18.286 B- 22099.056 437.827 23 039421.000 451.500 - 7 15 8 23 O x 14621.369 121.712 7163.485 5.292 B- 11336.106 126.190 23 015696.686 130.663 - 5 14 9 23 F 3285.263 33.320 7622.344 1.449 B- 8439.312 33.321 23 003526.874 35.770 - 3 13 10 23 Ne -n -5154.049 0.104 7955.256 0.005 B- 4375.804 0.104 22 994466.900 0.112 - 1 12 11 23 Na -9529.85248 0.00181 8111.493 0.000 B- -4056.340 0.158 22 989769.28199 0.00194 - -1 11 12 23 Mg - -5473.513 0.158 7901.115 0.007 B- -12221.583 0.379 22 994123.941 0.170 - -3 10 13 23 Al -- 6748.070 0.345 7335.727 0.015 B- -16949# 503# 23 007244.351 0.370 - -5 9 14 23 Si x 23697# 503# 6565# 22# B- * 23 025440# 540# -0 10 17 7 24 N x 46938# 401# 5887# 17# B- 28438# 433# 24 050390# 430# - 8 16 8 24 O x 18500.402 164.874 7039.685 6.870 B- 10955.887 191.633 24 019861.000 177.000 - 6 15 9 24 F x 7544.515 97.670 7463.582 4.070 B- 13496.161 97.672 24 008099.370 104.853 - 4 14 10 24 Ne -nn -5951.646 0.513 7993.325 0.021 B- 2466.255 0.513 23 993610.645 0.550 - 2 13 11 24 Na -n -8417.901 0.017 8063.488 0.001 B- 5515.669 0.021 23 990963.011 0.017 - 0 12 12 24 Mg -13933.569 0.013 8260.709 0.001 B- -13884.704 0.233 23 985041.697 0.014 - -2 11 13 24 Al ep -48.865 0.233 7649.582 0.010 B- -10794.060 19.473 23 999947.541 0.250 - -4 10 14 24 Si -- 10745.195 19.472 7167.232 0.811 B- -22574# 503# 24 011535.441 20.904 - -6 9 15 24 P x 33320# 503# 6194# 21# B- * 24 035770# 540# -0 11 18 7 25 N x 55983# 503# 5613# 20# B- 28654# 529# 25 060100# 540# - 9 17 8 25 O -n 27329.027 165.084 6727.805 6.603 B- 15994.862 191.191 25 029338.919 177.225 - 7 16 9 25 F x 11334.166 96.442 7336.306 3.858 B- 13369.667 100.721 25 012167.727 103.535 - 5 15 10 25 Ne -2035.502 29.045 7839.799 1.162 B- 7322.312 29.070 24 997814.799 31.181 - 3 14 11 25 Na -nn -9357.813 1.200 8101.397 0.048 B- 3834.969 1.201 24 989953.973 1.288 - 1 13 12 25 Mg -13192.783 0.047 8223.502 0.002 B- -4276.808 0.045 24 985836.964 0.050 - -1 12 13 25 Al -8915.975 0.065 8021.136 0.003 B- -12743.299 10.000 24 990428.306 0.069 - -3 11 14 25 Si +3n 3827.324 10.000 7480.110 0.400 B- -15911# 401# 25 004108.801 10.735 - -5 10 15 25 P x 19738# 401# 6812# 16# B- * 25 021190# 430# -0 10 18 8 26 O -nn 34661.037 164.950 6497.478 6.344 B- 16012.161 198.932 26 037210.155 177.081 - 8 17 9 26 F x 18648.875 111.199 7083.240 4.277 B- 18167.762 112.716 26 020020.392 119.377 - 6 16 10 26 Ne x 481.114 18.429 7751.910 0.709 B- 7341.893 18.758 26 000516.496 19.784 - 4 15 11 26 Na x -6860.780 3.502 8004.201 0.135 B- 9353.763 3.502 25 992634.649 3.759 - 2 14 12 26 Mg -16214.542 0.030 8333.870 0.001 B- -4004.391 0.063 25 982592.971 0.032 - 0 13 13 26 Al -12210.151 0.067 8149.765 0.003 B- -5069.136 0.085 25 986891.863 0.071 - -2 12 14 26 Si - -7141.015 0.108 7924.708 0.004 B- -18114# 196# 25 992333.804 0.115 - -4 11 15 26 P x 10973# 196# 7198# 8# B- -16106# 627# 26 011780# 210# - -6 10 16 26 S x 27079# 596# 6548# 23# B- * 26 029070# 640# -0 11 19 8 27 O x 44670# 500# 6185# 19# B- 19220# 634# 27 047955# 537# - 9 18 9 27 F x 25450.279 389.830 6867.932 14.438 B- 18399.370 400.258 27 027322.000 418.500 - 7 17 10 27 Ne x 7050.909 90.770 7520.414 3.362 B- 12568.699 90.847 27 007569.462 97.445 - 5 16 11 27 Na ++ -5517.790 3.726 7956.946 0.138 B- 9068.821 3.727 26 994076.408 4.000 - 3 15 12 27 Mg -n -14586.611 0.050 8263.852 0.002 B- 2610.251 0.069 26 984340.628 0.053 - 1 14 13 27 Al -17196.861 0.047 8331.553 0.002 B- -4812.359 0.096 26 981538.408 0.050 - -1 13 14 27 Si - -12384.503 0.107 8124.341 0.004 B- -11662.044 26.340 26 986704.688 0.115 - -3 12 15 27 P p4n -722.458 26.340 7663.438 0.976 B- -17750# 400# 26 999224.409 28.277 - -5 11 16 27 S - 17028# 401# 6977# 15# B- * 27 018280# 430# -0 12 20 8 28 O x 52080# 699# 5988# 25# B- 18338# 802# 28 055910# 750# - 10 19 9 28 F -n 33741.596 393.024 6614.792 14.037 B- 22441.859 412.748 28 036223.095 421.928 - 8 18 10 28 Ne x 11299.737 126.068 7388.346 4.502 B- 12288.052 126.483 28 012130.767 135.339 - 6 17 11 28 Na x -988.315 10.246 7799.264 0.366 B- 14030.529 10.440 27 998939.000 11.000 - 4 16 12 28 Mg + -15018.845 2.001 8272.413 0.071 B- 1831.800 2.000 27 983876.606 2.148 - 2 15 13 28 Al -n -16850.645 0.077 8309.894 0.003 B- 4642.150 0.077 27 981910.087 0.083 - 0 14 14 28 Si -21492.79430 0.00049 8447.744 0.000 B- -14345.055 1.152 27 976926.53499 0.00052 - -2 13 15 28 P -7147.740 1.152 7907.479 0.041 B- -11220.945 160.004 27 992326.585 1.236 - -4 12 16 28 S -- 4073.206 160.000 7478.790 5.714 B- -23443# 617# 28 004372.766 171.767 - -6 11 17 28 Cl x 27516# 596# 6614# 21# B- * 28 029540# 640# -0 11 20 9 29 F x 40150.186 525.363 6444.031 18.116 B- 21750.385 546.221 29 043103.000 564.000 - 9 19 10 29 Ne x 18399.801 149.505 7167.067 5.155 B- 15719.807 149.685 29 019753.000 160.500 - 7 18 11 29 Na 2679.994 7.337 7682.151 0.253 B- 13282.824 13.557 29 002877.092 7.876 - 5 17 12 29 Mg x -10602.829 11.400 8113.202 0.393 B- 7604.931 11.405 28 988617.393 12.238 - 3 16 13 29 Al x -18207.760 0.345 8348.464 0.012 B- 3687.318 0.345 28 980453.164 0.370 - 1 15 14 29 Si -21895.07838 0.00056 8448.635 0.000 B- -4942.230 0.359 28 976494.66525 0.00060 - -1 14 15 29 P -16952.848 0.359 8251.236 0.012 B- -13796.432 50.001 28 981800.368 0.385 - -3 13 16 29 S +3n -3156.416 50.000 7748.520 1.724 B- -16318.592 195.192 28 996611.448 53.677 - -5 12 17 29 Cl -p 13162.176 188.680 7158.832 6.506 B- * 29 014130.178 202.555 -0 12 21 9 30 F x 48112# 596# 6233# 20# B- 24832# 648# 30 051650# 640# - 10 20 10 30 Ne 23280.117 253.250 7034.531 8.442 B- 14805.448 253.295 30 024992.235 271.875 - 8 19 11 30 Na 8474.670 4.727 7501.968 0.158 B- 17358.490 5.850 30 009097.932 5.074 - 6 18 12 30 Mg x -8883.820 3.447 8054.506 0.115 B- 6981.024 4.496 29 990462.826 3.700 - 4 17 13 30 Al x -15864.844 2.888 8261.128 0.096 B- 8568.116 2.888 29 982968.388 3.100 - 2 16 14 30 Si -n -24432.960 0.022 8520.654 0.001 B- -4232.106 0.061 29 973770.136 0.023 - 0 15 15 30 P - -20200.854 0.065 8353.506 0.002 B- -6141.601 0.196 29 978313.489 0.069 - -2 14 16 30 S - -14059.253 0.206 8122.707 0.007 B- -18502# 196# 29 984906.769 0.221 - -4 13 17 30 Cl x 4443# 196# 7480# 7# B- -16488# 284# 30 004770# 210# - -6 12 18 30 Ar -pp 20931.147 206.155 6904.204 6.872 B- * 30 022470.511 221.316 -0 13 22 9 31 F -nn 56143# 546# 6033# 18# B- 24961# 608# 31 060272# 587# - 11 21 10 31 Ne 31181.591 266.195 6813.090 8.587 B- 18935.559 266.562 31 033474.816 285.772 - 9 20 11 31 Na x 12246.031 13.972 7398.677 0.451 B- 15368.182 14.307 31 013146.656 15.000 - 7 19 12 31 Mg x -3122.151 3.074 7869.188 0.099 B- 11828.555 3.801 30 996648.232 3.300 - 5 18 13 31 Al x -14950.706 2.236 8225.517 0.072 B- 7998.330 2.236 30 983949.756 2.400 - 3 17 14 31 Si -n -22949.036 0.043 8458.291 0.001 B- 1491.505 0.043 30 975363.194 0.046 - 1 16 15 31 P -24440.54095 0.00067 8481.167 0.000 B- -5398.016 0.229 30 973761.99863 0.00072 - -1 15 16 31 S -19042.525 0.229 8281.800 0.007 B- -12007.974 3.454 30 979557.007 0.246 - -3 14 17 31 Cl -- -7034.551 3.447 7869.209 0.111 B- -18360# 200# 30 992448.098 3.700 - -5 13 18 31 Ar - 11325# 200# 7252# 6# B- * 31 012158# 215# -0 12 22 10 32 Ne x 36999# 503# 6671# 16# B- 18359# 504# 32 039720# 540# - 10 21 11 32 Na x 18640.151 37.260 7219.881 1.164 B- 19469.051 37.402 32 020011.026 40.000 - 8 20 12 32 Mg x -828.900 3.260 7803.840 0.102 B- 10270.467 7.879 31 999110.139 3.500 - 6 19 13 32 Al x -11099.367 7.173 8100.344 0.224 B- 12978.319 7.179 31 988084.339 7.700 - 4 18 14 32 Si x -24077.686 0.298 8481.468 0.009 B- 227.188 0.301 31 974151.539 0.320 - 2 17 15 32 P -n -24304.874 0.040 8464.120 0.001 B- 1710.660 0.040 31 973907.643 0.042 - 0 16 16 32 S -26015.53355 0.00132 8493.129 0.000 B- -12680.860 0.562 31 972071.17443 0.00141 - -2 15 17 32 Cl -13334.674 0.562 8072.404 0.018 B- -11134.323 1.857 31 985684.637 0.603 - -4 14 18 32 Ar x -2200.351 1.770 7700.008 0.055 B- -23299# 401# 31 997637.826 1.900 - -6 13 19 32 K x 21098# 401# 6947# 13# B- * 32 022650# 430# -0 13 23 10 33 Ne x 45997# 596# 6440# 18# B- 22217# 747# 33 049380# 640# - 11 22 11 33 Na x 23780.110 449.912 7089.926 13.634 B- 18817.813 449.921 33 025529.000 483.000 - 9 21 12 33 Mg x 4962.297 2.888 7636.455 0.088 B- 13459.677 7.559 33 005327.245 3.100 - 7 20 13 33 Al x -8497.380 6.986 8020.616 0.212 B- 12016.945 7.021 32 990877.687 7.500 - 5 19 14 33 Si x -20514.325 0.699 8361.059 0.021 B- 5823.021 1.295 32 977976.964 0.750 - 3 18 15 33 P + -26337.346 1.090 8513.806 0.033 B- 248.508 1.090 32 971725.694 1.170 - 1 17 16 33 S -26585.85434 0.00135 8497.630 0.000 B- -5582.517 0.391 32 971458.90985 0.00145 - -1 16 17 33 Cl -21003.337 0.391 8304.755 0.012 B- -11619.044 0.560 32 977451.989 0.419 - -3 15 18 33 Ar x -9384.292 0.401 7928.955 0.012 B- -16426# 196# 32 989925.547 0.430 - -5 14 19 33 K x 7042# 196# 7407# 6# B- * 33 007560# 210# -0 14 24 10 34 Ne -nn 52842# 513# 6287# 15# B- 21161# 789# 34 056728# 551# - 12 23 11 34 Na x 31680.111 599.416 6886.437 17.630 B- 23356.764 600.112 34 034010.000 643.500 - 10 22 12 34 Mg x 8323.348 28.876 7550.390 0.849 B- 11323.637 29.039 34 008935.481 31.000 - 8 21 13 34 Al x -3000.289 3.074 7860.428 0.090 B- 16956.563 14.448 33 996779.057 3.300 - 6 20 14 34 Si +pp -19956.852 14.118 8336.141 0.415 B- 4591.847 14.141 33 978575.437 15.155 - 4 19 15 34 P x -24548.698 0.810 8448.185 0.024 B- 5382.987 0.812 33 973645.887 0.870 - 2 18 16 34 S -29931.685 0.045 8583.498 0.001 B- -5491.603 0.038 33 967867.012 0.047 - 0 17 17 34 Cl -24440.082 0.049 8398.970 0.001 B- -6061.792 0.063 33 973762.491 0.052 - -2 16 18 34 Ar -18378.290 0.078 8197.672 0.002 B- -17158# 196# 33 980270.093 0.083 - -4 15 19 34 K x -1220# 196# 7670# 6# B- -15072# 357# 33 998690# 210# - -6 14 20 34 Ca x 13851# 298# 7204# 9# B- * 34 014870# 320# -0 13 24 11 35 Na -n 38231# 670# 6733# 19# B- 22592# 723# 35 041043# 720# - 11 23 12 35 Mg x 15639.784 269.668 7356.233 7.705 B- 15863.512 269.768 35 016790.000 289.500 - 9 22 13 35 Al x -223.728 7.359 7787.124 0.210 B- 14167.729 36.605 34 999759.817 7.900 - 7 21 14 35 Si 2p-n -14391.457 35.857 8169.563 1.024 B- 10466.342 35.905 34 984550.134 38.494 - 5 20 15 35 P +p -24857.799 1.866 8446.249 0.053 B- 3988.407 1.867 34 973314.053 2.003 - 3 19 16 35 S -28846.206 0.040 8537.850 0.001 B- 167.322 0.026 34 969032.322 0.043 - 1 18 17 35 Cl -29013.528 0.035 8520.278 0.001 B- -5966.243 0.679 34 968852.694 0.038 - -1 17 18 35 Ar - -23047.284 0.680 8327.461 0.019 B- -11874.394 0.852 34 975257.721 0.730 - -3 16 19 35 K 4n -11172.891 0.512 7965.840 0.015 B- -15961# 196# 34 988005.407 0.550 - -5 15 20 35 Ca x 4788# 196# 7487# 6# B- * 35 005140# 210# -0 14 25 11 36 Na -n 46303# 678# 6546# 19# B- 25923# 967# 36 049708# 728# - 12 24 12 36 Mg x 20380.157 690.237 7244.419 19.173 B- 14429.774 706.243 36 021879.000 741.000 - 10 23 13 36 Al x 5950.384 149.505 7623.515 4.153 B- 18386.508 165.851 36 006388.000 160.500 - 8 22 14 36 Si x -12436.124 71.797 8112.519 1.994 B- 7814.911 72.985 35 986649.271 77.077 - 6 21 15 36 P + -20251.034 13.114 8307.868 0.364 B- 10413.096 13.112 35 978259.619 14.078 - 4 20 16 36 S -30664.131 0.188 8575.389 0.005 B- -1142.126 0.189 35 967080.699 0.201 - 2 19 17 36 Cl -29522.005 0.036 8521.931 0.001 B- 709.535 0.045 35 968306.822 0.038 - 0 18 18 36 Ar -30231.540 0.027 8519.909 0.001 B- -12814.475 0.342 35 967545.105 0.028 - -2 17 19 36 K -17417.065 0.341 8142.219 0.009 B- -10965.916 40.001 35 981302.010 0.366 - -4 16 20 36 Ca 4n -6451.149 40.000 7815.879 1.111 B- -21802# 301# 35 993074.406 42.941 - -6 15 21 36 Sc x 15351# 298# 7189# 8# B- * 36 016480# 320# -0 15 26 11 37 Na -nn 53534# 687# 6392# 19# B- 25323# 980# 37 057471# 737# - 13 25 12 37 Mg -n 28211.474 698.947 7055.111 18.890 B- 18401.911 721.814 37 030286.265 750.350 - 11 24 13 37 Al x 9809.563 180.244 7531.315 4.871 B- 16381.075 213.168 37 010531.000 193.500 - 9 23 14 37 Si x -6571.511 113.809 7952.903 3.076 B- 12424.486 119.969 36 992945.191 122.179 - 7 22 15 37 P p-2n -18995.998 37.948 8267.555 1.026 B- 7900.419 37.947 36 979606.956 40.738 - 5 21 16 37 S -n -26896.417 0.198 8459.935 0.005 B- 4865.121 0.196 36 971125.507 0.212 - 3 20 17 37 Cl -31761.538 0.052 8570.281 0.001 B- -813.873 0.200 36 965902.584 0.055 - 1 19 18 37 Ar - -30947.664 0.207 8527.139 0.006 B- -6147.465 0.227 36 966776.314 0.221 - -1 18 19 37 K -p -24800.199 0.094 8339.847 0.003 B- -11664.133 0.641 36 973375.889 0.100 - -3 17 20 37 Ca x -13136.066 0.634 8003.456 0.017 B- -16656# 300# 36 985897.852 0.680 - -5 16 21 37 Sc x 3520# 300# 7532# 8# B- * 37 003779# 322# -0 14 26 12 38 Mg x 34074# 503# 6928# 13# B- 17864# 627# 38 036580# 540# - 12 25 13 38 Al x 16209.859 374.461 7377.097 9.854 B- 20380.157 388.847 38 017402.000 402.000 - 10 24 14 38 Si x -4170.299 104.793 7892.829 2.758 B- 10451.265 127.474 37 995523.000 112.500 - 8 23 15 38 P x -14621.563 72.581 8147.274 1.910 B- 12239.640 72.934 37 984303.105 77.918 - 6 22 16 38 S + -26861.203 7.172 8448.782 0.189 B- 2936.900 7.171 37 971163.310 7.699 - 4 21 17 38 Cl -n -29798.103 0.098 8505.481 0.003 B- 4916.718 0.218 37 968010.418 0.105 - 2 20 18 38 Ar -34714.821 0.195 8614.280 0.005 B- -5914.066 0.045 37 962732.104 0.209 - 0 19 19 38 K -28800.755 0.195 8438.058 0.005 B- -6742.256 0.063 37 969081.116 0.209 - -2 18 20 38 Ca -22058.499 0.194 8240.043 0.005 B- -17809# 200# 37 976319.226 0.208 - -4 17 21 38 Sc x -4249# 200# 7751# 5# B- -15119# 361# 37 995438# 215# - -6 16 22 38 Ti x 10870# 300# 7332# 8# B- * 38 011669# 322# -0 15 27 12 39 Mg -n 42275# 513# 6747# 13# B- 21625# 650# 39 045384# 551# - 13 26 13 39 Al x 20650# 400# 7281# 10# B- 18330# 422# 39 022169# 429# - 11 25 14 39 Si x 2320.352 135.532 7730.979 3.475 B- 15094.986 176.232 39 002491.000 145.500 - 9 24 15 39 P x -12774.634 112.645 8097.969 2.888 B- 10388.033 123.243 38 986285.865 120.929 - 7 23 16 39 S 2p-n -23162.667 50.000 8344.269 1.282 B- 6637.538 50.030 38 975133.852 53.677 - 5 22 17 39 Cl -nn -29800.205 1.732 8494.402 0.044 B- 3441.985 5.292 38 968008.162 1.859 - 3 21 18 39 Ar + -33242.190 5.000 8562.598 0.128 B- 565.000 5.000 38 964313.039 5.367 - 1 20 19 39 K -33807.19010 0.00458 8557.025 0.000 B- -6524.488 0.596 38 963706.48661 0.00492 - -1 19 20 39 Ca -27282.702 0.596 8369.670 0.015 B- -13109.993 24.007 38 970710.813 0.640 - -3 18 21 39 Sc 2n-p -14172.709 24.000 8013.456 0.615 B- -16373# 202# 38 984784.970 25.765 - -5 17 22 39 Ti x 2200# 200# 7574# 5# B- * 39 002362# 215# -0 16 28 12 40 Mg x 48350# 500# 6628# 13# B- 20760# 640# 40 051906# 537# - 14 27 13 40 Al x 27590# 400# 7127# 10# B- 22160# 528# 40 029619# 429# - 12 26 14 40 Si x 5429.679 345.119 7661.754 8.628 B- 13544.049 377.749 40 005829.000 370.500 - 10 25 15 40 P x -8114.370 153.582 7980.796 3.840 B- 14723.476 153.633 39 991288.865 164.876 - 8 24 16 40 S -22837.846 3.982 8329.325 0.100 B- 4719.967 32.312 39 975482.562 4.274 - 6 23 17 40 Cl + -27557.813 32.066 8427.765 0.802 B- 7482.082 32.066 39 970415.469 34.423 - 4 22 18 40 Ar -35039.89464 0.00224 8595.259 0.000 B- -1504.403 0.056 39 962383.12378 0.00240 - 2 21 19 40 K -33535.492 0.056 8538.090 0.001 B- 1310.893 0.060 39 963998.166 0.060 - 0 20 20 40 Ca -34846.384 0.021 8551.303 0.001 B- -14323.050 2.828 39 962590.865 0.022 - -2 19 21 40 Sc - -20523.335 2.828 8173.669 0.071 B- -11672.950 160.025 39 977967.292 3.036 - -4 18 22 40 Ti -- -8850.384 160.000 7862.286 4.000 B- -21020# 340# 39 990498.721 171.767 - -6 17 23 40 V x 12170# 300# 7317# 7# B- * 40 013065# 322# -0 15 28 13 41 Al x 33420# 500# 7008# 12# B- 21300# 747# 41 035878# 537# - 13 27 14 41 Si x 12119.668 554.705 7508.573 13.529 B- 17099.435 567.571 41 013011.000 595.500 - 11 26 15 41 P x -4979.767 120.163 7906.551 2.931 B- 14028.810 120.233 40 994654.000 129.000 - 9 25 16 41 S x -19008.577 4.099 8229.635 0.100 B- 8298.611 68.846 40 979593.451 4.400 - 7 24 17 41 Cl x -27307.189 68.723 8412.959 1.676 B- 5760.317 68.724 40 970684.525 73.777 - 5 23 18 41 Ar -n -33067.505 0.347 8534.372 0.008 B- 2492.038 0.347 40 964500.571 0.372 - 3 22 19 41 K -35559.54331 0.00380 8576.072 0.000 B- -421.653 0.138 40 961825.25796 0.00408 - 1 21 20 41 Ca -35137.890 0.138 8546.706 0.003 B- -6495.478 0.158 40 962277.921 0.147 - -1 20 21 41 Sc -28642.412 0.083 8369.198 0.002 B- -12944.875 27.945 40 969251.104 0.088 - -3 19 22 41 Ti x -15697.537 27.945 8034.388 0.682 B- -16018# 202# 40 983148.000 30.000 - -5 18 23 41 V x 320# 200# 7625# 5# B- * 41 000344# 215# -0 16 29 13 42 Al x 40100# 600# 6874# 14# B- 23630# 781# 42 043049# 644# - 14 28 14 42 Si x 16470# 500# 7418# 12# B- 15460# 591# 42 017681# 537# - 12 27 15 42 P x 1009.740 314.379 7767.866 7.485 B- 18647.485 314.392 42 001084.000 337.500 - 10 26 16 42 S x -17637.746 2.794 8193.227 0.067 B- 7194.021 59.681 41 981065.100 3.000 - 8 25 17 42 Cl x -24831.767 59.616 8345.886 1.419 B- 9590.908 59.895 41 973342.000 64.000 - 6 24 18 42 Ar x -34422.675 5.775 8555.613 0.138 B- 599.351 5.776 41 963045.736 6.200 - 4 23 19 42 K -n -35022.026 0.106 8551.256 0.003 B- 3525.219 0.183 41 962402.306 0.113 - 2 22 20 42 Ca -38547.245 0.149 8616.563 0.004 B- -6426.092 0.097 41 958617.828 0.159 - 0 21 21 42 Sc -32121.153 0.169 8444.933 0.004 B- -7016.479 0.224 41 965516.522 0.181 - -2 20 22 42 Ti -25104.674 0.277 8259.247 0.007 B- -17485# 196# 41 973049.022 0.297 - -4 19 23 42 V x -7620# 196# 7824# 5# B- -14350# 445# 41 991820# 210# - -6 18 24 42 Cr x 6730# 400# 7464# 10# B- * 42 007225# 429# -0 17 30 13 43 Al x 47020# 800# 6741# 19# B- 23919# 998# 43 050478# 859# - 15 29 14 43 Si x 23101# 596# 7279# 14# B- 18421# 814# 43 024800# 640# - 13 28 15 43 P x 4679.826 554.705 7689.572 12.900 B- 16875.285 554.727 43 005024.000 595.500 - 11 27 16 43 S x -12195.459 4.970 8063.827 0.116 B- 11964.049 62.058 42 986907.635 5.335 - 9 26 17 43 Cl x -24159.508 61.858 8323.866 1.439 B- 7850.300 62.086 42 974063.700 66.407 - 7 25 18 43 Ar x -32009.808 5.310 8488.237 0.123 B- 4565.581 5.325 42 965636.055 5.700 - 5 24 19 43 K -4n -36575.389 0.410 8576.220 0.010 B- 1833.434 0.469 42 960734.703 0.440 - 3 23 20 43 Ca -38408.822 0.228 8600.663 0.005 B- -2220.720 1.865 42 958766.430 0.244 - 1 22 21 43 Sc -p -36188.102 1.863 8530.825 0.043 B- -6867.020 7.481 42 961150.472 1.999 - -1 21 22 43 Ti -n2p -29321.082 7.245 8352.932 0.168 B- -11404.726 43.457 42 968522.521 7.777 - -3 20 23 43 V x -17916.356 42.849 8069.512 0.996 B- -15946# 402# 42 980766.000 46.000 - -5 19 24 43 Cr x -1970# 400# 7680# 9# B- * 42 997885# 429# -0 16 30 14 44 Si x 28513# 596# 7174# 14# B- 18063# 778# 44 030610# 640# - 14 29 15 44 P x 10450# 500# 7567# 11# B- 19655# 500# 44 011219# 537# - 12 28 16 44 S x -9204.233 5.216 7996.015 0.119 B- 11180.290 136.421 43 990118.848 5.600 - 10 27 17 44 Cl x -20384.523 136.321 8232.332 3.098 B- 12288.731 136.330 43 978116.312 146.346 - 8 26 18 44 Ar x -32673.255 1.584 8493.840 0.036 B- 3108.237 1.638 43 964923.816 1.700 - 6 25 19 44 K x -35781.492 0.419 8546.701 0.010 B- 5687.183 0.530 43 961586.986 0.450 - 4 24 20 44 Ca -41468.675 0.325 8658.175 0.007 B- -3652.690 1.757 43 955481.543 0.348 - 2 23 21 44 Sc -p -37815.985 1.756 8557.379 0.040 B- -267.416 1.890 43 959402.867 1.884 - 0 22 22 44 Ti -a -37548.569 0.700 8533.520 0.016 B- -13432.189 181.643 43 959689.951 0.751 - -2 21 23 44 V x -24116.380 181.641 8210.463 4.128 B- -10756# 351# 43 974110.000 195.000 - -4 20 24 44 Cr x -13360# 300# 7948# 7# B- -20390# 583# 43 985657# 322# - -6 19 25 44 Mn x 7030# 500# 7467# 11# B- * 44 007547# 537# -0 17 31 14 45 Si x 37490# 700# 6995# 16# B- 21890# 860# 45 040247# 751# - 15 30 15 45 P x 15600# 500# 7464# 11# B- 19589# 1150# 45 016747# 537# - 13 29 16 45 S x -3989.589 1035.356 7881.807 23.008 B- 14272.954 1044.271 44 995717.000 1111.500 - 11 28 17 45 Cl x -18262.543 136.163 8181.598 3.026 B- 11508.254 136.164 44 980394.353 146.177 - 9 27 18 45 Ar x -29770.796 0.512 8419.952 0.011 B- 6844.841 0.731 44 968039.733 0.550 - 7 26 19 45 K x -36615.638 0.522 8554.674 0.012 B- 4196.536 0.637 44 960691.493 0.560 - 5 25 20 45 Ca -40812.174 0.366 8630.545 0.008 B- 259.722 0.747 44 956186.326 0.392 - 3 24 21 45 Sc -41071.896 0.675 8618.931 0.015 B- -2062.056 0.509 44 955907.503 0.724 - 1 23 22 45 Ti -39009.840 0.845 8555.722 0.019 B- -7123.824 0.214 44 958121.211 0.907 - -1 22 23 45 V -31886.016 0.872 8380.029 0.019 B- -12371.217 35.408 44 965768.951 0.935 - -3 21 24 45 Cr x -19514.799 35.397 8087.728 0.787 B- -14265# 401# 44 979050.000 38.000 - -5 20 25 45 Mn x -5250# 400# 7753# 9# B- -19012# 565# 44 994364# 429# - -7 19 26 45 Fe -pp 13762# 400# 7313# 9# B- * 45 014774# 429# -0 16 31 15 46 P x 22970# 700# 7317# 15# B- 22630# 860# 46 024659# 751# - 14 30 16 46 S x 340# 500# 7792# 11# B- 14199# 542# 46 000365# 537# - 12 29 17 46 Cl x -13859.398 208.661 8083.480 4.536 B- 15913.528 208.664 45 985121.323 224.006 - 10 28 18 46 Ar x -29772.926 1.118 8412.419 0.024 B- 5640.997 1.333 45 968037.446 1.200 - 8 27 19 46 K x -35413.924 0.727 8518.042 0.016 B- 7725.438 2.350 45 961981.586 0.780 - 6 26 20 46 Ca -43139.361 2.235 8668.979 0.049 B- -1378.143 2.333 45 953687.988 2.399 - 4 25 21 46 Sc -n -41761.219 0.683 8622.012 0.015 B- 2366.581 0.667 45 955167.485 0.732 - 2 24 22 46 Ti -44127.799 0.165 8656.451 0.004 B- -7052.449 0.093 45 952626.856 0.176 - 0 23 23 46 V -37075.351 0.202 8486.130 0.004 B- -7603.784 11.455 45 960197.971 0.216 - -2 22 24 46 Cr -29471.567 11.453 8303.823 0.249 B- -16902# 400# 45 968360.970 12.295 - -4 21 25 46 Mn x -12570# 400# 7919# 9# B- -13480# 640# 45 986506# 429# - -6 20 26 46 Fe x 910# 500# 7609# 11# B- * 46 000977# 537# -0 17 32 15 47 P x 29710# 800# 7190# 17# B- 22340# 944# 47 031895# 859# - 15 31 16 47 S x 7370# 500# 7648# 11# B- 17150# 640# 47 007912# 537# - 13 30 17 47 Cl x -9780# 400# 7996# 9# B- 15587# 400# 46 989501# 429# - 11 29 18 47 Ar x -25366.338 1.118 8311.404 0.024 B- 10345.638 1.789 46 972768.114 1.200 - 9 28 19 47 K x -35711.976 1.397 8514.879 0.030 B- 6632.442 2.625 46 961661.614 1.500 - 7 27 20 47 Ca -42344.418 2.222 8639.349 0.047 B- 1992.177 1.185 46 954541.394 2.385 - 5 26 21 47 Sc -44336.595 1.933 8665.090 0.041 B- 600.769 1.929 46 952402.704 2.074 - 3 25 22 47 Ti -44937.364 0.115 8661.227 0.002 B- -2930.746 0.138 46 951757.752 0.123 - 1 24 23 47 V -42006.618 0.169 8582.225 0.004 B- -7444.040 6.032 46 954904.038 0.181 - -1 23 24 47 Cr -34562.578 6.030 8407.195 0.128 B- -11996.204 32.240 46 962895.544 6.473 - -3 22 25 47 Mn x -22566.374 31.671 8135.311 0.674 B- -15697# 501# 46 975774.000 34.000 - -5 21 26 47 Fe x -6870# 500# 7785# 11# B- -17240# 781# 46 992625# 537# - -7 20 27 47 Co x 10370# 600# 7401# 13# B- * 47 011133# 644# -0 16 32 16 48 S x 12761# 596# 7545# 12# B- 17042# 778# 48 013700# 640# - 14 31 17 48 Cl x -4280# 500# 7883# 10# B- 18001# 587# 47 995405# 537# - 12 30 18 48 Ar x -22281.337 307.393 8242.132 6.404 B- 10003.140 307.394 47 976080.000 330.000 - 10 29 19 48 K x -32284.477 0.773 8434.232 0.016 B- 11940.153 0.779 47 965341.186 0.830 - 8 28 20 48 Ca -44224.629 0.096 8666.686 0.002 B- 279.213 4.950 47 952522.904 0.103 - 6 27 21 48 Sc -44503.842 4.951 8656.204 0.103 B- 3988.866 4.950 47 952223.157 5.314 - 4 26 22 48 Ti -48492.709 0.109 8723.006 0.002 B- -4015.015 0.969 47 947940.932 0.117 - 2 25 23 48 V -44477.694 0.975 8623.061 0.020 B- -1655.673 7.388 47 952251.229 1.046 - 0 24 24 48 Cr +nn -42822.020 7.324 8572.269 0.153 B- -13525.682 10.087 47 954028.667 7.862 - -2 23 25 48 Mn -29296.338 6.939 8274.185 0.145 B- -11296# 400# 47 968549.085 7.449 - -4 22 26 48 Fe x -18000# 400# 8023# 8# B- -19500# 640# 47 980676# 429# - -6 21 27 48 Co x 1500# 500# 7600# 10# B- -15293# 708# 48 001610# 537# - -8 20 28 48 Ni -pp 16793# 502# 7265# 10# B- * 48 018028# 538# -0 17 33 16 49 S -n 21093# 667# 7385# 14# B- 20153# 897# 49 022644# 716# - 15 32 17 49 Cl x 940# 600# 7781# 12# B- 18130# 721# 49 001009# 644# - 13 31 18 49 Ar x -17190# 400# 8135# 8# B- 12422# 400# 48 981546# 429# - 11 30 19 49 K x -29611.490 0.801 8372.274 0.016 B- 11688.275 0.826 48 968210.755 0.860 - 9 29 20 49 Ca -n -41299.765 0.201 8594.844 0.004 B- 5261.500 2.702 48 955662.875 0.216 - 7 28 21 49 Sc -46561.265 2.698 8686.256 0.055 B- 2002.522 2.697 48 950014.423 2.896 - 5 27 22 49 Ti -48563.787 0.114 8711.157 0.002 B- -601.856 0.820 48 947864.627 0.122 - 3 26 23 49 V - -47961.931 0.828 8682.908 0.017 B- -2628.871 2.391 48 948510.746 0.889 - 1 25 24 49 Cr -45333.060 2.243 8613.291 0.046 B- -7712.426 0.233 48 951332.955 2.407 - -1 24 25 49 Mn -37620.634 2.255 8439.929 0.046 B- -12869.907 24.324 48 959612.585 2.420 - -3 23 26 49 Fe x -24750.727 24.219 8161.311 0.494 B- -14870# 501# 48 973429.000 26.000 - -5 22 27 49 Co x -9880# 500# 7842# 10# B- -18080# 781# 48 989393# 537# - -7 21 28 49 Ni x 8200# 600# 7457# 12# B- * 49 008803# 644# -0 16 33 17 50 Cl x 7740# 600# 7651# 12# B- 21069# 781# 50 008309# 644# - 14 32 18 50 Ar x -13330# 500# 8056# 10# B- 12398# 500# 49 985690# 537# - 12 31 19 50 K x -25727.848 7.731 8288.582 0.155 B- 13861.376 7.892 49 972380.017 8.300 - 10 30 20 50 Ca x -39589.224 1.584 8550.163 0.032 B- 4958.158 15.084 49 957499.217 1.700 - 8 29 21 50 Sc -pn -44547.382 15.000 8633.679 0.300 B- 6884.278 15.000 49 952176.415 16.103 - 6 28 22 50 Ti -51431.660 0.121 8755.718 0.002 B- -2207.647 0.426 49 944785.839 0.129 - 4 27 23 50 V +n -49224.013 0.409 8695.918 0.008 B- 1038.059 0.299 49 947155.845 0.438 - 2 26 24 50 Cr -50262.072 0.437 8701.032 0.009 B- -7634.477 0.067 49 946041.443 0.468 - 0 25 25 50 Mn -42627.595 0.442 8532.696 0.009 B- -8151.139 8.395 49 954237.391 0.474 - -2 24 26 50 Fe x -34476.456 8.383 8354.026 0.168 B- -16846# 400# 49 962988.000 9.000 - -4 23 27 50 Co x -17630# 400# 8001# 8# B- -13510# 640# 49 981073# 429# - -6 22 28 50 Ni x -4120# 500# 7716# 10# B- * 49 995577# 537# -0 17 34 17 51 Cl x 14290# 700# 7530# 14# B- 20980# 922# 51 015341# 751# - 15 33 18 51 Ar x -6690# 600# 7926# 12# B- 15826# 600# 50 992818# 644# - 13 32 19 51 K x -22516.196 13.047 8221.349 0.256 B- 13816.107 13.057 50 975827.867 14.006 - 11 31 20 51 Ca x -36332.304 0.522 8476.913 0.010 B- 6896.381 20.007 50 960995.665 0.560 - 9 30 21 51 Sc -p2n -43228.684 20.000 8596.796 0.392 B- 6504.153 20.006 50 953592.095 21.471 - 7 29 22 51 Ti -n -49732.837 0.505 8708.988 0.010 B- 2471.005 0.644 50 946609.600 0.541 - 5 28 23 51 V -52203.842 0.401 8742.099 0.008 B- -752.447 0.213 50 943956.867 0.430 - 3 27 24 51 Cr -51451.395 0.400 8712.005 0.008 B- -3207.518 0.346 50 944764.652 0.429 - 1 26 25 51 Mn -48243.877 0.502 8633.772 0.010 B- -8041.321 8.977 50 948208.065 0.539 - -1 25 26 51 Fe -40202.555 8.964 8460.759 0.176 B- -12860.412 49.260 50 956840.779 9.623 - -3 24 27 51 Co x -27342.143 48.438 8193.254 0.950 B- -15442# 503# 50 970647.000 52.000 - -5 23 28 51 Ni x -11900# 500# 7875# 10# B- * 50 987225# 537# -0 16 34 18 52 Ar x -1280# 600# 7825# 12# B- 15858# 601# 51 998626# 644# - 14 33 19 52 K x -17137.627 33.534 8115.029 0.645 B- 17128.639 33.540 51 981602.000 36.000 - 12 32 20 52 Ca x -34266.266 0.671 8429.381 0.013 B- 6177.013 81.855 51 963213.648 0.720 - 10 31 21 52 Sc x -40443.279 81.852 8533.125 1.574 B- 9026.541 82.157 51 956582.351 87.871 - 8 30 22 52 Ti -nn -49469.820 7.072 8691.667 0.136 B- 1973.948 7.085 51 946891.960 7.592 - 6 29 23 52 V -n -51443.769 0.420 8714.582 0.008 B- 3975.473 0.531 51 944772.839 0.450 - 4 28 24 52 Cr -55419.242 0.340 8775.989 0.007 B- -4711.958 1.851 51 940504.992 0.364 - 2 27 25 52 Mn -50707.284 1.845 8670.329 0.035 B- -2376.920 5.017 51 945563.488 1.980 - 0 26 26 52 Fe -48330.363 5.117 8609.574 0.098 B- -13969.413 9.822 51 948115.217 5.493 - -2 25 27 52 Co x -34360.951 8.383 8325.886 0.161 B- -12031# 400# 51 963112.000 9.000 - -4 24 28 52 Ni x -22330# 400# 8079# 8# B- -20049# 721# 51 976028# 429# - -6 23 29 52 Cu x -2280# 600# 7679# 12# B- * 51 997552# 644# -0 17 35 18 53 Ar x 6791# 699# 7677# 13# B- 19086# 708# 53 007290# 750# - 15 34 19 53 K x -12295.721 111.779 8022.848 2.109 B- 17091.983 120.047 52 986800.000 120.000 - 13 33 20 53 Ca x -29387.704 43.780 8330.577 0.826 B- 9519.104 103.774 52 968451.000 47.000 - 11 32 21 53 Sc x -38906.808 94.087 8495.421 1.775 B- 7924.253 137.339 52 958231.821 101.006 - 9 31 22 53 Ti + -46831.061 100.049 8630.174 1.888 B- 5020.000 100.000 52 949724.785 107.406 - 7 30 23 53 V +p -51851.061 3.120 8710.130 0.059 B- 3435.938 3.102 52 944335.593 3.349 - 5 29 24 53 Cr -55286.999 0.348 8760.198 0.007 B- -596.884 0.356 52 940646.961 0.373 - 3 28 25 53 Mn -54690.116 0.450 8734.175 0.009 B- -3742.586 1.686 52 941287.742 0.483 - 1 27 26 53 Fe -50947.530 1.656 8648.799 0.031 B- -8288.101 0.443 52 945305.574 1.777 - -1 26 27 53 Co -42659.428 1.713 8477.658 0.032 B- -13028.604 25.209 52 954203.217 1.839 - -3 25 28 53 Ni x -29630.824 25.150 8217.074 0.475 B- -16361# 501# 52 968190.000 27.000 - -5 24 29 53 Cu x -13270# 500# 7894# 9# B- * 52 985754# 537# -0 16 35 19 54 K x -5002# 596# 7889# 11# B- 20158# 598# 53 994630# 640# - 14 34 20 54 Ca x -25160.585 48.438 8247.496 0.897 B- 8730.315 277.066 53 972989.000 52.000 - 12 33 21 54 Sc x -33890.900 272.800 8394.681 5.052 B- 11731.081 284.990 53 963616.620 292.862 - 10 32 22 54 Ti x -45621.981 82.461 8597.435 1.527 B- 4271.192 83.815 53 951022.786 88.526 - 8 31 23 54 V + -49893.173 15.004 8662.043 0.278 B- 7041.592 15.000 53 946437.472 16.107 - 6 30 24 54 Cr -56934.765 0.353 8777.955 0.007 B- -1377.136 1.008 53 938878.012 0.378 - 4 29 25 54 Mn -p -55557.629 1.059 8737.965 0.020 B- 696.872 1.076 53 940356.429 1.136 - 2 28 26 54 Fe -56254.500 0.372 8736.382 0.007 B- -8244.547 0.089 53 939608.306 0.399 - 0 27 27 54 Co -48009.953 0.383 8569.217 0.007 B- -8731.646 4.673 53 948459.192 0.411 - -2 26 28 54 Ni x -39278.308 4.657 8393.032 0.086 B- -17868# 400# 53 957833.000 5.000 - -4 25 29 54 Cu x -21410# 400# 8048# 7# B- -15139# 565# 53 977015# 429# - -6 24 30 54 Zn -pp -6272# 400# 7753# 7# B- * 53 993267# 430# -0 17 36 19 55 K x 708# 699# 7788# 13# B- 19058# 760# 55 000760# 750# - 15 35 20 55 Ca x -18350# 300# 8120# 5# B- 11809# 544# 54 980300# 322# - 13 34 21 55 Sc x -30159.352 454.342 8320.955 8.261 B- 11508.735 482.226 54 967622.601 487.756 - 11 33 22 55 Ti -41668.088 161.602 8515.980 2.938 B- 7476.498 157.206 54 955267.465 173.486 - 9 32 23 55 V -49144.586 95.104 8637.692 1.729 B- 5965.125 95.103 54 947241.114 102.098 - 7 31 24 55 Cr -55109.710 0.399 8731.924 0.007 B- 2602.703 0.368 54 940837.289 0.428 - 5 30 25 55 Mn -57712.413 0.303 8765.022 0.006 B- -231.114 0.179 54 938043.172 0.325 - 3 29 26 55 Fe -57481.300 0.342 8746.595 0.006 B- -3451.417 0.324 54 938291.283 0.367 - 1 28 27 55 Co -54029.883 0.428 8669.618 0.008 B- -8694.034 0.578 54 941996.531 0.459 - -1 27 28 55 Ni - -45335.849 0.719 8497.320 0.013 B- -13700.449 155.561 54 951329.961 0.771 - -3 26 29 55 Cu x -31635.399 155.559 8233.996 2.828 B- -17065# 429# 54 966038.000 167.000 - -5 25 30 55 Zn x -14570# 400# 7909# 7# B- * 54 984358# 429# -0 18 37 19 56 K x 7927# 801# 7664# 14# B- 21825# 895# 56 008510# 860# - 16 36 20 56 Ca x -13898# 400# 8040# 7# B- 10954# 710# 55 985080# 429# - 14 35 21 56 Sc x -24852.260 586.841 8221.728 10.479 B- 14467.788 599.236 55 973320.000 630.000 - 12 34 22 56 Ti -39320.048 121.247 8466.110 2.165 B- 6834.833 194.550 55 957788.190 130.164 - 10 33 23 56 V -46154.881 176.898 8574.191 3.159 B- 9130.120 176.899 55 950450.694 189.907 - 8 32 24 56 Cr ++ -55285.001 0.603 8723.258 0.011 B- 1626.538 0.561 55 940649.107 0.647 - 6 31 25 56 Mn -n -56911.538 0.331 8738.333 0.006 B- 3695.544 0.207 55 938902.947 0.355 - 4 30 26 56 Fe -60607.082 0.302 8790.354 0.005 B- -4566.680 0.411 55 934935.617 0.324 - 2 29 27 56 Co -56040.402 0.493 8694.836 0.009 B- -2132.863 0.374 55 939838.150 0.529 - 0 28 28 56 Ni -53907.539 0.422 8642.779 0.008 B- -15264.511 14.910 55 942127.872 0.452 - -2 27 29 56 Cu x -38643.029 14.904 8356.227 0.266 B- -13253# 400# 55 958515.000 16.000 - -4 26 30 56 Zn x -25390# 400# 8106# 7# B- -22000# 640# 55 972743# 429# - -6 25 31 56 Ga x -3390# 500# 7699# 9# B- * 55 996361# 537# -0 17 37 20 57 Ca x -6874# 400# 7917# 7# B- 14121# 1364# 56 992620# 429# - 15 36 21 57 Sc x -20995.875 1304.092 8151.433 22.879 B- 12919.758 1329.062 56 977460.000 1400.000 - 13 35 22 57 Ti x -33915.633 256.417 8364.370 4.499 B- 10497.818 268.750 56 963590.068 275.274 - 11 34 23 57 V x -44413.450 80.479 8534.817 1.412 B- 8111.252 80.486 56 952320.197 86.397 - 9 33 24 57 Cr x -52524.702 1.068 8663.394 0.019 B- 4961.548 1.846 56 943612.409 1.146 - 7 32 25 57 Mn -57486.251 1.505 8736.713 0.026 B- 2695.589 1.526 56 938285.968 1.615 - 5 31 26 57 Fe -60181.839 0.304 8770.279 0.005 B- -836.276 0.451 56 935392.134 0.326 - 3 30 27 57 Co -59345.564 0.533 8741.882 0.009 B- -3261.731 0.642 56 936289.913 0.572 - 1 29 28 57 Ni -56083.833 0.582 8670.933 0.010 B- -8774.947 0.439 56 939791.525 0.624 - -1 28 29 57 Cu -47308.886 0.519 8503.262 0.009 B- -14759# 200# 56 949211.819 0.557 - -3 27 30 57 Zn x -32550# 200# 8231# 4# B- -17540# 447# 56 965056# 215# - -5 26 31 57 Ga x -15010# 400# 7909# 7# B- * 56 983886# 429# -0 18 38 20 58 Ca x -1919# 500# 7835# 9# B- 12957# 640# 57 997940# 537# - 16 37 21 58 Sc x -14876# 400# 8045# 7# B- 16234# 447# 57 984030# 429# - 14 36 22 58 Ti x -31110# 200# 8311# 3# B- 9292# 219# 57 966602# 215# - 12 35 23 58 V x -40401.753 89.374 8457.658 1.541 B- 11590.049 89.386 57 956626.932 95.947 - 10 34 24 58 Cr x -51991.801 1.490 8643.998 0.026 B- 3835.759 3.085 57 944184.502 1.600 - 8 33 25 58 Mn x -55827.560 2.701 8696.643 0.047 B- 6327.553 2.723 57 940066.646 2.900 - 6 32 26 58 Fe -62155.113 0.343 8792.250 0.006 B- -2307.955 1.139 57 933273.738 0.368 - 4 31 27 58 Co -59847.158 1.160 8738.969 0.020 B- 381.586 1.107 57 935751.429 1.245 - 2 30 28 58 Ni -60228.744 0.373 8732.059 0.006 B- -8561.019 0.443 57 935341.780 0.400 - 0 29 29 58 Cu -51667.725 0.578 8570.967 0.010 B- -9368.981 50.002 57 944532.413 0.621 - -2 28 30 58 Zn -- -42298.744 50.001 8395.944 0.862 B- -18759# 304# 57 954590.428 53.678 - -4 27 31 58 Ga x -23540# 300# 8059# 5# B- -16459# 583# 57 974729# 322# - -6 26 32 58 Ge x -7080# 500# 7762# 9# B- * 57 992399# 537# -0 17 38 21 59 Sc x -10302# 400# 7967# 7# B- 15208# 447# 58 988940# 429# - 15 37 22 59 Ti x -25510# 200# 8212# 3# B- 12322# 258# 58 972614# 215# - 13 36 23 59 V x -37832.015 161.874 8407.555 2.744 B- 10253.745 270.218 58 959385.659 173.778 - 11 35 24 59 Cr x -48085.760 216.367 8568.087 3.667 B- 7439.560 216.380 58 948377.810 232.279 - 9 34 25 59 Mn x -55525.320 2.329 8680.921 0.039 B- 5139.485 2.356 58 940391.113 2.500 - 7 33 26 59 Fe -60664.805 0.355 8754.771 0.006 B- 1564.903 0.369 58 934873.649 0.380 - 5 32 27 59 Co -62229.709 0.418 8768.035 0.007 B- -1073.002 0.194 58 933193.656 0.448 - 3 31 28 59 Ni -61156.707 0.374 8736.588 0.006 B- -4798.380 0.397 58 934345.571 0.402 - 1 30 29 59 Cu -56358.327 0.544 8642.000 0.009 B- -9142.775 0.602 58 939496.844 0.584 - -1 29 30 59 Zn -47215.551 0.771 8473.777 0.013 B- -13455# 170# 58 949312.017 0.827 - -3 28 31 59 Ga x -33760# 170# 8232# 3# B- -17890# 434# 58 963757# 183# - -5 27 32 59 Ge x -15870# 400# 7916# 7# B- * 58 982963# 429# -0 18 39 21 60 Sc x -4052# 500# 7865# 8# B- 18278# 583# 59 995650# 537# - 16 38 22 60 Ti x -22330# 300# 8157# 5# B- 10912# 372# 59 976028# 322# - 14 37 23 60 V x -33241.956 220.159 8325.450 3.669 B- 13427.621 293.169 59 964313.290 236.350 - 12 36 24 60 Cr x -46669.576 193.593 8536.205 3.227 B- 6298.361 193.607 59 949898.146 207.830 - 10 35 25 60 Mn x -52967.938 2.329 8628.138 0.039 B- 8445.079 4.128 59 943136.576 2.500 - 8 34 26 60 Fe -nn -61413.017 3.409 8755.851 0.057 B- 237.293 3.411 59 934070.411 3.659 - 6 33 27 60 Co -n -61650.309 0.424 8746.766 0.007 B- 2822.809 0.212 59 933815.667 0.455 - 4 32 28 60 Ni -64473.118 0.376 8780.774 0.006 B- -6127.982 1.573 59 930785.256 0.403 - 2 31 29 60 Cu - -58345.137 1.618 8665.602 0.027 B- -4170.797 1.629 59 937363.916 1.736 - 0 30 30 60 Zn -54174.340 0.564 8583.050 0.009 B- -14584# 200# 59 941841.450 0.605 - -2 29 31 60 Ga x -39590# 200# 8327# 3# B- -12501# 361# 59 957498# 215# - -4 28 32 60 Ge x -27090# 300# 8106# 5# B- -21620# 500# 59 970918# 322# - -6 27 33 60 As x -5470# 400# 7732# 7# B- * 59 994128# 429# -0 19 40 21 61 Sc x 931# 600# 7787# 10# B- 17281# 721# 61 001000# 644# - 17 39 22 61 Ti x -16350# 400# 8057# 7# B- 14157# 979# 60 982448# 429# - 15 38 23 61 V x -30506.429 894.234 8276.439 14.660 B- 11968.800 899.958 60 967250.000 960.000 - 13 37 24 61 Cr x -42475.229 101.341 8459.824 1.661 B- 9266.893 101.367 60 954400.963 108.793 - 11 36 25 61 Mn x -51742.122 2.329 8598.915 0.038 B- 7178.372 3.497 60 944452.544 2.500 - 9 35 26 61 Fe x -58920.494 2.608 8703.768 0.043 B- 3977.572 2.742 60 936746.244 2.800 - 7 34 27 61 Co p2n -62898.066 0.846 8756.148 0.014 B- 1323.839 0.790 60 932476.145 0.908 - 5 33 28 61 Ni -64221.905 0.378 8765.025 0.006 B- -2237.845 0.966 60 931054.945 0.405 - 3 32 29 61 Cu p2n -61984.059 0.953 8715.514 0.016 B- -5635.156 15.903 60 933457.371 1.023 - 1 31 30 61 Zn -56348.903 15.899 8610.309 0.261 B- -9214.245 37.679 60 939506.960 17.068 - -1 30 31 61 Ga -47134.659 37.994 8446.431 0.623 B- -13775# 302# 60 949398.859 40.787 - -3 29 32 61 Ge x -33360# 300# 8208# 5# B- -16459# 424# 60 964187# 322# - -5 28 33 61 As x -16900# 300# 7925# 5# B- * 60 981857# 322# -0 18 40 22 62 Ti x -12500# 400# 7995# 6# B- 12977# 499# 61 986581# 429# - 16 39 23 62 V x -25476# 298# 8192# 5# B- 15419# 333# 61 972650# 320# - 14 38 24 62 Cr x -40894.961 148.099 8428.069 2.389 B- 7628.996 148.244 61 956097.451 158.991 - 12 37 25 62 Mn IT -48523.957 6.542 8538.499 0.106 B- 10354.091 7.114 61 947907.386 7.023 - 10 36 26 62 Fe x -58878.048 2.794 8692.882 0.045 B- 2546.235 18.784 61 936791.812 3.000 - 8 35 27 62 Co + -61424.282 18.575 8721.332 0.300 B- 5322.040 18.570 61 934058.317 19.940 - 6 34 28 62 Ni -66746.323 0.439 8794.553 0.007 B- -3958.896 0.475 61 928344.871 0.470 - 4 33 29 62 Cu - -62787.426 0.647 8718.081 0.010 B- -1619.455 0.651 61 932594.921 0.694 - 2 32 30 62 Zn -61167.972 0.625 8679.343 0.010 B- -9181.066 0.376 61 934333.477 0.670 - 0 31 31 62 Ga -51986.906 0.647 8518.642 0.010 B- -10247# 140# 61 944189.757 0.694 - -2 30 32 62 Ge x -41740# 140# 8341# 2# B- -17420# 331# 61 955190# 150# - -4 29 33 62 As x -24320# 300# 8047# 5# B- * 61 973891# 322# -0 19 41 22 63 Ti x -5750# 500# 7889# 8# B- 16140# 640# 62 993827# 537# - 17 40 23 63 V x -21890# 400# 8133# 6# B- 14117# 537# 62 976500# 429# - 15 39 24 63 Cr x -36007.474 358.073 8344.828 5.684 B- 10879.579 358.092 62 961344.384 384.407 - 13 38 25 63 Mn x -46887.053 3.726 8505.101 0.059 B- 8748.568 5.692 62 949664.675 4.000 - 11 37 26 63 Fe -55635.621 4.302 8631.549 0.068 B- 6215.819 19.067 62 940272.700 4.618 - 9 36 27 63 Co -61851.440 18.575 8717.795 0.295 B- 3661.335 18.570 62 933599.744 19.941 - 7 35 28 63 Ni -65512.775 0.440 8763.493 0.007 B- 66.977 0.015 62 929669.139 0.472 - 5 34 29 63 Cu -65579.752 0.440 8752.138 0.007 B- -3366.355 1.546 62 929597.236 0.472 - 3 33 30 63 Zn -62213.397 1.561 8686.285 0.025 B- -5666.304 2.034 62 933211.167 1.676 - 1 32 31 63 Ga x -56547.093 1.304 8583.926 0.021 B- -9625.877 37.283 62 939294.195 1.400 - -1 31 32 63 Ge x -46921.216 37.260 8418.716 0.591 B- -13421# 204# 62 949628.000 40.000 - -3 30 33 63 As x -33500# 200# 8193# 3# B- * 62 964036# 215# -0 20 42 22 64 Ti x -1025# 600# 7818# 9# B- 15295# 721# 63 998900# 644# - 18 41 23 64 V x -16320# 400# 8045# 6# B- 17160# 594# 63 982480# 429# - 16 40 24 64 Cr x -33479.757 439.665 8301.058 6.870 B- 9509.277 439.679 63 964058.000 472.000 - 14 39 25 64 Mn x -42989.035 3.540 8437.417 0.055 B- 11980.510 6.140 63 953849.370 3.800 - 12 38 26 64 Fe x -54969.544 5.017 8612.388 0.078 B- 4822.785 20.625 63 940987.763 5.386 - 10 37 27 64 Co + -59792.329 20.006 8675.520 0.313 B- 7306.592 20.000 63 935810.291 21.476 - 8 36 28 64 Ni -67098.921 0.475 8777.461 0.007 B- -1674.376 0.225 63 927966.341 0.510 - 6 35 29 64 Cu -65424.545 0.448 8739.075 0.007 B- 579.469 0.650 63 929763.857 0.481 - 4 34 30 64 Zn -66004.014 0.647 8735.905 0.010 B- -7171.194 1.483 63 929141.772 0.694 - 2 33 31 64 Ga -58832.821 1.429 8611.631 0.022 B- -4517.325 3.991 63 936840.365 1.533 - 0 32 32 64 Ge x -54315.496 3.726 8528.823 0.058 B- -14783# 203# 63 941689.913 4.000 - -2 31 33 64 As -p -39532# 203# 8286# 3# B- -12832# 543# 63 957560# 218# - -4 30 34 64 Se x -26700# 503# 8073# 8# B- * 63 971336# 540# -0 19 42 23 65 V x -11780# 500# 7976# 8# B- 16440# 583# 64 987354# 537# - 17 41 24 65 Cr x -28220# 300# 8217# 5# B- 12748# 300# 64 969705# 322# - 15 40 25 65 Mn x -40967.339 3.726 8400.681 0.057 B- 10250.557 6.326 64 956019.750 4.000 - 13 39 26 65 Fe x -51217.895 5.112 8546.346 0.079 B- 7967.303 5.520 64 945015.324 5.487 - 11 38 27 65 Co x -59185.198 2.083 8656.884 0.032 B- 5940.487 2.141 64 936462.073 2.235 - 9 37 28 65 Ni -n -65125.685 0.495 8736.240 0.008 B- 2137.975 0.706 64 930084.697 0.531 - 7 36 29 65 Cu -67263.660 0.650 8757.096 0.010 B- -1351.640 0.360 64 927789.487 0.697 - 5 35 30 65 Zn -65912.019 0.650 8724.265 0.010 B- -3254.513 0.662 64 929240.532 0.697 - 3 34 31 65 Ga -62657.507 0.815 8662.160 0.013 B- -6179.291 2.313 64 932734.395 0.874 - 1 33 32 65 Ge -56478.216 2.165 8555.058 0.033 B- -9541.165 84.794 64 939368.137 2.323 - -1 32 33 65 As x -46937.051 84.766 8396.234 1.304 B- -13917# 312# 64 949611.000 91.000 - -3 31 34 65 Se x -33020# 300# 8170# 5# B- * 64 964552# 322# -0 20 43 23 66 V x -5610# 500# 7884# 8# B- 19110# 640# 65 993977# 537# - 18 42 24 66 Cr x -24720# 400# 8161# 6# B- 12030# 400# 65 973462# 429# - 16 41 25 66 Mn x -36750.387 11.178 8331.798 0.169 B- 13317.452 11.906 65 960546.834 12.000 - 14 40 26 66 Fe x -50067.839 4.099 8521.724 0.062 B- 6340.694 14.561 65 946249.960 4.400 - 12 39 27 66 Co x -56408.533 13.972 8605.941 0.212 B- 9597.752 14.042 65 939442.945 15.000 - 10 38 28 66 Ni x -66006.285 1.397 8739.508 0.021 B- 251.987 1.543 65 929139.334 1.500 - 8 37 29 66 Cu -66258.272 0.655 8731.472 0.010 B- 2640.888 0.931 65 928868.814 0.703 - 6 36 30 66 Zn -68899.160 0.749 8759.632 0.011 B- -5175.500 0.800 65 926033.704 0.804 - 4 35 31 66 Ga - -63723.660 1.096 8669.361 0.017 B- -2116.628 2.639 65 931589.832 1.176 - 2 34 32 66 Ge x -61607.032 2.401 8625.437 0.036 B- -9581.955 6.168 65 933862.126 2.577 - 0 33 33 66 As x -52025.077 5.682 8468.403 0.086 B- -10365# 200# 65 944148.779 6.100 - -2 32 34 66 Se x -41660# 200# 8300# 3# B- * 65 955276# 215# -0 21 44 23 67 V x -650# 600# 7812# 9# B- 18030# 721# 66 999302# 644# - 19 43 24 67 Cr x -18680# 400# 8070# 6# B- 14780# 500# 66 979946# 429# - 17 42 25 67 Mn x -33460# 300# 8279# 4# B- 12150# 404# 66 964079# 322# - 15 41 26 67 Fe x -45610.155 270.285 8448.469 4.034 B- 9711.620 270.362 66 951035.482 290.163 - 13 40 27 67 Co x -55321.775 6.443 8581.741 0.096 B- 8420.905 7.061 66 940609.628 6.917 - 11 39 28 67 Ni x -63742.680 2.888 8695.750 0.043 B- 3576.832 3.023 66 931569.414 3.100 - 9 38 29 67 Cu -67319.513 0.894 8737.458 0.013 B- 560.800 0.830 66 927729.526 0.959 - 7 37 30 67 Zn -67880.313 0.760 8734.152 0.011 B- -1001.265 1.122 66 927127.482 0.815 - 5 36 31 67 Ga -66879.048 1.181 8707.531 0.018 B- -4220.819 4.799 66 928202.384 1.268 - 3 35 32 67 Ge -n2p -62658.230 4.661 8632.857 0.070 B- -6071.005 4.682 66 932733.620 5.003 - 1 34 33 67 As -56587.225 0.443 8530.568 0.007 B- -10006.936 67.069 66 939251.111 0.475 - -1 33 34 67 Se x -46580.289 67.068 8369.534 1.001 B- -13790# 405# 66 949994.000 72.000 - -3 32 35 67 Br x -32790# 400# 8152# 6# B- * 66 964798# 429# -0 20 44 24 68 Cr x -14800# 500# 8013# 7# B- 13580# 640# 67 984112# 537# - 18 43 25 68 Mn x -28380# 400# 8201# 6# B- 15107# 541# 67 969533# 429# - 16 42 26 68 Fe x -43486.914 365.259 8411.698 5.371 B- 8443.751 411.489 67 953314.875 392.121 - 14 41 27 68 Co x -51930.665 189.497 8524.366 2.787 B- 11533.150 189.520 67 944250.135 203.433 - 12 40 28 68 Ni x -63463.814 2.981 8682.466 0.044 B- 2103.220 3.375 67 931868.789 3.200 - 10 39 29 68 Cu x -65567.035 1.584 8701.890 0.023 B- 4440.057 1.767 67 929610.889 1.700 - 8 38 30 68 Zn -70007.092 0.784 8755.680 0.012 B- -2921.100 1.200 67 924844.291 0.841 - 6 37 31 68 Ga - -67085.992 1.433 8701.218 0.021 B- -107.203 2.361 67 927980.221 1.538 - 4 36 32 68 Ge x -66978.789 1.876 8688.136 0.028 B- -8084.270 2.632 67 928095.308 2.014 - 2 35 33 68 As -58894.519 1.846 8557.745 0.027 B- -4705.078 1.911 67 936774.130 1.981 - 0 34 34 68 Se x -54189.441 0.496 8477.047 0.007 B- -15398# 259# 67 941825.239 0.532 - -2 33 35 68 Br -p -38791# 259# 8239# 4# B- * 67 958356# 278# -0 21 45 24 69 Cr x -8580# 500# 7924# 7# B- 16190# 640# 68 990789# 537# - 19 44 25 69 Mn x -24770# 400# 8147# 6# B- 14259# 565# 68 973408# 429# - 17 43 26 69 Fe x -39030# 400# 8342# 6# B- 11250# 424# 68 958100# 429# - 15 42 27 69 Co x -50279.157 140.506 8493.865 2.036 B- 9699.492 140.556 68 946023.102 150.839 - 13 41 28 69 Ni x -59978.648 3.726 8623.099 0.054 B- 5757.564 3.979 68 935610.268 4.000 - 11 40 29 69 Cu x -65736.213 1.397 8695.204 0.020 B- 2681.632 1.610 68 929429.268 1.500 - 9 39 30 69 Zn -n -68417.845 0.800 8722.729 0.012 B- 909.964 1.426 68 926550.418 0.858 - 7 38 31 69 Ga -69327.809 1.197 8724.579 0.017 B- -2227.146 0.550 68 925573.531 1.285 - 5 37 32 69 Ge -67100.663 1.318 8680.963 0.019 B- -3988.492 31.982 68 927964.471 1.414 - 3 36 33 69 As -63112.171 31.999 8611.821 0.464 B- -6677.465 32.021 68 932246.294 34.352 - 1 35 34 69 Se -56434.706 1.490 8503.707 0.022 B- -10175.236 42.029 68 939414.847 1.599 - -1 34 35 69 Br -p -46259.470 42.003 8344.902 0.609 B- -13825# 403# 68 950338.413 45.092 - -3 33 36 69 Kr x -32435# 401# 8133# 6# B- * 68 965180# 430# -0 22 46 24 70 Cr x -4480# 600# 7867# 9# B- 15020# 781# 69 995191# 644# - 20 45 25 70 Mn x -19500# 500# 8070# 7# B- 17010# 640# 69 979066# 537# - 18 44 26 70 Fe x -36510# 400# 8302# 6# B- 10120# 500# 69 960805# 429# - 16 43 27 70 Co x -46630# 300# 8436# 4# B- 12584# 300# 69 949941# 322# - 14 42 28 70 Ni x -59213.860 2.144 8604.291 0.031 B- 3762.513 2.401 69 936431.303 2.301 - 12 41 29 70 Cu x -62976.373 1.082 8646.865 0.015 B- 6588.362 2.202 69 932392.079 1.161 - 10 40 30 70 Zn -69564.735 1.918 8729.808 0.027 B- -654.595 1.574 69 925319.181 2.058 - 8 39 31 70 Ga -68910.140 1.201 8709.280 0.017 B- 1651.736 1.462 69 926021.917 1.289 - 6 38 32 70 Ge -70561.876 0.838 8721.700 0.012 B- -6220.000 50.000 69 924248.706 0.900 - 4 37 33 70 As - -64341.876 50.007 8621.666 0.714 B- -2411.985 50.032 69 930926.151 53.684 - 2 36 34 70 Se x -61929.891 1.584 8576.033 0.023 B- -10504.272 14.988 69 933515.523 1.700 - 0 35 35 70 Br x -51425.619 14.904 8414.796 0.213 B- -10325# 201# 69 944792.323 16.000 - -2 34 36 70 Kr x -41100# 200# 8256# 3# B- * 69 955877# 215# -0 21 46 25 71 Mn x -15570# 500# 8015# 7# B- 15860# 640# 70 983285# 537# - 19 45 26 71 Fe x -31430# 400# 8227# 6# B- 12940# 613# 70 966259# 429# - 17 44 27 71 Co x -44369.926 465.030 8398.734 6.550 B- 11036.302 465.035 70 952366.923 499.230 - 15 43 28 71 Ni x -55406.228 2.237 8543.156 0.032 B- 7304.899 2.688 70 940518.964 2.401 - 13 42 29 71 Cu x -62711.127 1.490 8635.022 0.021 B- 4617.651 3.044 70 932676.832 1.600 - 11 41 30 71 Zn -67328.777 2.654 8689.041 0.037 B- 2810.358 2.775 70 927719.580 2.849 - 9 40 31 71 Ga -70139.135 0.812 8717.604 0.011 B- -232.638 0.223 70 924702.536 0.871 - 7 39 32 71 Ge -69906.497 0.834 8703.309 0.012 B- -2013.400 4.082 70 924952.284 0.894 - 5 38 33 71 As - -67893.097 4.167 8663.932 0.059 B- -4746.590 5.017 70 927113.758 4.473 - 3 37 34 71 Se x -63146.507 2.794 8586.060 0.039 B- -6644.089 6.082 70 932209.432 3.000 - 1 36 35 71 Br -56502.418 5.402 8481.462 0.076 B- -10175.212 128.845 70 939342.156 5.799 - -1 35 36 71 Kr -46327.205 128.769 8327.130 1.814 B- -14267# 420# 70 950265.696 138.238 - -3 34 37 71 Rb x -32060# 400# 8115# 6# B- * 70 965582# 429# -0 22 47 25 72 Mn x -9900# 600# 7937# 8# B- 18530# 781# 71 989372# 644# - 20 46 26 72 Fe x -28430# 500# 8184# 7# B- 11769# 640# 71 969479# 537# - 18 45 27 72 Co x -40200# 400# 8336# 6# B- 14027# 400# 71 956844# 429# - 16 44 28 72 Ni x -54226.060 2.237 8520.211 0.031 B- 5556.938 2.637 71 941785.926 2.401 - 14 43 29 72 Cu x -59782.999 1.397 8586.525 0.019 B- 8362.487 2.558 71 935820.307 1.500 - 12 42 30 72 Zn x -68145.486 2.142 8691.805 0.030 B- 442.807 2.294 71 926842.807 2.300 - 10 41 31 72 Ga -68588.293 0.819 8687.089 0.011 B- 3997.607 0.822 71 926367.434 0.878 - 8 40 32 72 Ge -72585.900 0.076 8731.745 0.001 B- -4356.102 4.082 71 922075.826 0.081 - 6 39 33 72 As - -68229.798 4.083 8660.378 0.057 B- -361.618 4.528 71 926752.295 4.383 - 4 38 34 72 Se x -67868.180 1.956 8644.489 0.027 B- -8806.437 2.208 71 927140.507 2.100 - 2 37 35 72 Br x -59061.743 1.025 8511.312 0.014 B- -5121.168 8.076 71 936594.607 1.100 - 0 36 36 72 Kr x -53940.575 8.011 8429.319 0.111 B- -15611# 500# 71 942092.407 8.600 - -2 35 37 72 Rb x -38330# 500# 8202# 7# B- * 71 958851# 537# -0 21 47 26 73 Fe x -22900# 500# 8106# 7# B- 14518# 640# 72 975416# 537# - 19 46 27 73 Co x -37418# 400# 8295# 5# B- 12690# 400# 72 959830# 429# - 17 45 28 73 Ni x -50108.152 2.423 8457.652 0.033 B- 8879.285 3.104 72 946206.683 2.601 - 15 44 29 73 Cu -58987.437 1.942 8568.569 0.027 B- 6605.966 2.691 72 936674.378 2.084 - 13 43 30 73 Zn x -65593.402 1.863 8648.345 0.026 B- 4105.932 2.506 72 929582.582 2.000 - 11 42 31 73 Ga x -69699.335 1.677 8693.873 0.023 B- 1598.188 1.678 72 925174.682 1.800 - 9 41 32 73 Ge -71297.523 0.057 8705.049 0.001 B- -344.776 3.853 72 923458.956 0.061 - 7 40 33 73 As -70952.747 3.853 8689.609 0.053 B- -2725.360 7.399 72 923829.089 4.136 - 5 39 34 73 Se -68227.387 7.424 8641.558 0.102 B- -4579.912 10.388 72 926754.883 7.969 - 3 38 35 73 Br x -63647.475 7.266 8568.103 0.100 B- -7095.725 9.801 72 931671.621 7.800 - 1 37 36 73 Kr x -56551.751 6.578 8460.184 0.090 B- -10470# 200# 72 939289.195 7.061 - -1 36 37 73 Rb -p -46082# 200# 8306# 3# B- -14131# 448# 72 950529# 215# - -3 35 38 73 Sr x -31950# 401# 8102# 5# B- * 72 965700# 430# -0 22 48 26 74 Fe x -19590# 600# 8061# 8# B- 13230# 781# 73 978969# 644# - 20 47 27 74 Co x -32820# 500# 8229# 7# B- 15636# 537# 73 964766# 537# - 18 46 28 74 Ni x -48456# 196# 8430# 3# B- 7550# 196# 73 947980# 210# - 16 45 29 74 Cu x -56006.205 6.148 8521.562 0.083 B- 9750.507 6.642 73 939874.862 6.600 - 14 44 30 74 Zn x -65756.712 2.515 8642.754 0.034 B- 2292.905 3.910 73 929407.262 2.700 - 12 43 31 74 Ga x -68049.617 2.994 8663.167 0.040 B- 5372.824 2.994 73 926945.726 3.214 - 10 42 32 74 Ge -73422.442 0.013 8725.200 0.000 B- -2562.387 1.693 73 921177.762 0.013 - 8 41 33 74 As -70860.054 1.693 8680.001 0.023 B- 1353.147 1.693 73 923928.598 1.817 - 6 40 34 74 Se -72213.201 0.015 8687.715 0.000 B- -6925.049 5.835 73 922475.935 0.015 - 4 39 35 74 Br -65288.153 5.835 8583.561 0.079 B- -2956.317 6.173 73 929910.281 6.264 - 2 38 36 74 Kr -62331.836 2.013 8533.038 0.027 B- -10415.827 3.424 73 933084.017 2.161 - 0 37 37 74 Rb -51916.009 3.027 8381.712 0.041 B- -11089# 100# 73 944265.868 3.249 - -2 36 38 74 Sr x -40827# 100# 8221# 1# B- * 73 956170# 107# -0 23 49 26 75 Fe x -13640# 600# 7982# 8# B- 16010# 781# 74 985357# 644# - 21 48 27 75 Co x -29649# 500# 8185# 7# B- 14380# 583# 74 968170# 537# - 19 47 28 75 Ni x -44030# 300# 8366# 4# B- 10441# 300# 74 952732# 322# - 17 46 29 75 Cu x -54471.341 2.330 8495.094 0.031 B- 8087.567 3.042 74 941522.606 2.501 - 15 45 30 75 Zn x -62558.908 1.956 8592.497 0.026 B- 5905.672 3.113 74 932840.246 2.100 - 13 44 31 75 Ga x -68464.580 2.422 8660.808 0.032 B- 3392.384 2.422 74 926500.246 2.600 - 11 43 32 75 Ge -n -71856.965 0.052 8695.609 0.001 B- 1177.231 0.885 74 922858.371 0.055 - 9 42 33 75 As -73034.195 0.884 8700.874 0.012 B- -864.714 0.882 74 921594.562 0.948 - 7 41 34 75 Se -72169.481 0.073 8678.913 0.001 B- -3062.472 4.285 74 922522.871 0.078 - 5 40 35 75 Br x -69107.009 4.285 8627.649 0.057 B- -4783.385 9.167 74 925810.570 4.600 - 3 39 36 75 Kr x -64323.624 8.104 8553.439 0.108 B- -7104.929 8.189 74 930945.746 8.700 - 1 38 37 75 Rb x -57218.694 1.180 8448.275 0.016 B- -10600.000 220.000 74 938573.201 1.266 - -1 37 38 75 Sr - -46618.694 220.003 8296.511 2.933 B- -14799# 372# 74 949952.770 236.183 - -3 36 39 75 Y x -31820# 300# 8089# 4# B- * 74 965840# 322# -0 22 49 27 76 Co x -24510# 600# 8116# 8# B- 17120# 721# 75 973687# 644# - 20 48 28 76 Ni x -41630# 400# 8331# 5# B- 9346# 400# 75 955308# 429# - 18 47 29 76 Cu x -50975.985 6.707 8443.527 0.088 B- 11327.031 6.863 75 945275.025 7.200 - 16 46 30 76 Zn -62303.016 1.456 8582.273 0.019 B- 3993.624 2.438 75 933114.957 1.562 - 14 45 31 76 Ga x -66296.640 1.956 8624.526 0.026 B- 6916.249 1.956 75 928827.625 2.100 - 12 44 32 76 Ge -73212.889 0.018 8705.236 0.000 B- -921.512 0.886 75 921402.726 0.019 - 10 43 33 76 As -n -72291.377 0.886 8682.816 0.012 B- 2960.573 0.886 75 922392.010 0.951 - 8 42 34 76 Se -75251.950 0.016 8711.477 0.000 B- -4962.881 9.322 75 919213.704 0.017 - 6 41 35 76 Br - -70289.068 9.322 8635.882 0.123 B- -1275.355 10.149 75 924541.577 10.007 - 4 40 36 76 Kr -69013.714 4.013 8608.807 0.053 B- -8534.633 4.121 75 925910.726 4.308 - 2 39 37 76 Rb x -60479.081 0.938 8486.215 0.012 B- -6231.442 34.478 75 935073.032 1.006 - 0 38 38 76 Sr x -54247.639 34.465 8393.929 0.453 B- -15768# 302# 75 941762.761 37.000 - -2 37 39 76 Y x -38480# 300# 8176# 4# B- * 75 958690# 322# -0 23 50 27 77 Co x -21015# 600# 8070# 8# B- 15785# 781# 76 977440# 644# - 21 49 28 77 Ni x -36800# 500# 8265# 6# B- 11824# 522# 76 960494# 537# - 19 48 29 77 Cu x -48624# 149# 8408# 2# B- 10165# 149# 76 947800# 160# - 17 47 30 77 Zn -58789.195 1.973 8530.003 0.026 B- 7203.149 3.124 76 936887.199 2.117 - 15 46 31 77 Ga x -65992.344 2.422 8613.390 0.031 B- 5220.518 2.422 76 929154.300 2.600 - 13 45 32 77 Ge -n -71212.862 0.053 8671.028 0.001 B- 2703.456 1.694 76 923549.844 0.056 - 11 44 33 77 As -73916.318 1.693 8695.978 0.022 B- 683.170 1.693 76 920647.564 1.817 - 9 43 34 77 Se -74599.488 0.062 8694.690 0.001 B- -1364.680 2.810 76 919914.150 0.067 - 7 42 35 77 Br - -73234.809 2.811 8666.806 0.037 B- -3065.366 3.424 76 921379.194 3.017 - 5 41 36 77 Kr x -70169.443 1.956 8616.836 0.025 B- -5338.951 2.351 76 924670.000 2.100 - 3 40 37 77 Rb x -64830.492 1.304 8537.339 0.017 B- -7027.055 8.024 76 930401.600 1.400 - 1 39 38 77 Sr x -57803.436 7.918 8435.918 0.103 B- -11365# 203# 76 937945.455 8.500 - -1 38 39 77 Y -p -46439# 203# 8278# 3# B- -14399# 448# 76 950146# 218# - -3 37 40 77 Zr x -32040# 400# 8081# 5# B- * 76 965604# 429# -0 22 50 28 78 Ni x -33890# 600# 8225# 8# B- 10608# 783# 77 963618# 644# - 20 49 29 78 Cu x -44497.469 503.007 8350.925 6.449 B- 12985.766 503.011 77 952230.000 540.000 - 18 48 30 78 Zn -57483.235 1.944 8507.379 0.025 B- 6222.716 2.719 77 938289.205 2.086 - 16 47 31 78 Ga -63705.950 1.903 8577.127 0.024 B- 8156.099 4.015 77 931608.845 2.043 - 14 46 32 78 Ge -nn -71862.050 3.536 8671.663 0.045 B- 954.890 10.400 77 922852.912 3.795 - 12 45 33 78 As +pn -72816.940 9.781 8673.875 0.125 B- 4209.004 9.782 77 921827.795 10.500 - 10 44 34 78 Se -77025.944 0.179 8717.806 0.002 B- -3573.784 3.575 77 917309.243 0.191 - 8 43 35 78 Br - -73452.160 3.580 8661.959 0.046 B- 726.116 3.584 77 921145.859 3.842 - 6 42 36 78 Kr -74178.275 0.307 8661.238 0.004 B- -7242.857 3.252 77 920366.341 0.329 - 4 41 37 78 Rb x -66935.419 3.237 8558.350 0.042 B- -3761.477 8.125 77 928141.868 3.475 - 2 40 38 78 Sr x -63173.941 7.452 8500.096 0.096 B- -11001# 298# 77 932179.980 8.000 - 0 39 39 78 Y x -52173# 298# 8349# 4# B- -11323# 499# 77 943990# 320# - -2 38 40 78 Zr x -40850# 400# 8194# 5# B- * 77 956146# 429# -0 23 51 28 79 Ni x -27570# 600# 8143# 8# B- 14170# 671# 78 970402# 644# - 21 50 29 79 Cu x -41740# 300# 8312# 4# B- 11692# 300# 78 955190# 322# - 19 49 30 79 Zn -53432.295 2.225 8450.582 0.028 B- 9115.384 2.901 78 942638.068 2.388 - 17 48 31 79 Ga -62547.679 1.868 8556.063 0.024 B- 6978.913 37.147 78 932852.301 2.005 - 15 47 32 79 Ge -69526.592 37.181 8634.501 0.471 B- 4109.457 37.456 78 925360.129 39.915 - 13 46 33 79 As -73636.049 5.328 8676.616 0.067 B- 2281.410 5.331 78 920948.445 5.719 - 11 45 34 79 Se -n -75917.459 0.223 8695.592 0.003 B- 150.576 1.038 78 918499.251 0.238 - 9 44 35 79 Br +n -76068.035 1.021 8687.594 0.013 B- -1625.778 3.333 78 918337.601 1.095 - 7 43 36 79 Kr - -74442.257 3.486 8657.112 0.044 B- -3639.271 4.092 78 920082.945 3.742 - 5 42 37 79 Rb x -70802.985 2.142 8601.142 0.027 B- -5326.096 8.653 78 923989.864 2.300 - 3 41 38 79 Sr x -65476.889 8.383 8523.820 0.106 B- -7659.056 79.620 78 929707.664 9.000 - 1 40 39 79 Y x -57817.833 79.177 8416.967 1.002 B- -11048# 310# 78 937930.000 85.000 - -1 39 40 79 Zr x -46770# 300# 8267# 4# B- -15120# 583# 78 949790# 322# - -3 38 41 79 Nb x -31650# 500# 8066# 6# B- * 78 966022# 537# -0 24 52 28 80 Ni x -22630# 700# 8080# 9# B- 13570# 806# 79 975706# 751# - 22 51 29 80 Cu x -36200# 400# 8240# 5# B- 15449# 400# 79 961138# 429# - 20 50 30 80 Zn -51648.612 2.585 8423.545 0.032 B- 7575.055 3.877 79 944552.930 2.774 - 18 49 31 80 Ga x -59223.667 2.891 8508.454 0.036 B- 10311.639 3.541 79 936420.774 3.103 - 16 48 32 80 Ge x -69535.306 2.054 8627.570 0.026 B- 2679.187 3.915 79 925350.774 2.205 - 14 47 33 80 As x -72214.493 3.333 8651.280 0.042 B- 5544.964 3.445 79 922474.548 3.577 - 12 46 34 80 Se -77759.457 0.963 8710.813 0.012 B- -1870.464 0.310 79 916521.785 1.034 - 10 45 35 80 Br - -75888.993 1.012 8677.653 0.013 B- 2004.353 1.154 79 918529.810 1.086 - 8 44 36 80 Kr -77893.346 0.691 8692.928 0.009 B- -5717.879 1.987 79 916378.048 0.742 - 6 43 37 80 Rb x -72175.467 1.863 8611.675 0.023 B- -1864.009 3.933 79 922516.444 2.000 - 4 42 38 80 Sr x -70311.459 3.464 8578.596 0.043 B- -9163.307 7.139 79 924517.540 3.718 - 2 41 39 80 Y x -61148.152 6.242 8454.275 0.078 B- -6788# 300# 79 934354.755 6.701 - 0 40 40 80 Zr x -54360# 300# 8360# 4# B- -15940# 500# 79 941642# 322# - -2 39 41 80 Nb x -38420# 400# 8151# 5# B- * 79 958754# 429# -0 23 52 29 81 Cu x -31420# 500# 8179# 6# B- 14779# 500# 80 966269# 537# - 21 51 30 81 Zn x -46199.663 5.030 8351.925 0.062 B- 11428.292 5.996 80 950402.619 5.400 - 19 50 31 81 Ga x -57627.954 3.264 8483.357 0.040 B- 8663.733 3.851 80 938133.842 3.503 - 17 49 32 81 Ge x -66291.687 2.055 8580.658 0.025 B- 6241.617 3.344 80 928832.942 2.205 - 15 48 33 81 As -72533.304 2.644 8648.056 0.033 B- 3855.684 2.812 80 922132.290 2.838 - 13 47 34 81 Se -76388.988 0.992 8685.999 0.012 B- 1588.046 1.389 80 917993.044 1.065 - 11 46 35 81 Br -77977.034 0.978 8695.946 0.012 B- -280.853 0.471 80 916288.206 1.049 - 9 45 36 81 Kr -77696.181 1.074 8682.820 0.013 B- -2239.511 5.019 80 916589.714 1.152 - 7 44 37 81 Rb -75456.670 4.904 8645.513 0.061 B- -3928.545 5.817 80 918993.927 5.264 - 5 43 38 81 Sr x -71528.125 3.128 8587.354 0.039 B- -5815.214 6.245 80 923211.394 3.358 - 3 42 39 81 Y x -65712.912 5.405 8505.902 0.067 B- -8252.773 94.236 80 929454.283 5.802 - 1 41 40 81 Zr x -57460.139 94.081 8394.358 1.161 B- -11100# 411# 80 938314.000 101.000 - -1 40 41 81 Nb x -46360# 400# 8248# 5# B- -14610# 640# 80 950230# 429# - -3 39 42 81 Mo x -31750# 500# 8058# 6# B- * 80 965915# 537# -0 24 53 29 82 Cu x -25320# 600# 8103# 7# B- 16994# 600# 81 972818# 644# - 22 52 30 82 Zn x -42313.954 3.074 8301.117 0.037 B- 10616.764 3.916 81 954574.099 3.300 - 20 51 31 82 Ga x -52930.719 2.426 8421.049 0.030 B- 12484.348 3.296 81 943176.533 2.604 - 18 50 32 82 Ge x -65415.067 2.241 8563.756 0.027 B- 4690.352 4.345 81 929774.033 2.405 - 16 49 33 82 As x -70105.419 3.729 8611.414 0.045 B- 7488.463 3.758 81 924738.733 4.003 - 14 48 34 82 Se -77593.882 0.467 8693.196 0.006 B- -95.221 1.077 81 916699.537 0.500 - 12 47 35 82 Br -77498.661 0.971 8682.494 0.012 B- 3093.124 0.971 81 916801.760 1.042 - 10 46 36 82 Kr -80591.78515 0.00549 8710.675 0.000 B- -4403.982 3.009 81 913481.15520 0.00589 - 8 45 37 82 Rb IT -76187.803 3.009 8647.427 0.037 B- -177.751 6.705 81 918209.024 3.230 - 6 44 38 82 Sr -76010.053 5.992 8635.718 0.073 B- -7945.961 8.132 81 918399.847 6.432 - 4 43 39 82 Y x -68064.091 5.499 8529.275 0.067 B- -4432.804 12.457 81 926930.188 5.902 - 2 42 40 82 Zr x -63631.287 11.178 8465.676 0.136 B- -11541# 300# 81 931689.000 12.000 - 0 41 41 82 Nb x -52090# 300# 8315# 4# B- -11720# 500# 81 944079# 322# - -2 40 42 82 Mo x -40370# 400# 8163# 5# B- * 81 956661# 429# -0 23 53 30 83 Zn x -36290# 300# 8226# 4# B- 12967# 300# 82 961041# 322# - 21 52 31 83 Ga x -49257.122 2.613 8372.575 0.031 B- 11719.312 3.559 82 947120.301 2.804 - 19 51 32 83 Ge x -60976.435 2.427 8504.345 0.029 B- 8692.888 3.698 82 934539.101 2.605 - 17 50 33 83 As x -69669.323 2.799 8599.653 0.034 B- 5671.207 4.129 82 925206.901 3.004 - 15 49 34 83 Se -n -75340.530 3.036 8658.555 0.037 B- 3673.179 4.839 82 919118.609 3.259 - 13 48 35 83 Br -79013.709 3.795 8693.384 0.046 B- 976.924 3.795 82 915175.289 4.073 - 11 47 36 83 Kr -79990.633 0.009 8695.729 0.000 B- -920.004 2.329 82 914126.518 0.009 - 9 46 37 83 Rb -79070.630 2.329 8675.218 0.028 B- -2273.024 6.424 82 915114.182 2.500 - 7 45 38 83 Sr -76797.606 6.834 8638.407 0.082 B- -4591.941 19.844 82 917554.374 7.336 - 5 44 39 83 Y x -72205.665 18.631 8573.656 0.224 B- -6294.012 19.707 82 922484.025 20.000 - 3 43 40 83 Zr x -65911.654 6.430 8488.399 0.077 B- -8355.571 151.039 82 929240.925 6.902 - 1 42 41 83 Nb x -57556.083 150.902 8378.304 1.818 B- -11216# 428# 82 938211.000 162.000 - -1 41 42 83 Mo x -46340# 401# 8234# 5# B- -15020# 641# 82 950252# 430# - -3 40 43 83 Tc x -31320# 500# 8043# 6# B- * 82 966377# 537# -0 24 54 30 84 Zn x -31930# 400# 8172# 5# B- 12158# 447# 83 965722# 429# - 22 53 31 84 Ga x -44088# 200# 8307# 2# B- 14061# 200# 83 952670# 215# - 20 52 32 84 Ge x -58148.428 3.171 8465.524 0.038 B- 7705.132 4.479 83 937575.091 3.403 - 18 51 33 84 As x -65853.560 3.171 8547.938 0.038 B- 10094.161 3.722 83 929303.291 3.403 - 16 50 34 84 Se -75947.721 1.961 8658.793 0.023 B- 1835.363 25.765 83 918466.762 2.105 - 14 49 35 84 Br -77783.084 25.730 8671.329 0.306 B- 4656.251 25.730 83 916496.419 27.622 - 12 48 36 84 Kr -82439.33510 0.00379 8717.446 0.000 B- -2680.371 2.194 83 911497.72863 0.00407 - 10 47 37 84 Rb -79758.964 2.194 8676.224 0.026 B- 890.606 2.336 83 914375.225 2.355 - 8 46 38 84 Sr -80649.570 1.243 8677.512 0.015 B- -6755.139 4.411 83 913419.120 1.334 - 6 45 39 84 Y -73894.431 4.299 8587.780 0.051 B- -2472.745 6.977 83 920671.061 4.615 - 4 44 40 84 Zr x -71421.686 5.499 8549.029 0.065 B- -10202.968 14.153 83 923325.662 5.903 - 2 43 41 84 Nb x -61218.717 13.041 8418.252 0.155 B- -7049# 298# 83 934279.000 14.000 - 0 42 42 84 Mo x -54170# 298# 8325# 4# B- -16470# 499# 83 941846# 320# - -2 41 43 84 Tc x -37700# 400# 8120# 5# B- * 83 959527# 429# -0 25 55 30 85 Zn x -25230# 500# 8092# 6# B- 14619# 582# 84 972914# 537# - 23 54 31 85 Ga x -39849# 298# 8255# 4# B- 13274# 298# 84 957220# 320# - 21 53 32 85 Ge x -53123.420 3.729 8401.768 0.044 B- 10065.724 4.830 84 942969.659 4.003 - 19 52 33 85 As x -63189.144 3.078 8510.984 0.036 B- 9224.492 4.031 84 932163.659 3.304 - 17 51 34 85 Se +3p -72413.636 2.613 8610.304 0.031 B- 6161.833 4.031 84 922260.759 2.804 - 15 50 35 85 Br +n2p -78575.469 3.078 8673.592 0.036 B- 2904.861 3.671 84 915645.759 3.304 - 13 49 36 85 Kr + -81480.331 2.000 8698.562 0.024 B- 687.000 2.000 84 912527.262 2.147 - 11 48 37 85 Rb -82167.33050 0.00498 8697.441 0.000 B- -1064.051 2.813 84 911789.73760 0.00534 - 9 47 38 85 Sr -81103.280 2.813 8675.718 0.033 B- -3261.157 19.173 84 912932.043 3.020 - 7 46 39 85 Y x -77842.123 18.965 8628.148 0.223 B- -4666.934 20.026 84 916433.039 20.360 - 5 45 40 85 Zr x -73175.189 6.430 8564.039 0.076 B- -6895.514 7.625 84 921443.198 6.902 - 3 44 41 85 Nb x -66279.676 4.099 8473.711 0.048 B- -8769.923 16.357 84 928845.837 4.400 - 1 43 42 85 Mo x -57509.753 15.835 8361.331 0.186 B- -11660# 400# 84 938260.737 17.000 - -1 42 43 85 Tc x -45850# 400# 8215# 5# B- -14900# 640# 84 950778# 429# - -3 41 44 85 Ru x -30950# 500# 8030# 6# B- * 84 966774# 537# -0 24 55 31 86 Ga x -34080# 400# 8186# 5# B- 15320# 593# 85 963414# 429# - 22 54 32 86 Ge x -49399.922 437.802 8354.629 5.091 B- 9562.221 437.816 85 946967.000 470.000 - 20 53 33 86 As x -58962.142 3.450 8456.721 0.040 B- 11541.024 4.267 85 936701.533 3.703 - 18 52 34 86 Se x -70503.167 2.520 8581.822 0.029 B- 5129.085 3.972 85 924311.733 2.705 - 16 51 35 86 Br +pp -75632.252 3.078 8632.365 0.036 B- 7633.414 3.078 85 918805.433 3.304 - 14 50 36 86 Kr -83265.66564 0.00369 8712.029 0.000 B- -518.672 0.200 85 910610.62627 0.00396 - 12 49 37 86 Rb -n -82746.993 0.200 8696.901 0.002 B- 1776.096 0.200 85 911167.443 0.214 - 10 48 38 86 Sr -84523.08935 0.00522 8708.456 0.000 B- -5240.000 14.142 85 909260.72631 0.00561 - 8 47 39 86 Y - -79283.089 14.142 8638.428 0.164 B- -1314.075 14.585 85 914886.098 15.182 - 6 46 40 86 Zr -77969.014 3.566 8614.051 0.041 B- -8834.960 6.552 85 916296.815 3.827 - 4 45 41 86 Nb x -69134.054 5.499 8502.222 0.064 B- -5023.810 6.642 85 925781.535 5.903 - 2 44 42 86 Mo x -64110.245 3.726 8434.709 0.043 B- -12540# 300# 85 931174.817 4.000 - 0 43 43 86 Tc x -51570# 300# 8280# 3# B- -11800# 500# 85 944637# 322# - -2 42 44 86 Ru x -39770# 400# 8133# 5# B- * 85 957305# 429# -0 25 56 31 87 Ga x -29250# 500# 8129# 6# B- 14828# 583# 86 968599# 537# - 23 55 32 87 Ge x -44078# 300# 8290# 3# B- 11540# 300# 86 952680# 322# - 21 54 33 87 As x -55617.907 2.985 8413.851 0.034 B- 10808.218 3.726 86 940291.718 3.204 - 19 53 34 87 Se x -66426.125 2.241 8529.091 0.026 B- 7465.552 3.877 86 928688.618 2.405 - 17 52 35 87 Br 2p-n -73891.676 3.171 8605.910 0.036 B- 6817.845 3.181 86 920674.018 3.404 - 15 51 36 87 Kr -n -80709.522 0.246 8675.283 0.003 B- 3888.269 0.246 86 913354.759 0.264 - 13 50 37 87 Rb -84597.791 0.006 8710.983 0.000 B- 282.275 0.006 86 909180.531 0.006 - 11 49 38 87 Sr -84880.06595 0.00510 8705.236 0.000 B- -1861.690 1.128 86 908877.49615 0.00548 - 9 48 39 87 Y - -83018.376 1.128 8674.844 0.013 B- -3671.239 4.296 86 910876.102 1.210 - 7 47 40 87 Zr -79347.137 4.146 8623.654 0.048 B- -5472.651 7.963 86 914817.339 4.450 - 5 46 41 87 Nb x -73874.486 6.802 8551.757 0.078 B- -6989.678 7.378 86 920692.472 7.302 - 3 45 42 87 Mo -66884.808 2.857 8462.424 0.033 B- -9194.764 5.073 86 928196.201 3.067 - 1 44 43 87 Tc x -57690.044 4.192 8347.744 0.048 B- -12170# 400# 86 938067.187 4.500 - -1 43 44 87 Ru x -45520# 400# 8199# 5# B- * 86 951132# 429# -0 24 56 32 88 Ge x -40138# 400# 8243# 5# B- 10582# 445# 87 956910# 429# - 22 55 33 88 As x -50720# 196# 8354# 2# B- 13164# 196# 87 945550# 210# - 20 54 34 88 Se x -63884.195 3.357 8495.004 0.038 B- 6831.763 4.613 87 931417.491 3.604 - 18 53 35 88 Br ++ -70715.959 3.171 8563.747 0.036 B- 8975.327 4.106 87 924083.291 3.404 - 16 52 36 88 Kr x -79691.286 2.608 8656.849 0.030 B- 2917.709 2.613 87 914447.881 2.800 - 14 51 37 88 Rb -82608.995 0.159 8681.115 0.002 B- 5312.623 0.159 87 911315.591 0.171 - 12 50 38 88 Sr -87921.61793 0.00558 8732.595 0.000 B- -3622.600 1.500 87 905612.25561 0.00599 - 10 49 39 88 Y - -84299.018 1.500 8682.539 0.017 B- -670.147 5.608 87 909501.276 1.610 - 8 48 40 88 Zr -83628.871 5.403 8666.033 0.061 B- -7455.284 58.886 87 910220.709 5.800 - 6 47 41 88 Nb -76173.586 58.810 8572.424 0.668 B- -3487.042 58.933 87 918224.287 63.134 - 4 46 42 88 Mo x -72686.544 3.819 8523.908 0.043 B- -11005.229 149.088 87 921967.781 4.100 - 2 45 43 88 Tc x -61681.315 149.039 8389.958 1.694 B- -7342# 335# 87 933782.381 160.000 - 0 44 44 88 Ru x -54340# 300# 8298# 3# B- -17479# 500# 87 941664# 322# - -2 43 45 88 Rh x -36860# 400# 8090# 5# B- * 87 960429# 429# -0 25 57 32 89 Ge x -33729# 400# 8169# 4# B- 13069# 499# 88 963790# 429# - 23 56 33 89 As x -46798# 298# 8307# 3# B- 12194# 298# 88 949760# 320# - 21 55 34 89 Se x -58992.391 3.729 8435.279 0.042 B- 9281.872 4.951 88 936669.059 4.003 - 19 54 35 89 Br x -68274.263 3.264 8530.779 0.037 B- 8261.522 3.904 88 926704.559 3.504 - 17 53 36 89 Kr x -76535.785 2.142 8614.815 0.024 B- 5176.604 5.834 88 917835.450 2.300 - 15 52 37 89 Rb -81712.388 5.427 8664.189 0.061 B- 4496.628 5.427 88 912278.137 5.825 - 13 51 38 89 Sr -86209.017 0.092 8705.922 0.001 B- 1499.336 1.615 88 907450.808 0.098 - 11 50 39 89 Y -87708.352 1.612 8713.978 0.018 B- -2832.792 2.776 88 905841.205 1.730 - 9 49 40 89 Zr -84875.561 3.083 8673.359 0.035 B- -4250.351 23.743 88 908882.332 3.310 - 7 48 41 89 Nb -80625.209 23.631 8616.812 0.266 B- -5610.275 23.953 88 913445.272 25.369 - 5 47 42 89 Mo x -75014.935 3.912 8544.984 0.044 B- -7620.087 5.467 88 919468.150 4.200 - 3 46 43 89 Tc x -67394.848 3.819 8450.575 0.043 B- -9135# 298# 88 927648.650 4.100 - 1 45 44 89 Ru x -58260# 298# 8339# 3# B- -12400# 468# 88 937455# 320# - -1 44 45 89 Rh -p -45861# 361# 8191# 4# B- * 88 950767# 387# -0 26 58 32 90 Ge x -29221# 500# 8118# 6# B- 12109# 640# 89 968630# 537# - 24 57 33 90 As x -41330# 400# 8244# 4# B- 14470# 518# 89 955630# 429# - 22 56 34 90 Se x -55800.217 329.749 8395.766 3.664 B- 8200.081 329.766 89 940096.000 354.000 - 20 55 35 90 Br x -64000.298 3.357 8478.186 0.037 B- 10958.952 3.840 89 931292.850 3.604 - 18 54 36 90 Kr x -74959.250 1.863 8591.259 0.021 B- 4405.154 6.746 89 919527.930 2.000 - 16 53 37 90 Rb -79364.404 6.484 8631.512 0.072 B- 6583.723 6.544 89 914798.803 6.960 - 14 52 38 90 Sr -85948.127 2.124 8695.972 0.024 B- 545.934 1.406 89 907730.885 2.280 - 12 51 39 90 Y -86494.062 1.611 8693.345 0.018 B- 2278.474 1.609 89 907144.800 1.729 - 10 50 40 90 Zr -88772.535 0.118 8709.969 0.001 B- -6111.016 3.316 89 904698.758 0.126 - 8 49 41 90 Nb -82661.519 3.317 8633.376 0.037 B- -2489.016 3.316 89 911259.204 3.561 - 6 48 42 90 Mo -80172.503 3.463 8597.028 0.038 B- -9447.816 3.611 89 913931.272 3.717 - 4 47 43 90 Tc x -70724.687 1.025 8483.359 0.011 B- -5840.895 3.869 89 924073.921 1.100 - 2 46 44 90 Ru -64883.792 3.730 8409.768 0.041 B- -13184# 300# 89 930344.379 4.004 - 0 45 45 90 Rh x -51700# 300# 8255# 3# B- -11990# 500# 89 944498# 322# - -2 44 46 90 Pd x -39710# 400# 8113# 4# B- * 89 957370# 429# -0 25 58 33 91 As x -36896# 400# 8193# 4# B- 13684# 589# 90 960390# 429# - 23 57 34 91 Se x -50580.124 433.145 8334.837 4.760 B- 10527.169 433.159 90 945700.000 465.000 - 21 56 35 91 Br -n2p -61107.294 3.544 8441.923 0.039 B- 9866.671 4.190 90 934398.618 3.804 - 19 55 36 91 Kr x -70973.965 2.236 8541.751 0.025 B- 6771.072 8.115 90 923806.310 2.400 - 17 54 37 91 Rb -77745.037 7.801 8607.561 0.086 B- 5906.890 8.873 90 916537.265 8.375 - 15 53 38 91 Sr -83651.927 5.453 8663.875 0.060 B- 2699.369 5.247 90 910195.958 5.853 - 13 52 39 91 Y -86351.295 1.843 8684.941 0.020 B- 1544.271 1.840 90 907298.066 1.978 - 11 51 40 91 Zr -87895.566 0.105 8693.314 0.001 B- -1257.565 2.924 90 905640.223 0.112 - 9 50 41 91 Nb -86638.001 2.926 8670.897 0.032 B- -4429.180 6.744 90 906990.274 3.141 - 7 49 42 91 Mo -82208.821 6.238 8613.628 0.069 B- -6222.175 6.671 90 911745.195 6.696 - 5 48 43 91 Tc -75986.646 2.363 8536.655 0.026 B- -7746.824 3.242 90 918424.975 2.536 - 3 47 44 91 Ru -68239.823 2.221 8442.928 0.024 B- -9670# 298# 90 926741.532 2.384 - 1 46 45 91 Rh x -58570# 298# 8328# 3# B- -12639# 499# 90 937123# 320# - -1 45 46 91 Pd x -45930# 401# 8181# 4# B- * 90 950692# 430# -0 26 59 33 92 As x -30981# 500# 8127# 5# B- 15742# 640# 91 966740# 537# - 24 58 34 92 Se x -46724# 400# 8290# 4# B- 9509# 400# 91 949840# 429# - 22 57 35 92 Br x -56232.805 6.709 8384.911 0.073 B- 12536.514 7.232 91 939631.597 7.202 - 20 56 36 92 Kr x -68769.320 2.701 8512.674 0.029 B- 6003.118 6.692 91 926173.094 2.900 - 18 55 37 92 Rb -74772.438 6.123 8569.422 0.067 B- 8094.923 6.419 91 919728.481 6.573 - 16 54 38 92 Sr -82867.361 3.423 8648.906 0.037 B- 1949.132 9.384 91 911038.224 3.675 - 14 53 39 92 Y -84816.492 9.127 8661.589 0.099 B- 3642.535 9.127 91 908945.745 9.798 - 12 52 40 92 Zr -88459.028 0.102 8692.678 0.001 B- -2005.736 1.782 91 905035.322 0.109 - 10 51 41 92 Nb -86453.292 1.785 8662.372 0.019 B- 355.284 1.791 91 907188.568 1.915 - 8 50 42 92 Mo -86808.576 0.157 8657.730 0.002 B- -7882.884 3.106 91 906807.155 0.168 - 6 49 43 92 Tc -78925.693 3.102 8563.543 0.034 B- -4624.492 4.125 91 915269.779 3.330 - 4 48 44 92 Ru -74301.201 2.718 8504.773 0.030 B- -11302.114 5.153 91 920234.375 2.917 - 2 47 45 92 Rh x -62999.087 4.378 8373.420 0.048 B- -8419# 300# 91 932367.694 4.700 - 0 46 46 92 Pd x -54580# 300# 8273# 3# B- -17450# 583# 91 941406# 322# - -2 45 47 92 Ag x -37130# 500# 8075# 5# B- * 91 960139# 537# -0 25 59 34 93 Se x -40716# 400# 8223# 4# B- 12175# 588# 92 956290# 429# - 23 58 35 93 Br x -52890.230 430.816 8345.598 4.632 B- 11245.765 430.823 92 943220.000 462.500 - 21 57 36 93 Kr x -64135.994 2.515 8458.108 0.027 B- 8483.907 8.224 92 931147.174 2.700 - 19 56 37 93 Rb -72619.901 7.830 8540.920 0.084 B- 7465.938 8.876 92 922039.325 8.406 - 17 55 38 93 Sr -80085.838 7.554 8612.787 0.081 B- 4141.319 11.697 92 914024.311 8.109 - 15 54 39 93 Y -84227.157 10.488 8648.905 0.113 B- 2894.875 10.483 92 909578.422 11.259 - 13 53 40 93 Zr -87122.032 0.457 8671.620 0.005 B- 90.806 1.484 92 906470.646 0.490 - 11 52 41 93 Nb -87212.838 1.491 8664.184 0.016 B- -405.769 1.501 92 906373.161 1.600 - 9 51 42 93 Mo -n -86807.069 0.181 8651.409 0.002 B- -3200.963 1.004 92 906808.773 0.193 - 7 50 43 93 Tc -p -83606.106 1.012 8608.577 0.011 B- -6389.393 2.299 92 910245.149 1.086 - 5 49 44 93 Ru -77216.713 2.065 8531.462 0.022 B- -8204.913 3.343 92 917104.444 2.216 - 3 48 45 93 Rh -69011.800 2.629 8434.825 0.028 B- -10011# 301# 92 925912.781 2.821 - 1 47 46 93 Pd +p -59001# 300# 8319# 3# B- -12734# 501# 92 936660# 323# - -1 46 47 93 Ag x -46267# 401# 8173# 4# B- * 92 950330# 430# -0 26 60 34 94 Se x -36803# 500# 8180# 5# B- 10597# 583# 93 960490# 537# - 24 59 35 94 Br x -47400# 300# 8284# 3# B- 13948# 300# 93 949114# 322# - 22 58 36 94 Kr x -61347.772 12.109 8424.331 0.129 B- 7215.013 12.278 93 934140.454 13.000 - 20 57 37 94 Rb -68562.785 2.029 8492.764 0.022 B- 10282.926 2.623 93 926394.818 2.177 - 18 56 38 94 Sr -78845.711 1.663 8593.834 0.018 B- 3505.752 6.422 93 915355.643 1.785 - 16 55 39 94 Y -82351.463 6.380 8622.806 0.068 B- 4917.859 6.380 93 911592.063 6.849 - 14 54 40 94 Zr -87269.322 0.164 8666.801 0.002 B- -900.260 1.500 93 906312.524 0.175 - 12 53 41 94 Nb -86369.062 1.491 8648.901 0.016 B- 2045.002 1.494 93 907278.992 1.601 - 10 52 42 94 Mo -88414.065 0.141 8662.333 0.002 B- -4255.748 4.069 93 905083.592 0.151 - 8 51 43 94 Tc - -84158.317 4.071 8608.736 0.043 B- -1574.726 5.143 93 909652.325 4.370 - 6 50 44 94 Ru -82583.591 3.143 8583.661 0.033 B- -9675.978 4.615 93 911342.863 3.374 - 4 49 45 94 Rh -72907.613 3.379 8472.402 0.036 B- -6805.345 5.459 93 921730.453 3.627 - 2 48 46 94 Pd x -66102.268 4.287 8391.682 0.046 B- -13693# 400# 93 929036.292 4.602 - 0 47 47 94 Ag x -52410# 400# 8238# 4# B- -12270# 640# 93 943736# 429# - -2 46 48 94 Cd x -40140# 500# 8099# 5# B- * 93 956908# 537# -0 27 61 34 95 Se x -30460# 500# 8112# 5# B- 13311# 582# 94 967300# 537# - 25 60 35 95 Br x -43771# 298# 8244# 3# B- 12388# 299# 94 953010# 320# - 23 59 36 95 Kr x -56158.913 18.630 8365.995 0.196 B- 9732.580 27.513 94 939710.923 20.000 - 21 58 37 95 Rb -65891.493 20.245 8460.208 0.213 B- 9228.058 20.204 94 929262.568 21.734 - 19 57 38 95 Sr -75119.551 5.812 8549.111 0.061 B- 6089.296 7.240 94 919355.840 6.239 - 17 56 39 95 Y -81208.848 6.779 8604.973 0.071 B- 4451.092 6.772 94 912818.711 7.277 - 15 55 40 95 Zr -85659.940 0.869 8643.592 0.009 B- 1126.318 0.985 94 908040.267 0.933 - 13 54 41 95 Nb -86786.258 0.508 8647.212 0.005 B- 925.601 0.494 94 906831.115 0.545 - 11 53 42 95 Mo -87711.858 0.123 8648.720 0.001 B- -1690.518 5.078 94 905837.442 0.132 - 9 52 43 95 Tc -86021.341 5.080 8622.690 0.053 B- -2563.596 10.531 94 907652.287 5.453 - 7 51 44 95 Ru -83457.745 9.502 8587.470 0.100 B- -5117.138 10.266 94 910404.420 10.200 - 5 50 45 95 Rh -78340.606 3.886 8525.370 0.041 B- -8374.706 4.928 94 915897.895 4.171 - 3 49 46 95 Pd x -69965.900 3.031 8428.980 0.032 B- -10369# 298# 94 924888.512 3.253 - 1 48 47 95 Ag x -59597# 298# 8312# 3# B- -12966# 499# 94 936020# 320# - -1 47 48 95 Cd x -46631# 401# 8167# 4# B- * 94 949940# 430# -0 26 61 35 96 Br x -38163# 298# 8184# 3# B- 14916# 299# 95 959030# 320# - 24 60 36 96 Kr x -53079.678 20.493 8330.851 0.213 B- 8274.671 20.765 95 943016.618 22.000 - 22 59 37 96 Rb -61354.349 3.353 8408.896 0.035 B- 11569.808 9.115 95 934133.393 3.599 - 20 58 38 96 Sr -72924.157 8.475 8521.265 0.088 B- 5411.738 9.726 95 921712.692 9.098 - 18 57 39 96 Y -78335.895 6.088 8569.488 0.063 B- 7102.951 6.087 95 915902.953 6.535 - 16 56 40 96 Zr -85438.846 0.114 8635.327 0.001 B- 163.971 0.100 95 908277.621 0.122 - 14 55 41 96 Nb -85602.816 0.147 8628.886 0.002 B- 3192.059 0.107 95 908101.591 0.157 - 12 54 42 96 Mo -88794.876 0.120 8653.987 0.001 B- -2973.242 5.145 95 904674.774 0.128 - 10 53 43 96 Tc - -85821.634 5.146 8614.866 0.054 B- 258.738 5.146 95 907866.681 5.524 - 8 52 44 96 Ru -86080.372 0.170 8609.412 0.002 B- -6392.654 10.000 95 907588.914 0.182 - 6 51 45 96 Rh - -79687.718 10.001 8534.673 0.104 B- -3504.312 10.844 95 914451.710 10.737 - 4 50 46 96 Pd x -76183.406 4.194 8490.020 0.044 B- -11671.771 90.181 95 918213.744 4.502 - 2 49 47 96 Ag ep -64511.636 90.084 8360.290 0.938 B- -8939# 411# 95 930743.906 96.708 - 0 48 48 96 Cd x -55573# 401# 8259# 4# B- -17683# 641# 95 940340# 430# - -2 47 49 96 In x -37890# 500# 8067# 5# B- * 95 959323# 537# -0 27 62 35 97 Br x -34055# 401# 8140# 4# B- 13368# 421# 96 963440# 430# - 25 61 36 97 Kr x -47423.492 130.409 8269.864 1.344 B- 11095.645 130.423 96 949088.784 140.000 - 23 60 37 97 Rb -58519.137 1.912 8376.186 0.020 B- 10062.317 3.888 96 937177.118 2.052 - 21 59 38 97 Sr -68581.454 3.385 8471.856 0.035 B- 7539.969 7.521 96 926374.776 3.633 - 19 58 39 97 Y + -76121.424 6.719 8541.522 0.069 B- 6821.237 6.707 96 918280.286 7.213 - 17 57 40 97 Zr -82942.661 0.414 8603.779 0.004 B- 2663.115 4.248 96 910957.386 0.444 - 15 56 41 97 Nb -85605.776 4.249 8623.168 0.044 B- 1938.915 4.248 96 908098.414 4.561 - 13 55 42 97 Mo -87544.691 0.165 8635.092 0.002 B- -320.266 4.117 96 906016.903 0.176 - 11 54 43 97 Tc -87224.424 4.118 8623.725 0.042 B- -1103.873 4.956 96 906360.723 4.420 - 9 53 44 97 Ru -n -86120.552 2.763 8604.279 0.028 B- -3523.000 35.355 96 907545.779 2.965 - 7 52 45 97 Rh - -82597.552 35.463 8559.894 0.366 B- -4791.709 35.792 96 911327.876 38.071 - 5 51 46 97 Pd x -77805.843 4.844 8502.430 0.050 B- -6980.000 110.000 96 916471.987 5.200 - 3 50 47 97 Ag - -70825.843 110.107 8422.405 1.135 B- -10372# 318# 96 923965.326 118.204 - 1 49 48 97 Cd x -60454# 298# 8307# 3# B- -13264# 499# 96 935100# 320# - -1 48 49 97 In x -47189# 401# 8163# 4# B- * 96 949340# 430# -0 28 63 35 98 Br x -28250# 400# 8080# 4# B- 16061# 499# 97 969672# 429# - 26 62 36 98 Kr x -44311# 298# 8236# 3# B- 10058# 299# 97 952430# 320# - 24 61 37 98 Rb -54369.146 16.083 8330.729 0.164 B- 12053.958 16.403 97 941632.317 17.265 - 22 60 38 98 Sr -66423.104 3.226 8445.745 0.033 B- 5871.673 8.558 97 928691.860 3.463 - 20 59 39 98 Y p-2n -72294.777 7.929 8497.677 0.081 B- 8991.932 11.576 97 922388.360 8.511 - 18 58 40 98 Zr -81286.709 8.451 8581.448 0.086 B- 2237.890 9.819 97 912735.124 9.072 - 16 57 41 98 Nb -pn -83524.598 5.001 8596.301 0.051 B- 4591.373 5.003 97 910332.650 5.369 - 14 56 42 98 Mo -88115.972 0.174 8635.168 0.002 B- -1683.766 3.377 97 905403.608 0.186 - 12 55 43 98 Tc -86432.205 3.380 8610.004 0.034 B- 1792.653 7.157 97 907211.205 3.628 - 10 54 44 98 Ru -88224.858 6.463 8620.313 0.066 B- -5049.653 10.000 97 905286.713 6.937 - 8 53 45 98 Rh - -83175.205 11.906 8560.803 0.121 B- -1854.229 12.816 97 910707.740 12.782 - 6 52 46 98 Pd -81320.975 4.742 8533.899 0.048 B- -8254.560 33.098 97 912698.337 5.090 - 4 51 47 98 Ag -73066.415 32.907 8441.686 0.336 B- -5430.000 40.000 97 921559.972 35.327 - 2 50 48 98 Cd - -67636.415 51.797 8378.295 0.529 B- -13740# 303# 97 927389.317 55.605 - 0 49 49 98 In x -53896# 298# 8230# 3# B- * 97 942140# 320# -0 27 63 36 99 Kr x -38759# 401# 8178# 4# B- 12362# 401# 98 958390# 430# - 25 62 37 99 Rb x -51121.143 4.031 8295.300 0.041 B- 11400.258 6.223 98 945119.192 4.327 - 23 61 38 99 Sr -62521.401 4.741 8402.552 0.048 B- 8128.424 8.138 98 932880.511 5.089 - 21 60 39 99 Y x -70649.825 6.627 8476.755 0.067 B- 6970.792 12.409 98 924154.288 7.114 - 19 59 40 99 Zr -77620.617 10.502 8539.264 0.106 B- 4714.724 15.950 98 916670.835 11.274 - 17 58 41 99 Nb +p -82335.341 12.004 8578.985 0.121 B- 3634.758 12.006 98 911609.371 12.886 - 15 57 42 99 Mo -85970.098 0.229 8607.797 0.002 B- 1357.764 0.890 98 907707.298 0.245 - 13 56 43 99 Tc -87327.862 0.908 8613.610 0.009 B- 297.519 0.946 98 906249.678 0.974 - 11 55 44 99 Ru -87625.381 0.344 8608.712 0.003 B- -2044.081 6.690 98 905930.278 0.369 - 9 54 45 99 Rh -85581.300 6.697 8580.163 0.068 B- -3398.649 8.008 98 908124.690 7.189 - 7 53 46 99 Pd -82182.651 4.981 8537.930 0.050 B- -5470.178 8.004 98 911773.290 5.347 - 5 52 47 99 Ag x -76712.473 6.265 8474.774 0.063 B- -6781.350 6.462 98 917645.768 6.725 - 3 51 48 99 Cd x -69931.123 1.584 8398.373 0.016 B- -8555# 298# 98 924925.847 1.700 - 1 50 49 99 In x -61376# 298# 8304# 3# B- -13432# 585# 98 934110# 320# - -1 49 50 99 Sn x -47944# 503# 8160# 5# B- * 98 948530# 540# -0 28 64 36 100 Kr x -35052# 401# 8140# 4# B- 11195# 401# 99 962370# 430# - 26 63 37 100 Rb x -46247.064 19.561 8244.320 0.196 B- 13573.838 20.831 99 950351.731 21.000 - 24 62 38 100 Sr -59820.903 7.160 8372.234 0.072 B- 7506.493 13.273 99 935779.615 7.686 - 22 61 39 100 Y x -67327.396 11.186 8439.476 0.112 B- 9050.041 13.830 99 927721.063 12.008 - 20 60 40 100 Zr -76377.437 8.149 8522.153 0.081 B- 3419.963 11.398 99 918005.444 8.748 - 18 59 41 100 Nb IT -79797.399 7.986 8548.529 0.080 B- 6395.626 7.992 99 914333.963 8.573 - 16 58 42 100 Mo -86193.025 0.302 8604.662 0.003 B- -172.080 1.371 99 907467.976 0.323 - 14 57 43 100 Tc -n -86020.945 1.351 8595.118 0.014 B- 3206.444 1.376 99 907652.711 1.450 - 12 56 44 100 Ru -89227.389 0.343 8619.359 0.003 B- -3636.262 18.123 99 904210.452 0.368 - 10 55 45 100 Rh -85591.126 18.125 8575.172 0.181 B- -378.348 25.289 99 908114.141 19.458 - 8 54 46 100 Pd -85212.778 17.638 8563.566 0.176 B- -7074.819 18.333 99 908520.315 18.935 - 6 53 47 100 Ag x -78137.959 5.000 8484.994 0.050 B- -3943.363 5.273 99 916115.445 5.367 - 4 52 48 100 Cd -74194.596 1.677 8437.737 0.017 B- -9881.624 182.517 99 920348.820 1.799 - 2 51 49 100 In -64312.972 182.519 8331.097 1.825 B- -7030.000 240.000 99 930957.180 195.942 - 0 50 50 100 Sn - -57282.972 301.518 8252.974 3.015 B- * 99 938504.196 323.693 -0 29 65 36 101 Kr x -29128# 503# 8081# 5# B- 13717# 541# 100 968730# 540# - 27 64 37 101 Rb + -42845# 200# 8209# 2# B- 12480# 200# 100 954004# 215# - 25 63 38 101 Sr x -55324.907 8.480 8324.740 0.084 B- 9736.095 11.055 100 940606.266 9.103 - 23 62 39 101 Y x -65061.002 7.092 8413.391 0.070 B- 8104.955 10.933 100 930154.138 7.614 - 21 61 40 101 Zr -73165.957 8.339 8485.892 0.083 B- 5725.534 9.143 100 921453.110 8.951 - 19 60 41 101 Nb x -78891.491 3.749 8534.835 0.037 B- 4628.458 3.738 100 915306.496 4.024 - 17 59 42 101 Mo -n -83519.949 0.309 8572.915 0.003 B- 2824.645 24.002 100 910337.641 0.331 - 15 58 43 101 Tc + -86344.594 24.004 8593.136 0.238 B- 1613.520 24.000 100 907305.260 25.768 - 13 57 44 101 Ru -87958.114 0.415 8601.365 0.004 B- -545.697 5.852 100 905573.075 0.445 - 11 56 45 101 Rh -87412.416 5.841 8588.216 0.058 B- -1980.284 3.903 100 906158.905 6.270 - 9 55 46 101 Pd -85432.132 4.588 8560.864 0.045 B- -4097.759 6.668 100 908284.828 4.925 - 7 54 47 101 Ag x -81334.374 4.838 8512.546 0.048 B- -5497.918 5.063 100 912683.953 5.193 - 5 53 48 101 Cd x -75836.456 1.490 8450.365 0.015 B- -7223# 196# 100 918586.211 1.600 - 3 52 49 101 In x -68614# 196# 8371# 2# B- -8308# 358# 100 926340# 210# - 1 51 50 101 Sn ep -60305.626 300.005 8281.102 2.970 B- * 100 935259.244 322.068 -0 28 65 37 102 Rb x -37707# 298# 8157# 3# B- 14452# 306# 101 959520# 320# - 26 64 38 102 Sr x -52159.304 67.068 8291.220 0.658 B- 9013.873 67.191 101 944004.680 72.000 - 24 63 39 102 Y x -61173.177 4.077 8371.922 0.040 B- 10414.530 9.669 101 934327.889 4.377 - 22 62 40 102 Zr -71587.707 8.767 8466.355 0.086 B- 4716.837 9.053 101 923147.431 9.412 - 20 61 41 102 Nb -76304.544 2.545 8504.928 0.025 B- 7261.517 8.675 101 918083.697 2.732 - 18 60 42 102 Mo -83566.061 8.312 8568.450 0.081 B- 1006.817 12.373 101 910288.138 8.923 - 16 59 43 102 Tc -84572.878 9.166 8570.650 0.090 B- 4533.558 9.165 101 909207.275 9.840 - 14 58 44 102 Ru -89106.437 0.418 8607.427 0.004 B- -2323.119 6.396 101 904340.300 0.448 - 12 57 45 102 Rh - -86783.318 6.410 8576.981 0.063 B- 1119.853 6.406 101 906834.270 6.881 - 10 56 46 102 Pd -87903.171 0.554 8580.290 0.005 B- -5656.480 8.190 101 905632.058 0.594 - 8 55 47 102 Ag + -82246.691 8.171 8517.164 0.080 B- -2587.000 8.000 101 911704.540 8.771 - 6 54 48 102 Cd -79659.691 1.662 8484.131 0.016 B- -8964.807 4.865 101 914481.799 1.784 - 4 53 49 102 In -70694.884 4.573 8388.571 0.045 B- -5760.000 100.000 101 924105.916 4.909 - 2 52 50 102 Sn - -64934.884 100.105 8324.430 0.981 B- * 101 930289.530 107.466 -0 29 66 37 103 Rb x -33608# 401# 8117# 4# B- 13814# 446# 102 963920# 430# - 27 65 38 103 Sr x -47422# 196# 8243# 2# B- 11035# 196# 102 949090# 210# - 25 64 39 103 Y x -58457.575 11.204 8342.638 0.109 B- 9357.759 14.518 102 937243.208 12.028 - 23 63 40 103 Zr x -67815.334 9.232 8425.895 0.090 B- 7213.337 10.036 102 927197.240 9.911 - 21 62 41 103 Nb x -75028.671 3.935 8488.331 0.038 B- 5931.999 10.036 102 919453.403 4.224 - 19 61 42 103 Mo x -80960.670 9.232 8538.328 0.090 B- 3643.197 13.471 102 913085.140 9.911 - 17 60 43 103 Tc +p -84603.867 9.810 8566.103 0.095 B- 2663.304 9.808 102 909174.008 10.531 - 15 59 44 103 Ru -87267.171 0.443 8584.365 0.004 B- 764.538 2.260 102 906314.833 0.475 - 13 58 45 103 Rh -88031.708 2.301 8584.192 0.022 B- -574.519 2.420 102 905494.068 2.470 - 11 57 46 103 Pd -n -87457.189 0.950 8571.019 0.009 B- -2654.498 4.207 102 906110.840 1.019 - 9 56 47 103 Ag x -84802.692 4.099 8537.651 0.040 B- -4151.075 4.481 102 908960.560 4.400 - 7 55 48 103 Cd -80651.616 1.811 8489.754 0.018 B- -6019.026 9.754 102 913416.923 1.943 - 5 54 49 103 In -74632.591 9.625 8423.721 0.093 B- -7660.000 70.000 102 919878.613 10.332 - 3 53 50 103 Sn - -66972.591 70.659 8341.757 0.686 B- -10794# 306# 102 928101.962 75.855 - 1 52 51 103 Sb x -56178# 298# 8229# 3# B- * 102 939690# 320# -0 28 66 38 104 Sr x -44106# 298# 8210# 3# B- 9958# 499# 103 952650# 320# - 26 65 39 104 Y x -54064# 401# 8298# 4# B- 11660# 401# 103 941960# 430# - 24 64 40 104 Zr x -65724.060 9.325 8402.377 0.090 B- 6094.952 9.699 103 929442.315 10.011 - 22 63 41 104 Nb x -71819.012 2.737 8453.459 0.026 B- 8530.957 9.311 103 922899.115 2.938 - 20 62 42 104 Mo -80349.968 8.921 8527.965 0.086 B- 2153.476 24.167 103 913740.756 9.576 - 18 61 43 104 Tc -82503.444 24.888 8541.149 0.239 B- 5592.266 24.939 103 911428.905 26.718 - 16 60 44 104 Ru -88095.710 2.498 8587.399 0.024 B- -1136.362 3.364 103 905425.360 2.681 - 14 59 45 104 Rh -n -86959.348 2.303 8568.949 0.022 B- 2435.758 2.660 103 906645.295 2.472 - 12 58 46 104 Pd +n -89395.105 1.336 8584.848 0.013 B- -4278.654 4.000 103 904030.401 1.434 - 10 57 47 104 Ag - -85116.452 4.217 8536.184 0.041 B- -1148.072 4.537 103 908623.725 4.527 - 8 56 48 104 Cd -83968.380 1.673 8517.622 0.016 B- -7785.716 6.013 103 909856.230 1.795 - 6 55 49 104 In x -76182.665 5.775 8435.237 0.056 B- -4555.617 8.146 103 918214.540 6.200 - 4 54 50 104 Sn -71627.047 5.745 8383.911 0.055 B- -12453.427 122.579 103 923105.197 6.167 - 2 53 51 104 Sb -p -59173.620 122.444 8256.644 1.177 B- * 103 936474.502 131.449 -0 29 67 38 105 Sr x -38610# 503# 8156# 5# B- 12660# 1428# 104 958550# 540# - 27 66 39 105 Y x -51270.361 1336.694 8269.020 12.730 B- 10194.373 1336.749 104 944959.000 1435.000 - 25 65 40 105 Zr x -61464.734 12.118 8358.659 0.115 B- 8450.817 12.770 104 934014.890 13.008 - 23 64 41 105 Nb x -69915.551 4.028 8431.692 0.038 B- 7421.590 9.920 104 924942.564 4.324 - 21 63 42 105 Mo -77337.141 9.065 8494.923 0.086 B- 4952.947 35.031 104 916975.159 9.731 - 19 62 43 105 Tc -82290.088 35.264 8534.643 0.336 B- 3644.402 35.280 104 911657.952 37.857 - 17 61 44 105 Ru -85934.490 2.499 8561.900 0.024 B- 1916.752 2.851 104 907745.525 2.682 - 15 60 45 105 Rh -87851.243 2.502 8572.704 0.024 B- 566.646 2.346 104 905687.806 2.685 - 13 59 46 105 Pd -88417.888 1.138 8570.650 0.011 B- -1347.052 4.670 104 905079.487 1.222 - 11 58 47 105 Ag -87070.836 4.544 8550.370 0.043 B- -2736.997 4.362 104 906525.607 4.877 - 9 57 48 105 Cd -84333.839 1.392 8516.852 0.013 B- -4693.267 10.341 104 909463.895 1.494 - 7 56 49 105 In x -79640.572 10.246 8464.704 0.098 B- -6302.580 10.989 104 914502.324 11.000 - 5 55 50 105 Sn -73337.992 3.971 8397.228 0.038 B- -9322.510 22.185 104 921268.423 4.263 - 3 54 51 105 Sb +a -64015.482 21.827 8300.992 0.208 B- -11203.972 300.813 104 931276.549 23.431 - 1 53 52 105 Te -a -52811.510 300.020 8186.836 2.857 B- * 104 943304.508 322.084 -0 30 68 38 106 Sr x -34790# 600# 8119# 6# B- 11263# 783# 105 962651# 644# - 28 67 39 106 Y x -46053# 503# 8218# 5# B- 12497# 664# 105 950560# 540# - 26 66 40 106 Zr x -58549.987 433.145 8328.450 4.086 B- 7653.370 433.164 105 937144.000 465.000 - 24 65 41 106 Nb x -66203.357 4.122 8393.271 0.039 B- 9931.170 10.026 105 928927.768 4.424 - 22 64 42 106 Mo x -76134.528 9.140 8479.581 0.086 B- 3641.695 15.284 105 918266.218 9.812 - 20 63 43 106 Tc + -79776.223 12.250 8506.556 0.116 B- 6547.000 11.000 105 914356.697 13.150 - 18 62 44 106 Ru -86323.223 5.391 8560.940 0.051 B- 39.404 0.212 105 907328.203 5.787 - 16 61 45 106 Rh -86362.627 5.390 8553.931 0.051 B- 3544.901 5.335 105 907285.901 5.785 - 14 60 46 106 Pd -89907.527 1.106 8579.992 0.010 B- -2965.145 2.817 105 903480.293 1.186 - 12 59 47 106 Ag -86942.383 3.016 8544.639 0.028 B- 189.755 2.819 105 906663.507 3.237 - 10 58 48 106 Cd -87132.138 1.104 8539.048 0.010 B- -6524.004 12.176 105 906459.797 1.184 - 8 57 49 106 In - -80608.134 12.226 8470.120 0.115 B- -3254.447 13.244 105 913463.603 13.125 - 6 56 50 106 Sn -77353.687 5.091 8432.038 0.048 B- -10880.396 9.025 105 916957.396 5.465 - 4 55 51 106 Sb x -66473.292 7.452 8322.012 0.070 B- -8253.544 100.816 105 928637.982 8.000 - 2 54 52 106 Te -a -58219.748 100.541 8236.767 0.948 B- * 105 937498.526 107.934 -0 31 69 38 107 Sr x -28900# 700# 8064# 7# B- 13465# 862# 106 968975# 751# - 29 68 39 107 Y x -42364# 503# 8182# 5# B- 12015# 1230# 106 954520# 540# - 27 67 40 107 Zr x -54379.688 1122.450 8287.073 10.490 B- 9344.122 1122.479 106 941621.000 1205.000 - 25 66 41 107 Nb x -63723.810 8.023 8367.089 0.075 B- 8827.750 12.232 106 931589.672 8.612 - 23 65 42 107 Mo x -72551.560 9.233 8442.280 0.086 B- 6198.355 12.667 106 922112.692 9.912 - 21 64 43 107 Tc x -78749.914 8.673 8492.897 0.081 B- 5112.598 11.724 106 915458.485 9.310 - 19 63 44 107 Ru -nn -83862.512 8.673 8533.366 0.081 B- 3001.191 14.847 106 909969.885 9.310 - 17 62 45 107 Rh +p -86863.703 12.051 8554.103 0.113 B- 1508.936 12.111 106 906747.974 12.937 - 15 61 46 107 Pd -88372.639 1.201 8560.894 0.011 B- 34.031 2.318 106 905128.064 1.289 - 13 60 47 107 Ag -88406.670 2.382 8553.900 0.022 B- -1416.409 2.567 106 905091.531 2.557 - 11 59 48 107 Cd -86990.261 1.665 8533.351 0.016 B- -3426.000 11.000 106 906612.108 1.787 - 9 58 49 107 In - -83564.261 11.125 8494.021 0.104 B- -5052.033 12.327 106 910290.071 11.943 - 7 57 50 107 Sn x -78512.228 5.310 8439.494 0.050 B- -7858.989 6.738 106 915713.651 5.700 - 5 56 51 107 Sb -70653.239 4.148 8358.734 0.039 B- -10113.913 70.952 106 924150.624 4.452 - 3 55 52 107 Te -a -60539.326 70.830 8256.899 0.662 B- -11110# 308# 106 935008.356 76.039 - 1 54 53 107 I x -49430# 300# 8146# 3# B- * 106 946935# 322# -0 30 69 39 108 Y x -37297# 596# 8134# 6# B- 14056# 718# 107 959960# 640# - 28 68 40 108 Zr x -51353# 401# 8257# 4# B- 8193# 401# 107 944870# 430# - 26 67 41 108 Nb x -59545.765 8.237 8325.665 0.076 B- 11210.177 12.373 107 936074.988 8.842 - 24 66 42 108 Mo x -70755.942 9.233 8422.219 0.085 B- 5166.835 12.734 107 924040.367 9.912 - 22 65 43 108 Tc x -75922.778 8.769 8462.816 0.081 B- 7738.573 11.790 107 918493.541 9.413 - 20 64 44 108 Ru -3n -83661.350 8.680 8527.225 0.080 B- 1370.370 16.469 107 910185.841 9.318 - 18 63 45 108 Rh x -85031.721 13.996 8532.670 0.130 B- 4492.486 14.039 107 908714.688 15.024 - 16 62 46 108 Pd -89524.206 1.108 8567.023 0.010 B- -1917.444 2.633 107 903891.805 1.189 - 14 61 47 108 Ag -n -87606.763 2.388 8542.025 0.022 B- 1645.651 2.639 107 905950.266 2.563 - 12 60 48 108 Cd -89252.414 1.123 8550.019 0.010 B- -5132.595 8.584 107 904183.587 1.205 - 10 59 49 108 In -84119.819 8.641 8495.251 0.080 B- -2049.881 9.836 107 909693.655 9.276 - 8 58 50 108 Sn -82069.938 5.382 8469.027 0.050 B- -9624.607 7.692 107 911894.292 5.778 - 6 57 51 108 Sb x -72445.331 5.496 8372.666 0.051 B- -6663.664 7.712 107 922226.734 5.900 - 4 56 52 108 Te -65781.667 5.411 8303.721 0.050 B- -13132.062 132.370 107 929380.471 5.808 - 2 55 53 108 I -a -52649.605 132.260 8174.884 1.225 B- * 107 943478.321 141.986 -0 31 70 39 109 Y x -33200# 700# 8096# 6# B- 12992# 862# 108 964358# 751# - 29 69 40 109 Zr x -46193# 503# 8208# 5# B- 10497# 566# 108 950410# 540# - 27 68 41 109 Nb x -56689.794 258.490 8297.130 2.371 B- 9976.202 258.732 108 939141.000 277.500 - 25 67 42 109 Mo x -66665.996 11.188 8381.477 0.103 B- 7616.780 14.787 108 928431.106 12.010 - 23 66 43 109 Tc x -74282.775 9.669 8444.178 0.089 B- 6455.626 12.657 108 920254.156 10.380 - 21 65 44 109 Ru -4n -80738.401 8.954 8496.227 0.082 B- 4261.054 9.822 108 913323.756 9.612 - 19 64 45 109 Rh -84999.455 4.039 8528.142 0.037 B- 2607.021 4.187 108 908749.326 4.336 - 17 63 46 109 Pd -87606.476 1.114 8544.882 0.010 B- 1112.950 1.402 108 905950.574 1.195 - 15 62 47 109 Ag -88719.426 1.287 8547.915 0.012 B- -215.105 1.780 108 904755.773 1.381 - 13 61 48 109 Cd -88504.321 1.536 8538.764 0.014 B- -2014.809 4.066 108 904986.698 1.649 - 11 60 49 109 In -86489.511 3.969 8513.102 0.036 B- -3859.327 8.887 108 907149.685 4.261 - 9 59 50 109 Sn -82630.184 7.949 8470.518 0.073 B- -6379.206 8.807 108 911292.843 8.533 - 7 58 51 109 Sb -76250.977 5.265 8404.815 0.048 B- -8535.587 6.850 108 918141.204 5.652 - 5 57 52 109 Te -67715.390 4.382 8319.330 0.040 B- -10042.894 8.030 108 927304.534 4.704 - 3 56 53 109 I -p -57672.496 6.729 8220.016 0.062 B- -11502.948 300.183 108 938086.025 7.223 - 1 55 54 109 Xe -a -46169.548 300.108 8107.306 2.753 B- * 108 950434.948 322.178 -0 30 70 40 110 Zr x -42886# 596# 8177# 5# B- 9424# 1029# 109 953960# 640# - 28 69 41 110 Nb x -52309.909 838.345 8255.260 7.621 B- 12232.677 838.694 109 943843.000 900.000 - 26 68 42 110 Mo x -64542.585 24.223 8359.354 0.220 B- 6491.925 26.018 109 930710.680 26.004 - 24 67 43 110 Tc x -71034.510 9.497 8411.259 0.086 B- 9038.066 12.509 109 923741.312 10.195 - 22 66 44 110 Ru -80072.576 8.924 8486.311 0.081 B- 2756.110 19.404 109 914038.548 9.580 - 20 65 45 110 Rh -82828.686 17.805 8504.254 0.162 B- 5502.218 17.797 109 911079.742 19.114 - 18 64 46 110 Pd -88330.905 0.612 8547.162 0.006 B- -873.603 1.378 109 905172.868 0.657 - 16 63 47 110 Ag -87457.302 1.286 8532.108 0.012 B- 2890.667 1.277 109 906110.719 1.380 - 14 62 48 110 Cd -90347.969 0.380 8551.275 0.003 B- -3878.000 11.547 109 903007.460 0.407 - 12 61 49 110 In - -86469.969 11.553 8508.908 0.105 B- -627.985 17.980 109 907170.665 12.402 - 10 60 50 110 Sn x -85841.983 13.777 8496.087 0.125 B- -8392.250 15.012 109 907844.835 14.790 - 8 59 51 110 Sb x -77449.734 5.962 8412.681 0.054 B- -5219.923 8.875 109 916854.286 6.400 - 6 58 52 110 Te -72229.811 6.575 8358.115 0.060 B- -11765.635 50.978 109 922458.104 7.058 - 4 57 53 110 I -a -60464.176 50.552 8244.043 0.460 B- -8541.551 112.934 109 935089.033 54.270 - 2 56 54 110 Xe -a -51922.625 100.988 8159.280 0.918 B- * 109 944258.765 108.415 -0 31 71 40 111 Zr x -37560# 700# 8128# 6# B- 11316# 760# 110 959678# 751# - 29 70 41 111 Nb x -48875# 298# 8223# 3# B- 11064# 298# 110 947530# 320# - 27 69 42 111 Mo + -59939.761 12.578 8315.292 0.113 B- 9084.861 6.800 110 935652.016 13.502 - 25 68 43 111 Tc x -69024.622 10.581 8390.089 0.095 B- 7760.649 13.848 110 925899.016 11.359 - 23 67 44 111 Ru x -76785.271 9.682 8452.957 0.087 B- 5519.181 11.860 110 917567.616 10.394 - 21 66 45 111 Rh -82304.452 6.850 8495.631 0.062 B- 3681.435 6.887 110 911642.531 7.354 - 19 65 46 111 Pd -n -85985.888 0.731 8521.749 0.007 B- 2229.560 1.572 110 907690.347 0.785 - 17 64 47 111 Ag + -88215.447 1.459 8534.787 0.013 B- 1036.800 1.414 110 905296.816 1.565 - 15 63 48 111 Cd -89252.247 0.357 8537.079 0.003 B- -860.204 3.417 110 904183.766 0.383 - 13 62 49 111 In -88392.043 3.424 8522.282 0.031 B- -2453.456 6.337 110 905107.233 3.675 - 11 61 50 111 Sn +n -85938.587 5.336 8493.130 0.048 B- -5101.851 10.334 110 907741.126 5.728 - 9 60 51 111 Sb x -80836.736 8.849 8440.120 0.080 B- -7249.259 10.937 110 913218.189 9.500 - 7 59 52 111 Te x -73587.477 6.427 8367.763 0.058 B- -8633.692 7.994 110 921000.589 6.900 - 5 58 53 111 I -64953.785 4.754 8282.934 0.043 B- -10558.252 86.830 110 930269.239 5.103 - 3 57 54 111 Xe -a -54395.534 86.700 8180.766 0.781 B- -11575# 214# 110 941603.989 93.076 - 1 56 55 111 Cs x -42821# 196# 8069# 2# B- * 110 954030# 210# -0 32 72 40 112 Zr x -33810# 700# 8094# 6# B- 10463# 760# 111 963703# 751# - 30 71 41 112 Nb x -44274# 298# 8180# 3# B- 13190# 357# 111 952470# 320# - 28 70 42 112 Mo x -57464# 196# 8291# 2# B- 7795# 196# 111 938310# 210# - 26 69 43 112 Tc x -65258.938 5.515 8353.621 0.049 B- 10371.881 11.060 111 929941.644 5.920 - 24 68 44 112 Ru x -75630.818 9.599 8439.242 0.086 B- 4100.685 45.118 111 918806.972 10.305 - 22 67 45 112 Rh -79731.503 44.085 8468.870 0.394 B- 6590.059 43.927 111 914404.705 47.327 - 20 66 46 112 Pd -86321.562 6.544 8520.724 0.058 B- 262.156 6.978 111 907329.986 7.025 - 18 65 47 112 Ag x -86583.718 2.422 8516.080 0.022 B- 3991.141 2.435 111 907048.550 2.600 - 16 64 48 112 Cd -90574.859 0.250 8544.730 0.002 B- -2584.728 4.243 111 902763.883 0.268 - 14 63 49 112 In -87990.131 4.251 8514.667 0.038 B- 664.925 4.243 111 905538.704 4.563 - 12 62 50 112 Sn -88655.056 0.294 8513.618 0.003 B- -7056.091 17.832 111 904824.877 0.315 - 10 61 51 112 Sb x -81598.965 17.829 8443.632 0.159 B- -4031.457 19.702 111 912399.903 19.140 - 8 60 52 112 Te x -77567.508 8.383 8400.652 0.075 B- -10504.178 13.239 111 916727.850 9.000 - 6 59 53 112 I x -67063.330 10.246 8299.879 0.091 B- -7036.991 13.175 111 928004.550 11.000 - 4 58 54 112 Xe -a -60026.338 8.283 8230.064 0.074 B- -13736.062 87.190 111 935559.071 8.891 - 2 57 55 112 Cs -p -46290.277 86.796 8100.435 0.775 B- * 111 950305.341 93.178 -0 31 72 41 113 Nb x -40511# 401# 8146# 4# B- 11979# 500# 112 956510# 430# - 29 71 42 113 Mo x -52490# 300# 8245# 3# B- 10322# 300# 112 943650# 322# - 27 70 43 113 Tc x -62811.541 3.353 8329.464 0.030 B- 9056.578 37.028 112 932569.033 3.600 - 25 69 44 113 Ru -71868.119 36.875 8402.688 0.326 B- 6899.417 37.558 112 922846.396 39.587 - 23 68 45 113 Rh x -78767.536 7.130 8456.821 0.063 B- 4823.555 9.881 112 915439.567 7.653 - 21 67 46 113 Pd x -83591.092 6.945 8492.584 0.061 B- 3435.731 18.033 112 910261.267 7.455 - 19 66 47 113 Ag + -87026.822 16.643 8516.065 0.147 B- 2016.462 16.641 112 906572.858 17.866 - 17 65 48 113 Cd -89043.284 0.244 8526.987 0.002 B- 323.833 0.265 112 904408.097 0.262 - 15 64 49 113 In -89367.117 0.188 8522.929 0.002 B- -1038.985 1.573 112 904060.448 0.202 - 13 63 50 113 Sn -88328.132 1.575 8506.811 0.014 B- -3911.164 17.121 112 905175.845 1.690 - 11 62 51 113 Sb - -84416.968 17.193 8465.275 0.152 B- -6069.939 32.810 112 909374.652 18.457 - 9 61 52 113 Te x -78347.029 27.945 8404.636 0.247 B- -7227.522 29.070 112 915891.000 30.000 - 7 60 53 113 I x -71119.507 8.011 8333.752 0.071 B- -8915.889 10.533 112 923650.064 8.600 - 5 59 54 113 Xe -62203.618 6.840 8247.927 0.061 B- -10439.088 10.970 112 933221.666 7.342 - 3 58 55 113 Cs -p -51764.530 8.577 8148.622 0.076 B- -11980# 298# 112 944428.488 9.207 - 1 57 56 113 Ba x -39784# 298# 8036# 3# B- * 112 957290# 320# -0 32 73 41 114 Nb x -35387# 503# 8100# 4# B- 14420# 585# 113 962010# 540# - 30 72 42 114 Mo x -49807# 298# 8220# 3# B- 8793# 526# 113 946530# 320# - 28 71 43 114 Tc x -58600.288 433.145 8290.259 3.800 B- 11621.524 433.159 113 937090.000 465.000 - 26 70 44 114 Ru x -70221.811 3.550 8385.340 0.031 B- 5488.813 71.643 113 924613.780 3.811 - 24 69 45 114 Rh -75710.625 71.561 8426.624 0.628 B- 7780.319 71.891 113 918721.296 76.824 - 22 68 46 114 Pd x -83490.943 6.945 8488.010 0.061 B- 1439.856 8.311 113 910368.780 7.456 - 20 67 47 114 Ag x -84930.800 4.564 8493.778 0.040 B- 5084.133 4.573 113 908823.031 4.900 - 18 66 48 114 Cd -90014.932 0.276 8531.513 0.002 B- -1445.132 0.382 113 903364.990 0.296 - 16 65 49 114 In -88569.801 0.301 8511.973 0.003 B- 1989.923 0.302 113 904916.402 0.323 - 14 64 50 114 Sn -90559.723 0.029 8522.566 0.000 B- -6063.149 21.838 113 902780.132 0.031 - 12 63 51 114 Sb -84496.574 21.838 8462.518 0.192 B- -2608.005 35.466 113 909289.191 23.444 - 10 62 52 114 Te x -81888.569 27.945 8432.778 0.245 B- -9092# 152# 113 912089.000 30.000 - 8 61 53 114 I x -72796# 149# 8346# 1# B- -5710# 149# 113 921850# 160# - 6 60 54 114 Xe x -67085.890 11.178 8289.205 0.098 B- -12403.629 71.976 113 927980.331 12.000 - 4 59 55 114 Cs -a -54682.261 71.102 8173.538 0.624 B- -8776.835 124.892 113 941296.175 76.331 - 2 58 56 114 Ba -a -45905.426 102.676 8089.686 0.901 B- * 113 950718.495 110.227 -0 33 74 41 115 Nb x -31354# 503# 8065# 4# B- 13395# 643# 114 966340# 540# - 31 73 42 115 Mo x -44749# 401# 8175# 3# B- 11571# 885# 114 951960# 430# - 29 72 43 115 Tc x -56319.990 789.441 8268.527 6.865 B- 9869.744 794.386 114 939538.000 847.500 - 27 71 44 115 Ru x -66189.734 88.496 8347.547 0.770 B- 8040.097 88.790 114 928942.393 95.004 - 25 70 45 115 Rh x -74229.831 7.316 8410.658 0.064 B- 6196.554 15.350 114 920310.993 7.854 - 23 69 46 115 Pd -80426.386 13.546 8457.738 0.118 B- 4556.268 21.649 114 913658.718 14.541 - 21 68 47 115 Ag -84982.654 18.268 8490.555 0.159 B- 3101.825 18.274 114 908767.363 19.611 - 19 67 48 115 Cd -88084.479 0.651 8510.724 0.006 B- 1451.867 0.651 114 905437.417 0.699 - 17 66 49 115 In -89536.346 0.012 8516.546 0.000 B- 497.489 0.010 114 903878.773 0.012 - 15 65 50 115 Sn -90033.835 0.015 8514.069 0.000 B- -3030.432 16.025 114 903344.697 0.016 - 13 64 51 115 Sb x -87003.403 16.025 8480.915 0.139 B- -4940.644 32.214 114 906598.000 17.203 - 11 63 52 115 Te x -82062.759 27.945 8431.150 0.243 B- -5724.962 40.184 114 911902.000 30.000 - 9 62 53 115 I x -76337.797 28.876 8374.564 0.251 B- -7681.049 31.313 114 918048.000 31.000 - 7 61 54 115 Xe x -68656.748 12.109 8300.970 0.105 B- -8957# 103# 114 926293.945 13.000 - 5 60 55 115 Cs x -59699# 102# 8216# 1# B- -10680# 225# 114 935910# 110# - 3 59 56 115 Ba x -49020# 200# 8117# 2# B- * 114 947375# 215# -0 32 74 42 116 Mo x -41500# 500# 8146# 4# B- 9956# 582# 115 955448# 537# - 30 73 43 116 Tc x -51456# 298# 8225# 3# B- 12613# 298# 115 944760# 320# - 28 72 44 116 Ru x -64068.909 3.726 8326.883 0.032 B- 6667.213 73.926 115 931219.193 4.000 - 26 71 45 116 Rh -70736.122 73.832 8377.615 0.636 B- 9095.512 74.169 115 924061.645 79.261 - 24 70 46 116 Pd x -79831.635 7.132 8449.280 0.061 B- 2711.019 7.842 115 914297.210 7.656 - 22 69 47 116 Ag x -82542.653 3.260 8465.907 0.028 B- 6169.827 3.264 115 911386.812 3.500 - 20 68 48 116 Cd -88712.480 0.160 8512.350 0.001 B- -462.731 0.272 115 904763.230 0.172 - 18 67 49 116 In -n -88249.749 0.220 8501.617 0.002 B- 3276.221 0.240 115 905259.992 0.236 - 16 66 50 116 Sn -91525.970 0.096 8523.116 0.001 B- -4703.820 5.160 115 901742.824 0.103 - 14 65 51 116 Sb -86822.150 5.160 8475.821 0.044 B- -1553.189 28.417 115 906792.583 5.539 - 12 64 52 116 Te x -85268.961 27.945 8455.687 0.241 B- -7776.725 100.553 115 908460.000 30.000 - 10 63 53 116 I + -77492.236 96.592 8381.902 0.833 B- -4445.512 95.707 115 916808.658 103.695 - 8 62 54 116 Xe x -73046.724 13.041 8336.834 0.112 B- -11004# 101# 115 921581.112 14.000 - 6 61 55 116 Cs ea -62043# 100# 8235# 1# B- -7463# 224# 115 933395# 108# - 4 60 56 116 Ba x -54580# 200# 8164# 2# B- -13935# 371# 115 941406# 215# - 2 59 57 116 La -a -40645# 312# 8037# 3# B- * 115 956365# 335# -0 33 75 42 117 Mo x -36170# 500# 8100# 4# B- 12212# 641# 116 961170# 537# - 31 74 43 117 Tc x -48382# 401# 8197# 3# B- 11108# 590# 116 948060# 430# - 29 73 44 117 Ru x -59489.865 433.145 8285.562 3.702 B- 9407.508 433.236 116 936135.000 465.000 - 27 72 45 117 Rh x -68897.373 8.892 8359.281 0.076 B- 7527.104 11.411 116 926035.623 9.546 - 25 71 46 117 Pd -76424.477 7.252 8416.929 0.062 B- 5757.537 14.766 116 917954.944 7.785 - 23 70 47 117 Ag -82182.014 13.572 8459.452 0.116 B- 4236.375 13.610 116 911773.974 14.570 - 21 69 48 117 Cd -n -86418.389 1.013 8488.973 0.009 B- 2524.653 4.983 116 907226.038 1.087 - 19 68 49 117 In -88943.042 4.881 8503.865 0.042 B- 1454.709 4.857 116 904515.712 5.239 - 17 67 50 117 Sn -90397.751 0.483 8509.611 0.004 B- -1758.212 8.445 116 902954.017 0.518 - 15 66 51 117 Sb -88639.539 8.437 8487.897 0.072 B- -3544.128 13.079 116 904841.535 9.057 - 13 65 52 117 Te -85095.411 13.456 8450.919 0.115 B- -4659.334 28.673 116 908646.313 14.446 - 11 64 53 117 I -80436.077 26.196 8404.409 0.224 B- -6250.740 28.177 116 913648.314 28.123 - 9 63 54 117 Xe x -74185.337 10.378 8344.297 0.089 B- -7692.245 63.267 116 920358.760 11.141 - 7 62 55 117 Cs x -66493.092 62.410 8271.864 0.533 B- -9035.338 258.002 116 928616.726 67.000 - 5 61 56 117 Ba ep -57457.753 250.340 8187.953 2.140 B- -10987# 321# 116 938316.561 268.750 - 3 60 57 117 La -p -46471# 200# 8087# 2# B- * 116 950111# 215# -0 34 76 42 118 Mo x -32630# 500# 8069# 4# B- 11159# 641# 117 964970# 537# - 32 75 43 118 Tc x -43790# 401# 8157# 3# B- 13470# 448# 117 952990# 430# - 30 74 44 118 Ru x -57260# 200# 8265# 2# B- 7628# 202# 117 938529# 215# - 28 73 45 118 Rh x -64887.460 24.235 8322.858 0.205 B- 10501.286 24.342 117 930340.443 26.017 - 26 72 46 118 Pd -75388.746 2.491 8405.222 0.021 B- 4165.046 3.539 117 919066.847 2.673 - 24 71 47 118 Ag x -79553.792 2.515 8433.889 0.021 B- 7147.849 20.158 117 914595.487 2.700 - 22 70 48 118 Cd -nn -86701.641 20.001 8487.834 0.169 B- 526.570 21.450 117 906921.955 21.471 - 20 69 49 118 In -87228.211 7.752 8485.667 0.066 B- 4424.643 7.740 117 906356.659 8.322 - 18 68 50 118 Sn -91652.853 0.499 8516.533 0.004 B- -3656.640 2.975 117 901606.609 0.536 - 16 67 51 118 Sb - -87996.213 3.016 8478.915 0.026 B- -299.630 18.726 117 905532.174 3.238 - 14 66 52 118 Te +nn -87696.584 18.481 8469.746 0.157 B- -6725.536 27.056 117 905853.839 19.840 - 12 65 53 118 I x -80971.048 19.760 8406.120 0.167 B- -2891.991 22.320 117 913074.000 21.213 - 10 64 54 118 Xe x -78079.057 10.378 8374.981 0.088 B- -9669.689 16.442 117 916178.680 11.141 - 8 63 55 118 Cs IT -68409.367 12.753 8286.404 0.108 B- -6055# 196# 117 926559.519 13.690 - 6 62 56 118 Ba x -62354# 196# 8228# 2# B- -12794# 358# 117 933060# 210# - 4 61 57 118 La x -49560# 300# 8113# 3# B- * 117 946795# 322# -0 33 76 43 119 Tc x -40371# 503# 8128# 4# B- 12193# 585# 118 956660# 540# - 31 75 44 119 Ru x -52564# 298# 8224# 3# B- 10259# 298# 118 943570# 320# - 29 74 45 119 Rh x -62822.794 9.315 8303.394 0.078 B- 8585.108 12.440 118 932556.952 10.000 - 27 73 46 119 Pd x -71407.902 8.245 8368.964 0.069 B- 7237.863 16.855 118 923340.459 8.851 - 25 72 47 119 Ag -78645.765 14.703 8423.212 0.124 B- 5331.303 35.926 118 915570.293 15.783 - 23 71 48 119 Cd -83977.068 37.695 8461.438 0.317 B- 3722.212 38.088 118 909846.903 40.467 - 21 70 49 119 In -87699.281 7.307 8486.143 0.061 B- 2365.742 7.336 118 905850.944 7.844 - 19 69 50 119 Sn -90065.022 0.725 8499.449 0.006 B- -590.843 7.689 118 903311.216 0.778 - 17 68 51 119 Sb -89474.180 7.701 8487.910 0.065 B- -2293.000 2.000 118 903945.512 8.267 - 15 67 52 119 Te - -87181.180 7.957 8462.066 0.067 B- -3415.650 29.055 118 906407.148 8.541 - 13 66 53 119 I x -83765.530 27.945 8426.789 0.235 B- -4971.117 29.810 118 910074.000 30.000 - 11 65 54 119 Xe x -78794.413 10.378 8378.441 0.087 B- -6489.361 17.379 118 915410.713 11.141 - 9 64 55 119 Cs IT -72305.051 13.940 8317.334 0.117 B- -7714.965 200.754 118 922377.330 14.965 - 7 63 56 119 Ba ep -64590.086 200.269 8245.928 1.683 B- -9801# 361# 118 930659.686 214.997 - 5 62 57 119 La x -54790# 300# 8157# 3# B- -10849# 583# 118 941181# 322# - 3 61 58 119 Ce x -43940# 500# 8059# 4# B- * 118 952828# 537# -0 34 77 43 120 Tc x -35518# 503# 8087# 4# B- 14494# 643# 119 961870# 540# - 32 76 44 120 Ru x -50012# 401# 8201# 3# B- 8803# 446# 119 946310# 430# - 30 75 45 120 Rh x -58815# 196# 8268# 2# B- 11466# 196# 119 936860# 210# - 28 74 46 120 Pd -70280.050 2.291 8357.085 0.019 B- 5371.451 5.024 119 924551.258 2.459 - 26 73 47 120 Ag x -75651.502 4.471 8395.327 0.037 B- 8305.853 5.820 119 918784.767 4.800 - 24 72 48 120 Cd x -83957.354 3.726 8458.023 0.031 B- 1771.015 40.183 119 909868.067 4.000 - 22 71 49 120 In + -85728.369 40.010 8466.262 0.333 B- 5370.000 40.000 119 907966.805 42.952 - 20 70 50 120 Sn -91098.369 0.896 8504.492 0.007 B- -2680.608 7.140 119 902201.873 0.962 - 18 69 51 120 Sb - -88417.761 7.196 8475.635 0.060 B- 950.226 7.811 119 905079.624 7.725 - 16 68 52 120 Te -89367.987 3.085 8477.034 0.026 B- -5615.000 15.000 119 904059.514 3.311 - 14 67 53 120 I - -83752.987 15.314 8423.722 0.128 B- -1580.563 19.343 119 910087.465 16.440 - 12 66 54 120 Xe x -82172.423 11.817 8404.031 0.098 B- -8283.785 15.461 119 911784.270 12.686 - 10 65 55 120 Cs IT -73888.639 9.970 8328.480 0.083 B- -5000.000 300.000 119 920677.279 10.702 - 8 64 56 120 Ba - -68888.639 300.166 8280.294 2.501 B- -11319# 424# 119 926045.000 322.241 - 6 63 57 120 La x -57570# 300# 8179# 2# B- -7970# 583# 119 938196# 322# - 4 62 58 120 Ce x -49600# 500# 8107# 4# B- * 119 946752# 537# -0 35 78 43 121 Tc x -31780# 500# 8056# 4# B- 13267# 641# 120 965883# 537# - 33 77 44 121 Ru x -45047# 401# 8159# 3# B- 11203# 738# 120 951640# 430# - 31 76 45 121 Rh x -56250.128 619.444 8245.239 5.119 B- 9932.201 619.453 120 939613.000 665.000 - 29 75 46 121 Pd x -66182.329 3.353 8320.858 0.028 B- 8220.492 12.565 120 928950.343 3.600 - 27 74 47 121 Ag x -74402.821 12.109 8382.330 0.100 B- 6671.005 12.264 120 920125.282 13.000 - 25 73 48 121 Cd x -81073.826 1.942 8430.996 0.016 B- 4762.148 27.483 120 912963.663 2.085 - 23 72 49 121 In +p -85835.974 27.414 8463.887 0.227 B- 3361.291 27.408 120 907851.286 29.430 - 21 71 50 121 Sn -89197.265 0.955 8485.201 0.008 B- 403.057 2.690 120 904242.792 1.025 - 19 70 51 121 Sb -89600.321 2.582 8482.066 0.021 B- -1054.819 25.767 120 903810.093 2.771 - 17 69 52 121 Te -88545.502 25.850 8466.883 0.214 B- -2294.053 26.047 120 904942.488 27.751 - 15 68 53 121 I -86251.449 5.356 8441.458 0.044 B- -3770.463 11.558 120 907405.255 5.749 - 13 67 54 121 Xe -82480.986 10.243 8403.832 0.085 B- -5378.654 13.979 120 911453.014 10.995 - 11 66 55 121 Cs -77102.331 14.290 8352.914 0.118 B- -6357.495 141.176 120 917227.238 15.340 - 9 65 56 121 Ba - -70744.837 141.898 8293.907 1.173 B- -8555# 332# 120 924052.289 152.333 - 7 64 57 121 La x -62190# 300# 8217# 2# B- -9500# 500# 120 933236# 322# - 5 63 58 121 Ce x -52690# 401# 8132# 3# B- -11268# 641# 120 943435# 430# - 3 62 59 121 Pr -p -41422# 500# 8032# 4# B- * 120 955532# 537# -0 34 78 44 122 Ru x -42150# 500# 8135# 4# B- 9930# 583# 121 954750# 537# - 32 77 45 122 Rh x -52080# 300# 8210# 2# B- 12536# 301# 121 944090# 322# - 30 76 46 122 Pd x -64616.161 19.561 8305.975 0.160 B- 6489.948 42.909 121 930631.694 21.000 - 28 75 47 122 Ag x -71106.108 38.191 8352.758 0.313 B- 9506.265 38.260 121 923664.448 41.000 - 26 74 48 122 Cd -80612.374 2.299 8424.266 0.019 B- 2960.368 50.110 121 913459.052 2.468 - 24 73 49 122 In + -83572.741 50.057 8442.118 0.410 B- 6368.592 50.000 121 910280.966 53.738 - 22 72 50 122 Sn -89941.333 2.395 8487.907 0.020 B- -1605.963 3.384 121 903444.001 2.570 - 20 71 51 122 Sb -88335.370 2.578 8468.331 0.021 B- 1979.089 2.127 121 905168.074 2.768 - 18 70 52 122 Te -90314.460 1.507 8478.140 0.012 B- -4234.000 5.000 121 903043.434 1.617 - 16 69 53 122 I - -86080.460 5.222 8437.023 0.043 B- -725.483 12.277 121 907588.820 5.606 - 14 68 54 122 Xe x -85354.977 11.111 8424.664 0.091 B- -7210.218 35.472 121 908367.658 11.928 - 12 67 55 122 Cs -78144.759 33.687 8359.151 0.276 B- -3535.815 43.769 121 916108.145 36.164 - 10 66 56 122 Ba x -74608.944 27.945 8323.756 0.229 B- -10066# 299# 121 919904.000 30.000 - 8 65 57 122 La x -64543# 298# 8235# 2# B- -6669# 499# 121 930710# 320# - 6 64 58 122 Ce x -57874# 401# 8174# 3# B- -13094# 641# 121 937870# 430# - 4 63 59 122 Pr x -44780# 500# 8060# 4# B- * 121 951927# 537# -0 35 79 44 123 Ru x -37080# 500# 8093# 4# B- 12280# 640# 122 960193# 537# - 33 78 45 123 Rh x -49360# 400# 8186# 3# B- 11070# 885# 122 947010# 429# - 31 77 46 123 Pd x -60429.742 789.441 8270.031 6.418 B- 9118.336 790.039 122 935126.000 847.500 - 29 76 47 123 Ag x -69548.078 30.739 8337.803 0.250 B- 7866.103 30.857 122 925337.062 33.000 - 27 75 48 123 Cd -77414.181 2.696 8395.395 0.022 B- 6016.172 19.893 122 916892.453 2.894 - 25 74 49 123 In -83430.353 19.827 8437.946 0.161 B- 4385.828 19.839 122 910433.826 21.285 - 23 73 50 123 Sn -87816.181 2.416 8467.243 0.020 B- 1407.888 2.662 122 905725.446 2.594 - 21 72 51 123 Sb -89224.069 1.506 8472.328 0.012 B- -51.913 0.066 122 904214.016 1.616 - 19 71 52 123 Te -89172.156 1.505 8465.546 0.012 B- -1228.429 3.445 122 904269.747 1.615 - 17 70 53 123 I -87943.727 3.740 8449.198 0.030 B- -2695.027 9.690 122 905588.520 4.014 - 15 69 54 123 Xe -85248.701 9.537 8420.927 0.078 B- -4205.055 15.414 122 908481.750 10.238 - 13 68 55 123 Cs x -81043.646 12.109 8380.379 0.098 B- -5388.693 17.125 122 912996.062 13.000 - 11 67 56 123 Ba x -75654.953 12.109 8330.208 0.098 B- -7004# 196# 122 918781.062 13.000 - 9 66 57 123 La x -68651# 196# 8267# 2# B- -8365# 357# 122 926300# 210# - 7 65 58 123 Ce x -60286# 298# 8193# 2# B- -10056# 499# 122 935280# 320# - 5 64 59 123 Pr x -50230# 400# 8104# 3# B- * 122 946076# 429# -0 36 80 44 124 Ru x -33960# 600# 8068# 5# B- 10929# 721# 123 963542# 644# - 34 79 45 124 Rh x -44890# 400# 8149# 3# B- 13500# 499# 123 951809# 429# - 32 78 46 124 Pd x -58390# 298# 8252# 2# B- 7810# 390# 123 937316# 320# - 30 77 47 124 Ag x -66200.134 251.503 8308.655 2.028 B- 10501.538 251.521 123 928931.229 270.000 - 28 76 48 124 Cd -76701.672 2.995 8387.035 0.024 B- 4168.529 30.539 123 917657.363 3.215 - 26 75 49 124 In -80870.201 30.572 8414.343 0.247 B- 7363.992 30.576 123 913182.263 32.820 - 24 74 50 124 Sn -88234.193 1.014 8467.421 0.008 B- -613.944 1.513 123 905276.692 1.088 - 22 73 51 124 Sb -n -87620.248 1.507 8456.160 0.012 B- 2905.073 0.132 123 905935.789 1.618 - 20 72 52 124 Te -90525.321 1.502 8473.279 0.012 B- -3159.587 1.859 123 902817.064 1.612 - 18 71 53 124 I - -87365.734 2.390 8441.489 0.019 B- 295.686 2.846 123 906209.021 2.566 - 16 70 54 124 Xe -87661.421 1.793 8437.565 0.014 B- -5930.086 8.495 123 905891.588 1.924 - 14 69 55 124 Cs x -81731.334 8.304 8383.432 0.067 B- -2641.559 15.004 123 912257.798 8.914 - 12 68 56 124 Ba x -79089.775 12.497 8355.820 0.101 B- -8831.165 58.030 123 915093.629 13.416 - 10 67 57 124 La x -70258.610 56.669 8278.292 0.457 B- -5343# 303# 123 924574.275 60.836 - 8 66 58 124 Ce x -64916# 298# 8229# 2# B- -11765# 499# 123 930310# 320# - 6 65 59 124 Pr x -53151# 401# 8128# 3# B- -8626# 643# 123 942940# 430# - 4 64 60 124 Nd x -44525# 503# 8052# 4# B- * 123 952200# 540# -0 35 80 45 125 Rh x -42000# 500# 8126# 4# B- 12120# 640# 124 954911# 537# - 33 79 46 125 Pd x -54120# 400# 8216# 3# B- 10400# 589# 124 941900# 429# - 31 78 47 125 Ag x -64519.932 433.145 8293.314 3.465 B- 8828.163 433.154 124 930735.000 465.000 - 29 77 48 125 Cd -73348.095 2.885 8357.681 0.023 B- 7128.710 27.119 124 921257.577 3.097 - 27 76 49 125 In -80476.805 27.023 8408.452 0.216 B- 5419.571 27.011 124 913604.591 29.010 - 25 75 50 125 Sn -85896.376 1.033 8445.550 0.008 B- 2359.899 2.610 124 907786.442 1.109 - 23 74 51 125 Sb + -88256.274 2.599 8458.170 0.021 B- 766.700 2.121 124 905252.987 2.790 - 21 73 52 125 Te -89022.974 1.502 8458.045 0.012 B- -185.770 0.060 124 904429.900 1.612 - 19 72 53 125 I - -88837.204 1.504 8450.300 0.012 B- -1643.824 2.192 124 904629.333 1.614 - 17 71 54 125 Xe -87193.381 1.836 8430.890 0.015 B- -3105.430 7.831 124 906394.050 1.971 - 15 70 55 125 Cs -84087.950 7.744 8399.788 0.062 B- -4418.985 13.446 124 909727.867 8.313 - 13 69 56 125 Ba -79668.965 10.992 8358.178 0.088 B- -5909.481 27.631 124 914471.843 11.800 - 11 68 57 125 La -73759.484 25.997 8304.643 0.208 B- -7102# 197# 124 920815.932 27.909 - 9 67 58 125 Ce x -66658# 196# 8242# 2# B- -8718# 358# 124 928440# 210# - 7 66 59 125 Pr x -57940# 300# 8166# 2# B- -10341# 500# 124 937799# 322# - 5 65 60 125 Nd x -47599# 401# 8077# 3# B- * 124 948900# 430# -0 36 81 45 126 Rh x -37300# 500# 8088# 4# B- 14560# 640# 125 959957# 537# - 34 80 46 126 Pd x -51860# 400# 8197# 3# B- 8820# 447# 125 944326# 429# - 32 79 47 126 Ag x -60680# 200# 8261# 2# B- 11576# 200# 125 934857# 215# - 30 78 48 126 Cd -72256.802 2.476 8346.747 0.020 B- 5516.106 26.908 125 922429.127 2.658 - 28 77 49 126 In -77772.908 26.921 8384.317 0.214 B- 8242.332 27.078 125 916507.344 28.901 - 26 76 50 126 Sn -86015.240 10.447 8443.523 0.083 B- 378.000 30.000 125 907658.836 11.215 - 24 75 51 126 Sb - -86393.240 31.767 8440.314 0.252 B- 3672.108 31.787 125 907253.036 34.103 - 22 74 52 126 Te -90065.348 1.504 8463.248 0.012 B- -2154.031 3.677 125 903310.866 1.614 - 20 73 53 126 I -87911.318 3.809 8439.944 0.030 B- 1235.644 5.173 125 905623.313 4.089 - 18 72 54 126 Xe -89146.962 3.500 8443.541 0.028 B- -4796.133 10.671 125 904296.794 3.757 - 16 71 55 126 Cs -84350.829 10.401 8399.268 0.083 B- -1680.927 16.259 125 909445.655 11.166 - 14 70 56 126 Ba x -82669.902 12.497 8379.718 0.099 B- -7696.435 91.366 125 911250.204 13.416 - 12 69 57 126 La x -74973.468 90.508 8312.426 0.718 B- -4152.910 94.723 125 919512.667 97.163 - 10 68 58 126 Ce x -70820.558 27.945 8273.257 0.222 B- -10497# 198# 125 923971.000 30.000 - 8 67 59 126 Pr x -60324# 196# 8184# 2# B- -7331# 357# 125 935240# 210# - 6 66 60 126 Nd x -52993# 298# 8119# 2# B- -13643# 582# 125 943110# 320# - 4 65 61 126 Pm x -39350# 500# 8005# 4# B- * 125 957756# 537# -0 37 82 45 127 Rh x -34030# 600# 8062# 5# B- 13150# 781# 126 963467# 644# - 35 81 46 127 Pd x -47180# 500# 8159# 4# B- 11260# 539# 126 949350# 537# - 33 80 47 127 Ag x -58440# 200# 8242# 2# B- 10307# 201# 126 937262# 215# - 31 79 48 127 Cd x -68747.402 12.109 8316.945 0.095 B- 8148.782 24.378 126 926196.624 13.000 - 29 78 49 127 In -76896.184 21.157 8374.949 0.167 B- 6574.619 19.098 126 917448.546 22.713 - 27 77 50 127 Sn -83470.803 10.057 8420.557 0.079 B- 3228.674 10.875 126 910390.401 10.796 - 25 76 51 127 Sb -86699.477 5.126 8439.820 0.040 B- 1582.201 4.913 126 906924.277 5.502 - 23 75 52 127 Te -88281.678 1.514 8446.118 0.012 B- 702.231 3.575 126 905225.714 1.625 - 21 74 53 127 I -88983.909 3.647 8445.487 0.029 B- -662.349 2.044 126 904471.838 3.915 - 19 73 54 127 Xe -88321.560 4.110 8434.111 0.032 B- -2081.406 6.421 126 905182.899 4.412 - 17 72 55 127 Cs -86240.154 5.578 8411.562 0.044 B- -3422.210 12.653 126 907417.381 5.988 - 15 71 56 127 Ba -82817.944 11.357 8378.455 0.089 B- -4921.836 27.740 126 911091.275 12.192 - 13 70 57 127 La -77896.108 26.000 8333.540 0.205 B- -5916.772 38.857 126 916375.084 27.912 - 11 69 58 127 Ce x -71979.336 28.876 8280.791 0.227 B- -7436# 198# 126 922727.000 31.000 - 9 68 59 127 Pr x -64543# 196# 8216# 2# B- -9008# 357# 126 930710# 210# - 7 67 60 127 Nd x -55536# 298# 8139# 2# B- -10749# 499# 126 940380# 320# - 5 66 61 127 Pm x -44786# 401# 8048# 3# B- * 126 951920# 430# -0 36 82 46 128 Pd x -44490# 500# 8138# 4# B- 10130# 583# 127 952238# 537# - 34 81 47 128 Ag x -54620# 300# 8211# 2# B- 12622# 300# 127 941363# 322# - 32 80 48 128 Cd -67241.890 7.244 8303.264 0.057 B- 6904.051 153.554 127 927812.857 7.776 - 30 79 49 128 In -74145.941 153.479 8351.090 1.199 B- 9216.067 153.027 127 920401.053 164.766 - 28 78 50 128 Sn -83362.008 17.660 8416.979 0.138 B- 1268.278 13.796 127 910507.197 18.958 - 26 77 51 128 Sb IT -84630.286 19.119 8420.775 0.149 B- 4363.429 19.117 127 909145.645 20.525 - 24 76 52 128 Te -88993.716 0.866 8448.752 0.007 B- -1254.992 3.714 127 904461.311 0.929 - 22 75 53 128 I -87738.724 3.647 8432.836 0.028 B- 2121.575 3.748 127 905808.600 3.915 - 20 74 54 128 Xe -89860.298 1.061 8443.298 0.008 B- -3928.717 5.380 127 903530.996 1.138 - 18 73 55 128 Cs -85931.581 5.443 8406.493 0.043 B- -553.084 7.525 127 907748.648 5.843 - 16 72 56 128 Ba -85378.497 5.195 8396.060 0.041 B- -6753.066 54.695 127 908342.408 5.577 - 14 71 57 128 La x -78625.431 54.448 8337.190 0.425 B- -3091.513 61.200 127 915592.123 58.452 - 12 70 58 128 Ce x -75533.917 27.945 8306.925 0.218 B- -9203.161 40.859 127 918911.000 30.000 - 10 69 59 128 Pr x -66330.757 29.808 8228.913 0.233 B- -6017# 198# 127 928791.000 32.000 - 8 68 60 128 Nd x -60314# 196# 8176# 2# B- -12529# 357# 127 935250# 210# - 6 67 61 128 Pm x -47786# 298# 8072# 2# B- -9116# 582# 127 948700# 320# - 4 66 62 128 Sm x -38670# 500# 7994# 4# B- * 127 958486# 537# -0 37 83 46 129 Pd x -37610# 600# 8084# 5# B- 14370# 721# 128 959624# 644# - 35 82 47 129 Ag x -51980# 400# 8189# 3# B- 11078# 400# 128 944197# 429# - 33 81 48 129 Cd x -63058.046 16.767 8269.034 0.130 B- 9779.674 16.982 128 932304.399 18.000 - 31 80 49 129 In -72837.720 2.693 8338.780 0.021 B- 7753.183 17.302 128 921805.486 2.891 - 29 79 50 129 Sn -80590.903 17.277 8392.818 0.134 B- 4038.404 27.372 128 913482.102 18.547 - 27 78 51 129 Sb + -84629.307 21.231 8418.058 0.165 B- 2375.500 21.213 128 909146.696 22.792 - 25 77 52 129 Te -87004.807 0.869 8430.409 0.007 B- 1502.318 3.142 128 906596.492 0.933 - 23 76 53 129 I -88507.125 3.168 8435.990 0.025 B- 188.934 3.168 128 904983.687 3.401 - 21 75 54 129 Xe -88696.05896 0.00537 8431.390 0.000 B- -1196.813 4.555 128 904780.85892 0.00576 - 19 74 55 129 Cs -87499.246 4.555 8416.047 0.035 B- -2436.048 10.623 128 906065.690 4.889 - 17 73 56 129 Ba -85063.198 10.577 8391.098 0.082 B- -3738.625 21.639 128 908680.896 11.354 - 15 72 57 129 La -81324.573 21.351 8356.052 0.166 B- -5037.077 35.168 128 912694.475 22.920 - 13 71 58 129 Ce x -76287.496 27.945 8310.940 0.217 B- -6513.938 40.859 128 918102.000 30.000 - 11 70 59 129 Pr x -69773.558 29.808 8254.380 0.231 B- -7459# 204# 128 925095.000 32.000 - 9 69 60 129 Nd ep -62315# 202# 8190# 2# B- -9434# 360# 128 933102# 217# - 7 68 61 129 Pm x -52881# 298# 8111# 2# B- -10881# 582# 128 943230# 320# - 5 67 62 129 Sm x -42000# 500# 8021# 4# B- * 128 954911# 537# -0 36 83 47 130 Ag -nn -45697# 500# 8140# 4# B- 15420# 500# 129 950942# 537# - 34 82 48 130 Cd x -61117.589 22.356 8252.586 0.172 B- 8765.617 44.128 129 934387.566 24.000 - 32 81 49 130 In + -69883.206 38.046 8313.996 0.293 B- 10249.000 38.000 129 924977.288 40.844 - 30 80 50 130 Sn -80132.206 1.873 8386.816 0.014 B- 2153.470 14.113 129 913974.533 2.010 - 28 79 51 130 Sb -82285.676 14.212 8397.363 0.109 B- 5067.273 14.212 129 911662.688 15.257 - 26 78 52 130 Te -87352.949 0.011 8430.324 0.000 B- -416.811 3.168 129 906222.747 0.012 - 24 77 53 130 I -n -86936.138 3.168 8421.100 0.024 B- 2944.325 3.168 129 906670.211 3.401 - 22 76 54 130 Xe -89880.463 0.009 8437.731 0.000 B- -2980.720 8.357 129 903509.349 0.010 - 20 75 55 130 Cs -86899.743 8.357 8408.784 0.064 B- 361.801 8.738 129 906709.283 8.971 - 18 74 56 130 Ba -87261.544 2.553 8405.549 0.020 B- -5634.178 26.071 129 906320.874 2.741 - 16 73 57 130 La x -81627.366 25.946 8356.191 0.200 B- -2204.461 38.133 129 912369.413 27.854 - 14 72 58 130 Ce x -79422.905 27.945 8333.216 0.215 B- -8247.448 70.085 129 914736.000 30.000 - 12 71 59 130 Pr x -71175.457 64.273 8263.756 0.494 B- -4579.225 70.085 129 923590.000 69.000 - 10 70 60 130 Nd x -66596.232 27.945 8222.513 0.215 B- -11200# 198# 129 928506.000 30.000 - 8 69 61 130 Pm x -55396# 196# 8130# 2# B- -7890# 446# 129 940530# 210# - 6 68 62 130 Sm x -47506# 401# 8064# 3# B- -13823# 641# 129 949000# 430# - 4 67 63 130 Eu -p -33683# 500# 7951# 4# B- * 129 963840# 537# -0 37 84 47 131 Ag x -40380# 500# 8099# 4# B- 14839# 511# 130 956650# 537# - 35 83 48 131 Cd x -55218.965 102.464 8206.175 0.782 B- 12806.066 102.500 130 940720.000 110.000 - 33 82 49 131 In x -68025.030 2.701 8297.959 0.021 B- 9239.541 4.518 130 926972.122 2.900 - 31 81 50 131 Sn -77264.571 3.621 8362.517 0.028 B- 4716.830 3.962 130 917053.066 3.887 - 29 80 51 131 Sb -81981.401 2.084 8392.552 0.016 B- 3229.611 2.085 130 911989.341 2.236 - 27 79 52 131 Te -n -85211.012 0.061 8411.233 0.001 B- 2231.699 0.608 130 908522.211 0.065 - 25 78 53 131 I + -87442.710 0.605 8422.297 0.005 B- 970.848 0.605 130 906126.384 0.649 - 23 77 54 131 Xe -88413.558 0.009 8423.736 0.000 B- -354.772 4.974 130 905084.136 0.009 - 21 76 55 131 Cs -88058.786 4.974 8415.056 0.038 B- -1375.055 5.279 130 905464.999 5.340 - 19 75 56 131 Ba -86683.731 2.569 8398.587 0.020 B- -2914.475 28.063 130 906941.181 2.757 - 17 74 57 131 La x -83769.256 27.945 8370.367 0.213 B- -4060.816 43.092 130 910070.000 30.000 - 15 73 58 131 Ce -79708.440 32.802 8333.396 0.250 B- -5407.784 55.446 130 914429.465 35.214 - 13 72 59 131 Pr -74300.656 46.995 8286.143 0.359 B- -6532.623 53.081 130 920234.960 50.451 - 11 71 60 131 Nd -67768.033 27.517 8230.304 0.210 B- -8108# 202# 130 927248.020 29.541 - 9 70 61 131 Pm x -59660# 200# 8162# 2# B- -9527# 448# 130 935952# 215# - 7 69 62 131 Sm x -50133# 401# 8084# 3# B- -10863# 566# 130 946180# 430# - 5 68 63 131 Eu -p -39270# 401# 7995# 3# B- * 130 957842# 430# -0 38 85 47 132 Ag x -33790# 500# 8049# 4# B- 16473# 537# 131 963725# 537# - 36 84 48 132 Cd x -50263# 196# 8168# 1# B- 12148# 205# 131 946040# 210# - 34 83 49 132 In + -62411.542 60.033 8253.715 0.455 B- 14135.000 60.000 131 932998.449 64.447 - 32 82 50 132 Sn -76546.542 1.976 8354.872 0.015 B- 3088.729 3.161 131 917823.902 2.121 - 30 81 51 132 Sb -79635.271 2.467 8372.344 0.019 B- 5552.915 4.271 131 914508.015 2.648 - 28 80 52 132 Te -85188.186 3.486 8408.485 0.026 B- 515.304 3.483 131 908546.716 3.742 - 26 79 53 132 I -85703.490 4.065 8406.462 0.031 B- 3575.472 4.065 131 907993.514 4.364 - 24 78 54 132 Xe -89278.96179 0.00515 8427.622 0.000 B- -2126.280 1.036 131 904155.08697 0.00553 - 22 77 55 132 Cs -87152.681 1.036 8405.587 0.008 B- 1282.336 1.478 131 906437.743 1.112 - 20 76 56 132 Ba -88435.017 1.054 8409.375 0.008 B- -4711.367 36.354 131 905061.098 1.131 - 18 75 57 132 La -83723.650 36.359 8367.756 0.275 B- -1252.754 41.718 131 910118.959 39.032 - 16 74 58 132 Ce -82470.896 20.442 8352.338 0.155 B- -7243.440 35.380 131 911463.846 21.945 - 14 73 59 132 Pr x -75227.456 28.876 8291.537 0.219 B- -3801.648 37.679 131 919240.000 31.000 - 12 72 60 132 Nd x -71425.807 24.205 8256.810 0.183 B- -9798# 151# 131 923321.237 25.985 - 10 71 61 132 Pm x -61628# 149# 8177# 1# B- -6548# 333# 131 933840# 160# - 8 70 62 132 Sm x -55079# 298# 8121# 2# B- -12879# 499# 131 940870# 320# - 6 69 63 132 Eu x -42200# 400# 8018# 3# B- * 131 954696# 429# -0 37 85 48 133 Cd x -43920# 298# 8119# 2# B- 13544# 357# 132 952850# 320# - 35 84 49 133 In x -57464# 196# 8215# 1# B- 13410# 196# 132 938310# 210# - 33 83 50 133 Sn -70873.880 1.904 8310.088 0.014 B- 8049.623 3.662 132 923913.756 2.044 - 31 82 51 133 Sb -78923.503 3.128 8364.729 0.024 B- 4013.619 3.518 132 915272.130 3.357 - 29 81 52 133 Te -82937.122 2.066 8389.025 0.016 B- 2921.139 6.751 132 910963.332 2.218 - 27 80 53 133 I ++ -85858.260 6.427 8405.106 0.048 B- 1785.311 6.861 132 907827.361 6.900 - 25 79 54 133 Xe + -87643.571 2.400 8412.647 0.018 B- 427.360 2.400 132 905910.750 2.576 - 23 78 55 133 Cs -88070.931 0.008 8409.978 0.000 B- -517.319 0.992 132 905451.961 0.008 - 21 77 56 133 Ba -87553.613 0.992 8400.206 0.007 B- -2059.230 27.962 132 906007.325 1.065 - 19 76 57 133 La x -85494.383 27.945 8378.841 0.210 B- -3076.168 32.379 132 908218.000 30.000 - 17 75 58 133 Ce x -82418.214 16.354 8349.829 0.123 B- -4480.634 20.583 132 911520.402 17.557 - 15 74 59 133 Pr x -77937.581 12.497 8310.258 0.094 B- -5605.208 48.222 132 916330.561 13.416 - 13 73 60 133 Nd x -72332.372 46.575 8262.231 0.350 B- -6924.726 68.552 132 922348.000 50.000 - 11 72 61 133 Pm x -65407.646 50.301 8204.283 0.378 B- -8177# 302# 132 929782.000 54.000 - 9 71 62 133 Sm x -57231# 298# 8137# 2# B- -9995# 422# 132 938560# 320# - 7 70 63 133 Eu x -47236# 298# 8056# 2# B- -11376# 582# 132 949290# 320# - 5 69 64 133 Gd x -35860# 500# 7964# 4# B- * 132 961503# 537# -0 38 86 48 134 Cd x -38920# 400# 8082# 3# B- 12741# 499# 133 958218# 429# - 36 85 49 134 In x -51661# 298# 8171# 2# B- 14773# 298# 133 944540# 320# - 34 84 50 134 Sn x -66433.748 3.167 8275.171 0.024 B- 7586.794 3.597 133 928680.433 3.400 - 32 83 51 134 Sb x -74020.542 1.705 8325.950 0.013 B- 8513.200 3.233 133 920535.675 1.830 - 30 82 52 134 Te -82533.741 2.746 8383.643 0.020 B- 1509.687 4.933 133 911396.379 2.948 - 28 81 53 134 I -84043.429 4.857 8389.071 0.036 B- 4082.393 4.857 133 909775.663 5.213 - 26 80 54 134 Xe -88125.822 0.009 8413.699 0.000 B- -1234.667 0.018 133 905393.033 0.010 - 24 79 55 134 Cs -86891.154 0.016 8398.646 0.000 B- 2058.699 0.304 133 906718.503 0.017 - 22 78 56 134 Ba -88949.853 0.304 8408.171 0.002 B- -3731.204 19.932 133 904508.399 0.326 - 20 77 57 134 La x -85218.650 19.930 8374.488 0.149 B- -385.760 28.510 133 908514.011 21.395 - 18 76 58 134 Ce x -84832.889 20.387 8365.771 0.152 B- -6304.898 28.781 133 908928.142 21.886 - 16 75 59 134 Pr x -78527.991 20.316 8312.881 0.152 B- -2881.559 23.503 133 915696.729 21.810 - 14 74 60 134 Nd x -75646.432 11.817 8285.538 0.088 B- -8907.681 58.949 133 918790.210 12.686 - 12 73 61 134 Pm x -66738.751 57.753 8213.225 0.431 B- -5363# 204# 133 928353.000 62.000 - 10 72 62 134 Sm x -61376# 196# 8167# 1# B- -11448# 357# 133 934110# 210# - 8 71 63 134 Eu x -49928# 298# 8076# 2# B- -8626# 499# 133 946400# 320# - 6 70 64 134 Gd x -41302# 401# 8006# 3# B- * 133 955660# 430# -0 37 86 49 135 In x -46528# 401# 8132# 3# B- 14104# 401# 134 950050# 430# - 35 85 50 135 Sn x -60632.244 3.074 8230.687 0.023 B- 9058.079 4.052 134 934908.605 3.300 - 33 84 51 135 Sb -69690.323 2.640 8291.989 0.020 B- 8038.457 3.152 134 925184.357 2.834 - 31 83 52 135 Te -77728.780 1.722 8345.738 0.013 B- 6050.366 2.686 134 916554.718 1.848 - 29 82 53 135 I -83779.145 2.061 8384.760 0.015 B- 2634.005 3.868 134 910059.382 2.212 - 27 81 54 135 Xe -86413.151 3.720 8398.476 0.028 B- 1168.492 3.675 134 907231.661 3.993 - 25 80 55 135 Cs -87581.643 0.992 8401.336 0.007 B- 268.855 1.038 134 905977.234 1.064 - 23 79 56 135 Ba -87850.498 0.306 8397.533 0.002 B- -1207.181 9.430 134 905688.606 0.328 - 21 78 57 135 La -86643.317 9.434 8382.795 0.070 B- -2027.146 4.610 134 906984.568 10.127 - 19 77 58 135 Ce -84616.171 10.267 8361.984 0.076 B- -3680.310 15.655 134 909160.799 11.022 - 17 76 59 135 Pr x -80935.861 11.817 8328.928 0.088 B- -4722.252 22.484 134 913111.774 12.686 - 15 75 60 135 Nd x -76213.609 19.128 8288.153 0.142 B- -6161.534 77.838 134 918181.320 20.534 - 13 74 61 135 Pm x -70052.075 75.451 8236.717 0.559 B- -7194.860 172.054 134 924796.000 81.000 - 11 73 62 135 Sm x -62857.215 154.628 8177.626 1.145 B- -8709# 249# 134 932520.000 166.000 - 9 72 63 135 Eu x -54148# 196# 8107# 1# B- -9757# 445# 134 941870# 210# - 7 71 64 135 Gd x -44390# 400# 8029# 3# B- -11565# 566# 134 952345# 429# - 5 70 65 135 Tb -p -32825# 401# 7938# 3# B- * 134 964760# 430# -0 38 87 49 136 In x -40510# 400# 8087# 3# B- 15389# 499# 135 956511# 429# - 36 86 50 136 Sn x -55899# 298# 8195# 2# B- 8608# 298# 135 939990# 320# - 34 85 51 136 Sb -64506.880 5.830 8252.252 0.043 B- 9918.389 6.260 135 930749.011 6.258 - 32 84 52 136 Te -74425.269 2.281 8319.429 0.017 B- 5119.945 14.188 135 920101.182 2.448 - 30 83 53 136 I -79545.214 14.188 8351.323 0.104 B- 6883.945 14.188 135 914604.695 15.231 - 28 82 54 136 Xe -86429.159 0.007 8396.188 0.000 B- -90.462 1.882 135 907214.476 0.007 - 26 81 55 136 Cs + -86338.697 1.882 8389.770 0.014 B- 2548.224 1.857 135 907311.590 2.020 - 24 80 56 136 Ba -88886.921 0.306 8402.755 0.002 B- -2849.443 53.172 135 904575.959 0.328 - 22 79 57 136 La x -86037.479 53.171 8376.050 0.391 B- 470.893 53.173 135 907634.962 57.081 - 20 78 58 136 Ce -86508.372 0.408 8373.760 0.003 B- -5168.017 11.457 135 907129.438 0.438 - 18 77 59 136 Pr -81340.355 11.455 8330.008 0.084 B- -2141.068 16.458 135 912677.532 12.297 - 16 76 60 136 Nd x -79199.287 11.817 8308.512 0.087 B- -8029.371 70.076 135 914976.064 12.686 - 14 75 61 136 Pm x -71169.915 69.073 8243.720 0.508 B- -4359.026 70.194 135 923595.949 74.152 - 12 74 62 136 Sm x -66810.890 12.497 8205.916 0.092 B- -10567# 196# 135 928275.555 13.416 - 10 73 63 136 Eu x -56244# 196# 8122# 1# B- -7154# 357# 135 939620# 210# - 8 72 64 136 Gd x -49090# 298# 8064# 2# B- -12960# 582# 135 947300# 320# - 6 71 65 136 Tb x -36130# 500# 7963# 4# B- * 135 961213# 537# -0 39 88 49 137 In x -35040# 500# 8047# 4# B- 14748# 641# 136 962383# 537# - 37 87 50 137 Sn x -49788# 401# 8149# 3# B- 10272# 404# 136 946550# 430# - 35 86 51 137 Sb x -60060.384 52.164 8218.476 0.381 B- 9243.369 52.206 136 935522.522 56.000 - 33 85 52 137 Te -69303.753 2.100 8280.235 0.015 B- 7052.506 8.643 136 925599.357 2.254 - 31 84 53 137 I p-2n -76356.258 8.383 8326.002 0.061 B- 6027.145 8.384 136 918028.180 9.000 - 29 83 54 137 Xe -n -82383.404 0.103 8364.286 0.001 B- 4162.203 0.373 136 911557.773 0.111 - 27 82 55 137 Cs + -86545.606 0.358 8388.956 0.003 B- 1175.629 0.172 136 907089.464 0.384 - 25 81 56 137 Ba -87721.235 0.314 8391.827 0.002 B- -580.547 1.632 136 905827.375 0.337 - 23 80 57 137 La + -87140.688 1.659 8381.879 0.012 B- -1222.100 1.600 136 906450.618 1.780 - 21 79 58 137 Ce -85918.588 0.437 8367.248 0.003 B- -2716.895 8.133 136 907762.596 0.469 - 19 78 59 137 Pr -83201.693 8.137 8341.706 0.059 B- -3617.126 14.282 136 910679.304 8.735 - 17 77 60 137 Nd -79584.567 11.737 8309.593 0.086 B- -5511.719 17.545 136 914562.448 12.600 - 15 76 61 137 Pm x -74072.848 13.041 8263.651 0.095 B- -6046.323 44.355 136 920479.522 14.000 - 13 75 62 137 Sm -68026.525 42.395 8213.806 0.309 B- -7880.630 42.620 136 926970.517 45.512 - 11 74 63 137 Eu x -60145.895 4.378 8150.573 0.032 B- -8932# 298# 136 935430.722 4.700 - 9 73 64 137 Gd x -51214# 298# 8080# 2# B- -10246# 499# 136 945020# 320# - 7 72 65 137 Tb x -40967# 401# 7999# 3# B- * 136 956020# 430# -0 38 88 50 138 Sn x -44861# 503# 8113# 4# B- 9360# 1177# 137 951840# 540# - 36 87 51 138 Sb x -54220.403 1064.232 8175.091 7.712 B- 11475.582 1064.239 137 941792.000 1142.500 - 34 86 52 138 Te -65695.985 3.787 8252.578 0.027 B- 6283.914 7.063 137 929472.454 4.065 - 32 85 53 138 I x -71979.900 5.962 8292.444 0.043 B- 7992.334 6.588 137 922726.394 6.400 - 30 84 54 138 Xe -79972.233 2.804 8344.690 0.020 B- 2914.704 9.579 137 914146.271 3.010 - 28 83 55 138 Cs -82886.937 9.159 8360.142 0.066 B- 5374.700 9.159 137 911017.207 9.832 - 26 82 56 138 Ba -88261.638 0.317 8393.420 0.002 B- -1742.458 3.191 137 905247.229 0.339 - 24 81 57 138 La -86519.180 3.188 8375.125 0.023 B- 1051.742 4.038 137 907117.834 3.422 - 22 80 58 138 Ce -87570.922 4.931 8377.077 0.036 B- -4437.000 10.000 137 905988.743 5.293 - 20 79 59 138 Pr - -83133.922 11.150 8339.255 0.081 B- -1115.612 16.090 137 910752.059 11.969 - 18 78 60 138 Nd -82018.310 11.601 8325.502 0.084 B- -7077.827 28.756 137 911949.717 12.454 - 16 77 61 138 Pm -74940.483 27.739 8268.544 0.201 B- -3442.721 30.151 137 919548.077 29.778 - 14 76 62 138 Sm x -71497.762 11.817 8237.928 0.086 B- -9748.093 30.341 137 923243.990 12.686 - 12 75 63 138 Eu x -61749.669 27.945 8161.620 0.202 B- -5949# 198# 137 933709.000 30.000 - 10 74 64 138 Gd x -55800# 196# 8113# 1# B- -12132# 357# 137 940096# 210# - 8 73 65 138 Tb x -43668# 298# 8019# 2# B- -8737# 585# 137 953120# 320# - 6 72 66 138 Dy x -34931# 503# 7950# 4# B- * 137 962500# 540# -0 39 89 50 139 Sn x -38440# 500# 8066# 4# B- 11348# 641# 138 958733# 537# - 37 88 51 139 Sb x -49788# 401# 8142# 3# B- 10417# 401# 138 946550# 430# - 35 87 52 139 Te x -60205.072 3.540 8211.771 0.025 B- 8265.882 5.345 138 935367.193 3.800 - 33 86 53 139 I x -68470.954 4.005 8265.609 0.029 B- 7173.622 4.542 138 926493.403 4.300 - 31 85 54 139 Xe x -75644.576 2.142 8311.590 0.015 B- 5056.346 3.801 138 918792.203 2.300 - 29 84 55 139 Cs + -80700.921 3.140 8342.338 0.023 B- 4212.829 3.123 138 913363.992 3.370 - 27 83 56 139 Ba -84913.751 0.319 8367.017 0.002 B- 2312.461 2.013 138 908841.334 0.342 - 25 82 57 139 La -87226.212 2.009 8378.025 0.014 B- -278.350 6.953 138 906358.804 2.156 - 23 81 58 139 Ce -86947.862 7.230 8370.395 0.052 B- -2129.064 2.996 138 906657.625 7.761 - 21 80 59 139 Pr -84818.798 7.809 8349.449 0.056 B- -2804.856 28.033 138 908943.270 8.383 - 19 79 60 139 Nd -82013.942 27.600 8323.642 0.199 B- -4513.460 25.927 138 911954.407 29.630 - 17 78 61 139 Pm -77500.481 13.593 8285.543 0.098 B- -5120.263 17.414 138 916799.806 14.592 - 15 77 62 139 Sm x -72380.219 10.884 8243.078 0.078 B- -6982.177 17.071 138 922296.634 11.684 - 13 76 63 139 Eu x -65398.042 13.151 8187.218 0.095 B- -7767# 196# 138 929792.310 14.117 - 11 75 64 139 Gd x -57632# 196# 8126# 1# B- -9501# 357# 138 938130# 210# - 9 74 65 139 Tb x -48130# 298# 8052# 2# B- -10489# 585# 138 948330# 320# - 7 73 66 139 Dy x -37642# 503# 7971# 4# B- * 138 959590# 540# -0 38 89 51 140 Sb x -43939# 596# 8100# 4# B- 12638# 599# 139 952830# 640# - 36 88 52 140 Te x -56576.228 62.410 8184.847 0.446 B- 7029.985 63.574 139 939262.917 67.000 - 34 87 53 140 I x -63606.213 12.109 8229.473 0.086 B- 9380.238 12.331 139 931715.917 13.000 - 32 86 54 140 Xe x -72986.451 2.329 8290.887 0.017 B- 4063.654 8.525 139 921645.817 2.500 - 30 85 55 140 Cs -77050.105 8.201 8314.325 0.059 B- 6219.249 9.882 139 917283.305 8.804 - 28 84 56 140 Ba -83269.354 7.933 8353.160 0.057 B- 1046.517 7.993 139 910606.666 8.516 - 26 83 57 140 La -84315.871 2.009 8355.047 0.014 B- 3760.218 1.727 139 909483.184 2.156 - 24 82 58 140 Ce -88076.089 1.596 8376.317 0.011 B- -3388.000 6.000 139 905446.424 1.713 - 22 81 59 140 Pr - -84688.089 6.209 8346.529 0.044 B- -429.177 7.101 139 909083.592 6.665 - 20 80 60 140 Nd x -84258.912 3.447 8337.875 0.025 B- -6045.200 24.000 139 909544.332 3.700 - 18 79 61 140 Pm - -78213.712 24.246 8289.107 0.173 B- -2757.777 27.277 139 916034.122 26.029 - 16 78 62 140 Sm x -75455.935 12.497 8263.820 0.089 B- -8470.000 50.000 139 918994.717 13.416 - 14 77 63 140 Eu - -66985.935 51.538 8197.732 0.368 B- -5203.664 58.627 139 928087.637 55.328 - 12 76 64 140 Gd x -61782.271 27.945 8154.975 0.200 B- -11300.000 800.000 139 933674.000 30.000 - 10 75 65 140 Tb - -50482.271 800.488 8068.672 5.718 B- -7652# 895# 139 945805.049 859.359 - 8 74 66 140 Dy x -42830# 401# 8008# 3# B- -13571# 643# 139 954020# 430# - 6 73 67 140 Ho -p -29259# 503# 7906# 4# B- * 139 968589# 540# -0 39 90 51 141 Sb x -39110# 500# 8066# 4# B- 11377# 641# 140 958014# 537# - 37 89 52 141 Te x -50487# 401# 8141# 3# B- 9440# 401# 140 945800# 430# - 35 88 53 141 I x -59926.657 15.835 8202.255 0.112 B- 8270.642 16.097 140 935666.084 17.000 - 33 87 54 141 Xe x -68197.299 2.888 8255.364 0.020 B- 6280.224 9.638 140 926787.184 3.100 - 31 86 55 141 Cs -74477.523 9.195 8294.356 0.065 B- 5255.103 9.617 140 920045.086 9.871 - 29 85 56 141 Ba -79732.626 5.319 8326.078 0.038 B- 3199.010 6.600 140 914403.500 5.710 - 27 84 57 141 La -82931.636 4.219 8343.217 0.030 B- 2501.280 3.928 140 910969.222 4.528 - 25 83 58 141 Ce -85432.916 1.597 8355.408 0.011 B- 582.728 1.202 140 908283.987 1.714 - 23 82 59 141 Pr -86015.644 1.665 8353.992 0.012 B- -1823.014 2.809 140 907658.403 1.787 - 21 81 60 141 Nd - -84192.630 3.265 8335.515 0.023 B- -3669.709 14.349 140 909615.488 3.505 - 19 80 61 141 Pm x -80522.921 13.972 8303.940 0.099 B- -4589.012 16.375 140 913555.084 15.000 - 17 79 62 141 Sm -75933.909 8.539 8265.845 0.061 B- -6008.280 14.285 140 918481.591 9.167 - 15 78 63 141 Eu -69925.629 12.639 8217.684 0.090 B- -6701.405 23.456 140 924931.745 13.568 - 13 77 64 141 Gd x -63224.224 19.760 8164.608 0.140 B- -8683.387 107.098 140 932126.000 21.213 - 11 76 65 141 Tb x -54540.837 105.259 8097.475 0.747 B- -9158# 316# 140 941448.000 113.000 - 9 75 66 141 Dy x -45382# 298# 8027# 2# B- -11018# 499# 140 951280# 320# - 7 74 67 141 Ho -p -34364# 401# 7943# 3# B- * 140 963108# 430# -0 38 90 52 142 Te x -46370# 503# 8111# 4# B- 8400# 627# 141 950220# 540# - 36 89 53 142 I x -54769.984 374.461 8165.019 2.637 B- 10459.655 374.470 141 941202.000 402.000 - 34 88 54 142 Xe x -65229.639 2.701 8233.169 0.019 B- 5284.911 7.565 141 929973.098 2.900 - 32 87 55 142 Cs -70514.550 7.067 8264.877 0.050 B- 7327.714 8.363 141 924299.512 7.586 - 30 86 56 142 Ba -77842.264 5.920 8310.971 0.042 B- 2181.963 8.391 141 916432.888 6.355 - 28 85 57 142 La -80024.227 6.309 8320.828 0.044 B- 4508.961 5.845 141 914090.454 6.773 - 26 84 58 142 Ce -84533.188 2.509 8347.071 0.018 B- -745.712 2.500 141 909249.884 2.693 - 24 83 59 142 Pr -83787.476 1.665 8336.310 0.012 B- 2162.505 1.416 141 910050.440 1.786 - 22 82 60 142 Nd -85949.980 1.368 8346.030 0.010 B- -4807.936 23.629 141 907728.895 1.468 - 20 81 61 142 Pm -81142.044 23.597 8306.662 0.166 B- -2155.574 23.752 141 912890.428 25.332 - 18 80 62 142 Sm -78986.470 3.079 8285.972 0.022 B- -7673.000 30.000 141 915204.532 3.305 - 16 79 63 142 Eu - -71313.470 30.158 8226.427 0.212 B- -4353.955 41.114 141 923441.836 32.375 - 14 78 64 142 Gd x -66959.515 27.945 8190.256 0.197 B- -10400.000 700.000 141 928116.000 30.000 - 12 77 65 142 Tb - -56559.515 700.558 8111.507 4.934 B- -6440# 200# 141 939280.859 752.079 - 10 76 66 142 Dy - -50120# 729# 8061# 5# B- -12869# 831# 141 946194# 782# - 8 75 67 142 Ho x -37250# 401# 7965# 3# B- -9221# 641# 141 960010# 430# - 6 74 68 142 Er x -28030# 500# 7894# 4# B- * 141 969909# 537# -0 39 91 52 143 Te x -40278# 503# 8068# 4# B- 10353# 541# 142 956760# 540# - 37 90 53 143 I x -50630# 200# 8135# 1# B- 9572# 200# 142 945646# 215# - 35 89 54 143 Xe x -60202.873 4.657 8196.885 0.033 B- 7472.636 8.891 142 935369.553 5.000 - 33 88 55 143 Cs -67675.509 7.573 8243.670 0.053 B- 6261.688 9.730 142 927347.348 8.130 - 31 87 56 143 Ba -73937.197 6.756 8281.987 0.047 B- 4234.318 9.969 142 920625.150 7.253 - 29 86 57 143 La -78171.515 7.330 8306.127 0.051 B- 3435.156 7.595 142 916079.422 7.869 - 27 85 58 143 Ce -81606.671 2.508 8324.678 0.018 B- 1461.575 1.866 142 912391.630 2.692 - 25 84 59 143 Pr -83068.246 1.897 8329.428 0.013 B- 933.988 1.368 142 910822.564 2.037 - 23 83 60 143 Nd -84002.234 1.367 8330.488 0.010 B- -1041.583 2.682 142 909819.887 1.467 - 21 82 61 143 Pm -82960.651 2.996 8317.733 0.021 B- -3443.499 3.560 142 910938.073 3.216 - 19 81 62 143 Sm -79517.152 2.804 8288.182 0.020 B- -5275.851 11.338 142 914634.821 3.010 - 17 80 63 143 Eu x -74241.300 10.986 8245.817 0.077 B- -6010.000 200.000 142 920298.681 11.793 - 15 79 64 143 Gd - -68231.300 200.301 8198.318 1.401 B- -7812.117 206.750 142 926750.682 215.032 - 13 78 65 143 Tb x -60419.183 51.232 8138.217 0.358 B- -8250.242 52.866 142 935137.335 55.000 - 11 77 66 143 Dy x -52168.941 13.041 8075.052 0.091 B- -10121# 298# 142 943994.335 14.000 - 9 76 67 143 Ho x -42048# 298# 7999# 2# B- -10788# 499# 142 954860# 320# - 7 75 68 143 Er x -31260# 400# 7918# 3# B- * 142 966441# 429# -0 38 91 53 144 I x -45280# 401# 8098# 3# B- 11592# 401# 143 951390# 430# - 36 90 54 144 Xe x -56872.293 5.310 8172.884 0.037 B- 6399.060 20.820 143 938945.079 5.700 - 34 89 55 144 Cs -63271.353 20.132 8211.889 0.140 B- 8495.768 20.416 143 932075.404 21.612 - 32 88 56 144 Ba -71767.121 7.136 8265.454 0.050 B- 3082.530 14.774 143 922954.821 7.661 - 30 87 57 144 La x -74849.652 12.937 8281.428 0.090 B- 5582.219 13.254 143 919645.589 13.888 - 28 86 58 144 Ce + -80431.870 2.885 8314.760 0.020 B- 318.646 0.832 143 913652.830 3.096 - 26 85 59 144 Pr + -80750.517 2.762 8311.540 0.019 B- 2997.440 2.400 143 913310.750 2.965 - 24 84 60 144 Nd -83747.957 1.367 8326.922 0.009 B- -2331.863 2.646 143 910092.865 1.467 - 22 83 61 144 Pm -81416.093 2.964 8305.296 0.021 B- 549.442 2.668 143 912596.224 3.181 - 20 82 62 144 Sm -81965.535 1.554 8303.679 0.011 B- -6346.402 10.814 143 912006.373 1.668 - 18 81 63 144 Eu -75619.133 10.789 8254.173 0.075 B- -3859.630 29.955 143 918819.517 11.582 - 16 80 64 144 Gd x -71759.504 27.945 8221.937 0.194 B- -9391.323 39.520 143 922963.000 30.000 - 14 79 65 144 Tb x -62368.181 27.945 8151.287 0.194 B- -5798.098 28.851 143 933045.000 30.000 - 12 78 66 144 Dy x -56570.083 7.173 8105.589 0.050 B- -11960.569 11.104 143 939269.514 7.700 - 10 77 67 144 Ho x -44609.513 8.477 8017.097 0.059 B- -8002# 196# 143 952109.714 9.100 - 8 76 68 144 Er x -36608# 196# 7956# 1# B- -14349# 445# 143 960700# 210# - 6 75 69 144 Tm -p -22259# 400# 7851# 3# B- * 143 976104# 429# -0 39 92 53 145 I x -40939# 503# 8068# 3# B- 10554# 503# 144 956050# 540# - 37 91 54 145 Xe x -51493.329 11.178 8135.087 0.077 B- 8561.086 14.393 144 944719.634 12.000 - 35 90 55 145 Cs -60054.415 9.067 8188.733 0.063 B- 7461.761 12.412 144 935528.930 9.733 - 33 89 56 145 Ba x -67516.176 8.477 8234.798 0.058 B- 5319.141 14.912 144 927518.400 9.100 - 31 88 57 145 La -72835.317 12.269 8266.087 0.085 B- 4231.705 35.300 144 921808.066 13.170 - 29 87 58 145 Ce -77067.022 33.902 8289.875 0.234 B- 2558.918 33.635 144 917265.144 36.395 - 27 86 59 145 Pr -79625.940 7.169 8302.127 0.049 B- 1806.012 7.037 144 914518.033 7.696 - 25 85 60 145 Nd -81431.951 1.384 8309.187 0.010 B- -164.478 2.536 144 912579.199 1.485 - 23 84 61 145 Pm -81267.474 2.859 8302.657 0.020 B- -616.156 2.539 144 912755.773 3.068 - 21 83 62 145 Sm -80651.318 1.578 8293.013 0.011 B- -2659.810 2.723 144 913417.244 1.694 - 19 82 63 145 Eu -77991.507 3.106 8269.274 0.021 B- -5065.187 19.953 144 916272.668 3.334 - 17 81 64 145 Gd -72926.320 19.709 8228.946 0.136 B- -6537.909 108.066 144 921710.370 21.158 - 15 80 65 145 Tb -66388.411 109.174 8178.461 0.753 B- -8145.812 109.369 144 928729.105 117.203 - 13 79 66 145 Dy x -58242.599 6.520 8116.888 0.045 B- -9122.493 9.902 144 937473.994 7.000 - 11 78 67 145 Ho x -49120.105 7.452 8048.578 0.051 B- -9880# 200# 144 947267.394 8.000 - 9 77 68 145 Er x -39240# 200# 7975# 1# B- -11657# 280# 144 957874# 215# - 7 76 69 145 Tm -p -27583# 196# 7889# 1# B- * 144 970389# 210# -0 38 92 54 146 Xe x -47954.943 24.219 8110.415 0.166 B- 7355.429 24.391 145 948518.248 26.000 - 36 91 55 146 Cs x -55310.372 2.893 8155.436 0.020 B- 9636.714 21.063 145 940621.870 3.106 - 34 90 56 146 Ba -64947.086 20.863 8216.082 0.143 B- 4103.197 33.503 145 930276.431 22.397 - 32 89 57 146 La -69050.283 33.616 8238.828 0.230 B- 6585.107 34.456 145 925871.468 36.088 - 30 88 58 146 Ce -75635.389 16.306 8278.573 0.112 B- 1045.616 32.519 145 918802.065 17.504 - 28 87 59 146 Pr -76681.006 34.816 8280.376 0.238 B- 4244.861 34.830 145 917679.549 37.376 - 26 86 60 146 Nd -80925.867 1.386 8304.092 0.009 B- -1471.558 4.119 145 913122.503 1.488 - 24 85 61 146 Pm + -79454.309 4.308 8288.654 0.030 B- 1542.000 3.000 145 914702.286 4.624 - 22 84 62 146 Sm -80996.309 3.092 8293.857 0.021 B- -3878.768 5.869 145 913046.881 3.319 - 20 83 63 146 Eu -77117.541 6.026 8261.932 0.041 B- -1031.758 7.076 145 917210.909 6.468 - 18 82 64 146 Gd -76085.783 4.108 8249.506 0.028 B- -8322.173 44.749 145 918318.548 4.409 - 16 81 65 146 Tb -67763.610 44.862 8187.146 0.307 B- -5208.691 45.160 145 927252.768 48.161 - 14 80 66 146 Dy -62554.918 6.695 8146.112 0.046 B- -11316.699 9.392 145 932844.529 7.187 - 12 79 67 146 Ho -51238.219 6.587 8063.242 0.045 B- -6916.206 9.399 145 944993.506 7.071 - 10 78 68 146 Er -44322.012 6.705 8010.512 0.046 B- -13267# 200# 145 952418.359 7.197 - 8 77 69 146 Tm -p -31055# 200# 7914# 1# B- * 145 966661# 215# -0 39 93 54 147 Xe x -42360# 200# 8072# 1# B- 9560# 200# 146 954525# 215# - 37 92 55 147 Cs x -51920.064 8.383 8131.800 0.057 B- 8343.965 21.454 146 944261.515 9.000 - 35 91 56 147 Ba x -60264.029 19.748 8183.240 0.134 B- 6414.361 22.466 146 935303.900 21.200 - 33 90 57 147 La x -66678.390 10.712 8221.553 0.073 B- 5335.501 13.725 146 928417.800 11.500 - 31 89 58 147 Ce -72013.891 8.580 8252.527 0.058 B- 3430.176 15.533 146 922689.903 9.211 - 29 88 59 147 Pr -75444.067 15.857 8270.539 0.108 B- 2702.681 15.860 146 919007.458 17.023 - 27 87 60 147 Nd -78146.748 1.388 8283.603 0.009 B- 895.512 0.482 146 916106.010 1.490 - 25 86 61 147 Pm -79042.260 1.399 8284.372 0.010 B- 224.094 0.293 146 915144.638 1.501 - 23 85 62 147 Sm -79266.354 1.372 8280.575 0.009 B- -1721.598 2.281 146 914904.064 1.473 - 21 84 63 147 Eu -77544.756 2.630 8263.541 0.018 B- -2187.811 2.524 146 916752.276 2.823 - 19 83 64 147 Gd -75356.945 1.965 8243.336 0.013 B- -4614.280 8.147 146 919100.987 2.109 - 17 82 65 147 Tb -70742.665 8.100 8206.624 0.055 B- -6546.628 11.997 146 924054.620 8.695 - 15 81 66 147 Dy x -64196.038 8.849 8156.767 0.060 B- -8438.945 10.164 146 931082.715 9.500 - 13 80 67 147 Ho -55757.092 5.001 8094.037 0.034 B- -9149.286 38.517 146 940142.295 5.368 - 11 79 68 147 Er x -46607.807 38.191 8026.475 0.260 B- -10633.406 38.799 146 949964.458 41.000 - 9 78 69 147 Tm -35974.400 6.839 7948.817 0.047 B- * 146 961379.890 7.341 -0 40 94 54 148 Xe x -38600# 300# 8047# 2# B- 8311# 300# 147 958561# 322# - 38 93 55 148 Cs x -46910.942 13.041 8097.546 0.088 B- 10682.793 64.413 147 949639.029 14.000 - 36 92 56 148 Ba + -57593.735 63.079 8164.441 0.426 B- 5115.000 60.000 147 938170.578 67.718 - 34 91 57 148 La x -62708.735 19.468 8193.716 0.132 B- 7689.672 22.457 147 932679.400 20.900 - 32 90 58 148 Ce -70398.408 11.195 8240.387 0.076 B- 2137.016 12.567 147 924424.196 12.018 - 30 89 59 148 Pr -72535.424 15.043 8249.540 0.102 B- 4872.572 15.090 147 922130.015 16.148 - 28 88 60 148 Nd -77407.996 2.127 8277.177 0.014 B- -542.280 5.874 147 916899.093 2.283 - 26 87 61 148 Pm +p -76865.716 5.723 8268.226 0.039 B- 2470.548 5.642 147 917481.255 6.143 - 24 86 62 148 Sm -79336.264 1.367 8279.633 0.009 B- -3036.933 10.019 147 914829.012 1.467 - 22 85 63 148 Eu -76299.331 10.013 8253.827 0.068 B- -30.003 10.019 147 918089.294 10.748 - 20 84 64 148 Gd -76269.329 1.554 8248.338 0.011 B- -5732.246 12.529 147 918121.503 1.668 - 18 83 65 148 Tb -70537.082 12.464 8204.321 0.084 B- -2677.532 9.596 147 924275.323 13.380 - 16 82 66 148 Dy -67859.550 8.724 8180.943 0.059 B- -9868.393 84.287 147 927149.772 9.366 - 14 81 67 148 Ho x -57991.158 83.834 8108.979 0.566 B- -6512.169 84.458 147 937743.928 90.000 - 12 80 68 148 Er x -51478.989 10.246 8059.692 0.069 B- -12713.962 14.491 147 944735.029 11.000 - 10 79 69 148 Tm x -38765.027 10.246 7968.500 0.069 B- -8435# 400# 147 958384.029 11.000 - 8 78 70 148 Yb x -30330# 400# 7906# 3# B- * 147 967439# 429# -0 39 94 55 149 Cs x -43250# 400# 8073# 3# B- 9870# 593# 148 953569# 429# - 37 93 56 149 Ba x -53120.309 437.802 8133.793 2.938 B- 7099.605 481.431 148 942973.000 470.000 - 35 92 57 149 La + -60219.913 200.262 8176.191 1.344 B- 6450.000 200.000 148 935351.260 214.990 - 33 91 58 149 Ce x -66669.913 10.246 8214.229 0.069 B- 4369.452 14.230 148 928426.900 11.000 - 31 90 59 149 Pr x -71039.366 9.874 8238.303 0.066 B- 3336.100 10.101 148 923736.100 10.600 - 29 89 60 149 Nd -n -74375.466 2.128 8255.442 0.014 B- 1688.790 2.455 148 920154.648 2.284 - 27 88 61 149 Pm -76064.256 2.267 8261.526 0.015 B- 1071.481 1.875 148 918341.658 2.434 - 25 87 62 149 Sm -77135.737 1.311 8263.466 0.009 B- -694.625 3.788 148 917191.375 1.407 - 23 86 63 149 Eu -76441.112 3.951 8253.554 0.027 B- -1314.101 4.136 148 917937.086 4.241 - 21 85 64 149 Gd -75127.011 3.360 8239.484 0.023 B- -3638.343 4.339 148 919347.831 3.607 - 19 84 65 149 Tb -71488.669 3.667 8209.815 0.025 B- -3792.760 9.162 148 923253.753 3.936 - 17 83 66 149 Dy -67695.909 9.221 8179.109 0.062 B- -6049.330 12.803 148 927325.448 9.898 - 15 82 67 149 Ho -61646.578 11.990 8133.259 0.080 B- -7904.963 30.408 148 933819.672 12.871 - 13 81 68 149 Er x -53741.615 27.945 8074.955 0.188 B- -9859# 198# 148 942306.000 30.000 - 11 80 69 149 Tm x -43883# 196# 8004# 1# B- -10684# 358# 148 952890# 210# - 9 79 70 149 Yb x -33198# 300# 7927# 2# B- * 148 964360# 322# -0 40 95 55 150 Cs x -38170# 400# 8039# 3# B- 11730# 500# 149 959023# 429# - 38 94 56 150 Ba x -49900# 300# 8112# 2# B- 6230# 529# 149 946430# 322# - 36 93 57 150 La x -56129.966 435.473 8148.225 2.903 B- 8716.888 435.631 149 939742.000 467.500 - 34 92 58 150 Ce -64846.854 11.697 8201.122 0.078 B- 3453.625 14.291 149 930384.035 12.556 - 32 91 59 150 Pr -68300.479 9.015 8218.931 0.060 B- 5379.276 9.085 149 926676.415 9.678 - 30 90 60 150 Nd -73679.755 1.289 8249.577 0.009 B- -82.616 20.001 149 920901.525 1.384 - 28 89 61 150 Pm + -73597.139 20.041 8243.810 0.134 B- 3454.000 20.000 149 920990.217 21.514 - 26 88 62 150 Sm -77051.139 1.274 8261.621 0.009 B- -2258.905 6.181 149 917282.195 1.368 - 24 87 63 150 Eu -74792.234 6.253 8241.346 0.042 B- 971.700 3.543 149 919707.229 6.712 - 22 86 64 150 Gd -75763.934 6.076 8242.609 0.041 B- -4658.213 8.379 149 918664.066 6.523 - 20 85 65 150 Tb -71105.721 7.384 8206.338 0.049 B- -1796.122 8.389 149 923664.864 7.927 - 18 84 66 150 Dy -69309.600 4.348 8189.149 0.029 B- -7363.719 14.468 149 925593.080 4.667 - 16 83 67 150 Ho -61945.881 14.168 8134.842 0.094 B- -4114.568 13.591 149 933498.358 15.210 - 14 82 68 150 Er -57831.313 17.195 8102.195 0.115 B- -11340# 196# 149 937915.528 18.459 - 12 81 69 150 Tm x -46491# 196# 8021# 1# B- -7852# 358# 149 950090# 210# - 10 80 70 150 Yb x -38638# 300# 7964# 2# B- -13998# 424# 149 958520# 322# - 8 79 71 150 Lu -p -24640# 300# 7865# 2# B- * 149 973548# 322# -0 41 96 55 151 Cs x -34230# 500# 8013# 3# B- 10710# 640# 150 963253# 537# - 39 95 56 151 Ba x -44940# 400# 8079# 3# B- 8370# 591# 150 951755# 429# - 37 94 57 151 La x -53310.333 435.473 8129.043 2.884 B- 7914.718 435.833 150 942769.000 467.500 - 35 93 58 151 Ce x -61225.052 17.698 8176.277 0.117 B- 5554.578 21.189 150 934272.200 19.000 - 33 92 59 151 Pr -66779.630 11.651 8207.881 0.077 B- 4163.358 11.689 150 928309.114 12.507 - 31 91 60 151 Nd -70942.988 1.293 8230.272 0.009 B- 2443.075 4.473 150 923839.565 1.387 - 29 90 61 151 Pm -73386.063 4.653 8241.270 0.031 B- 1190.217 4.476 150 921216.817 4.994 - 27 89 62 151 Sm -74576.279 1.273 8243.971 0.008 B- 76.574 0.538 150 919939.066 1.367 - 25 88 63 151 Eu -74652.854 1.325 8239.297 0.009 B- -464.116 2.779 150 919856.860 1.422 - 23 87 64 151 Gd -74188.737 3.056 8231.043 0.020 B- -2565.233 3.762 150 920355.109 3.280 - 21 86 65 151 Tb -71623.504 4.137 8208.873 0.027 B- -2871.099 4.944 150 923109.001 4.441 - 19 85 66 151 Dy -a -68752.405 3.293 8184.678 0.022 B- -5129.667 8.756 150 926191.253 3.535 - 17 84 67 151 Ho -a -63622.738 8.302 8145.526 0.055 B- -5356.454 18.444 150 931698.177 8.912 - 15 83 68 151 Er x -58266.284 16.470 8104.872 0.109 B- -7493.528 25.460 150 937448.567 17.681 - 13 82 69 151 Tm +a -50772.756 19.416 8050.064 0.129 B- -9230.414 300.136 150 945493.201 20.843 - 11 81 70 151 Yb ep -41542.342 300.492 7983.755 1.990 B- -11434# 425# 150 955402.458 322.591 - 9 80 71 151 Lu -p -30108# 300# 7903# 2# B- * 150 967677# 322# -0 42 97 55 152 Cs x -28930# 500# 7979# 3# B- 12780# 640# 151 968942# 537# - 40 96 56 152 Ba x -41710# 400# 8057# 3# B- 7580# 500# 151 955222# 429# - 38 95 57 152 La x -49290# 300# 8102# 2# B- 9690# 361# 151 947085# 322# - 36 94 58 152 Ce x -58980# 200# 8161# 1# B- 4778# 201# 151 936682# 215# - 34 93 59 152 Pr x -63758.063 18.537 8187.104 0.122 B- 6391.344 30.710 151 931552.900 19.900 - 32 92 60 152 Nd -70149.407 24.485 8224.005 0.161 B- 1104.778 18.501 151 924691.509 26.285 - 30 91 61 152 Pm -71254.186 25.912 8226.127 0.170 B- 3508.417 25.886 151 923505.481 27.817 - 28 90 62 152 Sm -74762.603 1.212 8244.061 0.008 B- -1874.348 0.687 151 919739.040 1.301 - 26 89 63 152 Eu -72888.255 1.326 8226.583 0.009 B- 1818.661 0.702 151 921751.235 1.423 - 24 88 64 152 Gd -74706.916 1.206 8233.401 0.008 B- -3990.000 40.000 151 919798.822 1.294 - 22 87 65 152 Tb - -70716.916 40.018 8202.004 0.263 B- -599.044 40.258 151 924082.263 42.961 - 20 86 66 152 Dy -a -70117.873 4.624 8192.916 0.030 B- -6513.102 13.325 151 924725.363 4.963 - 18 85 67 152 Ho -63604.771 12.529 8144.919 0.082 B- -3104.394 9.815 151 931717.465 13.450 - 16 84 68 152 Er -60500.377 8.830 8119.349 0.058 B- -8780.104 54.743 151 935050.169 9.479 - 14 83 69 152 Tm -51720.273 54.027 8056.438 0.355 B- -5449.892 139.620 151 944476.000 58.000 - 12 82 70 152 Yb -46270.381 149.708 8015.436 0.985 B- -12848# 246# 151 950326.700 160.718 - 10 81 71 152 Lu x -33422# 196# 7926# 1# B- * 151 964120# 210# -0 41 97 56 153 Ba x -36470# 400# 8023# 3# B- 9590# 500# 152 960848# 429# - 39 96 57 153 La x -46060# 300# 8081# 2# B- 8850# 361# 152 950553# 322# - 37 95 58 153 Ce x -54910# 200# 8134# 1# B- 6659# 201# 152 941052# 215# - 35 94 59 153 Pr -61568.463 11.882 8172.036 0.078 B- 5761.834 12.190 152 933903.532 12.755 - 33 93 60 153 Nd -67330.297 2.747 8204.582 0.018 B- 3317.527 9.355 152 927717.949 2.948 - 31 92 61 153 Pm -70647.824 9.066 8221.152 0.059 B- 1911.862 9.093 152 924156.436 9.732 - 29 91 62 153 Sm -n -72559.686 1.219 8228.534 0.008 B- 807.536 0.708 152 922103.969 1.309 - 27 90 63 153 Eu -73367.222 1.330 8228.699 0.009 B- -484.671 0.717 152 921237.043 1.428 - 25 89 64 153 Gd -72882.551 1.203 8220.418 0.008 B- -1569.213 3.845 152 921757.359 1.291 - 23 88 65 153 Tb -71313.338 3.995 8205.048 0.026 B- -2170.394 1.933 152 923441.978 4.289 - 21 87 66 153 Dy -69142.943 4.048 8185.749 0.026 B- -4130.840 6.157 152 925771.992 4.346 - 19 86 67 153 Ho -a -65012.103 5.094 8153.637 0.033 B- -4543.499 9.916 152 930206.632 5.468 - 17 85 68 153 Er -60468.604 9.321 8118.827 0.061 B- -6495.276 12.891 152 935084.279 10.006 - 15 84 69 153 Tm -53973.329 11.983 8071.261 0.078 B- -6765# 196# 152 942057.244 12.864 - 13 83 70 153 Yb x -47208# 196# 8022# 1# B- -8835# 247# 152 949320# 210# - 11 82 71 153 Lu +a -38372.844 150.034 7959.070 0.981 B- -11073# 335# 152 958805.054 161.068 - 9 81 72 153 Hf x -27300# 300# 7882# 2# B- * 152 970692# 322# -0 42 98 56 154 Ba x -32820# 500# 8000# 3# B- 8709# 583# 153 964766# 537# - 40 97 57 154 La x -41530# 300# 8051# 2# B- 10690# 361# 153 955416# 322# - 38 96 58 154 Ce x -52220# 200# 8116# 1# B- 5885# 230# 153 943940# 215# - 36 95 59 154 Pr + -58104.976 113.009 8148.892 0.734 B- 7720.000 100.000 153 937621.738 121.320 - 34 94 60 154 Nd + -65824.976 52.642 8193.942 0.342 B- 2687.000 25.000 153 929333.977 56.513 - 32 93 61 154 Pm IT -68511.976 46.326 8206.310 0.301 B- 3943.200 46.303 153 926449.364 49.733 - 30 92 62 154 Sm -72455.176 1.462 8226.835 0.009 B- -717.056 1.103 153 922216.164 1.569 - 28 91 63 154 Eu -71738.121 1.346 8217.098 0.009 B- 1967.834 0.755 153 922985.955 1.444 - 26 90 64 154 Gd -73705.954 1.197 8224.796 0.008 B- -3549.651 45.298 153 920873.398 1.285 - 24 89 65 154 Tb - -70156.303 45.314 8196.666 0.294 B- 237.604 45.901 153 924684.106 48.646 - 22 88 66 154 Dy -70393.907 7.445 8193.129 0.048 B- -5754.596 10.178 153 924429.028 7.992 - 20 87 67 154 Ho -a -64639.311 8.228 8150.681 0.053 B- -2034.291 9.463 153 930606.841 8.833 - 18 86 68 154 Er -62605.020 4.986 8132.392 0.032 B- -8177.888 14.916 153 932790.743 5.353 - 16 85 69 154 Tm -a -54427.131 14.412 8074.208 0.094 B- -4495.048 13.953 153 941570.067 15.472 - 14 84 70 154 Yb -49932.083 17.281 8039.939 0.112 B- -10217# 197# 153 946395.701 18.552 - 12 83 71 154 Lu +a -39715# 196# 7969# 1# B- -7045# 358# 153 957364# 211# - 10 82 72 154 Hf x -32670# 300# 7918# 2# B- * 153 964927# 322# -0 41 98 57 155 La x -37930# 400# 8028# 3# B- 9850# 500# 154 959280# 429# - 39 97 58 155 Ce x -47780# 300# 8087# 2# B- 7635# 300# 154 948706# 322# - 37 96 59 155 Pr -55415.268 17.198 8131.039 0.111 B- 6868.456 19.472 154 940509.259 18.462 - 35 95 60 155 Nd -62283.724 9.153 8170.304 0.059 B- 4656.207 10.278 154 933135.668 9.826 - 33 94 61 155 Pm -66939.931 4.718 8195.296 0.030 B- 3250.888 4.946 154 928137.024 5.065 - 31 93 62 155 Sm -n -70190.819 1.487 8211.223 0.010 B- 1627.273 1.202 154 924647.051 1.595 - 29 92 63 155 Eu -71818.092 1.401 8216.674 0.009 B- 251.788 0.870 154 922900.102 1.504 - 27 91 64 155 Gd -72069.880 1.191 8213.251 0.008 B- -819.830 9.789 154 922629.796 1.278 - 25 90 65 155 Tb + -71250.050 9.849 8202.914 0.064 B- -2094.500 1.897 154 923509.921 10.573 - 23 89 66 155 Dy -69155.550 9.665 8184.354 0.062 B- -3116.011 16.590 154 925758.459 10.375 - 21 88 67 155 Ho -66039.539 17.474 8159.203 0.113 B- -3830.350 18.474 154 929103.634 18.759 - 19 87 68 155 Er -a -62209.189 6.098 8129.444 0.039 B- -5583.276 11.515 154 933215.684 6.546 - 17 86 69 155 Tm -a -56625.913 9.925 8088.375 0.064 B- -6123.305 19.341 154 939209.578 10.654 - 15 85 70 155 Yb -a -50502.608 16.600 8043.823 0.107 B- -7957.561 25.416 154 945783.217 17.820 - 13 84 71 155 Lu +a -42545.047 19.246 7987.436 0.124 B- -8375# 299# 154 954326.011 20.661 - 11 83 72 155 Hf x -34170# 298# 7928# 2# B- -10242# 423# 154 963317# 320# - 9 82 73 155 Ta -p -23928# 300# 7857# 2# B- * 154 974312# 322# -0 42 99 57 156 La x -33050# 400# 7997# 3# B- 11769# 500# 155 964519# 429# - 40 98 58 156 Ce x -44820# 300# 8068# 2# B- 6748# 361# 155 951884# 322# - 38 97 59 156 Pr x -51568# 200# 8106# 1# B- 8906# 283# 155 944640# 215# - 36 96 60 156 Nd + -60473.644 200.033 8158.066 1.282 B- 3690.000 200.000 155 935078.868 214.744 - 34 95 61 156 Pm -64163.644 3.631 8176.705 0.023 B- 5196.786 9.285 155 931117.490 3.897 - 32 94 62 156 Sm -69360.430 8.546 8205.003 0.055 B- 722.118 7.902 155 925538.511 9.174 - 30 93 63 156 Eu -70082.548 3.590 8204.617 0.023 B- 2452.366 3.409 155 924763.285 3.853 - 28 92 64 156 Gd -72534.914 1.190 8215.322 0.008 B- -2444.117 3.679 155 922130.562 1.277 - 26 91 65 156 Tb -70090.797 3.814 8194.639 0.024 B- 438.167 3.681 155 924754.430 4.094 - 24 90 66 156 Dy -70528.964 1.194 8192.433 0.008 B- -5050.000 60.000 155 924284.038 1.281 - 22 89 67 156 Ho - -65478.964 60.012 8155.046 0.385 B- -1267.255 64.869 155 929705.436 64.425 - 20 88 68 156 Er -64211.709 24.629 8141.908 0.158 B- -7377.159 26.710 155 931065.890 26.440 - 18 87 69 156 Tm -56834.550 14.279 8089.603 0.092 B- -3568.830 12.548 155 938985.597 15.328 - 16 86 70 156 Yb -53265.721 9.308 8061.711 0.060 B- -9566.176 54.916 155 942816.893 9.992 - 14 85 71 156 Lu -a -43699.544 54.122 7995.374 0.347 B- -5882.648 139.709 155 953086.606 58.102 - 12 84 72 156 Hf -37816.896 149.757 7952.650 0.960 B- -11956# 334# 155 959401.889 160.770 - 10 83 73 156 Ta -p -25861# 298# 7871# 2# B- * 155 972237# 320# -0 41 99 58 157 Ce x -39930# 400# 8037# 3# B- 8610# 500# 156 957133# 429# - 39 98 59 157 Pr x -48540# 300# 8086# 2# B- 7921# 301# 156 947890# 322# - 37 97 60 157 Nd -56461.543 24.918 8131.959 0.159 B- 5835.500 25.877 156 939386.037 26.750 - 35 96 61 157 Pm -62297.043 7.006 8164.145 0.045 B- 4380.534 8.265 156 933121.370 7.521 - 33 95 62 157 Sm -66677.577 4.433 8187.063 0.028 B- 2781.331 6.136 156 928418.673 4.759 - 31 94 63 157 Eu -69458.908 4.259 8199.795 0.027 B- 1364.565 4.203 156 925432.791 4.572 - 29 93 64 157 Gd -70823.473 1.189 8203.504 0.008 B- -60.043 0.297 156 923967.870 1.276 - 27 92 65 157 Tb -70763.430 1.222 8198.138 0.008 B- -1338.872 5.134 156 924032.328 1.311 - 25 91 66 157 Dy -69424.558 5.177 8184.627 0.033 B- -2591.725 23.787 156 925469.667 5.557 - 23 90 67 157 Ho -66832.832 23.469 8163.136 0.149 B- -3419.194 33.668 156 928251.999 25.195 - 21 89 68 157 Er -63413.639 26.505 8136.375 0.169 B- -4704.366 38.515 156 931922.655 28.454 - 19 88 69 157 Tm x -58709.273 27.945 8101.428 0.178 B- -5287.374 30.003 156 936973.000 30.000 - 17 87 70 157 Yb -53421.898 10.920 8062.767 0.070 B- -6981.376 14.241 156 942649.230 11.723 - 15 86 71 157 Lu -46440.523 12.078 8013.317 0.077 B- -7537# 196# 156 950144.045 12.965 - 13 85 72 157 Hf -a -38903# 196# 7960# 1# B- -9310# 247# 156 958236# 210# - 11 84 73 157 Ta IT -29593.330 150.069 7896.043 0.956 B- -10123# 427# 156 968230.251 161.105 - 9 83 74 157 W x -19470# 400# 7827# 3# B- * 156 979098# 429# -0 42 100 58 158 Ce x -36660# 400# 8016# 3# B- 7670# 500# 157 960644# 429# - 40 99 59 158 Pr x -44330# 300# 8060# 2# B- 9725# 361# 157 952410# 322# - 38 98 60 158 Nd x -54055# 200# 8116# 1# B- 5035# 201# 157 941970# 215# - 36 97 61 158 Pm -59089.209 13.453 8143.254 0.085 B- 6161.034 14.299 157 936565.121 14.442 - 34 96 62 158 Sm -65250.243 4.892 8177.297 0.031 B- 2004.945 10.369 157 929950.979 5.251 - 32 95 63 158 Eu -67255.188 10.255 8185.035 0.065 B- 3434.358 10.323 157 927798.581 11.008 - 30 94 64 158 Gd -70689.546 1.189 8201.819 0.008 B- -1218.878 0.987 157 924111.646 1.276 - 28 93 65 158 Tb -69470.668 1.401 8189.153 0.009 B- 936.681 2.495 157 925420.166 1.503 - 26 92 66 158 Dy -70407.349 2.365 8190.130 0.015 B- -4219.755 27.005 157 924414.597 2.538 - 24 91 67 158 Ho - -66187.593 27.108 8158.471 0.172 B- -883.785 37.025 157 928944.692 29.101 - 22 90 68 158 Er -65303.808 25.219 8147.926 0.160 B- -6600.615 31.341 157 929893.474 27.074 - 20 89 69 158 Tm -58703.194 25.219 8101.199 0.160 B- -2692.957 26.456 157 936979.525 27.074 - 18 88 70 158 Yb -56010.237 7.994 8079.203 0.051 B- -8798.047 16.872 157 939870.534 8.582 - 16 87 71 158 Lu -a -47212.190 15.125 8018.568 0.096 B- -5109.800 14.937 157 949315.626 16.237 - 14 86 72 158 Hf -42102.390 17.494 7981.276 0.111 B- -10936# 197# 157 954801.222 18.780 - 12 85 73 158 Ta +a -31167# 196# 7907# 1# B- -7534# 358# 157 966541# 210# - 10 84 74 158 W -a -23633# 300# 7854# 2# B- * 157 974629# 322# -0 41 100 59 159 Pr x -41088# 400# 8039# 3# B- 8719# 500# 158 955890# 429# - 39 99 60 159 Nd x -49807# 300# 8089# 2# B- 6747# 300# 158 946530# 322# - 37 98 61 159 Pm -56554.280 10.039 8126.859 0.063 B- 5653.495 11.644 158 939286.479 10.777 - 35 97 62 159 Sm -62207.775 5.934 8157.495 0.037 B- 3835.510 7.324 158 933217.202 6.369 - 33 96 63 159 Eu -66043.285 4.325 8176.697 0.027 B- 2518.150 4.391 158 929099.612 4.643 - 31 95 64 159 Gd -68561.436 1.192 8187.614 0.007 B- 970.927 0.759 158 926396.267 1.279 - 29 94 65 159 Tb -69532.363 1.256 8188.800 0.008 B- -365.229 1.162 158 925353.933 1.348 - 27 93 66 159 Dy -69167.134 1.528 8181.583 0.010 B- -1837.600 2.683 158 925746.023 1.640 - 25 92 67 159 Ho - -67329.534 3.088 8165.105 0.019 B- -2768.500 2.000 158 927718.768 3.314 - 23 91 68 159 Er - -64561.034 3.679 8142.773 0.023 B- -3990.637 28.186 158 930690.875 3.949 - 21 90 69 159 Tm x -60570.398 27.945 8112.754 0.176 B- -4731.792 33.129 158 934975.000 30.000 - 19 89 70 159 Yb x -55838.606 17.794 8078.074 0.112 B- -6130.002 41.655 158 940054.787 19.102 - 17 88 71 159 Lu x -49708.604 37.663 8034.600 0.237 B- -6856.004 41.246 158 946635.615 40.433 - 15 87 72 159 Hf -a -42852.600 16.813 7986.560 0.106 B- -8413.452 25.891 158 953995.838 18.049 - 13 86 73 159 Ta IT -34439.148 19.689 7928.725 0.124 B- -9145# 299# 158 963028.052 21.137 - 11 85 74 159 W -a -25295# 298# 7866# 2# B- -10550# 426# 158 972845# 320# - 9 84 75 159 Re IT -14745# 305# 7795# 2# B- * 158 984171# 327# -0 42 101 59 160 Pr x -36520# 400# 8011# 2# B- 10613# 500# 159 960794# 429# - 40 100 60 160 Nd x -47134# 300# 8073# 2# B- 5868# 361# 159 949400# 322# - 38 99 61 160 Pm x -53002# 200# 8104# 1# B- 7233# 200# 159 943100# 215# - 36 98 62 160 Sm -60234.793 5.934 8144.625 0.037 B- 3245.670 11.183 159 935335.286 6.370 - 34 97 63 160 Eu -63480.463 9.501 8160.021 0.059 B- 4461.278 9.586 159 931850.916 10.199 - 32 96 64 160 Gd -67941.741 1.278 8183.014 0.008 B- -105.483 1.022 159 927061.537 1.371 - 30 95 65 160 Tb -67836.257 1.262 8177.465 0.008 B- 1836.472 1.172 159 927174.778 1.354 - 28 94 66 160 Dy -69672.730 0.772 8184.054 0.005 B- -3290.000 15.000 159 925203.244 0.828 - 26 93 67 160 Ho - -66382.730 15.020 8158.602 0.094 B- -318.502 28.528 159 928735.204 16.124 - 24 92 68 160 Er -66064.228 24.254 8151.721 0.152 B- -5762.200 40.311 159 929077.130 26.037 - 22 91 69 160 Tm -60302.028 34.262 8110.818 0.214 B- -2139.322 35.017 159 935263.106 36.781 - 20 90 70 160 Yb -58162.706 7.233 8092.557 0.045 B- -7892.769 57.280 159 937559.763 7.764 - 18 89 71 160 Lu x -50269.937 56.821 8038.338 0.355 B- -4330.994 57.617 159 946033.000 61.000 - 16 88 72 160 Hf -45938.943 9.541 8006.380 0.060 B- -10115.249 55.147 159 950682.513 10.242 - 14 87 73 160 Ta -a -35823.695 54.316 7938.270 0.339 B- -6497.240 139.859 159 961541.679 58.310 - 12 86 74 160 W -29326.455 149.827 7892.772 0.936 B- -12588# 334# 159 968516.753 160.846 - 10 85 75 160 Re -a -16739# 298# 7809# 2# B- * 159 982030# 320# -0 41 101 60 161 Nd x -42588# 400# 8044# 2# B- 7648# 499# 160 954280# 429# - 39 100 61 161 Pm x -50235# 298# 8087# 2# B- 6436# 298# 160 946070# 320# - 37 99 62 161 Sm -56671.962 6.817 8122.041 0.042 B- 5119.562 12.415 160 939160.143 7.318 - 35 98 63 161 Eu -61791.524 10.399 8148.980 0.065 B- 3714.299 10.525 160 933664.066 11.164 - 33 97 64 161 Gd -n -65505.824 1.622 8167.191 0.010 B- 1955.765 1.442 160 929676.602 1.741 - 31 96 65 161 Tb -67461.589 1.350 8174.479 0.008 B- 594.212 1.260 160 927577.001 1.449 - 29 95 66 161 Dy -68055.801 0.768 8173.310 0.005 B- -858.530 2.166 160 926939.088 0.824 - 27 94 67 161 Ho -67197.270 2.242 8163.119 0.014 B- -1995.663 9.011 160 927860.759 2.406 - 25 93 68 161 Er +n -65201.608 8.780 8145.864 0.055 B- -3302.900 29.292 160 930003.191 9.425 - 23 92 69 161 Tm x -61898.708 27.945 8120.490 0.174 B- -4059.308 31.885 160 933549.000 30.000 - 21 91 70 161 Yb x -57839.400 15.354 8090.417 0.095 B- -5277.056 31.885 160 937906.846 16.483 - 19 90 71 161 Lu x -52562.344 27.945 8052.781 0.174 B- -6247.672 35.909 160 943572.000 30.000 - 17 89 72 161 Hf -46314.672 22.551 8009.117 0.140 B- -7535.674 33.097 160 950279.151 24.209 - 15 88 73 161 Ta +a -38778.997 24.382 7957.452 0.151 B- -8224# 197# 160 958369.031 26.175 - 13 87 74 161 W -a -30555# 196# 7902# 1# B- -9715# 247# 160 967197# 210# - 11 86 75 161 Re -20840.203 149.922 7836.312 0.931 B- -10861# 427# 160 977627.121 160.948 - 9 85 76 161 Os -a -9979# 400# 7764# 2# B- * 160 989287# 429# -0 42 102 60 162 Nd x -39550# 400# 8026# 2# B- 6819# 500# 161 957541# 429# - 40 101 61 162 Pm x -46370# 300# 8063# 2# B- 8160# 358# 161 950220# 322# - 38 100 62 162 Sm x -54530# 196# 8109# 1# B- 4174# 199# 161 941460# 210# - 36 99 63 162 Eu + -58703.401 35.229 8129.438 0.217 B- 5577.000 35.000 161 936979.303 37.819 - 34 98 64 162 Gd -nn -64280.401 4.009 8159.035 0.025 B- 1395.556 36.581 161 930992.146 4.303 - 32 97 65 162 Tb + -65675.958 36.372 8162.820 0.225 B- 2505.521 36.364 161 929493.955 39.046 - 30 96 66 162 Dy -68181.478 0.767 8173.457 0.005 B- -2139.937 3.113 161 926804.168 0.822 - 28 95 67 162 Ho -66041.541 3.166 8155.418 0.020 B- 292.978 3.127 161 929101.485 3.398 - 26 94 68 162 Er -66334.519 0.822 8152.397 0.005 B- -4856.728 26.047 161 928786.960 0.882 - 24 93 69 162 Tm - -61477.791 26.060 8117.588 0.161 B- -1651.444 30.240 161 934000.872 27.977 - 22 92 70 162 Yb x -59826.347 15.359 8102.565 0.095 B- -6994.593 76.592 161 935773.771 16.488 - 20 91 71 162 Lu x -52831.753 75.036 8054.559 0.463 B- -3662.746 75.570 161 943282.776 80.554 - 18 90 72 162 Hf -49169.008 8.968 8027.120 0.055 B- -9388.814 52.931 161 947214.896 9.627 - 16 89 73 162 Ta -a -39780.194 52.238 7964.335 0.322 B- -5780.987 52.239 161 957294.202 56.079 - 14 88 74 162 W -33999.207 17.658 7923.821 0.109 B- -11498# 197# 161 963500.347 18.956 - 12 87 75 162 Re +a -22501# 196# 7848# 1# B- -8061# 358# 161 975844# 210# - 10 86 76 162 Os -a -14440# 300# 7793# 2# B- * 161 984498# 322# -0 41 102 61 163 Pm x -43249# 400# 8044# 2# B- 7471# 499# 162 953570# 429# - 39 101 62 163 Sm x -50720# 298# 8085# 2# B- 5765# 305# 162 945550# 320# - 37 100 63 163 Eu + -56484.886 65.544 8115.471 0.402 B- 4829.000 65.000 162 939360.977 70.364 - 35 99 64 163 Gd -61313.886 8.426 8140.297 0.052 B- 3282.185 9.358 162 934176.832 9.045 - 33 98 65 163 Tb +p -64596.071 4.073 8155.633 0.025 B- 1785.099 4.001 162 930653.261 4.372 - 31 97 66 163 Dy -66381.170 0.765 8161.785 0.005 B- -2.834 0.019 162 928736.879 0.821 - 29 96 67 163 Ho -66378.335 0.765 8156.968 0.005 B- -1210.612 4.575 162 928739.921 0.821 - 27 95 68 163 Er -65167.723 4.639 8144.741 0.028 B- -2439.000 3.000 162 930039.567 4.980 - 25 94 69 163 Tm - -62728.723 5.524 8124.979 0.034 B- -3429.629 16.309 162 932657.941 5.930 - 23 93 70 163 Yb x -59299.094 15.364 8099.138 0.094 B- -4507.685 31.890 162 936339.800 16.493 - 21 92 71 163 Lu x -54791.409 27.945 8066.684 0.171 B- -5527.726 37.348 162 941179.000 30.000 - 19 91 72 163 Hf -49263.682 24.778 8027.972 0.152 B- -6729.054 45.416 162 947113.258 26.599 - 17 90 73 163 Ta -a -42534.629 38.061 7981.890 0.234 B- -7626.436 65.049 162 954337.195 40.860 - 15 89 74 163 W -a -34908.193 52.751 7930.302 0.324 B- -8905.949 55.912 162 962524.511 56.630 - 13 88 75 163 Re +a -26002.244 18.534 7870.865 0.114 B- -9810# 299# 162 972085.441 19.897 - 11 87 76 163 Os -a -16192# 298# 7806# 2# B- * 162 982617# 320# -0 42 103 61 164 Pm x -38870# 400# 8017# 2# B- 9232# 499# 163 958271# 429# - 40 102 62 164 Sm x -48102# 298# 8069# 2# B- 5279# 319# 163 948360# 320# - 38 101 63 164 Eu + -53381# 114# 8096# 1# B- 6393.000 50.000 163 942693# 122# - 36 100 64 164 Gd x -59774# 102# 8130# 1# B- 2304# 143# 163 935830# 110# - 34 99 65 164 Tb + -62077.965 100.003 8139.765 0.610 B- 3890.000 100.000 163 933356.559 107.357 - 32 98 66 164 Dy -65967.965 0.767 8158.714 0.005 B- -986.463 1.415 163 929180.472 0.823 - 30 97 67 164 Ho -64981.503 1.528 8147.929 0.009 B- 961.387 1.420 163 930239.483 1.639 - 28 96 68 164 Er -65942.890 0.775 8149.020 0.005 B- -4038.855 24.401 163 929207.392 0.832 - 26 95 69 164 Tm -61904.035 24.394 8119.623 0.149 B- -886.617 28.829 163 933543.281 26.188 - 24 94 70 164 Yb x -61017.418 15.369 8109.446 0.094 B- -6375.048 31.892 163 934495.103 16.499 - 22 93 71 164 Lu x -54642.370 27.945 8065.804 0.170 B- -2823.865 32.112 163 941339.000 30.000 - 20 92 72 164 Hf -51818.505 15.820 8043.814 0.096 B- -8535.704 32.112 163 944370.544 16.983 - 18 91 73 164 Ta x -43282.800 27.945 7986.997 0.170 B- -5047.041 29.572 163 953534.000 30.000 - 16 90 74 164 W -38235.759 9.674 7951.452 0.059 B- -10763.323 55.406 163 958952.222 10.385 - 14 89 75 164 Re -a -27472.436 54.555 7881.052 0.333 B- -7050.331 140.051 163 970507.124 58.566 - 12 88 76 164 Os -20422.106 149.919 7833.291 0.914 B- -13078# 348# 163 978075.966 160.945 - 10 87 77 164 Ir -a -7344# 314# 7749# 2# B- * 163 992116# 338# -0 41 103 62 165 Sm x -43808# 401# 8043# 2# B- 6916# 424# 164 952970# 430# - 39 102 63 165 Eu + -50724# 138# 8080# 1# B- 5729.000 65.000 164 945546# 148# - 37 101 64 165 Gd + -56453# 121# 8110# 1# B- 4113.000 65.000 164 939395# 130# - 35 100 65 165 Tb x -60566# 102# 8130# 1# B- 3047# 102# 164 934980# 110# - 33 99 66 165 Dy -n -63612.606 0.769 8143.909 0.005 B- 1286.400 0.829 164 931709.054 0.825 - 31 98 67 165 Ho -64899.006 1.010 8146.964 0.006 B- -377.396 1.033 164 930328.047 1.084 - 29 97 68 165 Er -64521.610 0.964 8139.936 0.006 B- -1591.990 1.538 164 930733.198 1.034 - 27 96 69 165 Tm -62929.621 1.671 8125.546 0.010 B- -2634.239 26.592 164 932442.269 1.793 - 25 95 70 165 Yb -60295.382 26.539 8104.839 0.161 B- -3853.140 35.432 164 935270.241 28.490 - 23 94 71 165 Lu -56442.242 26.539 8076.745 0.161 B- -4806.734 38.539 164 939406.758 28.490 - 21 93 72 165 Hf x -51635.507 27.945 8042.872 0.169 B- -5787.655 31.195 164 944567.000 30.000 - 19 92 73 165 Ta -45847.853 13.863 8003.054 0.084 B- -6986.830 28.566 164 950780.303 14.882 - 17 91 74 165 W -38861.022 24.977 7955.968 0.151 B- -8201.246 34.332 164 958280.974 26.813 - 15 90 75 165 Re +a -30659.776 23.594 7901.522 0.143 B- -8865# 197# 164 967085.375 25.329 - 13 89 76 165 Os -a -21795# 196# 7843# 1# B- -10202# 252# 164 976602# 210# - 11 88 77 165 Ir IT -11593# 158# 7776# 1# B- * 164 987555# 170# -0 42 104 62 166 Sm x -40730# 400# 8024# 2# B- 6478# 537# 165 956275# 429# - 40 103 63 166 Eu + -47208# 358# 8059# 2# B- 7322.000 300.000 165 949320# 384# - 38 102 64 166 Gd x -54530# 196# 8098# 1# B- 3355# 208# 165 941460# 210# - 36 101 65 166 Tb + -57884.789 70.005 8113.680 0.422 B- 4700.000 70.000 165 937858.119 75.153 - 34 100 66 166 Dy -n -62584.789 0.867 8137.280 0.005 B- 486.540 0.921 165 932812.461 0.930 - 32 99 67 166 Ho -63071.329 1.010 8135.499 0.006 B- 1854.713 0.922 165 932290.139 1.084 - 30 98 68 166 Er -64926.042 1.165 8141.959 0.007 B- -3037.667 11.547 165 930299.023 1.251 - 28 97 69 166 Tm - -61888.375 11.606 8118.946 0.070 B- -292.635 13.507 165 933560.092 12.459 - 26 96 70 166 Yb +nn -61595.740 7.101 8112.471 0.043 B- -5574.759 30.642 165 933874.249 7.623 - 24 95 71 166 Lu x -56020.981 29.808 8074.175 0.180 B- -2161.998 40.859 165 939859.000 32.000 - 22 94 72 166 Hf x -53858.983 27.945 8056.438 0.168 B- -7761.208 39.520 165 942180.000 30.000 - 20 93 73 166 Ta x -46097.775 27.945 8004.971 0.168 B- -4209.744 29.508 165 950512.000 30.000 - 18 92 74 166 W -41888.031 9.478 7974.898 0.057 B- -9994.553 72.879 165 955031.346 10.174 - 16 91 75 166 Re -a -31893.478 72.310 7909.977 0.436 B- -6461.961 72.387 165 965760.940 77.628 - 14 90 76 166 Os -25431.517 17.966 7866.336 0.108 B- -12078# 197# 165 972698.141 19.287 - 12 89 77 166 Ir -p -13354# 196# 7789# 1# B- -8624# 359# 165 985664# 210# - 10 88 78 166 Pt -a -4730# 300# 7732# 2# B- * 165 994923# 322# -0 41 104 63 167 Eu x -44010# 400# 8040# 2# B- 6803# 499# 166 952753# 429# - 39 103 64 167 Gd x -50813# 298# 8076# 2# B- 5114# 357# 166 945450# 320# - 37 102 65 167 Tb x -55927# 196# 8102# 1# B- 4004# 205# 166 939960# 210# - 35 101 66 167 Dy + -59930.626 60.228 8120.992 0.361 B- 2350.000 60.000 166 935661.823 64.657 - 33 100 67 167 Ho p2n -62280.626 5.234 8130.379 0.031 B- 1010.554 5.208 166 933138.994 5.618 - 31 99 68 167 Er -63291.180 1.166 8131.746 0.007 B- -747.539 1.501 166 932054.119 1.252 - 29 98 69 167 Tm -62543.641 1.298 8122.585 0.008 B- -1953.065 3.798 166 932856.635 1.393 - 27 97 70 167 Yb -60590.576 3.981 8106.205 0.024 B- -3089.451 31.920 166 934953.337 4.273 - 25 96 71 167 Lu x -57501.125 31.671 8083.021 0.190 B- -4033.369 42.237 166 938270.000 34.000 - 23 95 72 167 Hf x -53467.756 27.945 8054.184 0.167 B- -5116.697 39.520 166 942600.000 30.000 - 21 94 73 167 Ta x -48351.059 27.945 8018.861 0.167 B- -6253.001 33.382 166 948093.000 30.000 - 19 93 74 167 W -42098.058 18.261 7976.733 0.109 B- -7267# 44# 166 954805.873 19.603 - 17 92 75 167 Re +a -34831# 40# 7929# 0# B- -8329# 83# 166 962607# 43# - 15 91 76 167 Os -a -26501.993 72.682 7873.974 0.435 B- -9429.554 74.962 166 971548.938 78.027 - 13 90 77 167 Ir -17072.440 18.346 7812.825 0.110 B- -10460# 303# 166 981671.981 19.695 - 11 89 78 167 Pt -a -6612# 302# 7746# 2# B- * 166 992901# 325# -0 42 105 63 168 Eu x -39740# 500# 8014# 3# B- 8623# 641# 167 957337# 537# - 40 104 64 168 Gd x -48363# 401# 8061# 2# B- 4359# 499# 167 948080# 430# - 38 103 65 168 Tb x -52723# 298# 8082# 2# B- 5837# 329# 167 943400# 320# - 36 102 66 168 Dy +pp -58559.566 140.009 8112.536 0.833 B- 1501.605 143.186 167 937133.716 150.305 - 34 101 67 168 Ho + -60061.171 30.023 8116.817 0.179 B- 2930.000 30.000 167 935521.676 32.230 - 32 100 68 168 Er -62991.171 1.168 8129.601 0.007 B- -1678.250 1.870 167 932376.192 1.254 - 30 99 69 168 Tm -61312.921 1.709 8114.954 0.010 B- 268.980 1.887 167 934177.868 1.834 - 28 98 70 168 Yb -61581.901 1.195 8111.898 0.007 B- -4514.051 39.248 167 933889.106 1.282 - 26 97 71 168 Lu - -57067.850 39.266 8080.372 0.234 B- -1707.299 48.195 167 938735.139 42.153 - 24 96 72 168 Hf x -55360.552 27.945 8065.553 0.166 B- -6966.644 39.520 167 940568.000 30.000 - 22 95 73 168 Ta x -48393.908 27.945 8019.428 0.166 B- -3500.799 30.936 167 948047.000 30.000 - 20 94 74 168 W -44893.109 13.271 7993.933 0.079 B- -9098.224 33.556 167 951805.262 14.247 - 18 93 75 168 Re -a -35794.885 30.821 7935.120 0.183 B- -5799.672 32.373 167 961572.608 33.087 - 16 92 76 168 Os -29995.213 9.904 7895.941 0.059 B- -11328.987 56.098 167 967798.812 10.632 - 14 91 77 168 Ir -a -18666.226 55.216 7823.850 0.329 B- -7658.765 140.344 167 979960.981 59.277 - 12 90 78 168 Pt -a -11007.460 149.951 7773.605 0.893 B- * 167 988183.004 160.978 -0 41 105 64 169 Gd x -44153# 503# 8036# 3# B- 6176# 585# 168 952600# 540# - 39 104 65 169 Tb x -50329# 298# 8068# 2# B- 5269# 423# 168 945970# 320# - 37 103 66 169 Dy + -55597.177 300.670 8094.763 1.779 B- 3200.000 300.000 168 940313.971 322.782 - 35 102 67 169 Ho +p -58797.177 20.060 8109.068 0.119 B- 2125.927 20.053 168 936878.630 21.534 - 33 101 68 169 Er -n -60923.104 1.179 8117.019 0.007 B- 352.108 1.114 168 934596.353 1.265 - 31 100 69 169 Tm -61275.212 0.812 8114.473 0.005 B- -897.650 1.141 168 934218.350 0.871 - 29 99 70 169 Yb -n -60377.563 1.205 8104.532 0.007 B- -2293.000 3.000 168 935182.016 1.293 - 27 98 71 169 Lu - -58084.563 3.233 8086.335 0.019 B- -3367.673 28.131 168 937643.653 3.470 - 25 97 72 169 Hf x -54716.889 27.945 8061.778 0.165 B- -4426.460 39.520 168 941259.000 30.000 - 23 96 73 169 Ta x -50290.430 27.945 8030.957 0.165 B- -5372.557 31.925 168 946011.000 30.000 - 21 95 74 169 W -44917.873 15.436 7994.537 0.091 B- -6508.641 19.173 168 951778.677 16.571 - 19 94 75 169 Re +a -38409.232 11.374 7951.395 0.067 B- -7686.541 27.618 168 958765.991 12.210 - 17 93 76 169 Os -a -30722.691 25.167 7901.284 0.149 B- -8628.852 34.276 168 967017.833 27.017 - 15 92 77 169 Ir +a -22093.839 23.308 7845.596 0.138 B- -9581# 197# 168 976281.287 25.021 - 13 91 78 169 Pt -a -12512# 196# 7784# 1# B- -10724# 357# 168 986567# 210# - 11 90 79 169 Au x -1788# 298# 7716# 2# B- * 168 998080# 320# -0 42 106 64 170 Gd x -41380# 596# 8020# 4# B- 5344# 718# 169 955577# 640# - 40 105 65 170 Tb x -46724# 401# 8047# 2# B- 6940# 446# 169 949840# 430# - 38 104 66 170 Dy x -53663# 196# 8083# 1# B- 2575# 202# 169 942390# 210# - 36 103 67 170 Ho + -56238.681 50.024 8093.796 0.294 B- 3870.000 50.000 169 939625.289 53.702 - 34 102 68 170 Er -60108.681 1.546 8111.959 0.009 B- -312.828 1.821 169 935470.673 1.659 - 32 101 69 170 Tm -59795.853 0.801 8105.517 0.005 B- 968.066 0.801 169 935806.507 0.859 - 30 100 70 170 Yb -60763.919 0.010 8106.609 0.000 B- -3457.695 16.843 169 934767.245 0.011 - 28 99 71 170 Lu - -57306.224 16.843 8081.668 0.099 B- -1052.370 32.628 169 938479.234 18.081 - 26 98 72 170 Hf x -56253.854 27.945 8070.875 0.164 B- -6116.190 39.520 169 939609.000 30.000 - 24 97 73 170 Ta x -50137.665 27.945 8030.296 0.164 B- -2846.832 30.904 169 946175.000 30.000 - 22 96 74 170 W -47290.832 13.195 8008.948 0.078 B- -8377.639 26.611 169 949231.200 14.165 - 20 95 75 170 Re -38913.193 23.109 7955.065 0.136 B- -4986.946 25.091 169 958224.966 24.808 - 18 94 76 170 Os -33926.247 9.773 7921.128 0.057 B- -10567# 89# 169 963578.673 10.491 - 16 93 77 170 Ir -a -23360# 89# 7854# 1# B- -7060# 89# 169 974922# 95# - 14 92 78 170 Pt -16299.193 18.247 7808.236 0.107 B- -12547# 197# 169 982502.095 19.588 - 12 91 79 170 Au -p -3752# 196# 7730# 1# B- * 169 995972# 211# -0 41 106 65 171 Tb x -44032# 503# 8031# 3# B- 6157# 585# 170 952730# 540# - 39 105 66 171 Dy x -50189# 298# 8063# 2# B- 4330# 670# 170 946120# 320# - 37 104 67 171 Ho + -54518.956 600.002 8083.608 3.509 B- 3200.000 600.000 170 941471.490 644.128 - 35 103 68 171 Er -57718.956 1.557 8097.746 0.009 B- 1491.342 1.256 170 938036.148 1.670 - 33 102 69 171 Tm -59210.298 0.972 8101.893 0.006 B- 96.512 0.972 170 936435.126 1.043 - 31 101 70 171 Yb -59306.810 0.013 8097.882 0.000 B- -1478.414 1.862 170 936331.517 0.014 - 29 100 71 171 Lu -57828.395 1.862 8084.661 0.011 B- -2397.050 28.936 170 937918.660 1.998 - 27 99 72 171 Hf x -55431.345 28.876 8066.068 0.169 B- -3711.072 40.184 170 940492.000 31.000 - 25 98 73 171 Ta x -51720.273 27.945 8039.791 0.163 B- -4634.183 39.520 170 944476.000 30.000 - 23 97 74 171 W x -47086.090 27.945 8008.115 0.163 B- -5835.810 39.520 170 949451.000 30.000 - 21 96 75 171 Re x -41250.280 27.945 7969.412 0.163 B- -6948.339 33.145 170 955716.000 30.000 - 19 95 76 171 Os -34301.942 17.823 7924.204 0.104 B- -7889.916 42.395 170 963175.348 19.133 - 17 94 77 171 Ir -a -26412.025 38.466 7873.489 0.225 B- -8942.323 82.291 170 971645.522 41.295 - 15 93 78 171 Pt -a -17469.702 72.747 7816.619 0.425 B- -9907.407 75.639 170 981245.502 78.097 - 13 92 79 171 Au -p -7562.295 20.713 7754.106 0.121 B- -11043# 303# 170 991881.542 22.236 - 11 91 80 171 Hg -a 3480# 303# 7685# 2# B- * 171 003736# 325# -0 42 107 65 172 Tb x -39850# 503# 8007# 3# B- 8159# 585# 171 957219# 540# - 40 106 66 172 Dy x -48009# 298# 8050# 2# B- 3474# 357# 171 948460# 320# - 38 105 67 172 Ho x -51484# 196# 8066# 1# B- 5000# 196# 171 944730# 210# - 36 104 68 172 Er -56483.612 4.008 8090.410 0.023 B- 890.767 4.543 171 939362.344 4.302 - 34 103 69 172 Tm -57374.379 5.503 8091.041 0.032 B- 1881.067 5.503 171 938406.067 5.907 - 32 102 70 172 Yb -59255.446 0.014 8097.429 0.000 B- -2519.466 2.336 171 936386.658 0.014 - 30 101 71 172 Lu -56735.980 2.336 8078.232 0.014 B- -333.754 24.540 171 939091.417 2.507 - 28 100 72 172 Hf x -56402.226 24.428 8071.743 0.142 B- -5072.248 37.117 171 939449.716 26.224 - 26 99 73 172 Ta x -51329.977 27.945 8037.705 0.162 B- -2232.791 39.520 171 944895.000 30.000 - 24 98 74 172 W x -49097.186 27.945 8020.175 0.162 B- -7560.080 47.974 171 947292.000 30.000 - 22 97 75 172 Re -41537.106 38.995 7971.672 0.227 B- -4293.264 41.036 171 955408.079 41.862 - 20 96 76 172 Os -37243.842 12.782 7942.163 0.074 B- -9864.473 34.832 171 960017.088 13.721 - 18 95 77 172 Ir -a -27379.369 32.402 7880.263 0.188 B- -6272.449 34.023 171 970607.036 34.785 - 16 94 78 172 Pt -21106.920 10.377 7839.247 0.060 B- -11788.914 57.108 171 977340.788 11.139 - 14 93 79 172 Au -a -9318.006 56.158 7766.158 0.326 B- -8259.262 140.853 171 989996.708 60.287 - 12 92 80 172 Hg -a -1058.744 150.079 7713.591 0.873 B- * 171 998863.391 161.116 -0 41 107 66 173 Dy x -43939# 401# 8027# 2# B- 5412# 499# 172 952830# 430# - 39 106 67 173 Ho x -49351# 298# 8054# 2# B- 4304# 357# 172 947020# 320# - 37 105 68 173 Er x -53654# 196# 8074# 1# B- 2602# 196# 172 942400# 210# - 35 104 69 173 Tm p2n -56256.059 4.400 8084.463 0.025 B- 1295.166 4.400 172 939606.632 4.723 - 33 103 70 173 Yb -57551.225 0.011 8087.427 0.000 B- -670.310 1.567 172 938216.215 0.012 - 31 102 71 173 Lu -56880.916 1.567 8079.030 0.009 B- -1469.132 27.989 172 938935.822 1.682 - 29 101 72 173 Hf x -55411.784 27.945 8066.016 0.162 B- -3015.246 39.520 172 940513.000 30.000 - 27 100 73 173 Ta x -52396.538 27.945 8044.064 0.162 B- -3669.155 39.520 172 943750.000 30.000 - 25 99 74 173 W x -48727.383 27.945 8018.333 0.162 B- -5173.518 39.520 172 947689.000 30.000 - 23 98 75 173 Re x -43553.865 27.945 7983.906 0.162 B- -6115.608 31.697 172 953243.000 30.000 - 21 97 76 173 Os -37438.257 14.959 7944.033 0.086 B- -7169.822 18.583 172 959808.375 16.059 - 19 96 77 173 Ir -30268.435 11.026 7898.067 0.064 B- -8325.524 57.052 172 967505.496 11.837 - 17 95 78 173 Pt -a -21942.911 55.977 7845.420 0.324 B- -9110.471 60.421 172 976443.315 60.093 - 15 94 79 173 Au +a -12832.440 22.784 7788.237 0.132 B- -10123# 197# 172 986223.808 24.459 - 13 93 80 173 Hg -a -2710# 196# 7725# 1# B- * 172 997091# 210# -0 42 108 66 174 Dy x -41370# 503# 8012# 3# B- 4319# 585# 173 955587# 540# - 40 107 67 174 Ho x -45690# 298# 8033# 2# B- 6260# 422# 173 950950# 320# - 38 106 68 174 Er x -51949# 298# 8064# 2# B- 1915# 301# 173 944230# 320# - 36 105 69 174 Tm + -53864.512 44.721 8070.642 0.257 B- 3080.000 44.721 173 942174.064 48.010 - 34 104 70 174 Yb -56944.512 0.011 8083.847 0.000 B- -1374.317 1.567 173 938867.548 0.011 - 32 103 71 174 Lu -55570.195 1.567 8071.453 0.009 B- 274.286 2.169 173 940342.938 1.682 - 30 102 72 174 Hf -55844.481 2.259 8068.533 0.013 B- -4103.715 28.036 173 940048.480 2.424 - 28 101 73 174 Ta x -51740.766 27.945 8040.452 0.161 B- -1513.678 39.520 173 944454.000 30.000 - 26 100 74 174 W x -50227.088 27.945 8027.256 0.161 B- -6553.992 39.520 173 946079.000 30.000 - 24 99 75 174 Re x -43673.096 27.945 7985.094 0.161 B- -3677.681 29.767 173 953115.000 30.000 - 22 98 76 174 Os -39995.416 10.254 7959.461 0.059 B- -9131.924 26.395 173 957063.152 11.008 - 20 97 77 174 Ir -30863.492 24.322 7902.483 0.140 B- -5545.329 26.433 173 966866.676 26.111 - 18 96 78 174 Pt -a -25318.163 10.351 7866.117 0.059 B- -11083# 89# 173 972819.832 11.112 - 16 95 79 174 Au -a -14235# 89# 7798# 1# B- -7594# 89# 173 984718# 95# - 14 94 80 174 Hg -a -6641.009 19.211 7749.784 0.110 B- * 173 992870.583 20.624 -0 41 108 67 175 Ho x -43203# 401# 8019# 2# B- 5449# 566# 174 953620# 430# - 39 107 68 175 Er x -48652# 401# 8045# 2# B- 3659# 404# 174 947770# 430# - 37 106 69 175 Tm + -52310.549 50.000 8061.766 0.286 B- 2385.000 50.000 174 943842.313 53.677 - 35 105 70 175 Yb -54695.549 0.071 8070.925 0.000 B- 470.033 1.206 174 941281.910 0.076 - 33 104 71 175 Lu -55165.582 1.207 8069.140 0.007 B- -683.920 1.952 174 940777.308 1.295 - 31 103 72 175 Hf -54481.662 2.282 8060.761 0.013 B- -2073.015 28.038 174 941511.527 2.449 - 29 102 73 175 Ta x -52408.647 27.945 8044.445 0.160 B- -2775.852 39.520 174 943737.000 30.000 - 27 101 74 175 W x -49632.795 27.945 8024.112 0.160 B- -4344.488 39.520 174 946717.000 30.000 - 25 100 75 175 Re x -45288.307 27.945 7994.816 0.160 B- -5182.931 30.324 174 951381.000 30.000 - 23 99 76 175 Os -40105.376 11.775 7960.729 0.067 B- -6710.870 17.089 174 956945.105 12.640 - 21 98 77 175 Ir -33394.506 12.384 7917.910 0.071 B- -7681.040 22.001 174 964149.521 13.295 - 19 97 78 175 Pt -25713.466 18.185 7869.548 0.104 B- -8309.511 42.705 174 972395.457 19.522 - 17 96 79 175 Au -a -17403.955 38.640 7817.595 0.221 B- -9431.379 82.504 174 981316.085 41.481 - 15 95 80 175 Hg -a -7972.576 72.896 7759.231 0.417 B- * 174 991441.086 78.257 -0 42 109 67 176 Ho x -39290# 503# 7997# 3# B- 7340# 643# 175 957820# 540# - 40 108 68 176 Er x -46631# 401# 8034# 2# B- 2741# 413# 175 949940# 430# - 38 107 69 176 Tm + -49371.314 100.000 8045.121 0.568 B- 4120.000 100.000 175 946997.711 107.354 - 36 106 70 176 Yb -53491.314 0.015 8064.085 0.000 B- -109.078 1.212 175 942574.708 0.015 - 34 105 71 176 Lu -53382.236 1.212 8059.020 0.007 B- 1194.085 0.874 175 942691.809 1.301 - 32 104 72 176 Hf -54576.321 1.481 8061.359 0.008 B- -3210.948 30.775 175 941409.905 1.590 - 30 103 73 176 Ta x -51365.374 30.739 8038.670 0.175 B- -723.771 41.543 175 944857.000 33.000 - 28 102 74 176 W x -50641.603 27.945 8030.112 0.159 B- -5578.718 39.520 175 945634.000 30.000 - 26 101 75 176 Re x -45062.885 27.945 7993.970 0.159 B- -2964.945 39.520 175 951623.000 30.000 - 24 100 76 176 Os x -42097.940 27.945 7972.679 0.159 B- -8219.614 32.580 175 954806.000 30.000 - 22 99 77 176 Ir -33878.326 16.750 7921.531 0.095 B- -4944.459 21.035 175 963630.119 17.981 - 20 98 78 176 Pt -28933.867 12.724 7888.992 0.072 B- -10412.904 35.541 175 968938.214 13.660 - 18 97 79 176 Au -a -18520.963 33.185 7825.383 0.189 B- -6736.013 34.998 175 980116.927 35.625 - 16 96 80 176 Hg -11784.950 11.119 7782.665 0.063 B- -12366.544 75.904 175 987348.335 11.937 - 14 95 81 176 Tl -p 581.594 75.086 7707.955 0.427 B- * 176 000624.367 80.607 -0 41 109 68 177 Er x -42858# 503# 8013# 3# B- 4611# 585# 176 953990# 540# - 39 108 69 177 Tm x -47469# 298# 8035# 2# B- 3517# 298# 176 949040# 320# - 37 107 70 177 Yb -n -50986.397 0.220 8049.973 0.001 B- 1397.409 1.240 176 945263.848 0.236 - 35 106 71 177 Lu -52383.806 1.220 8053.448 0.007 B- 496.810 0.791 176 943763.668 1.310 - 33 105 72 177 Hf -52880.616 1.408 8051.835 0.008 B- -1166.000 3.000 176 943230.320 1.511 - 31 104 73 177 Ta - -51714.616 3.314 8040.827 0.019 B- -2012.890 28.141 176 944482.073 3.557 - 29 103 74 177 W x -49701.726 27.945 8025.035 0.158 B- -3432.555 39.520 176 946643.000 30.000 - 27 102 75 177 Re x -46269.170 27.945 8001.222 0.158 B- -4312.708 31.535 176 950328.000 30.000 - 25 101 76 177 Os +a -41956.462 14.613 7972.436 0.083 B- -5909.041 24.576 176 954957.882 15.687 - 23 100 77 177 Ir x -36047.421 19.760 7934.632 0.112 B- -6676.976 24.801 176 961301.500 21.213 - 21 99 78 177 Pt -29370.444 14.988 7892.489 0.085 B- -7825.341 18.297 176 968469.529 16.090 - 19 98 79 177 Au -21545.103 10.496 7843.858 0.059 B- -8762.561 75.786 176 976870.379 11.268 - 17 97 80 177 Hg -a -12782.542 75.056 7789.932 0.424 B- -9442.016 78.098 176 986277.376 80.575 - 15 96 81 177 Tl IT -3340.526 21.629 7732.167 0.122 B- * 176 996413.797 23.219 -0 42 110 68 178 Er x -40260# 596# 7999# 3# B- 3855# 718# 177 956779# 640# - 40 109 69 178 Tm x -44116# 401# 8016# 2# B- 5580# 401# 177 952640# 430# - 38 108 70 178 Yb -nn -49695.475 10.000 8042.841 0.056 B- 642.309 10.250 177 946649.710 10.735 - 36 107 71 178 Lu -50337.784 2.251 8042.054 0.013 B- 2097.451 2.057 177 945960.162 2.416 - 34 106 72 178 Hf -52435.236 1.412 8049.442 0.008 B- -1837# 52# 177 943708.456 1.516 - 32 105 73 178 Ta IT -50598# 52# 8035# 0# B- -191# 50# 177 945681# 56# - 30 104 74 178 W - -50406.936 15.199 8029.257 0.085 B- -4753.483 31.810 177 945885.925 16.316 - 28 103 75 178 Re x -45653.453 27.945 7998.157 0.157 B- -2109.183 31.093 177 950989.000 30.000 - 26 102 76 178 Os -43544.270 13.632 7981.912 0.077 B- -7292.386 24.006 177 953253.300 14.634 - 24 101 77 178 Ir x -36251.884 19.760 7936.549 0.111 B- -4254.365 22.207 177 961082.000 21.213 - 22 100 78 178 Pt -31997.519 10.133 7908.252 0.057 B- -9693.776 14.297 177 965649.248 10.878 - 20 99 79 178 Au -22303.743 10.086 7849.398 0.057 B- -5987.841 14.755 177 976055.945 10.827 - 18 98 80 178 Hg -a -16315.901 10.770 7811.363 0.061 B- -11526# 90# 177 982484.158 11.562 - 16 97 81 178 Tl -a -4790# 89# 7742# 1# B- -8365# 91# 177 994857# 96# - 14 96 82 178 Pb -a 3574.294 23.963 7690.830 0.135 B- * 178 003837.163 25.724 -0 41 110 69 179 Tm x -41601# 503# 8002# 3# B- 4937# 540# 178 955340# 540# - 39 109 70 179 Yb x -46537# 196# 8025# 1# B- 2521# 196# 178 950040# 210# - 37 108 71 179 Lu -49058.918 5.150 8035.073 0.029 B- 1403.989 5.067 178 947333.082 5.528 - 35 107 72 179 Hf -50462.907 1.413 8038.546 0.008 B- -105.584 0.409 178 945825.838 1.517 - 33 106 73 179 Ta -50357.323 1.463 8033.585 0.008 B- -1062.195 14.520 178 945939.187 1.571 - 31 105 74 179 W -49295.127 14.573 8023.281 0.081 B- -2710.847 26.802 178 947079.501 15.644 - 29 104 75 179 Re -46584.280 24.639 8003.766 0.138 B- -3564.785 29.656 178 949989.715 26.450 - 27 103 76 179 Os -43019.495 16.504 7979.480 0.092 B- -4937.781 19.180 178 953816.669 17.718 - 25 102 77 179 Ir -38081.714 9.771 7947.524 0.055 B- -5813.569 12.613 178 959117.596 10.489 - 23 101 78 179 Pt -32268.145 7.977 7910.675 0.045 B- -7279.578 14.157 178 965358.719 8.563 - 21 100 79 179 Au -24988.567 11.696 7865.637 0.065 B- -8060.433 29.669 178 973173.668 12.555 - 19 99 80 179 Hg -16928.134 27.267 7816.236 0.152 B- -8659.639 47.417 178 981826.899 29.272 - 17 98 81 179 Tl -a -8268.495 38.793 7763.487 0.217 B- -10319.134 84.963 178 991123.405 41.646 - 15 97 82 179 Pb -a 2050.639 75.590 7701.468 0.422 B- * 179 002201.452 81.149 -0 42 111 69 180 Tm x -37920# 503# 7982# 3# B- 6680# 585# 179 959291# 540# - 40 110 70 180 Yb x -44600# 298# 8015# 2# B- 2076# 306# 179 952120# 320# - 38 109 71 180 Lu + -46676.348 70.725 8022.038 0.393 B- 3103.000 70.711 179 949890.876 75.926 - 36 108 72 180 Hf -49779.348 1.419 8034.930 0.008 B- -846.471 2.269 179 946559.669 1.522 - 34 107 73 180 Ta +n -48932.877 1.939 8025.881 0.011 B- 703.238 2.281 179 947468.392 2.081 - 32 106 74 180 W -49636.115 1.436 8025.442 0.008 B- -3798.757 21.440 179 946713.435 1.542 - 30 105 75 180 Re x -45837.359 21.392 7999.991 0.119 B- -1479.549 26.950 179 950791.568 22.965 - 28 104 76 180 Os -44357.810 16.391 7987.425 0.091 B- -6380.284 27.200 179 952379.930 17.596 - 26 103 77 180 Ir x -37977.526 21.706 7947.633 0.121 B- -3541.649 24.323 179 959229.446 23.302 - 24 102 78 180 Pt +a -34435.877 10.974 7923.611 0.061 B- -8810.368 11.973 179 963031.563 11.781 - 22 101 79 180 Au -25625.509 4.786 7870.318 0.027 B- -5375.062 13.524 179 972489.883 5.137 - 20 100 80 180 Hg -20250.447 12.649 7836.110 0.070 B- -10863.800 61.329 179 978260.249 13.579 - 18 99 81 180 Tl -a -9386.647 60.010 7771.409 0.333 B- -7445.267 61.277 179 989923.019 64.423 - 16 98 82 180 Pb -a -1941.380 12.396 7725.700 0.069 B- * 179 997915.842 13.307 -0 43 112 69 181 Tm x -35170# 596# 7967# 3# B- 5918# 667# 180 962243# 640# - 41 111 70 181 Yb x -41088# 298# 7996# 2# B- 3709# 324# 180 955890# 320# - 39 110 71 181 Lu x -44797.410 125.752 8011.929 0.695 B- 2605.421 125.760 180 951908.000 135.000 - 37 109 72 181 Hf -n -47402.831 1.420 8022.002 0.008 B- 1035.480 1.834 180 949110.965 1.524 - 35 108 73 181 Ta -48438.311 1.403 8023.400 0.008 B- -204.493 1.854 180 947999.331 1.506 - 33 107 74 181 W -n -48233.818 1.445 8017.948 0.008 B- -1716.427 12.629 180 948218.863 1.551 - 31 106 75 181 Re 4n -46517.391 12.549 8004.143 0.069 B- -2967.428 28.275 180 950061.523 13.471 - 29 105 76 181 Os -43549.963 25.338 7983.426 0.140 B- -4086.935 25.876 180 953247.188 27.201 - 27 104 77 181 Ir +a -39463.028 5.245 7956.523 0.029 B- -5081.517 14.660 180 957634.694 5.631 - 25 103 78 181 Pt -34381.511 13.689 7924.126 0.076 B- -6510.375 24.216 180 963089.927 14.695 - 23 102 79 181 Au -a -27871.136 19.976 7883.835 0.110 B- -7210.000 25.212 180 970079.103 21.445 - 21 101 80 181 Hg -20661.136 15.382 7839.679 0.085 B- -7862.401 17.876 180 977819.357 16.513 - 19 100 81 181 Tl -12798.735 9.108 7791.918 0.050 B- -9681.385 75.959 180 986259.992 9.778 - 17 99 82 181 Pb -a -3117.350 75.411 7734.107 0.417 B- * 180 996653.386 80.957 -0 42 112 70 182 Yb x -38820# 401# 7984# 2# B- 3060# 446# 181 958325# 430# - 40 111 71 182 Lu x -41880# 196# 7996# 1# B- 4170# 196# 181 955040# 210# - 38 110 72 182 Hf -nn -46049.508 6.165 8014.837 0.034 B- 380.425 6.274 181 950563.816 6.618 - 36 109 73 182 Ta -46429.934 1.405 8012.628 0.008 B- 1816.126 1.399 181 950155.413 1.508 - 34 108 74 182 W -48246.060 0.738 8018.308 0.004 B- -2800.000 101.980 181 948205.721 0.791 - 32 107 75 182 Re IT -45446.060 101.983 7998.625 0.560 B- -836.955 104.276 181 951211.645 109.483 - 30 106 76 182 Os -44609.104 21.745 7989.728 0.119 B- -5557.426 30.207 181 952110.153 23.344 - 28 105 77 182 Ir -39051.679 20.967 7954.894 0.115 B- -2883.230 24.720 181 958076.296 22.509 - 26 104 78 182 Pt -36168.449 13.095 7934.754 0.072 B- -7867.680 24.123 181 961171.571 14.057 - 24 103 79 182 Au -a -28300.768 20.260 7887.226 0.111 B- -4723.846 22.501 181 969617.874 21.749 - 22 102 80 182 Hg -23576.922 9.790 7856.972 0.054 B- -10248.994 15.363 181 974689.132 10.510 - 20 101 81 182 Tl -a -13327.927 11.839 7796.360 0.065 B- -6502.815 16.927 181 985691.880 12.709 - 18 100 82 182 Pb -a -6825.112 12.098 7756.332 0.066 B- * 181 992672.940 12.987 -0 43 113 70 183 Yb x -35100# 401# 7964# 2# B- 4616# 408# 182 962319# 430# - 41 112 71 183 Lu x -39716.110 80.108 7984.812 0.438 B- 3566.687 85.553 182 957363.000 86.000 - 39 111 72 183 Hf + -43282.796 30.034 8000.027 0.164 B- 2010.000 30.000 182 953534.004 32.242 - 37 110 73 183 Ta -n -45292.796 1.419 8006.735 0.008 B- 1072.783 1.413 182 951376.180 1.523 - 35 109 74 183 W -46365.580 0.737 8008.322 0.004 B- -556.000 8.000 182 950224.500 0.790 - 33 108 75 183 Re - -45809.580 8.034 8001.009 0.044 B- -2145.537 50.405 182 950821.390 8.624 - 31 107 76 183 Os -43664.043 49.760 7985.010 0.272 B- -3460.732 52.733 182 953124.719 53.420 - 29 106 77 183 Ir -40203.311 24.398 7961.823 0.133 B- -4430.824 28.923 182 956839.968 26.191 - 27 105 78 183 Pt -35772.487 15.533 7933.336 0.085 B- -5581.004 18.168 182 961596.653 16.675 - 25 104 79 183 Au -30191.483 9.423 7898.564 0.051 B- -6386.809 11.789 182 967588.108 10.116 - 23 103 80 183 Hg -23804.674 7.084 7859.388 0.039 B- -7217.417 11.716 182 974444.629 7.604 - 21 102 81 183 Tl -16587.257 9.331 7815.673 0.051 B- -9012.038 29.657 182 982192.846 10.017 - 19 101 82 183 Pb -a -7575.218 28.151 7762.152 0.154 B- * 182 991867.668 30.221 -0 44 114 70 184 Yb x -32540# 503# 7951# 3# B- 3872# 585# 183 965067# 540# - 42 113 71 184 Lu x -36412# 298# 7967# 2# B- 5087# 301# 183 960910# 320# - 40 112 72 184 Hf + -41499.373 39.706 7990.722 0.216 B- 1340.000 30.000 183 955448.587 42.625 - 38 111 73 184 Ta + -42839.373 26.010 7993.752 0.141 B- 2866.000 26.000 183 954010.038 27.923 - 36 110 74 184 W -45705.373 0.731 8005.077 0.004 B- -1485.739 4.198 183 950933.260 0.785 - 34 109 75 184 Re -44219.634 4.275 7992.750 0.023 B- 32.898 4.140 183 952528.267 4.589 - 32 108 76 184 Os -44252.533 0.827 7988.677 0.005 B- -4641.682 27.957 183 952492.949 0.887 - 30 107 77 184 Ir x -39610.851 27.945 7959.198 0.152 B- -2276.608 31.997 183 957476.000 30.000 - 28 106 78 184 Pt -37334.243 15.584 7942.574 0.085 B- -7015.533 27.185 183 959920.039 16.730 - 26 105 79 184 Au -a -30318.710 22.275 7900.194 0.121 B- -3969.745 24.442 183 967451.524 23.912 - 24 104 80 184 Hg -26348.965 10.062 7874.367 0.055 B- -9465.723 14.200 183 971713.221 10.802 - 22 103 81 184 Tl -16883.242 10.020 7818.671 0.054 B- -5831.720 16.260 183 981875.093 10.757 - 20 102 82 184 Pb -11051.522 12.806 7782.725 0.070 B- -12114.590 79.153 183 988135.702 13.748 - 18 101 83 184 Bi -a 1063.068 78.110 7712.633 0.425 B- * 184 001141.250 83.854 -0 45 115 70 185 Yb x -28500# 503# 7929# 3# B- 5388# 585# 184 969404# 540# - 43 114 71 185 Lu x -33888# 298# 7954# 2# B- 4432# 305# 184 963620# 320# - 41 113 72 185 Hf x -38319.800 64.273 7973.970 0.347 B- 3074.492 65.815 184 958862.000 69.000 - 39 112 73 185 Ta + -41394.293 14.161 7986.360 0.077 B- 1993.500 14.142 184 955561.396 15.202 - 37 111 74 185 W -43387.793 0.733 7992.907 0.004 B- 431.234 0.661 184 953421.286 0.786 - 35 110 75 185 Re -43819.027 0.818 7991.009 0.004 B- -1013.147 0.419 184 952958.337 0.877 - 33 109 76 185 Os -42805.880 0.830 7981.304 0.004 B- -2470.326 27.957 184 954045.995 0.891 - 31 108 77 185 Ir x -40335.553 27.945 7963.722 0.151 B- -3647.414 38.055 184 956698.000 30.000 - 29 107 78 185 Pt -36688.140 25.832 7939.777 0.140 B- -4829.997 25.963 184 960613.659 27.731 - 27 106 79 185 Au x -31858.143 2.608 7909.440 0.014 B- -5674.477 13.886 184 965798.874 2.800 - 25 105 80 185 Hg -26183.666 13.639 7874.538 0.074 B- -6425.925 24.767 184 971890.676 14.641 - 23 104 81 185 Tl IT -19757.741 20.674 7835.575 0.112 B- -8216.521 26.249 184 978789.191 22.194 - 21 103 82 185 Pb -a -11541.220 16.175 7786.932 0.087 B- -9305# 83# 184 987609.989 17.364 - 19 102 83 185 Bi IT -2236# 81# 7732# 0# B- * 184 997600# 87# -0 44 115 71 186 Lu x -30210# 401# 7935# 2# B- 6214# 404# 185 967568# 430# - 42 114 72 186 Hf x -36424.210 51.232 7964.302 0.275 B- 2183.318 78.906 185 960897.000 55.000 - 40 113 73 186 Ta + -38607.528 60.012 7971.835 0.323 B- 3901.000 60.000 185 958553.111 64.425 - 38 112 74 186 W -42508.528 1.212 7988.601 0.007 B- -581.442 1.244 185 954365.215 1.300 - 36 111 75 186 Re -41927.086 0.826 7981.269 0.004 B- 1072.857 0.837 185 954989.419 0.886 - 34 110 76 186 Os -42999.943 0.761 7982.831 0.004 B- -3827.596 16.543 185 953837.660 0.816 - 32 109 77 186 Ir x -39172.346 16.526 7958.047 0.089 B- -1307.903 27.312 185 957946.754 17.740 - 30 108 78 186 Pt -37864.443 21.745 7946.809 0.117 B- -6149.591 30.207 185 959350.846 23.344 - 28 107 79 186 Au -31714.852 20.967 7909.540 0.113 B- -3175.756 23.987 185 965952.703 22.509 - 26 106 80 186 Hg -28539.097 11.650 7888.260 0.063 B- -8652.484 25.209 185 969362.017 12.507 - 24 105 81 186 Tl x -19886.613 22.356 7837.535 0.120 B- -5204.588 25.078 185 978650.841 24.000 - 22 104 82 186 Pb -a -14682.026 11.363 7805.347 0.061 B- -11535.814 20.329 185 984238.196 12.199 - 20 103 83 186 Bi -a -3146.212 16.857 7739.121 0.091 B- -7247.186 24.870 185 996622.402 18.096 - 18 102 84 186 Po -a 4100.974 18.286 7695.951 0.098 B- * 186 004402.577 19.630 -0 45 116 71 187 Lu x -27580# 401# 7922# 2# B- 5237# 499# 186 970392# 430# - 43 115 72 187 Hf x -32817# 298# 7946# 2# B- 4079# 303# 186 964770# 320# - 41 114 73 187 Ta x -36895.546 55.890 7963.212 0.299 B- 3008.424 55.903 186 960391.000 60.000 - 39 113 74 187 W -39903.970 1.212 7975.116 0.006 B- 1312.508 1.122 186 957161.323 1.300 - 37 112 75 187 Re -41216.478 0.736 7977.951 0.004 B- 2.467 0.002 186 955752.288 0.790 - 35 111 76 187 Os -41218.945 0.736 7973.780 0.004 B- -1669.572 27.955 186 955749.640 0.790 - 33 110 77 187 Ir x -39549.372 27.945 7960.668 0.149 B- -2864.323 36.868 186 957542.000 30.000 - 31 109 78 187 Pt -36685.050 24.048 7941.168 0.129 B- -3657.212 27.377 186 960616.976 25.816 - 29 108 79 187 Au -33027.838 22.308 7917.427 0.119 B- -4909.908 26.287 186 964543.155 23.948 - 27 107 80 187 Hg -28117.930 13.905 7886.987 0.074 B- -5673.343 16.067 186 969814.158 14.928 - 25 106 81 187 Tl -22444.587 8.048 7852.464 0.043 B- -7457.628 9.525 186 975904.743 8.640 - 23 105 82 187 Pb -14986.959 5.094 7808.400 0.027 B- -8603.688 11.227 186 983910.836 5.468 - 21 104 83 187 Bi -a -6383.271 10.005 7758.208 0.054 B- -9211.868 33.430 186 993147.276 10.740 - 19 103 84 187 Po -a 2828.597 31.898 7704.763 0.171 B- * 187 003036.624 34.243 -0 46 117 71 188 Lu x -23790# 503# 7902# 3# B- 7089# 585# 187 974460# 540# - 44 116 72 188 Hf x -30879# 298# 7936# 2# B- 2733# 303# 187 966850# 320# - 42 115 73 188 Ta x -33612.030 54.958 7946.321 0.292 B- 5055.781 55.045 187 963916.000 59.000 - 40 114 74 188 W + -38667.811 3.089 7969.052 0.016 B- 349.000 3.000 187 958488.395 3.316 - 38 113 75 188 Re -n -39016.811 0.738 7966.747 0.004 B- 2120.422 0.152 187 958113.728 0.791 - 36 112 76 188 Os -41137.233 0.734 7973.864 0.004 B- -2792.326 9.416 187 955837.361 0.787 - 34 111 77 188 Ir -38344.907 9.423 7954.850 0.050 B- -523.979 8.686 187 958835.046 10.116 - 32 110 78 188 Pt -37820.929 5.304 7947.902 0.028 B- -5449.621 5.953 187 959397.560 5.694 - 30 109 79 188 Au x -32371.308 2.701 7914.753 0.014 B- -2169.394 12.569 187 965247.969 2.900 - 28 108 80 188 Hg -30201.914 12.275 7899.052 0.065 B- -7865.513 32.325 187 967576.910 13.178 - 26 107 81 188 Tl x -22336.400 29.904 7853.053 0.159 B- -4521.198 31.734 187 976020.886 32.103 - 24 106 82 188 Pb -a -17815.202 10.622 7824.843 0.057 B- -10620.515 15.427 187 980874.592 11.403 - 22 105 83 188 Bi -a -7194.687 11.187 7764.189 0.060 B- -6650.374 22.892 187 992276.184 12.009 - 20 104 84 188 Po -a -544.313 19.973 7724.653 0.106 B- * 187 999415.655 21.441 -0 45 117 72 189 Hf x -27162# 298# 7917# 2# B- 4667# 357# 188 970840# 320# - 43 116 73 189 Ta x -31829# 196# 7938# 1# B- 3788# 200# 188 965830# 210# - 41 115 74 189 W x -35617.536 40.054 7953.454 0.212 B- 2361.507 40.883 188 961763.000 43.000 - 39 114 75 189 Re +p -37979.043 8.191 7961.809 0.043 B- 1007.702 8.167 188 959227.817 8.793 - 37 113 76 189 Os -38986.745 0.666 7963.002 0.004 B- -537.159 12.563 188 958146.005 0.715 - 35 112 77 189 Ir -38449.586 12.576 7956.020 0.067 B- -1980.238 13.636 188 958722.669 13.500 - 33 111 78 189 Pt -36469.348 10.090 7941.403 0.053 B- -2887.394 22.474 188 960848.542 10.832 - 31 110 79 189 Au x -33581.955 20.081 7921.987 0.106 B- -3955.554 37.401 188 963948.286 21.558 - 29 109 80 189 Hg -29626.401 31.553 7896.919 0.167 B- -5010.300 32.643 188 968194.748 33.873 - 27 108 81 189 Tl -24616.100 8.368 7866.270 0.044 B- -6772.066 16.364 188 973573.527 8.983 - 25 107 82 189 Pb -17844.035 14.062 7826.299 0.074 B- -7779.374 25.150 188 980843.639 15.096 - 23 106 83 189 Bi -a -10064.660 20.851 7780.999 0.110 B- -8642.656 30.354 188 989195.141 22.384 - 21 105 84 189 Po -a -1422.005 22.059 7731.131 0.117 B- * 188 998473.415 23.681 -0 46 118 72 190 Hf x -25030# 401# 7907# 2# B- 3483# 446# 189 973129# 430# - 44 117 73 190 Ta x -28513# 196# 7921# 1# B- 5869# 200# 189 969390# 210# - 42 116 74 190 W -34382.313 39.726 7947.573 0.209 B- 1253.517 63.522 189 963089.066 42.647 - 40 115 75 190 Re -35635.830 70.852 7950.053 0.373 B- 3071.941 70.854 189 961743.360 76.063 - 38 114 76 190 Os -38707.771 0.650 7962.104 0.003 B- -1954.227 1.213 189 958445.496 0.697 - 36 113 77 190 Ir +n -36753.544 1.370 7947.701 0.007 B- 552.906 1.282 189 960543.445 1.470 - 34 112 78 190 Pt -37306.450 0.657 7946.493 0.003 B- -4472.917 3.509 189 959949.876 0.704 - 32 111 79 190 Au x -32833.533 3.447 7918.834 0.018 B- -1462.836 16.276 189 964751.750 3.700 - 30 110 80 190 Hg -31370.697 15.907 7907.017 0.084 B- -6998.670 17.782 189 966322.169 17.076 - 28 109 81 190 Tl +a -24372.027 7.948 7866.064 0.042 B- -3955.382 14.825 189 973835.551 8.532 - 26 108 82 190 Pb -a -20416.645 12.514 7841.129 0.066 B- -9817.066 25.800 189 978081.828 13.434 - 24 107 83 190 Bi -a -10599.579 22.562 7785.342 0.119 B- -6035.742 26.275 189 988620.883 24.221 - 22 106 84 190 Po -a -4563.837 13.465 7749.458 0.071 B- * 189 995100.519 14.455 -0 45 118 73 191 Ta x -26492# 298# 7911# 2# B- 4684# 301# 190 971560# 320# - 43 117 74 191 W x -31176.173 41.917 7931.435 0.219 B- 3174.124 43.156 190 966531.000 45.000 - 41 116 75 191 Re +p -34350.296 10.265 7943.957 0.054 B- 2044.889 10.244 190 963123.437 11.019 - 39 115 76 191 Os -36395.185 0.659 7950.568 0.003 B- 313.570 1.141 190 960928.159 0.707 - 37 114 77 191 Ir -36708.756 1.311 7948.113 0.007 B- -1010.518 3.636 190 960591.527 1.406 - 35 113 78 191 Pt -35698.237 4.127 7938.727 0.022 B- -1900.333 6.426 190 961676.363 4.430 - 33 112 79 191 Au -33797.904 4.926 7924.681 0.026 B- -3206.008 22.710 190 963716.455 5.288 - 31 111 80 191 Hg -30591.896 22.280 7903.800 0.117 B- -4308.951 23.461 190 967158.247 23.918 - 29 110 81 191 Tl +a -26282.945 7.349 7877.144 0.038 B- -6051.827 37.978 190 971784.096 7.889 - 27 109 82 191 Pb x -20231.118 37.260 7841.363 0.195 B- -6991.772 38.005 190 978281.000 40.000 - 25 108 83 191 Bi -13239.347 7.487 7800.661 0.039 B- -8170.612 10.320 190 985786.975 8.037 - 23 107 84 191 Po -5068.735 7.103 7753.786 0.037 B- -8932.653 17.600 190 994558.488 7.624 - 21 106 85 191 At -a 3863.917 16.103 7702.923 0.084 B- * 191 004148.086 17.287 -0 46 119 73 192 Ta x -23064# 401# 7894# 2# B- 6586# 446# 191 975240# 430# - 44 118 74 192 W x -29649# 196# 7924# 1# B- 1939# 208# 191 968170# 210# - 42 117 75 192 Re x -31588.825 70.794 7930.238 0.369 B- 4293.366 70.831 191 966088.000 76.000 - 40 116 76 192 Os -35882.191 2.315 7948.525 0.012 B- -1046.630 2.397 191 961478.881 2.485 - 38 115 77 192 Ir -34835.561 1.314 7938.999 0.007 B- 1452.896 2.274 191 962602.485 1.410 - 36 114 78 192 Pt -36288.457 2.570 7942.491 0.013 B- -3516.341 15.617 191 961042.736 2.758 - 34 113 79 192 Au - -32772.116 15.827 7920.102 0.082 B- -760.563 22.178 191 964817.684 16.991 - 32 112 80 192 Hg x -32011.553 15.537 7912.066 0.081 B- -6139.307 35.277 191 965634.182 16.679 - 30 111 81 192 Tl x -25872.246 31.671 7876.016 0.165 B- -3316.226 34.348 191 972225.000 34.000 - 28 110 82 192 Pb -a -22556.020 13.295 7854.669 0.069 B- -9021.485 32.917 191 975785.115 14.273 - 26 109 83 192 Bi -a -13534.535 30.112 7803.608 0.157 B- -5463.873 32.096 191 985470.078 32.326 - 24 108 84 192 Po -a -8070.661 11.110 7771.075 0.058 B- -10996.516 30.008 191 991335.788 11.926 - 22 107 85 192 At -a 2925.854 27.876 7709.727 0.145 B- * 192 003141.034 29.926 -0 47 120 73 193 Ta x -20870# 401# 7884# 2# B- 5417# 446# 192 977595# 430# - 45 119 74 193 W x -26287# 196# 7908# 1# B- 3945# 199# 192 971780# 210# - 43 118 75 193 Re x -30231.638 39.123 7923.937 0.203 B- 3162.652 39.192 192 967545.000 42.000 - 41 117 76 193 Os -33394.289 2.321 7936.270 0.012 B- 1141.946 2.400 192 964149.753 2.491 - 39 116 77 193 Ir -34536.235 1.328 7938.133 0.007 B- -56.628 0.300 192 962923.824 1.425 - 37 115 78 193 Pt -34479.608 1.359 7933.786 0.007 B- -1074.787 8.768 192 962984.616 1.458 - 35 114 79 193 Au -33404.821 8.674 7924.164 0.045 B- -2342.642 14.370 192 964138.447 9.311 - 33 113 80 193 Hg -31062.179 15.505 7907.972 0.080 B- -3584.967 16.894 192 966653.377 16.645 - 31 112 81 193 Tl x -27477.212 6.707 7885.344 0.035 B- -5282.723 50.028 192 970501.997 7.200 - 29 111 82 193 Pb x -22194.490 49.577 7853.919 0.257 B- -6309.931 50.152 192 976173.234 53.222 - 27 110 83 193 Bi -15884.559 7.576 7817.171 0.039 B- -7559.241 16.387 192 982947.223 8.132 - 25 109 84 193 Po -a -8325.318 14.531 7773.950 0.075 B- -8257.998 26.059 192 991062.403 15.599 - 23 108 85 193 At -a -67.320 21.632 7727.109 0.112 B- -9110.231 33.144 192 999927.728 23.222 - 21 107 86 193 Rn -a 9042.911 25.112 7675.852 0.130 B- * 193 009707.964 26.958 -0 48 121 73 194 Ta x -17300# 503# 7866# 3# B- 7227# 585# 193 981428# 540# - 46 120 74 194 W x -24526# 298# 7899# 2# B- 2711# 357# 193 973670# 320# - 44 119 75 194 Re x -27237# 196# 7909# 1# B- 5198# 196# 193 970760# 210# - 42 118 76 194 Os + -32435.108 2.403 7932.022 0.012 B- 96.600 2.000 193 965179.477 2.579 - 40 117 77 194 Ir -n -32531.708 1.332 7928.487 0.007 B- 2228.362 1.257 193 965075.773 1.430 - 38 116 78 194 Pt -34760.070 0.496 7935.941 0.003 B- -2548.134 2.117 193 962683.527 0.532 - 36 115 79 194 Au +3n -32211.936 2.118 7918.774 0.011 B- -27.991 3.581 193 965419.062 2.273 - 34 114 80 194 Hg x -32183.945 2.888 7914.597 0.015 B- -5246.454 14.268 193 965449.111 3.100 - 32 113 81 194 Tl x -26937.491 13.972 7883.520 0.072 B- -2729.552 22.343 193 971081.411 15.000 - 30 112 82 194 Pb -24207.940 17.435 7865.418 0.090 B- -8179.128 18.498 193 974011.706 18.717 - 28 111 83 194 Bi +a -16028.811 6.178 7819.225 0.032 B- -5024.156 14.313 193 982792.362 6.632 - 26 110 84 194 Po -a -11004.655 12.911 7789.294 0.067 B- -10284.492 28.076 193 988186.015 13.860 - 24 109 85 194 At -a -720.163 24.931 7732.249 0.129 B- -6443.658 30.119 193 999226.872 26.764 - 22 108 86 194 Rn -a 5723.495 16.899 7695.001 0.087 B- * 194 006144.424 18.141 -0 47 121 74 195 W x -21010# 298# 7882# 2# B- 4569# 422# 194 977445# 320# - 45 120 75 195 Re x -25579# 298# 7902# 2# B- 3933# 303# 194 972540# 320# - 43 119 76 195 Os x -29511.593 55.890 7917.744 0.287 B- 2180.658 55.906 194 968318.000 60.000 - 41 118 77 195 Ir -n -31692.251 1.333 7924.915 0.007 B- 1101.598 1.264 194 965976.967 1.431 - 39 117 78 195 Pt -32793.849 0.503 7926.552 0.003 B- -226.817 1.000 194 964794.353 0.539 - 37 116 79 195 Au -32567.031 1.119 7921.377 0.006 B- -1553.638 23.156 194 965037.851 1.201 - 35 115 80 195 Hg -31013.393 23.142 7909.397 0.119 B- -2858.145 25.657 194 966705.751 24.843 - 33 114 81 195 Tl -28155.248 11.093 7890.728 0.057 B- -4447.555 21.106 194 969774.096 11.909 - 31 113 82 195 Pb -23707.693 17.960 7863.908 0.092 B- -5682.132 18.722 194 974548.743 19.280 - 29 112 83 195 Bi -18025.561 5.287 7830.757 0.027 B- -6969.303 37.737 194 980648.762 5.675 - 27 111 84 195 Po -a -11056.259 37.364 7791.005 0.192 B- -7585.964 38.571 194 988130.617 40.112 - 25 110 85 195 At -a -3470.295 9.573 7748.091 0.049 B- -8520.575 51.401 194 996274.485 10.276 - 23 109 86 195 Rn -a 5050.281 50.502 7700.383 0.259 B- * 195 005421.699 54.216 -0 48 122 74 196 W x -18880# 401# 7872# 2# B- 3662# 499# 195 979731# 430# - 46 121 75 196 Re x -22542# 298# 7887# 2# B- 5735# 301# 195 975800# 320# - 44 120 76 196 Os +pp -28277.105 40.055 7912.229 0.204 B- 1158.388 55.495 195 969643.277 43.000 - 42 119 77 196 Ir + -29435.493 38.414 7914.148 0.196 B- 3209.016 38.411 195 968399.696 41.239 - 40 118 78 196 Pt -32644.510 0.510 7926.529 0.003 B- -1505.803 2.960 195 964954.675 0.547 - 38 117 79 196 Au -31138.706 2.962 7914.855 0.015 B- 687.235 3.118 195 966571.221 3.179 - 36 116 80 196 Hg -31825.941 2.946 7914.369 0.015 B- -4329.349 12.463 195 965833.444 3.163 - 34 115 81 196 Tl x -27496.592 12.109 7888.289 0.062 B- -2148.280 14.356 195 970481.192 13.000 - 32 114 82 196 Pb -25348.312 7.710 7873.337 0.039 B- -7339.281 25.616 195 972787.466 8.277 - 30 113 83 196 Bi x -18009.031 24.428 7831.900 0.125 B- -4535.989 27.916 195 980666.509 26.224 - 28 112 84 196 Po -a -13473.042 13.512 7804.766 0.069 B- -9558.365 33.162 195 985536.094 14.506 - 26 111 85 196 At -a -3914.677 30.284 7752.007 0.155 B- -5885.668 33.540 195 995797.421 32.511 - 24 110 86 196 Rn -a 1970.991 14.417 7717.987 0.074 B- * 196 002115.945 15.476 -0 49 123 74 197 W x -15140# 401# 7854# 2# B- 5363# 499# 196 983747# 430# - 47 122 75 197 Re x -20502# 298# 7878# 2# B- 4807# 357# 196 977990# 320# - 45 121 76 197 Os x -25309# 196# 7898# 1# B- 2955# 197# 196 972830# 210# - 43 120 77 197 Ir +p -28264.105 20.110 7908.999 0.102 B- 2155.645 20.106 196 969657.233 21.588 - 41 119 78 197 Pt -30419.750 0.536 7915.971 0.003 B- 719.988 0.502 196 967343.053 0.575 - 39 118 79 197 Au -31139.738 0.542 7915.654 0.003 B- -599.509 3.202 196 966570.114 0.581 - 37 117 80 197 Hg -30540.229 3.207 7908.640 0.016 B- -2198.580 16.637 196 967213.713 3.442 - 35 116 81 197 Tl +a -28341.649 16.325 7893.508 0.083 B- -3596.247 17.014 196 969573.986 17.526 - 33 115 82 197 Pb -24745.401 4.804 7871.282 0.024 B- -5058.210 9.619 196 973434.717 5.157 - 31 114 83 197 Bi +a -19687.191 8.333 7841.634 0.042 B- -6329.202 50.373 196 978864.929 8.946 - 29 113 84 197 Po -a -13357.990 49.679 7805.535 0.252 B- -7002.739 50.317 196 985659.607 53.332 - 27 112 85 197 At -6355.250 7.983 7766.017 0.041 B- -7865.603 18.054 196 993177.357 8.570 - 25 111 86 197 Rn -a 1510.353 16.193 7722.118 0.082 B- -8743.618 56.762 197 001621.430 17.383 - 23 110 87 197 Fr -a 10253.971 54.404 7673.763 0.276 B- * 197 011008.090 58.404 -0 48 123 75 198 Re x -17139# 401# 7862# 2# B- 6697# 446# 197 981600# 430# - 46 122 76 198 Os x -23837# 196# 7891# 1# B- 1984# 277# 197 974410# 210# - 44 121 77 198 Ir x -25821# 196# 7897# 1# B- 4083# 196# 197 972280# 210# - 42 120 78 198 Pt -29903.999 2.100 7914.150 0.011 B- -323.219 2.059 197 967896.734 2.254 - 40 119 79 198 Au -29580.781 0.540 7908.567 0.003 B- 1373.530 0.490 197 968243.724 0.579 - 38 118 80 198 Hg -30954.310 0.458 7911.552 0.002 B- -3425.564 7.559 197 966769.179 0.491 - 36 117 81 198 Tl x -27528.746 7.545 7890.300 0.038 B- -1461.257 11.554 197 970446.673 8.100 - 34 116 82 198 Pb -26067.489 8.750 7878.969 0.044 B- -6698.003 29.283 197 972015.397 9.393 - 32 115 83 198 Bi x -19369.486 27.945 7841.189 0.141 B- -3896.134 32.932 197 979206.000 30.000 - 30 114 84 198 Po -15473.352 17.424 7817.561 0.088 B- -8758.839 18.386 197 983388.672 18.705 - 28 113 85 198 At x -6714.513 5.868 7769.373 0.030 B- -5484.155 14.647 197 992791.673 6.300 - 26 112 86 198 Rn -a -1230.358 13.420 7737.724 0.068 B- -10804.382 34.905 197 998679.156 14.406 - 24 111 87 198 Fr -a 9574.024 32.222 7679.205 0.163 B- * 198 010278.138 34.591 -0 49 124 75 199 Re x -14860# 401# 7851# 2# B- 5623# 446# 198 984047# 430# - 47 123 76 199 Os x -20484# 196# 7875# 1# B- 3915# 200# 198 978010# 210# - 45 122 77 199 Ir p-2n -24398.515 41.054 7891.206 0.206 B- 2990.167 41.003 198 973807.115 44.073 - 43 121 78 199 Pt -n -27388.682 2.159 7902.300 0.011 B- 1705.059 2.120 198 970597.038 2.317 - 41 120 79 199 Au -29093.741 0.542 7906.937 0.003 B- 452.327 0.613 198 968766.582 0.581 - 39 119 80 199 Hg -29546.068 0.526 7905.279 0.003 B- -1486.674 27.950 198 968280.989 0.564 - 37 118 81 199 Tl x -28059.394 27.945 7893.877 0.140 B- -2827.589 29.679 198 969877.000 30.000 - 35 117 82 199 Pb +a -25231.804 9.996 7875.736 0.050 B- -4434.239 14.547 198 972912.542 10.730 - 33 116 83 199 Bi -20797.566 10.568 7849.522 0.053 B- -5589.083 20.919 198 977672.893 11.345 - 31 115 84 199 Po -a -15208.483 18.060 7817.505 0.091 B- -6385.111 18.845 198 983673.021 19.387 - 29 114 85 199 At -8823.372 5.384 7781.488 0.027 B- -7323.921 37.972 198 990527.719 5.780 - 27 113 86 199 Rn -a -1499.451 37.588 7740.753 0.189 B- -8270.844 40.015 198 998390.273 40.352 - 25 112 87 199 Fr -a 6771.393 13.726 7695.259 0.069 B- * 199 007269.389 14.734 -0 48 124 76 200 Os x -18779# 298# 7868# 1# B- 2832# 357# 199 979840# 320# - 46 123 77 200 Ir x -21611# 196# 7878# 1# B- 4988# 197# 199 976800# 210# - 44 122 78 200 Pt -nn -26599.160 20.110 7899.198 0.101 B- 640.932 33.439 199 971444.625 21.588 - 42 121 79 200 Au -27240.092 26.717 7898.491 0.134 B- 2263.178 26.719 199 970756.556 28.681 - 40 120 80 200 Hg -29503.270 0.529 7905.895 0.003 B- -2456.040 5.735 199 968326.934 0.568 - 38 119 81 200 Tl - -27047.230 5.759 7889.703 0.029 B- -796.176 12.340 199 970963.602 6.182 - 36 118 82 200 Pb 4n -26251.054 10.927 7881.810 0.055 B- -5880.299 24.852 199 971818.332 11.730 - 34 117 83 200 Bi +a -20370.755 22.321 7848.497 0.112 B- -3428.994 23.573 199 978131.093 23.962 - 32 116 84 200 Po -16941.761 7.579 7827.440 0.038 B- -7953.869 25.612 199 981812.270 8.135 - 30 115 85 200 At -a -8987.892 24.465 7783.759 0.122 B- -4983.127 28.030 199 990351.100 26.264 - 28 114 86 200 Rn -a -4004.765 13.681 7754.932 0.068 B- -10137.263 33.529 199 995700.707 14.686 - 26 113 87 200 Fr -a 6132.498 30.611 7700.334 0.153 B- * 200 006583.507 32.861 -0 49 125 76 201 Os x -15239# 298# 7851# 1# B- 4657# 357# 200 983640# 320# - 47 124 77 201 Ir x -19897# 196# 7871# 1# B- 3844# 202# 200 978640# 210# - 45 123 78 201 Pt + -23740.714 50.103 7885.833 0.249 B- 2660.000 50.000 200 974513.293 53.788 - 43 122 79 201 Au -26400.714 3.218 7895.175 0.016 B- 1261.827 3.147 200 971657.665 3.454 - 41 121 80 201 Hg -27662.542 0.711 7897.560 0.004 B- -481.704 14.181 200 970303.038 0.763 - 39 120 81 201 Tl -27180.838 14.185 7891.271 0.071 B- -1909.802 18.530 200 970820.168 15.228 - 37 119 82 201 Pb -25271.036 13.747 7877.877 0.068 B- -3854.603 20.481 200 972870.425 14.758 - 35 118 83 201 Bi +a -21416.433 15.183 7854.808 0.076 B- -4895.248 15.962 200 977008.512 16.299 - 33 117 84 201 Po -16521.185 4.942 7826.561 0.025 B- -5731.747 9.561 200 982263.777 5.305 - 31 116 85 201 At +a -10789.438 8.184 7794.153 0.041 B- -6717.113 50.401 200 988417.061 8.786 - 29 115 86 201 Rn -a -4072.324 49.732 7756.842 0.247 B- -7660.902 50.554 200 995628.179 53.389 - 27 114 87 201 Fr -a 3588.577 9.080 7714.836 0.045 B- -8348.224 22.239 201 003852.496 9.747 - 25 113 88 201 Ra -a 11936.801 20.301 7669.410 0.101 B- * 201 012814.683 21.794 -0 50 126 76 202 Os x -13087# 401# 7842# 2# B- 3689# 499# 201 985950# 430# - 48 125 77 202 Ir x -16776# 298# 7856# 1# B- 5916# 299# 201 981990# 320# - 46 124 78 202 Pt x -22692.125 25.150 7881.560 0.125 B- 1660.854 34.276 201 975639.000 27.000 - 44 123 79 202 Au x -24352.979 23.287 7885.909 0.115 B- 2992.345 23.298 201 973856.000 25.000 - 42 122 80 202 Hg -27345.324 0.705 7896.850 0.003 B- -1365.108 1.636 201 970643.585 0.756 - 40 121 81 202 Tl -25980.216 1.606 7886.219 0.008 B- -39.602 4.096 201 972109.089 1.723 - 38 120 82 202 Pb -25940.614 3.796 7882.150 0.019 B- -5199.130 15.856 201 972151.604 4.075 - 36 119 83 202 Bi -20741.484 15.396 7852.539 0.076 B- -2799.868 17.666 201 977733.100 16.528 - 34 118 84 202 Po -17941.616 8.670 7834.805 0.043 B- -7350.884 29.289 201 980738.881 9.307 - 32 117 85 202 At -a -10590.732 27.977 7794.541 0.138 B- -4316.097 33.010 201 988630.380 30.034 - 30 116 86 202 Rn -a -6274.635 17.520 7769.301 0.087 B- -9370.871 18.881 201 993263.902 18.808 - 28 115 87 202 Fr -a 3096.237 7.040 7719.038 0.035 B- -5978.625 16.586 202 003323.946 7.557 - 26 114 88 202 Ra -a 9074.861 15.018 7685.568 0.074 B- * 202 009742.264 16.122 -0 51 127 76 203 Os x -7640# 401# 7816# 2# B- 7050# 566# 202 991798# 430# - 49 126 77 203 Ir x -14690# 401# 7847# 2# B- 4937# 446# 202 984230# 430# - 47 125 78 203 Pt x -19627# 196# 7867# 1# B- 3517# 196# 202 978930# 210# - 45 124 79 203 Au -23143.436 3.083 7880.864 0.015 B- 2125.829 3.451 202 975154.498 3.309 - 43 123 80 203 Hg -25269.265 1.627 7887.482 0.008 B- 492.112 1.225 202 972872.326 1.746 - 41 122 81 203 Tl -25761.377 1.166 7886.053 0.006 B- -974.820 6.461 202 972344.022 1.252 - 39 121 82 203 Pb -24786.557 6.554 7877.397 0.032 B- -3261.729 14.356 202 973390.535 7.036 - 37 120 83 203 Bi +a -21524.827 12.778 7857.475 0.063 B- -4213.939 15.433 202 976892.145 13.717 - 35 119 84 203 Po +a -17310.889 8.655 7832.863 0.043 B- -5148.332 13.666 202 981415.995 9.291 - 33 118 85 203 At -12162.557 10.576 7803.648 0.052 B- -6008.858 21.027 202 986942.957 11.353 - 31 117 86 203 Rn -a -6153.699 18.179 7770.193 0.090 B- -7030.116 19.218 202 993393.732 19.516 - 29 116 87 203 Fr 876.417 6.232 7731.708 0.031 B- -7785.309 38.630 203 000940.872 6.689 - 27 115 88 203 Ra -a 8661.726 38.124 7689.503 0.188 B- * 203 009298.745 40.928 -0 50 127 77 204 Ir x -9688# 401# 7824# 2# B- 8234# 446# 203 989600# 430# - 48 126 78 204 Pt x -17922# 196# 7860# 1# B- 2728# 280# 203 980760# 210# - 46 125 79 204 Au + -20650# 200# 7870# 1# B- 4040# 200# 203 977831# 215# - 44 124 80 204 Hg -24690.145 0.498 7885.545 0.002 B- -344.000 1.186 203 973494.037 0.534 - 42 123 81 204 Tl -24346.145 1.152 7880.023 0.006 B- 763.748 0.177 203 973863.337 1.236 - 40 122 82 204 Pb -25109.892 1.146 7879.932 0.006 B- -4463.996 9.248 203 973043.420 1.230 - 38 121 83 204 Bi +a -20645.896 9.180 7854.215 0.045 B- -2304.652 14.335 203 977835.717 9.854 - 36 120 84 204 Po -a -18341.244 11.013 7839.083 0.054 B- -6465.811 24.860 203 980309.863 11.822 - 34 119 85 204 At -11875.433 22.288 7803.552 0.109 B- -3905.240 23.498 203 987251.197 23.926 - 32 118 86 204 Rn -7970.193 7.444 7780.574 0.036 B- -8577.503 25.684 203 991443.644 7.991 - 30 117 87 204 Fr -a 607.310 24.581 7734.692 0.120 B- -5449.477 28.940 204 000651.974 26.389 - 28 116 88 204 Ra -a 6056.787 15.273 7704.144 0.075 B- * 204 006502.228 16.396 -0 51 128 77 205 Ir x -5960# 503# 7807# 2# B- 7007# 585# 204 993602# 540# - 49 127 78 205 Pt x -12966# 298# 7837# 1# B- 5803# 357# 204 986080# 320# - 47 126 79 205 Au x -18770# 196# 7861# 1# B- 3518# 196# 204 979850# 210# - 45 125 80 205 Hg -22287.740 3.654 7874.732 0.018 B- 1533.135 3.724 204 976073.125 3.923 - 43 124 81 205 Tl -23820.874 1.237 7878.394 0.006 B- -50.636 0.503 204 974427.237 1.328 - 41 123 82 205 Pb -23770.239 1.144 7874.331 0.006 B- -2705.734 5.107 204 974481.597 1.228 - 39 122 83 205 Bi -21064.504 5.111 7857.316 0.025 B- -3543.106 11.280 204 977386.323 5.487 - 37 121 84 205 Po -17521.398 10.059 7836.216 0.049 B- -4549.452 18.130 204 981190.004 10.798 - 35 120 85 205 At +a -12971.946 15.085 7810.207 0.074 B- -5262.161 15.913 204 986074.041 16.194 - 33 119 86 205 Rn -7709.786 5.080 7780.722 0.025 B- -6399.973 9.329 204 991723.204 5.453 - 31 118 87 205 Fr x -1309.813 7.824 7745.686 0.038 B- -7148.804 70.954 204 998593.858 8.399 - 29 117 88 205 Ra -a 5838.991 70.521 7706.998 0.344 B- -8267.702 86.923 205 006268.415 75.707 - 27 116 89 205 Ac -a 14106.693 50.818 7662.851 0.248 B- * 205 015144.158 54.555 -0 50 128 78 206 Pt x -9632# 298# 7822# 1# B- 4583# 422# 205 989660# 320# - 48 127 79 206 Au x -14215# 298# 7840# 1# B- 6731# 299# 205 984740# 320# - 46 126 80 206 Hg +a -20945.801 20.440 7869.172 0.099 B- 1307.566 20.410 205 977513.756 21.943 - 44 125 81 206 Tl -22253.367 1.284 7871.721 0.006 B- 1532.217 0.612 205 976110.026 1.378 - 42 124 82 206 Pb -23785.584 1.144 7875.362 0.006 B- -3757.306 7.546 205 974465.124 1.227 - 40 123 83 206 Bi - -20028.278 7.632 7853.324 0.037 B- -1839.604 8.600 205 978498.757 8.193 - 38 122 84 206 Po -a -18188.674 4.012 7840.597 0.019 B- -5758.956 15.580 205 980473.654 4.306 - 36 121 85 206 At -12429.718 15.056 7808.843 0.073 B- -3296.753 17.330 205 986656.148 16.162 - 34 120 86 206 Rn -9132.965 8.591 7789.041 0.042 B- -7890.549 29.475 205 990195.358 9.223 - 32 119 87 206 Fr -a -1242.416 28.195 7746.940 0.137 B- -4807.955 33.455 205 998666.211 30.268 - 30 118 88 206 Ra -a 3565.539 18.008 7719.802 0.087 B- -9913.913 53.608 206 003827.763 19.332 - 28 117 89 206 Ac -a 13479.452 50.493 7667.879 0.245 B- * 206 014470.787 54.206 -0 51 129 78 207 Pt x -4540# 401# 7798# 2# B- 6270# 500# 206 995126# 430# - 49 128 79 207 Au x -10810# 300# 7825# 1# B- 5677# 301# 206 988395# 322# - 47 127 80 207 Hg x -16487.444 29.808 7848.610 0.144 B- 4547.008 30.300 206 982300.000 32.000 - 45 126 81 207 Tl -21034.451 5.439 7866.797 0.026 B- 1417.595 5.402 206 977418.586 5.839 - 43 125 82 207 Pb -22452.047 1.147 7869.866 0.006 B- -2397.420 2.118 206 975896.735 1.230 - 41 124 83 207 Bi -20054.627 2.397 7854.505 0.012 B- -2908.852 6.614 206 978470.471 2.573 - 39 123 84 207 Po -17145.775 6.659 7836.673 0.032 B- -3918.358 14.075 206 981593.252 7.148 - 37 122 85 207 At +a -13227.416 12.406 7813.964 0.060 B- -4592.654 15.037 206 985799.783 13.318 - 35 121 86 207 Rn +a -8634.762 8.497 7787.998 0.041 B- -5790.421 19.458 206 990730.200 9.121 - 33 120 87 207 Fr -2844.341 17.505 7756.246 0.085 B- -6388.826 56.008 206 996946.474 18.792 - 31 119 88 207 Ra -a 3544.485 53.202 7721.602 0.257 B- -7601.748 73.276 207 003805.161 57.115 - 29 118 89 207 Ac -a 11146.233 50.387 7681.099 0.243 B- * 207 011965.973 54.092 -0 52 130 78 208 Pt x -990# 400# 7783# 2# B- 5111# 499# 207 998937# 429# - 50 129 79 208 Au x -6101# 298# 7804# 1# B- 7164# 300# 207 993450# 320# - 48 128 80 208 Hg x -13265.406 30.739 7834.191 0.148 B- 3484.726 30.795 207 985759.000 33.000 - 46 127 81 208 Tl +a -16750.132 1.854 7847.183 0.009 B- 4998.466 1.669 207 982017.992 1.990 - 44 126 82 208 Pb -21748.598 1.148 7867.453 0.006 B- -2878.375 2.013 207 976651.918 1.231 - 42 125 83 208 Bi +n -18870.223 2.304 7849.853 0.011 B- -1400.628 2.397 207 979741.981 2.473 - 40 124 84 208 Po -a -17469.596 1.737 7839.358 0.008 B- -4999.725 9.086 207 981245.616 1.864 - 38 123 85 208 At +a -12469.871 8.921 7811.560 0.043 B- -2814.279 14.269 207 986613.042 9.577 - 36 122 86 208 Rn -a -9655.591 11.138 7794.268 0.054 B- -6989.672 16.251 207 989634.295 11.957 - 34 121 87 208 Fr -2665.919 11.834 7756.903 0.057 B- -4393.774 14.881 207 997138.018 12.704 - 32 120 88 208 Ra -a 1727.856 9.023 7732.017 0.043 B- -9025.380 56.442 208 001854.929 9.686 - 30 119 89 208 Ac -a 10753.235 55.716 7684.865 0.268 B- -5930.495 65.370 208 011544.073 59.813 - 28 118 90 208 Th -a 16683.730 34.190 7652.592 0.164 B- * 208 017910.722 36.704 -0 51 130 79 209 Au x -2540# 400# 7788# 2# B- 6104# 426# 208 997273# 429# - 49 129 80 209 Hg x -8644# 149# 7813# 1# B- 5000# 149# 208 990720# 160# - 47 128 81 209 Tl +a -13644.757 6.110 7833.397 0.029 B- 3969.889 6.211 208 985351.750 6.559 - 45 127 82 209 Pb -17614.646 1.747 7848.648 0.008 B- 644.016 1.146 208 981089.898 1.875 - 43 126 83 209 Bi -18258.662 1.364 7847.987 0.007 B- -1892.570 1.564 208 980398.519 1.464 - 41 125 84 209 Po -a -16366.092 1.778 7835.188 0.009 B- -3483.478 5.287 208 982430.276 1.908 - 39 124 85 209 At -12882.613 5.102 7814.777 0.024 B- -3941.564 11.188 208 986169.944 5.477 - 37 123 86 209 Rn -8941.049 9.960 7792.175 0.048 B- -5171.477 17.713 208 990401.388 10.692 - 35 122 87 209 Fr x -3769.572 14.648 7763.688 0.070 B- -5627.791 15.730 208 995953.197 15.725 - 33 121 88 209 Ra -a 1858.219 5.747 7733.017 0.027 B- -6985.590 50.934 209 001994.879 6.169 - 31 120 89 209 Ac -a 8843.809 50.608 7695.850 0.242 B- -7523# 148# 209 009494.220 54.330 - 29 119 90 209 Th IT 16367# 140# 7656# 1# B- * 209 017571# 150# -0 52 131 79 210 Au x 2329# 401# 7766# 2# B- 7694# 446# 210 002500# 430# - 50 130 80 210 Hg x -5365# 196# 7799# 1# B- 3882# 196# 209 994240# 210# - 48 129 81 210 Tl +a -9246.969 11.604 7813.588 0.055 B- 5481.534 11.561 209 990072.970 12.456 - 46 128 82 210 Pb -14728.502 1.447 7835.965 0.007 B- 63.476 0.499 209 984188.301 1.553 - 44 127 83 210 Bi -14791.979 1.363 7832.542 0.006 B- 1161.159 0.766 209 984120.156 1.462 - 42 126 84 210 Po -15953.137 1.146 7834.346 0.005 B- -3980.960 7.610 209 982873.601 1.230 - 40 125 85 210 At -a -11972.177 7.695 7811.663 0.037 B- -2367.407 8.922 209 987147.338 8.261 - 38 124 86 210 Rn -a -9604.770 4.557 7796.665 0.022 B- -6271.565 15.824 209 989688.854 4.892 - 36 123 87 210 Fr -3333.205 15.154 7763.075 0.072 B- -3775.997 17.720 209 996421.657 16.268 - 34 122 88 210 Ra -a 442.792 9.193 7741.368 0.044 B- -8346.908 58.133 210 000475.356 9.868 - 32 121 89 210 Ac -a 8789.699 57.402 7697.896 0.273 B- -5269.747 60.436 210 009436.130 61.623 - 30 120 90 210 Th -a 14059.446 18.909 7669.076 0.090 B- * 210 015093.437 20.299 -0 51 131 80 211 Hg x -624# 196# 7778# 1# B- 5454# 200# 210 999330# 210# - 49 130 81 211 Tl x -6077.998 41.917 7799.791 0.199 B- 4414.950 41.978 210 993475.000 45.000 - 47 129 82 211 Pb -10492.948 2.261 7817.007 0.011 B- 1366.183 5.471 210 988735.356 2.426 - 45 128 83 211 Bi -11859.131 5.442 7819.774 0.026 B- 573.439 5.430 210 987268.698 5.842 - 43 127 84 211 Po -a -12432.571 1.255 7818.784 0.006 B- -785.307 2.539 210 986653.085 1.347 - 41 126 85 211 At -a -11647.264 2.729 7811.354 0.013 B- -2891.860 6.894 210 987496.147 2.929 - 39 125 86 211 Rn -a -8755.404 6.813 7793.941 0.032 B- -4615.155 13.786 210 990600.686 7.314 - 37 124 87 211 Fr -4140.249 11.991 7768.360 0.057 B- -4972.272 14.369 210 995555.259 12.872 - 35 123 88 211 Ra x 832.023 7.918 7741.087 0.038 B- -6370.191 53.564 211 000893.213 8.500 - 33 122 89 211 Ac -a 7202.214 52.976 7707.189 0.251 B- -6707.958 90.205 211 007731.894 56.871 - 31 121 90 211 Th -a 13910.171 73.010 7671.690 0.346 B- -8170# 126# 211 014933.183 78.379 - 29 120 91 211 Pa x 22080# 102# 7629# 0# B- * 211 023704# 110# -0 52 132 80 212 Hg x 2757# 298# 7763# 1# B- 4308# 359# 212 002960# 320# - 50 131 81 212 Tl +a -1551# 200# 7780# 1# B- 5998# 200# 211 998335# 215# - 48 130 82 212 Pb -7548.850 1.842 7804.319 0.009 B- 569.104 1.825 211 991895.975 1.977 - 46 129 83 212 Bi -8117.954 1.854 7803.313 0.009 B- 2251.533 1.667 211 991285.016 1.989 - 44 128 84 212 Po -10369.487 1.152 7810.243 0.005 B- -1741.266 2.107 211 988867.896 1.236 - 42 127 85 212 At -a -8628.221 2.384 7798.340 0.011 B- 31.387 3.605 211 990737.223 2.559 - 40 126 86 212 Rn -a -8659.608 3.145 7794.797 0.015 B- -5143.640 9.318 211 990703.528 3.376 - 38 125 87 212 Fr -3515.968 8.775 7766.845 0.041 B- -3317.000 14.276 211 996225.453 9.420 - 36 124 88 212 Ra -a -198.968 11.263 7747.508 0.053 B- -7476.266 52.601 211 999786.399 12.091 - 34 123 89 212 Ac -a 7277.298 51.381 7708.552 0.242 B- -4833.510 52.366 212 007812.501 55.160 - 32 122 90 212 Th -a 12110.808 10.109 7682.062 0.048 B- -9482.551 75.541 212 013001.487 10.852 - 30 121 91 212 Pa -a 21593.358 74.862 7633.643 0.353 B- * 212 023181.425 80.367 -0 53 133 80 213 Hg x 7666# 298# 7741# 1# B- 5882# 299# 213 008230# 320# - 51 132 81 213 Tl x 1783.811 27.013 7765.430 0.127 B- 4987.343 27.894 213 001915.000 29.000 - 49 131 82 213 Pb +a -3203.532 6.954 7785.172 0.033 B- 2028.103 8.371 212 996560.867 7.465 - 47 130 83 213 Bi -5231.635 5.082 7791.021 0.024 B- 1421.949 5.490 212 994383.608 5.456 - 45 129 84 213 Po -6653.584 3.053 7794.024 0.014 B- -73.989 5.465 212 992857.083 3.277 - 43 128 85 213 At -a -6579.595 4.898 7790.003 0.023 B- -883.569 5.724 212 992936.514 5.257 - 41 127 86 213 Rn -a -5696.026 3.370 7782.182 0.016 B- -2143.179 6.006 212 993885.064 3.617 - 39 126 87 213 Fr -3552.848 5.091 7768.447 0.024 B- -3898.405 11.057 212 996185.861 5.465 - 37 125 88 213 Ra 345.557 9.818 7746.472 0.046 B- -5809.134 18.156 213 000370.970 10.540 - 35 124 89 213 Ac -a 6154.692 15.272 7715.526 0.072 B- -5965.394 17.834 213 006607.333 16.395 - 33 123 90 213 Th -a 12120.086 9.217 7683.846 0.043 B- -7542.539 71.737 213 013011.447 9.895 - 31 122 91 213 Pa -a 19662.625 71.142 7644.762 0.334 B- * 213 021108.697 76.374 -0 54 134 80 214 Hg x 11178# 401# 7727# 2# B- 4713# 446# 214 012000# 430# - 52 133 81 214 Tl x 6465# 196# 7745# 1# B- 6647# 196# 214 006940# 210# - 50 132 82 214 Pb -182.769 1.975 7772.394 0.009 B- 1017.984 11.256 213 999803.788 2.120 - 48 131 83 214 Bi -1200.753 11.209 7773.495 0.052 B- 3269.293 11.165 213 998710.938 12.033 - 46 130 84 214 Po -4470.046 1.449 7785.116 0.007 B- -1090.215 4.107 213 995201.208 1.555 - 44 129 85 214 At -a -3379.831 4.298 7776.366 0.020 B- 939.911 10.014 213 996371.601 4.614 - 42 128 86 214 Rn -a -4319.742 9.187 7777.102 0.043 B- -3361.035 12.503 213 995362.566 9.862 - 40 127 87 214 Fr -a -958.707 8.634 7757.740 0.040 B- -1051.441 10.086 213 998970.785 9.268 - 38 126 88 214 Ra -a 92.734 5.250 7749.171 0.025 B- -6351.120 16.232 214 000099.554 5.636 - 36 125 89 214 Ac -a 6443.854 15.360 7715.837 0.072 B- -4251.030 18.693 214 006917.762 16.489 - 34 124 90 214 Th -a 10694.885 10.661 7692.317 0.050 B- -8790.630 76.867 214 011481.431 11.445 - 32 123 91 214 Pa -a 19485.515 76.125 7647.583 0.356 B- * 214 020918.561 81.723 -0 55 135 80 215 Hg x 16208# 401# 7705# 2# B- 6297# 499# 215 017400# 430# - 53 134 81 215 Tl x 9911# 298# 7730# 1# B- 5569# 303# 215 010640# 320# - 51 133 82 215 Pb +a 4342.244 52.448 7752.737 0.244 B- 2712.922 52.748 215 004661.590 56.304 - 49 132 83 215 Bi 1629.322 5.624 7761.717 0.026 B- 2171.028 5.530 215 001749.149 6.037 - 47 131 84 215 Po -541.706 2.121 7768.176 0.010 B- 714.049 6.819 214 999418.454 2.276 - 45 130 85 215 At -a -1255.756 6.799 7767.858 0.032 B- -87.195 10.168 214 998651.890 7.299 - 43 129 86 215 Rn -a -1168.561 7.672 7763.814 0.036 B- -1486.625 10.306 214 998745.498 8.236 - 41 128 87 215 Fr -a 318.065 7.066 7753.260 0.033 B- -2215.674 10.077 215 000341.456 7.585 - 39 127 88 215 Ra -a 2533.739 7.613 7739.316 0.035 B- -3496.877 14.551 215 002720.080 8.172 - 37 126 89 215 Ac -a 6030.615 12.406 7719.413 0.058 B- -4890.971 15.234 215 006474.132 13.318 - 35 125 90 215 Th -a 10921.586 8.840 7693.025 0.041 B- -6942.353 73.380 215 011724.805 9.490 - 33 124 91 215 Pa -a 17863.939 72.845 7657.096 0.339 B- -7059.147 114.616 215 019177.728 78.202 - 31 123 92 215 U -a 24923.087 88.490 7620.624 0.412 B- * 215 026756.035 94.997 -0 56 136 80 216 Hg x 19859# 401# 7690# 2# B- 5142# 499# 216 021320# 430# - 54 135 81 216 Tl x 14718# 298# 7710# 1# B- 7238# 357# 216 015800# 320# - 52 134 82 216 Pb x 7480# 196# 7740# 1# B- 1606# 196# 216 008030# 210# - 50 133 83 216 Bi x 5873.991 11.178 7743.499 0.052 B- 4091.571 11.324 216 006305.989 12.000 - 48 132 84 216 Po 1782.420 1.816 7758.819 0.008 B- -474.246 3.571 216 001913.506 1.949 - 46 131 85 216 At -a 2256.666 3.575 7753.002 0.017 B- 2003.799 6.836 216 002422.631 3.837 - 44 130 86 216 Rn -a 252.868 5.994 7758.657 0.028 B- -2718.082 7.126 216 000271.464 6.435 - 42 129 87 216 Fr -a 2970.950 4.173 7742.451 0.019 B- -320.128 9.548 216 003189.445 4.480 - 40 128 88 216 Ra -a 3291.077 8.737 7737.347 0.040 B- -4853.317 13.921 216 003533.117 9.379 - 38 127 89 216 Ac -a 8144.395 10.840 7711.256 0.050 B- -2153.937 16.201 216 008743.367 11.637 - 36 126 90 216 Th -a 10298.332 12.042 7697.662 0.056 B- -7500.882 54.864 216 011055.714 12.928 - 34 125 91 216 Pa -a 17799.214 53.526 7659.314 0.248 B- -5267.137 60.450 216 019108.242 57.462 - 32 124 92 216 U -a 23066.351 28.093 7631.307 0.130 B- * 216 024762.747 30.158 -0 55 136 81 217 Tl x 18313# 401# 7695# 2# B- 6073# 499# 217 019660# 430# - 53 135 82 217 Pb x 12240# 298# 7719# 1# B- 3510# 299# 217 013140# 320# - 51 134 83 217 Bi x 8729.962 17.698 7731.848 0.082 B- 2846.444 18.870 217 009372.000 19.000 - 49 133 84 217 Po +a 5883.518 6.544 7741.360 0.030 B- 1488.883 7.979 217 006316.216 7.025 - 47 132 85 217 At 4394.635 5.001 7744.616 0.023 B- 736.135 6.151 217 004717.835 5.369 - 45 131 86 217 Rn -a 3658.501 4.198 7744.403 0.019 B- -656.089 7.538 217 003927.562 4.506 - 43 130 87 217 Fr -a 4314.590 6.531 7737.775 0.030 B- -1575.067 9.588 217 004631.902 7.010 - 41 129 88 217 Ra -a 5889.656 7.202 7726.911 0.033 B- -2814.017 13.430 217 006322.806 7.731 - 39 128 89 217 Ac -a 8703.673 11.389 7710.338 0.052 B- -3502.107 15.566 217 009343.777 12.226 - 37 127 90 217 Th -a 12205.780 10.614 7690.594 0.049 B- -4862.630 19.132 217 013103.444 11.394 - 35 126 91 217 Pa -a 17068.410 15.918 7664.580 0.073 B- -5905# 73# 217 018323.692 17.089 - 33 125 92 217 U -a 22973# 71# 7634# 0# B- * 217 024663# 77# -0 56 137 81 218 Tl x 23180# 400# 7674# 2# B- 7727# 499# 218 024885# 429# - 54 136 82 218 Pb x 15453# 298# 7706# 1# B- 2237# 299# 218 016590# 320# - 52 135 83 218 Bi x 13216.037 27.013 7712.827 0.124 B- 4859.136 27.085 218 014188.000 29.000 - 50 134 84 218 Po 8356.901 1.973 7731.528 0.009 B- 258.738 11.649 218 008971.502 2.118 - 48 133 85 218 At -a 8098.162 11.604 7729.126 0.053 B- 2880.816 11.705 218 008693.735 12.456 - 46 132 86 218 Rn 5217.347 2.316 7738.752 0.011 B- -1841.770 4.942 218 005601.052 2.486 - 44 131 87 218 Fr -a 7059.117 4.757 7726.715 0.022 B- 407.947 12.039 218 007578.274 5.106 - 42 130 88 218 Ra -a 6651.170 11.176 7724.998 0.051 B- -4192.439 51.931 218 007140.325 11.997 - 40 129 89 218 Ac -a 10843.609 50.740 7702.177 0.233 B- -1523.132 51.815 218 011641.093 54.471 - 38 128 90 218 Th -a 12366.741 10.516 7691.602 0.048 B- -6317.029 21.130 218 013276.242 11.289 - 36 127 91 218 Pa -a 18683.770 18.329 7659.036 0.084 B- -3210.838 22.888 218 020057.853 19.676 - 34 126 92 218 U -a 21894.608 13.714 7640.719 0.063 B- * 218 023504.829 14.722 -0 55 137 82 219 Pb x 20279# 401# 7686# 2# B- 3996# 446# 219 021770# 430# - 53 136 83 219 Bi x 16283# 196# 7700# 1# B- 3601# 196# 219 017480# 210# - 51 135 84 219 Po x 12681.359 15.835 7713.333 0.072 B- 2285.283 16.163 219 013614.000 17.000 - 49 134 85 219 At 10396.076 3.237 7720.196 0.015 B- 1566.675 2.947 219 011160.647 3.474 - 47 133 86 219 Rn 8829.402 2.100 7723.777 0.010 B- 211.635 7.058 219 009478.753 2.254 - 45 132 87 219 Fr -a 8617.767 7.039 7721.171 0.032 B- -776.515 10.772 219 009251.553 7.556 - 43 131 88 219 Ra -a 9394.282 8.258 7714.053 0.038 B- -2175.199 51.142 219 010085.176 8.865 - 41 130 89 219 Ac -a 11569.480 50.497 7700.549 0.231 B- -2901.910 71.425 219 012420.348 54.210 - 39 129 90 219 Th -a 14471.390 50.576 7683.725 0.231 B- -4068.741 72.192 219 015535.677 54.295 - 37 128 91 219 Pa -a 18540.131 51.516 7661.574 0.235 B- -4746.439 72.333 219 019903.650 55.304 - 35 127 92 219 U -a 23286.569 50.775 7636.329 0.232 B- -6170.086 101.905 219 024999.161 54.509 - 33 126 93 219 Np -a 29456.655 88.354 7604.583 0.403 B- * 219 031623.021 94.851 -0 56 138 82 220 Pb x 23669# 401# 7672# 2# B- 2850# 499# 220 025410# 430# - 54 137 83 220 Bi x 20819# 298# 7682# 1# B- 5555# 299# 220 022350# 320# - 52 136 84 220 Po x 15263.461 17.698 7703.224 0.080 B- 887.714 22.549 220 016386.000 19.000 - 50 135 85 220 At x 14375.747 13.972 7703.703 0.064 B- 3763.670 14.090 220 015433.000 15.000 - 48 134 86 220 Rn 10612.077 1.815 7717.254 0.008 B- -870.242 4.026 220 011392.534 1.948 - 46 133 87 220 Fr -a 11482.320 4.028 7709.742 0.018 B- 1212.075 9.061 220 012326.778 4.324 - 44 132 88 220 Ra -a 10270.245 8.237 7711.696 0.037 B- -3473.437 10.141 220 011025.562 8.843 - 42 131 89 220 Ac -a 13743.682 6.129 7692.351 0.028 B- -925.417 22.941 220 014754.450 6.579 - 40 130 90 220 Th -a 14669.100 22.166 7684.589 0.101 B- -5549# 56# 220 015747.926 23.795 - 38 129 91 220 Pa -a 20218# 51# 7656# 0# B- -2715# 113# 220 021705# 55# - 36 128 92 220 U -a 22933# 101# 7640# 0# B- -7378# 220# 220 024620# 108# - 34 127 93 220 Np x 30311# 196# 7603# 1# B- * 220 032540# 210# -0 55 138 83 221 Bi x 24098# 298# 7668# 1# B- 4324# 299# 221 025870# 320# - 53 137 84 221 Po x 19773.755 19.561 7684.481 0.089 B- 2991.027 24.039 221 021228.000 21.000 - 51 136 85 221 At x 16782.727 13.972 7694.475 0.063 B- 2311.308 15.096 221 018017.000 15.000 - 49 135 86 221 Rn +a 14471.420 5.714 7701.393 0.026 B- 1194.130 7.231 221 015535.709 6.134 - 47 134 87 221 Fr 13277.290 4.886 7703.256 0.022 B- 313.479 6.386 221 014253.757 5.245 - 45 133 88 221 Ra -a 12963.811 4.630 7701.135 0.021 B- -1559.298 50.603 221 013917.224 4.970 - 43 132 89 221 Ac -a 14523.109 50.425 7690.539 0.228 B- -2417.261 51.056 221 015591.199 54.133 - 41 131 90 221 Th -a 16940.371 8.166 7676.061 0.037 B- -3435.918 51.915 221 018186.236 8.766 - 39 130 91 221 Pa -a 20376.288 51.281 7656.974 0.232 B- -4143.707 72.404 221 021874.846 55.052 - 37 129 92 221 U -a 24519.995 51.114 7634.684 0.231 B- -5330# 207# 221 026323.299 54.873 - 35 128 93 221 Np x 29850# 200# 7607# 1# B- * 221 032045# 215# -0 56 139 83 222 Bi x 28729# 300# 7649# 1# B- 6243# 303# 222 030842# 322# - 54 138 84 222 Po x 22486.265 40.054 7674.005 0.180 B- 1533.239 43.071 222 024140.000 43.000 - 52 137 85 222 At x 20953.026 15.835 7677.387 0.071 B- 4580.820 15.955 222 022494.000 17.000 - 50 136 86 222 Rn 16372.206 1.950 7694.497 0.009 B- -5.900 7.703 222 017576.286 2.093 - 48 135 87 222 Fr x 16378.105 7.452 7690.947 0.034 B- 2057.917 8.682 222 017582.620 8.000 - 46 134 88 222 Ra 14320.188 4.454 7696.692 0.020 B- -2301.285 6.637 222 015373.355 4.781 - 44 133 89 222 Ac -a 16621.474 5.174 7682.802 0.023 B- -581.637 13.228 222 017843.887 5.554 - 42 132 90 222 Th -a 17203.111 12.279 7676.658 0.055 B- -4951# 74# 222 018468.300 13.182 - 40 131 91 222 Pa -a 22155# 72# 7651# 0# B- -2118# 89# 222 023784# 78# - 38 130 92 222 U -a 24272.827 51.994 7637.764 0.234 B- -6746# 202# 222 026057.953 55.817 - 36 129 93 222 Np x 31019# 196# 7604# 1# B- * 222 033300# 210# -0 57 140 83 223 Bi x 32137# 401# 7636# 2# B- 5058# 446# 223 034500# 430# - 55 139 84 223 Po x 27079# 196# 7655# 1# B- 3651# 196# 223 029070# 210# - 53 138 85 223 At x 23428.006 13.972 7668.055 0.063 B- 3038.267 16.013 223 025151.000 15.000 - 51 137 86 223 Rn 20389.739 7.822 7678.171 0.035 B- 2007.344 8.057 223 021889.285 8.397 - 49 136 87 223 Fr 18382.394 1.932 7683.664 0.009 B- 1149.085 0.848 223 019734.313 2.073 - 47 135 88 223 Ra 17233.309 2.090 7685.309 0.009 B- -592.573 7.128 223 018500.719 2.244 - 45 134 89 223 Ac -a 17825.882 7.110 7679.143 0.032 B- -1559.948 11.563 223 019136.872 7.632 - 43 133 90 223 Th -a 19385.831 9.212 7668.640 0.041 B- -2934.845 71.639 223 020811.546 9.889 - 41 132 91 223 Pa -a 22320.676 71.063 7651.971 0.319 B- -3516.330 100.506 223 023962.232 76.289 - 39 131 92 223 U -a 25837.006 71.119 7632.694 0.319 B- -4763# 208# 223 027737.168 76.349 - 37 130 93 223 Np x 30600# 196# 7608# 1# B- * 223 032850# 210# -0 58 141 83 224 Bi x 36830# 400# 7617# 2# B- 6920# 445# 224 039539# 429# - 56 140 84 224 Po x 29910# 196# 7644# 1# B- 2199# 197# 224 032110# 210# - 54 139 85 224 At x 27711.015 22.356 7650.735 0.100 B- 5265.917 24.415 224 029749.000 24.000 - 52 138 86 224 Rn 22445.098 9.814 7670.751 0.044 B- 696.482 14.875 224 024095.804 10.536 - 50 137 87 224 Fr x 21748.616 11.178 7670.367 0.050 B- 2922.699 11.324 224 023348.100 12.000 - 48 136 88 224 Ra 18825.917 1.813 7679.922 0.008 B- -1408.219 4.087 224 020210.453 1.945 - 46 135 89 224 Ac -a 20234.135 4.089 7670.143 0.018 B- 240.401 10.823 224 021722.239 4.389 - 44 134 90 224 Th -a 19993.734 10.120 7667.724 0.045 B- -3868.544 12.546 224 021464.157 10.864 - 42 133 91 224 Pa -a 23862.278 7.587 7646.961 0.034 B- -1859.974 24.329 224 025617.210 8.145 - 40 132 92 224 U -a 25722.252 23.171 7635.165 0.103 B- -6153# 197# 224 027613.974 24.875 - 38 131 93 224 Np x 31876# 196# 7604# 1# B- * 224 034220# 210# -0 57 141 84 225 Po x 34530# 298# 7626# 1# B- 4136# 422# 225 037070# 320# - 55 140 85 225 At x 30395# 298# 7641# 1# B- 3861# 298# 225 032630# 320# - 53 139 86 225 Rn 26534.141 11.140 7654.357 0.050 B- 2713.531 16.349 225 028485.574 11.958 - 51 138 87 225 Fr 23820.610 11.967 7662.940 0.053 B- 1827.501 12.158 225 025572.478 12.847 - 49 137 88 225 Ra 21993.109 2.596 7667.586 0.012 B- 355.763 5.007 225 023610.574 2.787 - 47 136 89 225 Ac 21637.346 4.758 7665.690 0.021 B- -672.781 6.658 225 023228.647 5.107 - 45 135 90 225 Th -a 22310.127 5.093 7659.222 0.023 B- -2030.598 71.170 225 023950.907 5.467 - 43 134 91 225 Pa -a 24340.725 71.012 7646.720 0.316 B- -3039.196 71.827 225 026130.844 76.234 - 41 133 92 225 U -a 27379.921 10.909 7629.736 0.048 B- -4207.783 72.440 225 029393.555 11.711 - 39 132 93 225 Np -a 31587.704 71.622 7607.557 0.318 B- * 225 033910.797 76.889 -0 58 142 84 226 Po x 37549# 401# 7614# 2# B- 2934# 499# 226 040310# 430# - 56 141 85 226 At x 34614# 298# 7624# 1# B- 5867# 298# 226 037160# 320# - 54 140 86 226 Rn 28747.192 10.477 7646.410 0.046 B- 1226.653 12.190 226 030861.382 11.247 - 52 139 87 226 Fr 27520.539 6.230 7648.376 0.028 B- 3852.715 6.523 226 029544.515 6.688 - 50 138 88 226 Ra 23667.824 1.933 7661.962 0.009 B- -641.440 3.274 226 025408.455 2.075 - 48 137 89 226 Ac 24309.264 3.100 7655.662 0.014 B- 1111.630 4.563 226 026097.069 3.328 - 46 136 90 226 Th 23197.634 4.481 7657.119 0.020 B- -2835.642 12.165 226 024903.686 4.810 - 44 135 91 226 Pa -a 26033.276 11.420 7641.110 0.051 B- -1295.593 17.228 226 027947.872 12.259 - 42 134 92 226 U -a 27328.869 12.999 7631.916 0.058 B- -5448# 89# 226 029338.749 13.955 - 40 133 93 226 Np -a 32777# 88# 7604# 0# B- * 226 035188# 95# -0 59 143 84 227 Po x 42281# 401# 7596# 2# B- 4797# 499# 227 045390# 430# - 57 142 85 227 At x 37483# 298# 7613# 1# B- 4597# 298# 227 040240# 320# - 55 141 86 227 Rn 32885.834 14.091 7630.050 0.062 B- 3203.388 15.276 227 035304.396 15.127 - 53 140 87 227 Fr 29682.445 5.898 7640.715 0.026 B- 2504.734 6.213 227 031865.417 6.332 - 51 139 88 227 Ra -n 27177.711 1.952 7648.303 0.009 B- 1328.132 2.265 227 029176.474 2.095 - 49 138 89 227 Ac 25849.580 1.927 7650.707 0.008 B- 44.757 0.830 227 027750.666 2.068 - 47 137 90 227 Th 25804.823 2.088 7647.458 0.009 B- -1026.375 7.437 227 027702.618 2.241 - 45 136 91 227 Pa -a 26831.198 7.420 7639.490 0.033 B- -2214.264 12.146 227 028804.477 7.965 - 43 135 92 227 U -a 29045.462 9.705 7626.289 0.043 B- -3516.618 73.135 227 031181.587 10.419 - 41 134 93 227 Np -a 32562.080 72.506 7607.351 0.319 B- -4208# 123# 227 034956.832 77.838 - 39 133 94 227 Pu x 36770# 100# 7585# 0# B- * 227 039474# 107# -0 58 143 85 228 At x 41684# 401# 7597# 2# B- 6441# 401# 228 044750# 430# - 56 142 86 228 Rn 35243.465 17.677 7621.645 0.078 B- 1859.244 18.916 228 037835.418 18.977 - 54 141 87 228 Fr 33384.221 6.732 7626.368 0.030 B- 4443.953 7.021 228 035839.437 7.226 - 52 140 88 228 Ra +a 28940.268 1.996 7642.428 0.009 B- 45.540 0.634 228 031068.657 2.142 - 50 139 89 228 Ac - 28894.728 2.094 7639.196 0.009 B- 2123.743 2.645 228 031019.767 2.247 - 48 138 90 228 Th 26770.984 1.807 7645.080 0.008 B- -2152.602 4.340 228 028739.835 1.940 - 46 137 91 228 Pa -a 28923.586 4.340 7632.207 0.019 B- -298.640 14.929 228 031050.748 4.659 - 44 136 92 228 U -a 29222.226 14.354 7627.466 0.063 B- -4373.468 52.545 228 031371.351 15.409 - 42 135 93 228 Np -a 33595.694 50.572 7604.853 0.222 B- -2491.677 58.346 228 036066.462 54.291 - 40 134 94 228 Pu -a 36087.370 29.143 7590.493 0.128 B- * 228 038741.387 31.286 -0 59 144 85 229 At x 44823# 401# 7585# 2# B- 5461# 401# 229 048120# 430# - 57 143 86 229 Rn x 39362.400 13.041 7605.622 0.057 B- 3694.138 13.967 229 042257.276 14.000 - 55 142 87 229 Fr 35668.262 5.001 7618.337 0.022 B- 3106.298 16.231 229 038291.455 5.368 - 53 141 88 229 Ra x 32561.963 15.441 7628.485 0.067 B- 1872.030 19.623 229 034956.707 16.576 - 51 140 89 229 Ac x 30689.933 12.109 7633.244 0.053 B- 1104.350 12.346 229 032947.000 13.000 - 49 139 90 229 Th 29585.583 2.405 7634.650 0.011 B- -311.325 3.715 229 031761.431 2.581 - 47 138 91 229 Pa 29896.908 3.280 7629.874 0.014 B- -1313.646 6.655 229 032095.652 3.521 - 45 137 92 229 U -a 31210.554 5.938 7620.721 0.026 B- -2569.122 87.031 229 033505.909 6.374 - 43 136 93 229 Np -a 33779.675 86.848 7606.086 0.379 B- -3615.915 100.792 229 036263.974 93.235 - 41 135 94 229 Pu -a 37395.590 51.176 7586.880 0.223 B- -4754.430 101.230 229 040145.819 54.939 - 39 134 95 229 Am -a 42150.020 87.348 7562.702 0.381 B- * 229 045249.909 93.772 -0 58 144 86 230 Rn x 42048# 196# 7596# 1# B- 2561# 196# 230 045140# 210# - 56 143 87 230 Fr 39486.768 6.541 7603.704 0.028 B- 4970.462 12.198 230 042390.791 7.022 - 54 142 88 230 Ra x 34516.306 10.296 7621.914 0.045 B- 677.924 18.888 230 037054.780 11.053 - 52 141 89 230 Ac x 33838.383 15.835 7621.460 0.069 B- 2975.789 15.882 230 036327.000 17.000 - 50 140 90 230 Th 30862.593 1.210 7630.996 0.005 B- -1311.014 2.833 230 033132.358 1.299 - 48 139 91 230 Pa 32173.607 3.038 7621.895 0.013 B- 558.605 4.592 230 034539.789 3.261 - 46 138 92 230 U -a 31615.002 4.509 7620.922 0.020 B- -3621.290 51.461 230 033940.102 4.841 - 44 137 93 230 Np -a 35236.291 51.288 7601.776 0.223 B- -1698.101 53.363 230 037827.716 55.059 - 42 136 94 230 Pu -a 36934.392 14.824 7590.991 0.064 B- -5998# 134# 230 039650.703 15.913 - 40 135 95 230 Am -a 42932# 133# 7562# 1# B- * 230 046089# 143# -0 59 145 86 231 Rn x 46454# 298# 7579# 1# B- 4373# 298# 231 049870# 320# - 57 144 87 231 Fr x 42080.575 7.731 7594.500 0.033 B- 3864.089 13.749 231 045175.357 8.300 - 55 143 88 231 Ra 38216.486 11.370 7607.841 0.049 B- 2453.636 17.301 231 041027.086 12.206 - 53 142 89 231 Ac x 35762.849 13.041 7615.076 0.056 B- 1946.959 13.098 231 038393.000 14.000 - 51 141 90 231 Th 33815.891 1.218 7620.118 0.005 B- 391.487 1.460 231 036302.853 1.308 - 49 140 91 231 Pa 33424.404 1.772 7618.426 0.008 B- -381.611 2.033 231 035882.575 1.902 - 47 139 92 231 U -a 33806.015 2.670 7613.387 0.012 B- -1818.498 50.577 231 036292.252 2.866 - 45 138 93 231 Np -a 35624.513 50.547 7602.128 0.219 B- -2684.492 55.333 231 038244.490 54.264 - 43 137 94 231 Pu -a 38309.005 22.549 7587.120 0.098 B- -4101# 301# 231 041126.410 24.206 - 41 136 95 231 Am x 42410# 300# 7566# 1# B- -4860# 424# 231 045529# 322# - 39 135 96 231 Cm x 47270# 300# 7542# 1# B- * 231 050746# 322# -0 58 145 87 232 Fr x 46072.834 13.972 7579.347 0.060 B- 5575.880 16.702 232 049461.224 15.000 - 56 144 88 232 Ra 40496.953 9.151 7600.009 0.039 B- 1342.534 15.931 232 043475.270 9.823 - 54 143 89 232 Ac x 39154.419 13.041 7602.424 0.056 B- 3707.635 13.118 232 042034.000 14.000 - 52 142 90 232 Th 35446.784 1.422 7615.033 0.006 B- -499.850 7.734 232 038053.689 1.526 - 50 141 91 232 Pa + 35946.633 7.645 7609.506 0.033 B- 1337.103 7.428 232 038590.300 8.207 - 48 140 92 232 U 34609.530 1.809 7611.897 0.008 B- -2750# 100# 232 037154.860 1.942 - 46 139 93 232 Np - 37360# 100# 7597# 0# B- -1004# 102# 232 040107# 107# - 44 138 94 232 Pu -a 38363.140 17.595 7588.974 0.076 B- -4976# 300# 232 041184.526 18.888 - 42 137 95 232 Am x 43340# 300# 7564# 1# B- -2973# 362# 232 046527# 322# - 40 136 96 232 Cm -a 46312# 202# 7548# 1# B- * 232 049718# 217# -0 59 146 87 233 Fr x 48920.051 19.561 7569.239 0.084 B- 4585.991 21.369 233 052517.838 21.000 - 57 145 88 233 Ra 44334.060 8.603 7585.564 0.037 B- 3026.027 15.623 233 047594.573 9.235 - 55 144 89 233 Ac x 41308.033 13.041 7595.193 0.056 B- 2576.318 13.118 233 044346.000 14.000 - 53 143 90 233 Th 38731.715 1.425 7602.893 0.006 B- 1242.243 1.122 233 041580.208 1.529 - 51 142 91 233 Pa 37489.472 1.336 7604.866 0.006 B- 570.296 1.975 233 040246.605 1.434 - 49 141 92 233 U 36919.176 2.255 7603.956 0.010 B- -1029.415 51.005 233 039634.367 2.420 - 47 140 93 233 Np -a 37948.590 50.981 7596.181 0.219 B- -2103.179 71.642 233 040739.489 54.729 - 45 139 94 233 Pu -a 40051.769 50.351 7583.796 0.216 B- -3211# 113# 233 042997.345 54.054 - 43 138 95 233 Am -a 43263# 102# 7567# 0# B- -4031# 124# 233 046445# 109# - 41 137 96 233 Cm -a 47294.006 71.547 7545.998 0.307 B- -5567# 235# 233 050772.206 76.809 - 39 136 97 233 Bk -a 52861# 224# 7519# 1# B- * 233 056748# 240# -0 58 146 88 234 Ra x 46930.629 8.383 7576.543 0.036 B- 2089.439 16.294 234 050382.104 9.000 - 56 145 89 234 Ac x 44841.190 13.972 7582.129 0.060 B- 4228.181 14.210 234 048139.000 15.000 - 54 144 90 234 Th +a 40613.009 2.589 7596.855 0.011 B- 274.088 3.172 234 043599.860 2.779 - 52 143 91 234 Pa IT 40338.921 4.094 7594.683 0.017 B- 2193.896 4.000 234 043305.615 4.395 - 50 142 92 234 U 38145.025 1.130 7600.715 0.005 B- -1809.846 8.321 234 040950.370 1.213 - 48 141 93 234 Np - 39954.871 8.397 7589.637 0.036 B- -395.100 10.752 234 042893.320 9.014 - 46 140 94 234 Pu -a 40349.971 6.798 7584.605 0.029 B- -4111# 159# 234 043317.478 7.298 - 44 139 95 234 Am -a 44461# 159# 7564# 1# B- -2263# 159# 234 047731# 170# - 42 138 96 234 Cm -a 46724.633 17.394 7550.677 0.074 B- -6731# 143# 234 050160.959 18.673 - 40 137 97 234 Bk -a 53455# 142# 7519# 1# B- * 234 057387# 153# -0 59 147 88 235 Ra x 51130# 300# 7561# 1# B- 3773# 300# 235 054890# 322# - 57 146 89 235 Ac x 47357.155 13.972 7573.504 0.059 B- 3339.406 19.113 235 050840.000 15.000 - 55 145 90 235 Th x 44017.749 13.041 7584.385 0.055 B- 1728.853 19.113 235 047255.000 14.000 - 53 144 91 235 Pa x 42288.896 13.972 7588.413 0.059 B- 1370.050 14.017 235 045399.000 15.000 - 51 143 92 235 U 40918.846 1.117 7590.914 0.005 B- -124.262 0.852 235 043928.190 1.199 - 49 142 93 235 Np 41043.108 1.389 7587.056 0.006 B- -1139.302 20.499 235 044061.591 1.491 - 47 141 94 235 Pu -a 42182.410 20.521 7578.879 0.087 B- -2443.019 56.045 235 045284.682 22.030 - 45 140 95 235 Am -a 44625.429 52.192 7565.154 0.222 B- -3408# 208# 235 047907.371 56.030 - 43 139 96 235 Cm -a 48034# 201# 7547# 1# B- -4670# 448# 235 051567# 216# - 41 138 97 235 Bk x 52704# 401# 7524# 2# B- * 235 056580# 430# -0 58 147 89 236 Ac x 51220.992 38.191 7559.242 0.162 B- 4965.795 40.667 236 054988.000 41.000 - 56 146 90 236 Th x 46255.198 13.972 7576.968 0.059 B- 921.248 19.760 236 049657.000 15.000 - 54 145 91 236 Pa x 45333.950 13.972 7577.557 0.059 B- 2889.306 14.017 236 048668.000 15.000 - 52 144 92 236 U 42444.644 1.113 7586.484 0.005 B- -933.534 50.415 236 045566.201 1.194 - 50 143 93 236 Np IT 43378.178 50.421 7579.214 0.214 B- 476.585 50.389 236 046568.392 54.129 - 48 142 94 236 Pu 42901.593 1.811 7577.918 0.008 B- -3139# 112# 236 046056.756 1.944 - 46 141 95 236 Am -a 46041# 112# 7561# 0# B- -1814# 113# 236 049427# 120# - 44 140 96 236 Cm -a 47855.045 18.315 7550.299 0.078 B- -5687# 401# 236 051374.506 19.662 - 42 139 97 236 Bk x 53542# 401# 7523# 2# B- * 236 057480# 430# -0 59 148 89 237 Ac x 54020# 400# 7550# 2# B- 4065# 400# 237 057993# 429# - 57 147 90 237 Th x 49955.092 15.835 7563.443 0.067 B- 2427.473 20.514 237 053629.000 17.000 - 55 146 91 237 Pa x 47527.619 13.041 7570.384 0.055 B- 2137.425 13.096 237 051023.000 14.000 - 53 145 92 237 U 45390.194 1.203 7576.102 0.005 B- 518.534 0.520 237 048728.380 1.291 - 51 144 93 237 Np 44871.659 1.120 7574.989 0.005 B- -220.063 1.294 237 048171.710 1.202 - 49 143 94 237 Pu 45091.722 1.697 7570.759 0.007 B- -1478# 59# 237 048407.957 1.822 - 47 142 95 237 Am -a 46570# 59# 7561# 0# B- -2677# 93# 237 049995# 64# - 45 141 96 237 Cm -a 49247.085 70.960 7546.624 0.299 B- -3941# 235# 237 052868.923 76.178 - 43 140 97 237 Bk -a 53188# 224# 7527# 1# B- -4751# 241# 237 057100# 241# - 41 139 98 237 Cf -a 57938.921 87.287 7503.347 0.368 B- * 237 062199.993 93.706 -0 58 148 90 238 Th +a 52525# 283# 7555# 1# B- 1631# 284# 238 056388# 304# - 56 147 91 238 Pa x 50894.038 15.835 7558.344 0.067 B- 3586.255 15.906 238 054637.000 17.000 - 54 146 92 238 U 47307.783 1.493 7570.125 0.006 B- -146.874 1.201 238 050786.996 1.602 - 52 145 93 238 Np -n 47454.656 1.138 7566.221 0.005 B- 1291.443 0.457 238 050944.671 1.221 - 50 144 94 238 Pu 46163.213 1.139 7568.360 0.005 B- -2258.273 50.688 238 049558.250 1.222 - 48 143 95 238 Am -a 48421.487 50.700 7555.584 0.213 B- -1023.701 52.145 238 051982.607 54.428 - 46 142 96 238 Cm -a 49445.188 12.234 7547.996 0.051 B- -4771# 255# 238 053081.595 13.133 - 44 141 97 238 Bk -a 54216# 255# 7525# 1# B- -3061# 392# 238 058203# 274# - 42 140 98 238 Cf x 57278# 298# 7509# 1# B- * 238 061490# 320# -0 59 149 90 239 Th x 56450# 400# 7541# 2# B- 3113# 445# 239 060602# 429# - 57 148 91 239 Pa x 53337# 196# 7550# 1# B- 2765# 196# 239 057260# 210# - 55 147 92 239 U -n 50572.718 1.503 7558.561 0.006 B- 1261.661 1.493 239 054292.048 1.613 - 53 146 93 239 Np 49311.057 1.311 7560.567 0.005 B- 722.774 0.930 239 052937.599 1.407 - 51 145 94 239 Pu 48588.282 1.113 7560.318 0.005 B- -802.142 1.664 239 052161.669 1.195 - 49 144 95 239 Am -a 49390.424 1.982 7553.688 0.008 B- -1756.602 54.058 239 053022.803 2.128 - 47 143 96 239 Cm -a 51147.025 54.047 7543.065 0.226 B- -3103# 214# 239 054908.593 58.022 - 45 142 97 239 Bk -a 54250# 207# 7527# 1# B- -4019# 294# 239 058240# 222# - 43 141 98 239 Cf -a 58269# 209# 7507# 1# B- -5287# 364# 239 062554# 224# - 41 140 99 239 Es x 63556# 298# 7481# 1# B- * 239 068230# 320# -0 58 149 91 240 Pa x 56910# 200# 7538# 1# B- 4194# 200# 240 061095# 215# - 56 148 92 240 U 52715.505 2.553 7551.770 0.011 B- 399.233 17.083 240 056592.425 2.740 - 54 147 93 240 Np 52316.272 17.032 7550.173 0.071 B- 2190.891 17.015 240 056163.830 18.284 - 52 146 94 240 Pu 50125.380 1.106 7556.042 0.005 B- -1384.789 13.788 240 053811.812 1.187 - 50 145 95 240 Am +n 51510.169 13.832 7547.013 0.058 B- -214.137 13.897 240 055298.444 14.849 - 48 144 96 240 Cm 51724.306 1.906 7542.861 0.008 B- -3940# 150# 240 055528.329 2.046 - 46 143 97 240 Bk - 55664# 150# 7523# 1# B- -2327# 151# 240 059758# 161# - 44 142 98 240 Cf -a 57990.944 18.700 7510.230 0.078 B- -6208# 401# 240 062255.842 20.075 - 42 141 99 240 Es x 64199# 401# 7481# 2# B- * 240 068920# 430# -0 59 150 91 241 Pa x 59640# 300# 7528# 1# B- 3443# 358# 241 064026# 322# - 57 149 92 241 U x 56197# 196# 7539# 1# B- 1937# 208# 241 060330# 210# - 55 148 93 241 Np + 54260.175 70.719 7544.270 0.293 B- 1305.000 70.711 241 058250.697 75.920 - 53 147 94 241 Pu 52955.175 1.106 7546.439 0.005 B- 20.780 0.166 241 056849.722 1.187 - 51 146 95 241 Am 52934.395 1.114 7543.278 0.005 B- -767.434 1.168 241 056827.413 1.195 - 49 145 96 241 Cm 53701.830 1.608 7536.848 0.007 B- -2330# 200# 241 057651.288 1.726 - 47 144 97 241 Bk - 56032# 200# 7524# 1# B- -3295# 260# 241 060153# 215# - 45 143 98 241 Cf -a 59327# 166# 7507# 1# B- -4537# 280# 241 063690# 178# - 43 142 99 241 Es -a 63863# 225# 7485# 1# B- -5263# 374# 241 068560# 242# - 41 141 100 241 Fm x 69126# 298# 7460# 1# B- * 241 074210# 320# -0 58 150 92 242 U +a 58620# 201# 7532# 1# B- 1203# 283# 242 062931# 215# - 56 149 93 242 Np + 57416.932 200.004 7533.403 0.826 B- 2700.000 200.000 242 061639.615 214.713 - 54 148 94 242 Pu 54716.932 1.245 7541.327 0.005 B- -751.140 0.708 242 058741.045 1.336 - 52 147 95 242 Am -n 55468.072 1.119 7534.991 0.005 B- 664.309 0.414 242 059547.428 1.200 - 50 146 96 242 Cm 54803.764 1.142 7534.503 0.005 B- -2930# 200# 242 058834.263 1.225 - 48 145 97 242 Bk - 57734# 200# 7519# 1# B- -1653# 200# 242 061980# 215# - 46 144 98 242 Cf -a 59386.966 12.892 7509.098 0.053 B- -5414# 256# 242 063754.533 13.840 - 44 143 99 242 Es -a 64801# 256# 7483# 1# B- -3598# 475# 242 069567# 275# - 42 142 100 242 Fm x 68400# 401# 7465# 2# B- * 242 073430# 430# -0 59 151 92 243 U x 62360# 300# 7518# 1# B- 2484# 302# 243 066946# 322# - 57 150 93 243 Np IT 59876# 32# 7525# 0# B- 2121# 32# 243 064279# 34# - 55 149 94 243 Pu 57754.602 2.542 7531.008 0.010 B- 579.556 2.622 243 062002.119 2.728 - 53 148 95 243 Am 57175.046 1.388 7530.173 0.006 B- -6.952 1.569 243 061379.940 1.490 - 51 147 96 243 Cm -a 57181.998 1.496 7526.925 0.006 B- -1507.695 4.506 243 061387.403 1.606 - 49 146 97 243 Bk -a 58689.693 4.524 7517.501 0.019 B- -2300# 114# 243 063005.980 4.857 - 47 145 98 243 Cf -a 60990# 114# 7505# 0# B- -3757# 236# 243 065475# 123# - 45 144 99 243 Es -a 64747# 207# 7486# 1# B- -4640# 298# 243 069509# 222# - 43 143 100 243 Fm -a 69387# 215# 7464# 1# B- * 243 074490# 231# -0 58 151 93 244 Np x 63202# 298# 7514# 1# B- 3396# 298# 244 067850# 320# - 56 150 94 244 Pu 59806.028 2.346 7524.815 0.010 B- -73.168 2.686 244 064204.415 2.518 - 54 149 95 244 Am + 59879.196 1.492 7521.308 0.006 B- 1427.300 1.000 244 064282.964 1.601 - 52 148 96 244 Cm -a 58451.896 1.107 7523.952 0.005 B- -2261.989 14.357 244 062750.694 1.188 - 50 147 97 244 Bk -a 60713.885 14.399 7511.475 0.059 B- -764.294 14.572 244 065179.039 15.457 - 48 146 98 244 Cf 61478.179 2.618 7505.136 0.011 B- -4547# 181# 244 065999.543 2.810 - 46 145 99 244 Es -a 66026# 181# 7483# 1# B- -2940# 271# 244 070881# 195# - 44 144 100 244 Fm -a 68966# 201# 7468# 1# B- * 244 074038# 216# -0 59 152 93 245 Np x 65890# 300# 7505# 1# B- 2712# 300# 245 070736# 322# - 57 151 94 245 Pu -n 63178.179 13.620 7513.281 0.056 B- 1277.710 13.733 245 067824.568 14.621 - 55 150 95 245 Am +a 61900.469 1.887 7515.303 0.008 B- 895.889 1.549 245 066452.890 2.025 - 53 149 96 245 Cm 61004.580 1.150 7515.767 0.005 B- -809.256 1.496 245 065491.113 1.234 - 51 148 97 245 Bk -a 61813.836 1.793 7509.270 0.007 B- -1571.374 2.586 245 066359.885 1.924 - 49 147 98 245 Cf 63385.210 2.428 7499.663 0.010 B- -2981# 200# 245 068046.825 2.606 - 47 146 99 245 Es -a 66366# 200# 7484# 1# B- -3821# 279# 245 071247# 215# - 45 145 100 245 Fm -a 70187# 195# 7466# 1# B- -5085# 362# 245 075349# 209# - 43 144 101 245 Md -a 75272# 305# 7442# 1# B- * 245 080808# 328# -0 58 152 94 246 Pu 65394.801 14.985 7506.539 0.061 B- 401# 14# 246 070204.209 16.087 - 56 151 95 246 Am IT 64994# 18# 7505# 0# B- 2377# 18# 246 069774# 19# - 54 150 96 246 Cm 62616.967 1.526 7511.471 0.006 B- -1350.000 60.000 246 067222.082 1.638 - 52 149 97 246 Bk - 63966.967 60.019 7502.803 0.244 B- -123.325 60.020 246 068671.367 64.433 - 50 148 98 246 Cf 64090.292 1.515 7499.121 0.006 B- -3810# 224# 246 068803.762 1.626 - 48 147 99 246 Es -a 67901# 224# 7480# 1# B- -2288# 224# 246 072894# 240# - 46 146 100 246 Fm -a 70188.833 15.333 7467.970 0.062 B- -5926# 260# 246 075350.815 16.460 - 44 145 101 246 Md -a 76115# 259# 7441# 1# B- * 246 081713# 278# -0 59 153 94 247 Pu x 69108# 196# 7494# 1# B- 1954# 220# 247 074190# 210# - 57 152 95 247 Am + 67153# 100# 7499# 0# B- 1620# 100# 247 072092# 107# - 55 151 96 247 Cm 65533.143 3.797 7501.931 0.015 B- 43.581 6.324 247 070352.726 4.076 - 53 150 97 247 Bk -a 65489.562 5.189 7498.940 0.021 B- -614.341 16.188 247 070305.940 5.570 - 51 149 98 247 Cf +a 66103.903 15.334 7493.285 0.062 B- -2474.485 24.760 247 070965.462 16.461 - 49 148 99 247 Es +a 68578.388 19.441 7480.100 0.079 B- -3094# 116# 247 073621.932 20.870 - 47 147 100 247 Fm +a 71673# 115# 7464# 0# B- -4264# 237# 247 076944# 123# - 45 146 101 247 Md -a 75937# 207# 7444# 1# B- * 247 081521# 222# -0 58 153 95 248 Am + 70563# 200# 7487# 1# B- 3170# 200# 248 075752# 215# - 56 152 96 248 Cm 67392.755 2.358 7496.728 0.010 B- -687# 71# 248 072349.101 2.531 - 54 151 97 248 Bk IT 68080# 71# 7491# 0# B- 842# 71# 248 073087# 76# - 52 150 98 248 Cf -a 67238.012 5.121 7491.043 0.021 B- -3061# 53# 248 072182.978 5.497 - 50 149 99 248 Es -a 70299# 52# 7476# 0# B- -1599# 53# 248 075469# 56# - 48 148 100 248 Fm 71897.857 8.497 7465.944 0.034 B- -5250# 238# 248 077185.528 9.122 - 46 147 101 248 Md -a 77148# 237# 7442# 1# B- -3473# 327# 248 082822# 255# - 44 146 102 248 No -a 80621# 224# 7424# 1# B- * 248 086550# 241# -0 59 154 95 249 Am x 73104# 298# 7479# 1# B- 2353# 298# 249 078480# 320# - 57 153 96 249 Cm -n 70750.702 2.371 7485.550 0.010 B- 904.317 2.594 249 075954.006 2.545 - 55 152 97 249 Bk + 69846.384 1.249 7486.040 0.005 B- 123.600 0.400 249 074983.182 1.340 - 53 151 98 249 Cf 69722.784 1.183 7483.394 0.005 B- -1452# 30# 249 074850.491 1.270 - 51 150 99 249 Es -a 71175# 30# 7474# 0# B- -2344# 31# 249 076409# 32# - 49 149 100 249 Fm 73519.188 6.212 7461.864 0.025 B- -3713# 201# 249 078926.098 6.668 - 47 148 101 249 Md -a 77232# 201# 7444# 1# B- -4550# 344# 249 082912# 216# - 45 147 102 249 No -a 81782# 279# 7422# 1# B- * 249 087797# 300# -0 58 154 96 250 Cm -nn 72989.594 10.274 7478.938 0.041 B- 39.616 10.894 250 078357.556 11.029 - 56 153 97 250 Bk +a 72949.978 3.719 7475.967 0.015 B- 1779.587 3.386 250 078315.027 3.992 - 54 152 98 250 Cf -a 71170.391 1.538 7479.956 0.006 B- -2055# 100# 250 076404.561 1.651 - 52 151 99 250 Es - 73225# 100# 7469# 0# B- -847# 100# 250 078611# 107# - 50 150 100 250 Fm 74072.243 7.888 7462.090 0.032 B- -4558# 301# 250 079519.828 8.468 - 48 149 101 250 Md -a 78630# 301# 7441# 1# B- -2933# 362# 250 084413# 323# - 46 148 102 250 No -a 81564# 201# 7426# 1# B- * 250 087562# 215# -0 59 155 96 251 Cm + 76648.018 22.698 7466.722 0.090 B- 1420.000 20.000 251 082285.036 24.367 - 57 154 97 251 Bk + 75228.018 10.734 7469.263 0.043 B- 1093.000 10.000 251 080760.603 11.523 - 55 153 98 251 Cf -a 74135.018 3.901 7470.500 0.016 B- -377.259 7.057 251 079587.219 4.187 - 53 152 99 251 Es -a 74512.277 5.994 7465.881 0.024 B- -1441.641 16.342 251 079992.224 6.434 - 51 151 100 251 Fm +a 75953.919 15.203 7457.020 0.061 B- -3012.825 24.271 251 081539.889 16.320 - 49 150 101 251 Md +a 78966.744 18.919 7441.900 0.075 B- -3882# 116# 251 084774.291 20.310 - 47 149 102 251 No IT 82849# 114# 7423# 0# B- -4879# 319# 251 088942# 123# - 45 148 103 251 Lr x 87728# 298# 7401# 1# B- * 251 094180# 320# -0 60 156 96 252 Cm x 79056# 298# 7460# 1# B- 521# 359# 252 084870# 320# - 58 155 97 252 Bk + 78535# 200# 7459# 1# B- 2500# 200# 252 084310# 215# - 56 154 98 252 Cf -a 76034.617 2.358 7465.347 0.009 B- -1260.000 50.000 252 081626.523 2.531 - 54 153 99 252 Es - 77294.617 50.056 7457.242 0.199 B- 478.990 50.351 252 082979.189 53.736 - 52 152 100 252 Fm -a 76815.627 5.498 7456.038 0.022 B- -3695# 130# 252 082464.972 5.902 - 50 151 101 252 Md IT 80510# 130# 7438# 1# B- -2361# 131# 252 086432# 140# - 48 150 102 252 No 82871.427 9.292 7425.798 0.037 B- -5866# 238# 252 088966.141 9.975 - 46 149 103 252 Lr -a 88737# 238# 7399# 1# B- * 252 095263# 255# -0 59 156 97 253 Bk -a 80929# 359# 7451# 1# B- 1627# 359# 253 086880# 385# - 57 155 98 253 Cf -a 79301.567 4.257 7454.829 0.017 B- 291.030 4.385 253 085133.738 4.570 - 55 154 99 253 Es -a 79010.538 1.250 7452.887 0.005 B- -335.202 2.713 253 084821.305 1.341 - 53 153 100 253 Fm -a 79345.740 2.932 7448.470 0.012 B- -1827# 31# 253 085181.160 3.148 - 51 152 101 253 Md -a 81173# 31# 7438# 0# B- -3186# 32# 253 087143# 34# - 49 151 102 253 No 84358.735 6.912 7422.471 0.027 B- -4217# 202# 253 090562.831 7.420 - 47 150 103 253 Lr -a 88575# 202# 7403# 1# B- -4982# 457# 253 095089# 217# - 45 149 104 253 Rf -a 93557# 410# 7380# 2# B- * 253 100438# 440# -0 60 157 97 254 Bk x 84393# 298# 7440# 1# B- 3052# 298# 254 090600# 320# - 58 156 98 254 Cf -a 81341.401 11.462 7449.225 0.045 B- -649.193 12.113 254 087323.590 12.304 - 56 155 99 254 Es -a 81990.594 4.010 7443.589 0.016 B- 1087.800 3.202 254 088020.527 4.304 - 54 154 100 254 Fm -a 80902.794 2.414 7444.792 0.010 B- -2550# 100# 254 086852.726 2.591 - 52 153 101 254 Md - 83453# 100# 7432# 0# B- -1271# 100# 254 089590# 107# - 50 152 102 254 No 84723.347 9.658 7423.590 0.038 B- -5148# 301# 254 090954.259 10.367 - 48 151 103 254 Lr -a 89871# 301# 7400# 1# B- -3327# 414# 254 096481# 323# - 46 150 104 254 Rf -a 93199# 283# 7384# 1# B- * 254 100053# 304# -0 59 157 98 255 Cf + 84809# 200# 7438# 1# B- 720# 200# 255 091047# 215# - 57 156 99 255 Es -a 84089.274 10.817 7437.821 0.042 B- 289.620 10.247 255 090273.553 11.612 - 55 155 100 255 Fm -a 83799.654 4.291 7435.888 0.017 B- -1043.416 7.747 255 089962.633 4.607 - 53 154 101 255 Md -a 84843.070 6.553 7428.729 0.026 B- -1964.164 16.281 255 091082.787 7.035 - 51 153 102 255 No x 86807.234 14.904 7417.958 0.058 B- -3140.066 23.138 255 093191.404 16.000 - 49 152 103 255 Lr x 89947.300 17.698 7402.576 0.069 B- -4382# 116# 255 096562.404 19.000 - 47 151 104 255 Rf -a 94330# 115# 7382# 0# B- -5263# 377# 255 101267# 123# - 45 150 105 255 Db -a 99593# 359# 7359# 1# B- * 255 106918# 385# -0 60 158 98 256 Cf -a 87041# 314# 7432# 1# B- -146# 330# 256 093442# 338# - 58 157 99 256 Es + 87187# 100# 7428# 0# B- 1700# 100# 256 093599# 108# - 56 156 100 256 Fm -a 85486.817 5.600 7431.780 0.022 B- -1969# 123# 256 091773.878 6.012 - 54 155 101 256 Md IT 87456# 122# 7421# 0# B- -366# 123# 256 093888# 132# - 52 154 102 256 No -a 87822.062 7.743 7416.546 0.030 B- -3924.536 83.264 256 094280.866 8.312 - 50 153 103 256 Lr x 91746.598 82.903 7398.160 0.324 B- -2475.451 84.802 256 098494.029 89.000 - 48 152 104 256 Rf -a 94222.049 17.848 7385.434 0.070 B- -6276# 241# 256 101151.535 19.160 - 46 151 105 256 Db -a 100498# 240# 7358# 1# B- * 256 107889# 258# -0 59 158 99 257 Es -a 89403# 411# 7422# 2# B- 813# 411# 257 095979# 441# - 57 157 100 257 Fm -a 88590.033 4.486 7422.194 0.017 B- -403.020 4.715 257 095105.317 4.815 - 55 156 101 257 Md -a 88993.053 1.601 7417.582 0.006 B- -1254.202 6.661 257 095537.977 1.718 - 53 155 102 257 No -a 90247.256 6.678 7409.657 0.026 B- -2418# 45# 257 096884.419 7.169 - 51 154 103 257 Lr -a 92665# 44# 7397# 0# B- -3201# 45# 257 099480# 47# - 49 153 104 257 Rf -a 95866.427 10.817 7381.704 0.042 B- -4340# 203# 257 102916.848 11.612 - 47 152 105 257 Db -a 100207# 203# 7362# 1# B- * 257 107576# 218# -0 60 159 99 258 Es x 92702# 401# 7412# 2# B- 2276# 448# 258 099520# 430# - 58 158 100 258 Fm -a 90426# 200# 7418# 1# B- -1260# 200# 258 097077# 215# - 56 157 101 258 Md -a 91686.792 4.419 7409.675 0.017 B- 209# 100# 258 098429.825 4.743 - 54 156 102 258 No -a 91478# 100# 7407# 0# B- -3304# 143# 258 098205# 107# - 52 155 103 258 Lr -a 94782# 102# 7392# 0# B- -1559# 107# 258 101753# 109# - 50 154 104 258 Rf -a 96341.036 31.967 7382.538 0.124 B- -5456# 307# 258 103426.362 34.317 - 48 153 105 258 Db -a 101797# 305# 7358# 1# B- -3447# 513# 258 109284# 328# - 46 152 106 258 Sg -a 105244# 413# 7342# 2# B- * 258 112984# 443# -0 59 159 100 259 Fm -a 93704# 283# 7407# 1# B- 80# 346# 259 100596# 304# - 57 158 101 259 Md -a 93624# 200# 7405# 1# B- -454# 200# 259 100510# 215# - 55 157 102 259 No -a 94078.569 6.589 7399.974 0.025 B- -1773# 71# 259 100997.503 7.073 - 53 156 103 259 Lr -a 95852# 71# 7390# 0# B- -2510# 101# 259 102901# 76# - 51 155 104 259 Rf -a 98362# 72# 7377# 0# B- -3629# 90# 259 105596# 78# - 49 154 105 259 Db -a 101991.016 53.040 7360.362 0.205 B- -4529# 126# 259 109491.865 56.940 - 47 153 106 259 Sg -a 106520# 115# 7340# 0# B- * 259 114353# 123# -0 60 160 100 260 Fm -a 95766# 435# 7402# 2# B- -786# 537# 260 102809# 467# - 58 159 101 260 Md -a 96552# 316# 7396# 1# B- 940# 374# 260 103653# 340# - 56 158 102 260 No -a 95612# 200# 7397# 1# B- -2665# 235# 260 102643# 215# - 54 157 103 260 Lr -a 98277# 124# 7383# 0# B- -870# 236# 260 105504# 133# - 52 156 104 260 Rf -a 99147# 200# 7377# 1# B- -4526# 221# 260 106439# 215# - 50 155 105 260 Db -a 103673# 93# 7357# 0# B- -2875# 95# 260 111297# 100# - 48 154 106 260 Sg -a 106547.552 20.536 7342.562 0.079 B- -6776# 246# 260 114383.508 22.045 - 46 153 107 260 Bh -a 113323# 245# 7313# 1# B- * 260 121658# 263# -0 59 160 101 261 Md -a 98578# 509# 7391# 2# B- 123# 547# 261 105828# 546# - 57 159 102 261 No -a 98455# 200# 7388# 1# B- -1103# 283# 261 105696# 215# - 55 158 103 261 Lr -a 99558# 200# 7381# 1# B- -1761# 206# 261 106880# 215# - 53 157 104 261 Rf -a 101318.594 50.444 7371.384 0.193 B- -2990# 121# 261 108769.990 54.153 - 51 156 105 261 Db -a 104308# 110# 7357# 0# B- -3697# 112# 261 111980# 118# - 49 155 106 261 Sg -a 108005.043 18.494 7339.770 0.071 B- -5128# 210# 261 115948.188 19.853 - 47 154 107 261 Bh -a 113133# 209# 7317# 1# B- * 261 121454# 224# -0 60 161 101 262 Md -a 101627# 500# 7382# 2# B- 1526# 617# 262 109101# 537# - 58 160 102 262 No -a 100101# 361# 7385# 1# B- -2000# 412# 262 107463# 387# - 56 159 103 262 Lr -a 102102# 200# 7374# 1# B- -291# 300# 262 109611# 215# - 54 158 104 262 Rf -a 102393# 224# 7370# 1# B- -3861# 265# 262 109923# 240# - 52 157 105 262 Db -a 106253# 143# 7352# 1# B- -2112# 147# 262 114068# 154# - 50 156 106 262 Sg -a 108365.771 35.411 7341.185 0.135 B- -6176# 308# 262 116335.446 38.015 - 48 155 107 262 Bh -a 114541# 306# 7315# 1# B- * 262 122965# 328# -0 59 161 102 263 No -a 103129# 490# 7376# 2# B- -600# 566# 263 110714# 526# - 57 160 103 263 Lr -a 103729# 283# 7371# 1# B- -1027# 322# 263 111358# 304# - 55 159 104 263 Rf -a 104756# 153# 7364# 1# B- -2355# 227# 263 112460# 164# - 53 158 105 263 Db -a 107111# 168# 7352# 1# B- -3079# 193# 263 114988# 181# - 51 157 106 263 Sg -a 110190# 95# 7337# 0# B- -4306# 319# 263 118294# 102# - 49 156 107 263 Bh -a 114496# 305# 7318# 1# B- -5182# 329# 263 122916# 327# - 47 155 108 263 Hs -a 119678# 125# 7295# 0# B- * 263 128480# 134# -0 60 162 102 264 No -a 105011# 591# 7371# 2# B- -1366# 734# 264 112734# 634# - 58 161 103 264 Lr -a 106377# 436# 7363# 2# B- 300# 566# 264 114200# 468# - 56 160 104 264 Rf -a 106077# 361# 7361# 1# B- -3285# 431# 264 113878# 387# - 54 159 105 264 Db -a 109362# 235# 7346# 1# B- -1420# 368# 264 117405# 253# - 52 158 106 264 Sg -a 110782# 283# 7338# 1# B- -5276# 334# 264 118929# 304# - 50 157 107 264 Bh -a 116058# 177# 7315# 1# B- -3506# 180# 264 124593# 190# - 48 156 108 264 Hs -a 119563.222 28.881 7298.375 0.109 B- * 264 128356.405 31.005 -0 59 162 103 265 Lr -a 108233# 547# 7359# 2# B- -457# 655# 265 116193# 587# - 57 161 104 265 Rf -a 108690# 361# 7354# 1# B- -1793# 424# 265 116683# 387# - 55 160 105 265 Db -a 110483# 224# 7344# 1# B- -2312# 255# 265 118608# 240# - 53 159 106 265 Sg -a 112794# 123# 7333# 0# B- -3621# 264# 265 121090# 132# - 51 158 107 265 Bh -a 116415# 234# 7316# 1# B- -4485# 235# 265 124977# 251# - 49 157 108 265 Hs -a 120900.283 23.958 7296.247 0.090 B- -5778# 452# 265 129791.799 25.719 - 47 156 109 265 Mt -a 126678# 451# 7271# 2# B- * 265 135995# 484# -0 60 163 103 266 Lr -a 111622# 583# 7349# 2# B- 1546# 749# 266 119831# 626# - 58 162 104 266 Rf -a 110076# 469# 7352# 2# B- -2660# 548# 266 118172# 504# - 56 161 105 266 Db -a 112737# 283# 7339# 1# B- -881# 374# 266 121028# 304# - 54 160 106 266 Sg -a 113618# 245# 7332# 1# B- -4487# 294# 266 121973# 263# - 52 159 107 266 Bh -a 118104# 163# 7313# 1# B- -3032# 167# 266 126790# 175# - 50 158 108 266 Hs -a 121136.373 38.695 7298.273 0.145 B- -6826# 309# 266 130045.252 41.540 - 48 157 109 266 Mt -a 127962# 307# 7270# 1# B- * 266 137373# 329# -0 59 163 104 267 Rf -a 113444# 575# 7342# 2# B- -630# 707# 267 121787# 617# - 57 162 105 267 Db -a 114074# 412# 7336# 2# B- -1732# 486# 267 122464# 443# - 55 161 106 267 Sg -a 115806# 257# 7327# 1# B- -2960# 367# 267 124322# 276# - 53 160 107 267 Bh -a 118766# 263# 7313# 1# B- -3887# 279# 267 127500# 282# - 51 159 108 267 Hs -a 122653# 96# 7295# 0# B- -5138# 512# 267 131673# 103# - 49 158 109 267 Mt -a 127791# 503# 7273# 2# B- -6089# 521# 267 137189# 540# - 47 157 110 267 Ds -a 133880# 135# 7248# 1# B- * 267 143726# 145# -0 60 164 104 268 Rf -a 115476# 662# 7337# 2# B- -1586# 848# 268 123968# 711# - 58 163 105 268 Db -a 117062# 529# 7328# 2# B- 260# 707# 268 125671# 568# - 56 162 106 268 Sg -a 116802# 469# 7326# 2# B- -4005# 605# 268 125392# 504# - 54 161 107 268 Bh -a 120807# 381# 7308# 1# B- -2023# 475# 268 129691# 409# - 52 160 108 268 Hs -a 122830# 283# 7298# 1# B- -6321# 367# 268 131863# 304# - 50 159 109 268 Mt -a 129151# 233# 7271# 1# B- -4497# 381# 268 138649# 250# - 48 158 110 268 Ds -a 133648# 301# 7252# 1# B- * 268 143477# 324# -0 59 164 105 269 Db -a 119148# 624# 7323# 2# B- -614# 722# 269 127911# 669# - 57 163 106 269 Sg -a 119763# 364# 7318# 1# B- -1715# 522# 269 128570# 391# - 55 162 107 269 Bh -a 121478# 374# 7309# 1# B- -3086# 394# 269 130412# 402# - 53 161 108 269 Hs -a 124564# 124# 7294# 0# B- -4806# 480# 269 133725# 133# - 51 160 109 269 Mt -a 129370# 463# 7273# 2# B- -5465# 464# 269 138884# 497# - 49 159 110 269 Ds -a 134834.709 31.403 7250.154 0.117 B- * 269 144751.021 33.712 -0 60 165 105 270 Db -a 122307# 617# 7314# 2# B- 816# 831# 270 131302# 662# - 58 164 106 270 Sg -a 121491# 557# 7314# 2# B- -2735# 627# 270 130426# 598# - 56 163 107 270 Bh -a 124226# 287# 7301# 1# B- -886# 379# 270 133362# 308# - 54 162 108 270 Hs -a 125112# 248# 7295# 1# B- -5598# 301# 270 134314# 266# - 52 161 109 270 Mt -a 130710# 170# 7271# 1# B- -3968# 177# 270 140323# 183# - 50 160 110 270 Ds -a 134678.282 48.011 7253.775 0.178 B- * 270 144583.090 51.542 -0 59 165 106 271 Sg -a 124757# 585# 7305# 2# B- -1164# 718# 271 133932# 628# - 57 164 107 271 Bh -a 125921# 415# 7298# 2# B- -1819# 501# 271 135182# 446# - 55 163 108 271 Hs -a 127740# 280# 7288# 1# B- -3361# 433# 271 137135# 301# - 53 162 109 271 Mt -a 131101# 330# 7273# 1# B- -4847# 344# 271 140742# 354# - 51 161 110 271 Ds -a 135948# 97# 7252# 0# B- * 271 145946# 104# -0 60 166 106 272 Sg -a 126580# 727# 7301# 3# B- -2209# 901# 272 135890# 781# - 58 165 107 272 Bh -a 128789# 532# 7290# 2# B- -217# 737# 272 138261# 571# - 56 164 108 272 Hs -a 129006# 510# 7286# 2# B- -4575# 704# 272 138494# 547# - 54 163 109 272 Mt -a 133581# 485# 7267# 2# B- -2433# 637# 272 143406# 521# - 52 162 110 272 Ds -a 136015# 413# 7255# 2# B- -6758# 474# 272 146018# 443# - 50 161 111 272 Rg -a 142773# 233# 7227# 1# B- * 272 153273# 251# -0 61 167 106 273 Sg x 130018# 503# 7291# 2# B- -615# 855# 273 139580# 540# - 59 166 107 273 Bh -a 130633# 692# 7286# 3# B- -1257# 783# 273 140240# 743# - 57 165 108 273 Hs -a 131890# 367# 7279# 1# B- -2822# 561# 273 141590# 394# - 55 164 109 273 Mt -a 134713# 424# 7265# 2# B- -3643# 445# 273 144620# 455# - 53 163 110 273 Ds -a 138356# 134# 7249# 0# B- -4339# 543# 273 148531# 144# - 51 162 111 273 Rg -a 142695# 526# 7231# 2# B- * 273 153189# 565# -0 60 167 107 274 Bh -a 133682# 619# 7278# 2# B- 196# 856# 274 143513# 664# - 58 166 108 274 Hs -a 133486# 592# 7276# 2# B- -3760# 689# 274 143303# 635# - 56 165 109 274 Mt -a 137246# 354# 7259# 1# B- -1952# 526# 274 147339# 380# - 54 164 110 274 Ds -a 139197# 389# 7249# 1# B- -5416# 428# 274 149434# 418# - 52 163 111 274 Rg -a 144613# 177# 7227# 1# B- * 274 155249# 190# -0 61 168 107 275 Bh x 135691# 596# 7273# 2# B- -929# 837# 275 145670# 640# - 59 167 108 275 Hs -a 136620# 587# 7267# 2# B- -2209# 721# 275 146667# 631# - 57 166 109 275 Mt -a 138829# 418# 7256# 2# B- -2736# 586# 275 149039# 449# - 55 165 110 275 Ds -a 141565# 410# 7244# 1# B- -3731# 661# 275 151976# 441# - 53 164 111 275 Rg -a 145296# 519# 7227# 2# B- * 275 155981# 557# -0 60 168 108 276 Hs -a 138285# 754# 7264# 3# B- -3029# 923# 276 148455# 810# - 58 167 109 276 Mt -a 141315# 532# 7250# 2# B- -1227# 763# 276 151708# 571# - 56 166 110 276 Ds -a 142541# 548# 7243# 2# B- -4945# 834# 276 153024# 588# - 54 165 111 276 Rg -a 147486# 629# 7222# 2# B- -2866# 866# 276 158333# 675# - 52 164 112 276 Cn x 150352# 596# 7209# 2# B- * 276 161410# 640# -0 61 169 108 277 Hs -a 141493# 541# 7255# 2# B- -1475# 884# 277 151899# 581# - 59 168 109 277 Mt -a 142968# 699# 7247# 3# B- -2172# 798# 277 153483# 751# - 57 167 110 277 Ds -a 145140# 384# 7237# 1# B- -3197# 646# 277 155815# 412# - 55 166 111 277 Rg -a 148338# 520# 7222# 2# B- -4065# 539# 277 159247# 558# - 53 165 112 277 Cn -a 152403# 143# 7205# 1# B- * 277 163611# 153# -0 60 169 109 278 Mt -a 145736# 621# 7240# 2# B- -645# 881# 278 156454# 666# - 58 168 110 278 Ds -a 146381# 625# 7235# 2# B- -4136# 719# 278 157146# 671# - 56 167 111 278 Rg -a 150517# 357# 7218# 1# B- -2415# 565# 278 161587# 383# - 54 166 112 278 Cn -a 152932# 438# 7206# 2# B- -5957# 475# 278 164179# 470# - 52 165 113 278 Ed -a 158889# 184# 7182# 1# B- * 278 170574# 198# -0 61 170 109 279 Mt -a 147496# 667# 7237# 2# B- -1630# 896# 279 158343# 716# - 59 169 110 279 Ds -a 149126# 598# 7228# 2# B- -2649# 731# 279 160093# 642# - 57 168 111 279 Rg -a 151775# 421# 7216# 2# B- -3255# 621# 279 162937# 452# - 55 167 112 279 Cn -a 155030# 456# 7202# 2# B- -4209# 835# 279 166432# 490# - 53 166 113 279 Ed x 159239# 699# 7184# 3# B- * 279 170950# 750# -0 60 170 110 280 Ds -a 150520# 780# 7226# 3# B- -3366# 944# 280 161590# 838# - 58 169 111 280 Rg -a 153886# 532# 7212# 2# B- -1810# 789# 280 165203# 571# - 56 168 112 280 Cn -a 155696# 583# 7202# 2# B- -5444# 707# 280 167147# 626# - 54 167 113 280 Ed x 161140# 400# 7180# 1# B- * 280 172991# 429# -0 61 171 110 281 Ds -a 153431# 579# 7219# 2# B- -1866# 992# 281 164715# 622# - 59 170 111 281 Rg -a 155297# 806# 7210# 3# B- -2722# 894# 281 166718# 865# - 57 169 112 281 Cn -a 158019# 387# 7197# 1# B- -3790# 490# 281 169641# 416# - 55 168 113 281 Ed x 161810# 300# 7181# 1# B- * 281 173710# 322# -0 60 171 111 282 Rg -a 157800# 654# 7204# 2# B- -1176# 926# 282 169405# 702# - 58 170 112 282 Cn -a 158976# 656# 7197# 2# B- -4749# 748# 282 170668# 704# - 56 169 113 282 Ed -a 163725# 361# 7177# 1# B- * 282 175766# 387# -0 61 172 111 283 Rg -a 159281# 697# 7202# 2# B- -2205# 925# 283 170995# 748# - 59 171 112 283 Cn -a 161486# 608# 7191# 2# B- -3221# 748# 283 173362# 653# - 57 170 113 283 Ed -a 164707# 436# 7177# 2# B- * 283 176820# 468# -0 60 172 112 284 Cn -a 162545# 806# 7190# 3# B- -4046# 966# 284 174499# 865# - 58 171 113 284 Ed -a 166591# 534# 7173# 2# B- -2330# 846# 284 178843# 573# - 56 170 114 284 Fl -a 168921# 656# 7162# 2# B- * 284 181344# 704# -0 61 173 112 285 Cn -a 165173# 581# 7184# 2# B- -2557# 995# 285 177321# 624# - 59 172 113 285 Ed -a 167731# 807# 7173# 3# B- -3272# 897# 285 180066# 866# - 57 171 114 285 Fl -a 171003# 391# 7158# 1# B- * 285 183579# 419# -0 60 173 113 286 Ed -a 170014# 656# 7168# 2# B- -1758# 928# 286 182518# 704# - 58 172 114 286 Fl -a 171773# 657# 7159# 2# B- * 286 184406# 705# -0 61 174 113 287 Ed -a 171245# 725# 7167# 3# B- -2827# 948# 287 183840# 778# - 59 173 114 287 Fl -a 174073# 610# 7154# 2# B- -3823# 752# 287 186875# 655# - 57 172 115 287 Ef -a 177895# 439# 7138# 2# B- * 287 190978# 471# -0 60 174 114 288 Fl -a 175042# 806# 7154# 3# B- -4728# 968# 288 187916# 865# - 58 173 115 288 Ef -a 179771# 536# 7135# 2# B- * 288 192992# 576# -0 61 175 114 289 Fl -a 177564# 584# 7148# 2# B- -3102# 997# 289 190623# 626# - 59 174 115 289 Ef -a 180666# 809# 7135# 3# B- -3861# 947# 289 193953# 868# - 57 173 116 289 Lv -a 184528# 492# 7119# 2# B- * 289 198099# 529# -0 60 175 115 290 Ef -a 182894# 658# 7130# 2# B- -2304# 932# 290 196345# 706# - 58 174 116 290 Lv -a 185198# 660# 7120# 2# B- * 290 198818# 708# -0 61 176 115 291 Ef -a 183990# 784# 7130# 3# B- -3397# 995# 291 197522# 842# - 59 175 116 291 Lv -a 187387# 612# 7116# 2# B- -4413# 853# 291 201169# 658# - 57 174 117 291 Eh -a 191800# 594# 7098# 2# B- * 291 205906# 637# -0 60 176 116 292 Lv -a 188242# 806# 7116# 3# B- -5334# 1047# 292 202086# 865# - 58 175 117 292 Eh -a 193576# 669# 7095# 2# B- * 292 207812# 718# -0 61 177 116 293 Lv -a 190669# 586# 7111# 2# B- -3716# 1000# 293 204691# 629# - 59 176 117 293 Eh -a 194385# 810# 7095# 3# B- -4488# 1072# 293 208680# 870# - 57 175 118 293 Ei -a 198873# 702# 7077# 2# B- * 293 213498# 753# -0 60 177 117 294 Eh -a 196521# 660# 7092# 2# B- -2942# 936# 294 210974# 708# - 58 176 118 294 Ei -a 199463# 663# 7079# 2# B- * 294 214132# 712# -0 59 177 118 295 Ei -a 201512# 644# 7075# 2# B- * 295 216332# 692# diff --git a/openmc/data/mass_1.mas20.txt b/openmc/data/mass_1.mas20.txt new file mode 100644 index 0000000000..ce12b2c4a5 --- /dev/null +++ b/openmc/data/mass_1.mas20.txt @@ -0,0 +1,3594 @@ +1 a0dsskgw A T O M I C M A S S A D J U S T M E N T +0 DATE 3 Mar 2021 TIME 22:41 +0 ********************* A= 0 TO 295 + * file : mass.mas20 * + ********************* + + This is one file out of a series of 3 files published in: + "The Ame2020 atomic mass evaluation (I)" by W.J.Huang, M.Wang, F.G.Kondev, G.Audi and S.Naimi + Chinese Physics C45, 030002, March 2021. + "The Ame2020 atomic mass evaluation (II)" by M.Wang, W.J.Huang, F.G.Kondev, G.Audi and S.Naimi + Chinese Physics C45, 030003, March 2021. + for files : mass.mas20 : atomic masses + rct1.mas20 : react and sep energies, part 1 + rct2.mas20 : react and sep energies, part 2 + A fourth file is the "Rounded" version of the atomic mass table (the first file) + massround.mas20 atomic masses "Rounded" version + + Values in files 1, 2 and 3 are unrounded version of the published ones + Values in file 4 are exact copy of the published ones + + col 1 : Fortran character control: 1 = page feed 0 = line feed + format : a1,i3,i5,i5,i5,1x,a3,a4,1x,f14.6,f12.6,f13.5,1x,f10.5,1x,a2,f13.5,f11.5,1x,i3,1x,f13.6,f12.6 + cc NZ N Z A el o mass unc binding unc B beta unc atomic_mass unc + Warnings : this format is not identical to that used in AME2016; + one more digit is added to the "BINDING ENERGY/A", "BETA-DECAY ENERGY" and "ATOMIC-MASS" values and their uncertainties; + # in a place of decimal point : estimated (non-experimental) value; + * in a place of value : the not calculable quantity + +....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+...10....+...11....+...12....+...13 + + + MASS LIST + for analysis + +1N-Z N Z A EL O MASS EXCESS BINDING ENERGY/A BETA-DECAY ENERGY ATOMIC MASS + (keV) (keV) (keV) (micro-u) +0 1 1 0 1 n 8071.31806 0.00044 0.0 0.0 B- 782.3470 0.0004 1 008664.91590 0.00047 + -1 0 1 1 H 7288.971064 0.000013 0.0 0.0 B- * 1 007825.031898 0.000014 +0 0 1 1 2 H 13135.722895 0.000015 1112.2831 0.0002 B- * 2 014101.777844 0.000015 +0 1 2 1 3 H 14949.81090 0.00008 2827.2654 0.0003 B- 18.59202 0.00006 3 016049.28132 0.00008 + -1 1 2 3 He 14931.21888 0.00006 2572.68044 0.00015 B- -13736# 2000# 3 016029.32197 0.00006 + -3 0 3 3 Li -pp 28667# 2000# -2267# 667# B- * 3 030775# 2147# +0 2 3 1 4 H -n 24621.129 100.000 1720.4491 25.0000 B- 22196.2131 100.0000 4 026431.867 107.354 + 0 2 2 4 He 2424.91587 0.00015 7073.9156 0.0002 B- -22898.2740 212.1320 4 002603.25413 0.00016 + -2 1 3 4 Li -p 25323.190 212.132 1153.7603 53.0330 B- * 4 027185.561 227.733 +0 3 4 1 5 H -nn 32892.447 89.443 1336.3592 17.8885 B- 21661.2131 91.6515 5 035311.492 96.020 + 1 3 2 5 He -n 11231.234 20.000 5512.1325 4.0000 B- -447.6529 53.8516 5 012057.224 21.470 + -1 2 3 5 Li -p 11678.887 50.000 5266.1325 10.0000 B- -25460# 2003# 5 012537.800 53.677 + -3 1 4 5 Be x 37139# 2003# 18# 401# B- * 5 039870# 2150# +0 4 5 1 6 H -3n 41875.725 254.127 961.6395 42.3545 B- 24283.6294 254.1268 6 044955.437 272.816 + 2 4 2 6 He 17592.095 0.053 4878.5199 0.0089 B- 3505.2147 0.0532 6 018885.889 0.057 + 0 3 3 6 Li 14086.88044 0.00144 5332.3312 0.0003 B- -4288.1534 5.4478 6 015122.88742 0.00155 + -2 2 4 6 Be - 18375.034 5.448 4487.2478 0.9080 B- -28945# 2003# 6 019726.409 5.848 + -4 1 5 6 B x 47320# 2003# -467# 334# B- * 6 050800# 2150# +0 5 6 1 7 H -nn 49135# 1004# 940# 143# B- 23062# 1004# 7 052749# 1078# + 3 5 2 7 He -n 26073.128 7.559 4123.0578 1.0799 B- 11166.0229 7.5595 7 027990.652 8.115 + 1 4 3 7 Li 14907.10463 0.00419 5606.4401 0.0006 B- -861.8930 0.0707 7 016003.43426 0.00450 + -1 3 4 7 Be 15768.998 0.071 5371.5487 0.0101 B- -11907.5551 25.1504 7 016928.714 0.076 + -3 2 5 7 B p4n 27676.553 25.150 3558.7055 3.5929 B- * 7 029712.000 27.000 +0 4 6 2 8 He 31609.683 0.089 3924.5210 0.0111 B- 10663.8784 0.1005 8 033934.388 0.095 + 2 5 3 8 Li 20945.805 0.047 5159.7124 0.0059 B- 16004.1329 0.0591 8 022486.244 0.050 + 0 4 4 8 Be -a 4941.672 0.035 7062.4356 0.0044 B- -17979.8973 1.0005 8 005305.102 0.037 + -2 3 5 8 B 22921.569 1.000 4717.1551 0.1250 B- -12142.7002 18.2704 8 024607.315 1.073 + -4 2 6 8 C 35064.269 18.243 3101.5242 2.2804 B- * 8 037643.039 19.584 +0 5 7 2 9 He 40935.826 46.816 3349.0380 5.2018 B- 15980.9213 46.8169 9 043946.414 50.259 + 3 6 3 9 Li -3n 24954.905 0.186 5037.7685 0.0207 B- 13606.4541 0.2014 9 026790.191 0.200 + 1 5 4 9 Be 11348.451 0.076 6462.6693 0.0085 B- -1068.0349 0.8994 9 012183.062 0.082 + -1 4 5 9 B - 12416.486 0.903 6257.0713 0.1003 B- -16494.4854 2.3195 9 013329.645 0.969 + -3 3 6 9 C -pp 28910.971 2.137 4337.4233 0.2374 B- * 9 031037.202 2.293 +0 6 8 2 10 He -nn 49197.147 92.848 2995.1340 9.2848 B- 16144.5191 93.7152 10 052815.306 99.676 + 4 7 3 10 Li -n 33052.628 12.721 4531.3512 1.2721 B- 20445.1411 12.7216 10 035483.453 13.656 + 2 6 4 10 Be 12607.487 0.081 6497.6306 0.0081 B- 556.8759 0.0822 10 013534.692 0.086 + 0 5 5 10 B 12050.611 0.015 6475.0835 0.0015 B- -3648.0623 0.0687 10 012936.862 0.016 + -2 4 6 10 C 15698.673 0.070 6032.0426 0.0070 B- -23101.3545 400.0000 10 016853.217 0.075 + -4 3 7 10 N -- 38800.027 400.000 3643.6724 40.0000 B- * 10 041653.540 429.417 +0 5 8 3 11 Li x 40728.259 0.615 4155.3817 0.0559 B- 20551.0898 0.6591 11 043723.581 0.660 + 3 7 4 11 Be 20177.169 0.238 5952.5402 0.0216 B- 11509.4607 0.2380 11 021661.080 0.255 + 1 6 5 11 B 8667.708 0.012 6927.7323 0.0011 B- -1981.6889 0.0608 11 009305.166 0.013 + -1 5 6 11 C 10649.397 0.060 6676.4563 0.0054 B- -13716.2469 5.0008 11 011432.597 0.064 + -3 4 7 11 N -p 24365.644 5.000 5358.4023 0.4546 B- -23373.2693 60.2459 11 026157.593 5.368 + -5 3 8 11 O -pp 47738.913 60.038 3162.4372 5.4580 B- * 11 051249.828 64.453 +0 6 9 3 12 Li -n 49009.577 30.006 3791.5999 2.5005 B- 23931.8152 30.0669 12 052613.942 32.213 + 4 8 4 12 Be 25077.761 1.909 5720.7223 0.1590 B- 11708.3636 2.3214 12 026922.082 2.048 + 2 7 5 12 B 13369.398 1.321 6631.2237 0.1101 B- 13369.3979 1.3214 12 014352.638 1.418 + 0 6 6 12 C 0.0 0.0 7680.1446 0.0002 B- -17338.0681 0.9999 12 000000.0 0.0 + -2 5 7 12 N 17338.068 1.000 6170.1100 0.0833 B- -14675.2668 12.0418 12 018613.180 1.073 + -4 4 8 12 O -pp 32013.335 12.000 4881.9755 1.0000 B- * 12 034367.726 12.882 +0 7 10 3 13 Li -nn 56980.895 70.003 3507.6307 5.3848 B- 23321.8152 70.7391 13 061171.503 75.150 + 5 9 4 13 Be -n 33659.080 10.180 5241.4359 0.7831 B- 17097.1315 10.2295 13 036134.506 10.929 + 3 8 5 13 B -nn 16561.948 1.000 6496.4194 0.0769 B- 13436.9387 1.0001 13 017779.981 1.073 + 1 7 6 13 C 3125.00933 0.00023 7469.8495 0.0002 B- -2220.4718 0.2695 13 003354.83534 0.00025 + -1 6 7 13 N -p 5345.481 0.270 7238.8634 0.0207 B- -17769.9506 9.5301 13 005738.609 0.289 + -3 5 8 13 O +3n 23115.432 9.526 5811.7636 0.7328 B- -18915# 500# 13 024815.435 10.226 + -5 4 9 13 F x 42030# 500# 4297# 38# B- * 13 045121# 537# +0 6 10 4 14 Be x 39954.502 132.245 4993.8973 9.4461 B- 16290.8166 133.9357 14 042892.920 141.970 + 4 9 5 14 B 23663.686 21.213 6101.6451 1.5152 B- 20643.7926 21.2133 14 025404.010 22.773 + 2 8 6 14 C 3019.89328 0.00375 7520.3198 0.0004 B- 156.4765 0.0037 14 003241.98862 0.00403 + 0 7 7 14 N 2863.41683 0.00022 7475.6148 0.0002 B- -5144.3643 0.0252 14 003074.00425 0.00024 + -2 6 8 14 O 8007.781 0.025 7052.2783 0.0018 B- -23956.6215 41.1187 14 008596.706 0.027 + -4 5 9 14 F -p 31964.403 41.119 5285.2091 2.9371 B- * 14 034315.196 44.142 +0 7 11 4 15 Be -n 49825.821 165.797 4540.9708 11.0532 B- 20868.4411 167.1256 15 053490.215 177.990 + 5 10 5 15 B 28957.379 21.029 5880.0438 1.4019 B- 19084.2343 21.0442 15 031087.023 22.575 + 3 9 6 15 C -n 9873.145 0.800 7100.1696 0.0533 B- 9771.7071 0.8000 15 010599.256 0.858 + 1 8 7 15 N 101.43809 0.00058 7699.4603 0.0002 B- -2754.1841 0.4902 15 000108.89827 0.00062 + -1 7 8 15 O 2855.622 0.490 7463.6915 0.0327 B- -13711.1300 14.0086 15 003065.636 0.526 + -3 6 9 15 F -p 16566.752 14.000 6497.4597 0.9333 B- -23648.6215 68.1377 15 017785.139 15.029 + -5 5 10 15 Ne -pp 40215.374 66.684 4868.7285 4.4456 B- * 15 043172.977 71.588 +0 8 12 4 16 Be -nn 57447.139 165.797 4285.2851 10.3623 B- 20335.4399 167.6075 16 061672.036 177.990 + 6 11 5 16 B 37111.699 24.566 5507.3535 1.5354 B- 23417.5656 24.8254 16 039841.045 26.373 + 4 10 6 16 C -nn 13694.133 3.578 6922.0546 0.2236 B- 8010.2260 4.2540 16 014701.255 3.840 + 2 9 7 16 N -n 5683.907 2.301 7373.7971 0.1438 B- 10420.9094 2.3014 16 006101.925 2.470 + 0 8 8 16 O -4737.00217 0.00030 7976.2072 0.0002 B- -15412.1840 5.3642 15 994914.61926 0.00032 + -2 7 9 16 F 10675.182 5.364 6964.0490 0.3353 B- -13311.5932 21.1709 16 011460.278 5.758 + -4 6 10 16 Ne -- 23986.775 20.480 6083.1777 1.2800 B- * 16 025750.860 21.986 +0 7 12 5 17 B x 43716.322 204.104 5269.6677 12.0061 B- 22684.4422 204.8410 17 046931.399 219.114 + 5 11 6 17 C 2p-n 21031.880 17.365 6558.0262 1.0215 B- 13161.8007 22.9464 17 022578.650 18.641 + 3 10 7 17 N +p 7870.079 15.000 7286.2294 0.8824 B- 8678.8430 15.0000 17 008448.876 16.103 + 1 9 8 17 O -808.76421 0.00064 7750.7291 0.0002 B- -2760.4655 0.2479 16 999131.75595 0.00069 + -1 8 9 17 F 1951.701 0.248 7542.3284 0.0146 B- -14548.7507 0.4323 17 002095.237 0.266 + -3 7 10 17 Ne 16500.452 0.354 6640.4991 0.0208 B- -18219.1277 59.6167 17 017713.962 0.380 + -5 6 11 17 Na x 34719.580 59.616 5522.7653 3.5068 B- * 17 037273.000 64.000 +0 8 13 5 18 B -n 51792.640 204.165 4976.6306 11.3425 B- 26873.3742 206.3572 18 055601.683 219.180 + 6 12 6 18 C ++ 24919.266 30.000 6426.1321 1.6667 B- 11806.0982 35.2821 18 026751.930 32.206 + 4 11 7 18 N + 13113.167 18.570 7038.5627 1.0316 B- 13895.9838 18.5695 18 014077.563 19.935 + 2 10 8 18 O -782.81634 0.00064 7767.0981 0.0002 B- -1655.9288 0.4633 17 999159.61214 0.00069 + 0 9 9 18 F 873.112 0.463 7631.6383 0.0257 B- -4444.5049 0.5888 18 000937.324 0.497 + -2 8 10 18 Ne 5317.617 0.363 7341.2577 0.0202 B- -19720.3745 93.8819 18 005708.696 0.390 + -4 7 11 18 Na 25037.992 93.881 6202.2176 5.2156 B- * 18 026879.388 100.785 +0 9 14 5 19 B x 59770.251 525.363 4719.6346 27.6507 B- 27356.4961 534.4964 19 064166.000 564.000 + 7 13 6 19 C -n 32413.754 98.389 6118.2740 5.1784 B- 16557.4995 99.7475 19 034797.594 105.625 + 5 12 7 19 N p-2n 15856.255 16.404 6948.5452 0.8634 B- 12523.3972 16.6143 19 017022.389 17.610 + 3 11 8 19 O -n 3332.858 2.637 7566.4952 0.1388 B- 4820.3029 2.6370 19 003577.969 2.830 + 1 10 9 19 F -1487.44512 0.00082 7779.0192 0.0002 B- -3239.4986 0.1601 18 998403.16207 0.00088 + -1 9 10 19 Ne +3n 1752.054 0.160 7567.3431 0.0084 B- -11177.3310 10.5364 19 001880.906 0.171 + -3 8 11 19 Na 12929.384 10.535 6937.8864 0.5545 B- -18909.0095 60.9189 19 013880.264 11.309 + -5 7 12 19 Mg -pp 31838.394 60.001 5901.4992 3.1579 B- * 19 034179.920 64.413 +0 10 15 5 20 B -n 69401.569 546.357 4405.6529 27.3178 B- 31898.0019 593.0377 20 074505.644 586.538 + 8 14 6 20 C x 37503.567 230.625 5961.4356 11.5312 B- 15737.0689 243.7459 20 040261.732 247.585 + 6 13 7 20 N x 21766.498 78.894 6709.1717 3.9447 B- 17970.3261 78.8991 20 023367.295 84.696 + 4 12 8 20 O -nn 3796.172 0.885 7568.5707 0.0442 B- 3813.6349 0.8854 20 004075.357 0.950 + 2 11 9 20 F -n -17.463 0.030 7720.1351 0.0015 B- 7024.4689 0.0297 19 999981.252 0.031 + 0 10 10 20 Ne -7041.93217 0.00154 8032.2412 0.0002 B- -13892.4207 1.1090 19 992440.17525 0.00165 + -2 9 11 20 Na 6850.489 1.109 7298.5028 0.0554 B- -10627.2054 2.1681 20 007354.301 1.190 + -4 8 12 20 Mg +t 17477.694 1.863 6728.0252 0.0931 B- * 20 018763.075 2.000 +0 11 16 5 21 B -nn 78382.887 558.664 4152.5265 26.6031 B- 32740# 817# 21 084147.485 599.750 + 9 15 6 21 C x 45643# 596# 5674# 28# B- 20411# 611# 21 049000# 640# + 7 14 7 21 N x 25231.915 134.048 6609.0159 6.3832 B- 17169.8816 134.5840 21 027087.573 143.906 + 5 13 8 21 O -3n 8062.034 12.000 7389.3747 0.5714 B- 8109.6390 12.1342 21 008654.948 12.882 + 3 12 9 21 F -nn -47.605 1.800 7738.2934 0.0857 B- 5684.1712 1.8004 20 999948.893 1.932 + 1 11 10 21 Ne -5731.776 0.038 7971.7136 0.0018 B- -3546.9190 0.0177 20 993846.685 0.041 + -1 10 11 21 Na -2184.857 0.042 7765.5581 0.0020 B- -13088.7080 0.7557 20 997654.459 0.045 + -3 9 12 21 Mg x 10903.851 0.755 7105.0317 0.0359 B- -16186# 600# 21 011705.764 0.810 + -5 8 13 21 Al x 27090# 600# 6297# 29# B- * 21 029082# 644# +0 10 16 6 22 C -nn 53611.203 231.490 5421.0778 10.5223 B- 21846.3983 311.0627 22 057553.990 248.515 + 8 15 7 22 N x 31764.805 207.779 6378.5347 9.4445 B- 22481.7725 215.4350 22 034100.918 223.060 + 6 14 8 22 O -4n 9283.032 56.921 7364.8722 2.5873 B- 6489.6562 58.2558 22 009965.744 61.107 + 4 13 9 22 F + 2793.376 12.399 7624.2954 0.5636 B- 10818.0916 12.3990 22 002998.812 13.310 + 2 12 10 22 Ne -8024.716 0.018 8080.4656 0.0008 B- -2843.3243 0.1325 21 991385.113 0.018 + 0 11 11 22 Na -5181.391 0.132 7915.6624 0.0060 B- -4781.4051 0.1631 21 994437.547 0.141 + -2 10 12 22 Mg -399.986 0.159 7662.7645 0.0072 B- -18601# 401# 21 999570.597 0.170 + -4 9 13 22 Al x 18201# 401# 6782# 18# B- -15439# 641# 22 019540# 430# + -6 8 14 22 Si x 33640# 500# 6044# 23# B- * 22 036114# 537# +0 11 17 6 23 C x 64171# 997# 5077# 43# B- 27450# 1082# 23 068890# 1070# + 9 16 7 23 N x 36720.429 420.570 6236.6721 18.2856 B- 22099.0584 437.8271 23 039421.000 451.500 + 7 15 8 23 O x 14621.371 121.712 7163.4856 5.2918 B- 11336.1072 126.1904 23 015696.686 130.663 + 5 14 9 23 F 3285.263 33.320 7622.3447 1.4487 B- 8439.3084 33.3206 23 003526.875 35.770 + 3 13 10 23 Ne -n -5154.045 0.104 7955.2561 0.0045 B- 4375.8085 0.1044 22 994466.905 0.112 + 1 12 11 23 Na -9529.85352 0.00181 8111.4936 0.0002 B- -4056.1790 0.0317 22 989769.28195 0.00194 + -1 11 12 23 Mg - -5473.675 0.032 7901.1229 0.0014 B- -12221.7457 0.3461 22 994123.768 0.034 + -3 10 13 23 Al -- 6748.071 0.345 7335.7275 0.0150 B- -17202# 500# 23 007244.351 0.370 + -5 9 14 23 Si x 23950# 500# 6554# 22# B- * 23 025711# 537# +0 10 17 7 24 N x 46938# 401# 5887# 17# B- 28438# 433# 24 050390# 430# + 8 16 8 24 O x 18500.404 164.874 7039.6855 6.8698 B- 10955.8885 191.6327 24 019861.000 177.000 + 6 15 9 24 F x 7544.516 97.670 7463.5831 4.0696 B- 13496.1583 97.6717 24 008099.370 104.853 + 4 14 10 24 Ne -nn -5951.642 0.513 7993.3252 0.0214 B- 2466.2583 0.5130 23 993610.649 0.550 + 2 13 11 24 Na -n -8417.901 0.017 8063.4882 0.0007 B- 5515.6774 0.0210 23 990963.012 0.017 + 0 12 12 24 Mg -13933.578 0.013 8260.7103 0.0006 B- -13884.7660 0.2282 23 985041.689 0.013 + -2 11 13 24 Al -48.812 0.228 7649.5806 0.0095 B- -10793.9978 19.4734 23 999947.598 0.244 + -4 10 14 24 Si -- 10745.186 19.472 7167.2329 0.8113 B- -23275# 501# 24 011535.430 20.904 + -6 9 15 24 P x 34020# 500# 6165# 21# B- * 24 036522# 537# +0 11 18 7 25 N x 55983# 503# 5613# 20# B- 28654# 529# 25 060100# 540# + 9 17 8 25 O -n 27329.030 165.084 6727.8058 6.6034 B- 15994.8633 191.1909 25 029338.919 177.225 + 7 16 9 25 F x 11334.167 96.442 7336.3065 3.8577 B- 13369.6698 100.7212 25 012167.727 103.535 + 5 15 10 25 Ne -2035.503 29.045 7839.7994 1.1618 B- 7322.3107 29.0701 24 997814.797 31.181 + 3 14 11 25 Na -nn -9357.814 1.200 8101.3979 0.0480 B- 3834.9684 1.2009 24 989953.974 1.288 + 1 13 12 25 Mg -13192.782 0.047 8223.5028 0.0019 B- -4276.8080 0.0447 24 985836.966 0.050 + -1 12 13 25 Al -8915.974 0.065 8021.1366 0.0026 B- -12743.2956 10.0002 24 990428.308 0.069 + -3 11 14 25 Si +3n 3827.322 10.000 7480.1109 0.4000 B- -16363# 400# 25 004108.798 10.735 + -5 10 15 25 P x 20190# 400# 6794# 16# B- * 25 021675# 429# +0 10 18 8 26 O -nn 34661.041 164.950 6497.4790 6.3442 B- 15986.3855 196.6301 26 037210.155 177.081 + 8 17 9 26 F 18674.655 107.027 7082.2497 4.1164 B- 18193.5414 108.6022 26 020048.065 114.898 + 6 16 10 26 Ne x 481.114 18.429 7751.9110 0.7088 B- 7341.8940 18.7585 26 000516.496 19.784 + 4 15 11 26 Na x -6860.780 3.502 8004.2013 0.1347 B- 9353.7631 3.5018 25 992634.649 3.759 + 2 14 12 26 Mg -16214.544 0.029 8333.8711 0.0011 B- -4004.4042 0.0629 25 982592.972 0.031 + 0 13 13 26 Al -12210.139 0.066 8149.7653 0.0026 B- -5069.1361 0.0849 25 986891.876 0.071 + -2 12 14 26 Si - -7141.003 0.108 7924.7083 0.0041 B- -18114# 196# 25 992333.818 0.115 + -4 11 15 26 P x 10973# 196# 7198# 8# B- -16707# 631# 26 011780# 210# + -6 10 16 26 S x 27680# 600# 6525# 23# B- * 26 029716# 644# +0 11 19 8 27 O x 44670# 500# 6185# 19# B- 19536# 514# 27 047955# 537# + 9 18 9 27 F 25133.478 120.198 6879.6662 4.4518 B- 18082.5680 150.6211 27 026981.897 129.037 + 7 17 10 27 Ne x 7050.910 90.770 7520.4151 3.3619 B- 12568.7005 90.8467 27 007569.462 97.445 + 5 16 11 27 Na ++ -5517.791 3.726 7956.9467 0.1380 B- 9068.8037 3.7266 26 994076.408 4.000 + 3 15 12 27 Mg -14586.594 0.047 8263.8525 0.0018 B- 2610.2694 0.0669 26 984340.647 0.050 + 1 14 13 27 Al -17196.864 0.047 8331.5533 0.0018 B- -4812.3583 0.0964 26 981538.408 0.050 + -1 13 14 27 Si - -12384.505 0.107 8124.3420 0.0040 B- -11725.4730 9.0013 26 986704.687 0.115 + -3 12 15 27 P -p -659.032 9.001 7661.0894 0.3334 B- -18150# 400# 26 999292.499 9.662 + -5 11 16 27 S - 17491# 400# 6960# 15# B- * 27 018777# 430# +0 12 20 8 28 O x 52080# 699# 5988# 25# B- 18676# 709# 28 055910# 750# + 10 19 9 28 F -n 33403.796 120.347 6626.8567 4.2981 B- 22104.0579 174.2886 28 035860.448 129.198 + 8 18 10 28 Ne x 11299.738 126.068 7388.3463 4.5024 B- 12288.0534 126.4833 28 012130.767 135.339 + 6 17 11 28 Na x -988.315 10.246 7799.2644 0.3659 B- 14031.6303 10.2498 27 998939.000 11.000 + 4 16 12 28 Mg x -15019.946 0.261 8272.4531 0.0093 B- 1830.7740 0.2653 27 983875.426 0.280 + 2 15 13 28 Al -n -16850.719 0.049 8309.8969 0.0018 B- 4642.0776 0.0486 27 981910.009 0.052 + 0 14 14 28 Si -21492.79711 0.00051 8447.7445 0.0002 B- -14344.9407 1.1473 27 976926.53442 0.00055 + -2 13 15 28 P -7147.856 1.147 7907.4842 0.0410 B- -11221.0593 160.0041 27 992326.460 1.231 + -4 12 16 28 S -- 4073.203 160.000 7478.7911 5.7143 B- -24197# 525# 28 004372.762 171.767 + -6 11 17 28 Cl -p 28270# 500# 6587# 18# B- * 28 030349# 537# +0 11 20 9 29 F x 40150.190 525.363 6444.0314 18.1160 B- 21750.3873 546.2212 29 043103.000 564.000 + 9 19 10 29 Ne x 18399.803 149.505 7167.0673 5.1553 B- 15719.8092 149.6847 29 019753.000 160.500 + 7 18 11 29 Na 2679.994 7.337 7682.1522 0.2530 B- 13292.3538 7.3447 29 002877.091 7.876 + 5 17 12 29 Mg -10612.360 0.345 8113.5317 0.0119 B- 7595.4023 0.4873 28 988607.163 0.369 + 3 16 13 29 Al x -18207.762 0.345 8348.4647 0.0119 B- 3687.3192 0.3447 28 980453.164 0.370 + 1 15 14 29 Si -21895.08154 0.00056 8448.6361 0.0002 B- -4942.2325 0.3589 28 976494.66434 0.00060 + -1 14 15 29 P -16952.849 0.359 8251.2368 0.0124 B- -13858.4257 13.0459 28 981800.368 0.385 + -3 13 16 29 S x -3094.423 13.041 7746.3826 0.4497 B- -17117# 189# 28 996678.000 14.000 + -5 12 17 29 Cl -p 14022# 189# 7129# 7# B- -23947# 478# 29 015053# 203# + -7 11 18 29 Ar -pp 37969# 439# 6276# 15# B- * 29 040761# 471# +0 12 21 9 30 F x 48960# 500# 6205# 17# B- 25680# 561# 30 052561# 537# + 10 20 10 30 Ne 23280.120 253.250 7034.5317 8.4417 B- 14805.4501 253.2946 30 024992.235 271.875 + 8 19 11 30 Na 8474.670 4.727 7501.9685 0.1576 B- 17356.0423 4.9011 30 009097.931 5.074 + 6 18 12 30 Mg -8881.373 1.295 8054.4250 0.0432 B- 6982.7440 2.3287 29 990465.454 1.390 + 4 17 13 30 Al -15864.116 1.936 8261.1049 0.0645 B- 8568.8459 1.9357 29 982969.171 2.077 + 2 16 14 30 Si -n -24432.962 0.022 8520.6549 0.0008 B- -4232.1065 0.0615 29 973770.137 0.023 + 0 15 15 30 P - -20200.856 0.065 8353.5064 0.0022 B- -6141.6014 0.1956 29 978313.490 0.069 + -2 14 16 30 S - -14059.254 0.206 8122.7081 0.0069 B- -18733.8020 23.8769 29 984906.770 0.221 + -4 13 17 30 Cl -p 4674.548 23.876 7472.1698 0.7959 B- -17397# 180# 30 005018.333 25.631 + -6 12 18 30 Ar -pp 22071# 179# 6866# 6# B- * 30 023694# 192# +0 13 22 9 31 F -nn 56843# 535# 6011# 17# B- 25661# 597# 31 061023# 574# + 11 21 10 31 Ne 31181.594 266.195 6813.0902 8.5869 B- 18935.5625 266.5617 31 033474.816 285.772 + 9 20 11 31 Na x 12246.031 13.972 7398.6778 0.4507 B- 15368.1833 14.3065 31 013146.654 15.000 + 7 19 12 31 Mg x -3122.152 3.074 7869.1886 0.0992 B- 11828.5569 3.8009 30 996648.232 3.300 + 5 18 13 31 Al x -14950.709 2.236 8225.5180 0.0721 B- 7998.3286 2.2360 30 983949.754 2.400 + 3 17 14 31 Si -n -22949.037 0.043 8458.2916 0.0014 B- 1491.5071 0.0434 30 975363.196 0.046 + 1 16 15 31 P -24440.54442 0.00075 8481.1677 0.0002 B- -5398.0130 0.2292 30 973761.99768 0.00080 + -1 15 16 31 S -19042.531 0.229 8281.8013 0.0074 B- -12007.9790 3.4541 30 979557.002 0.246 + -3 14 17 31 Cl -- -7034.552 3.447 7869.2101 0.1112 B- -18360# 200# 30 992448.097 3.700 + -5 13 18 31 Ar - 11325# 200# 7252# 6# B- -22935# 361# 31 012158# 215# + -7 12 19 31 K x 34260# 300# 6487# 10# B- * 31 036780# 322# +0 12 22 10 32 Ne x 36999# 503# 6671# 16# B- 18359# 504# 32 039720# 540# + 10 21 11 32 Na x 18640.152 37.260 7219.8815 1.1644 B- 19469.0523 37.4021 32 020011.024 40.000 + 8 20 12 32 Mg x -828.901 3.260 7803.8411 0.1019 B- 10270.4677 7.8787 31 999110.138 3.500 + 6 19 13 32 Al x -11099.368 7.173 8100.3449 0.2241 B- 12978.3208 7.1787 31 988084.338 7.700 + 4 18 14 32 Si x -24077.689 0.298 8481.4690 0.0093 B- 227.1872 0.3008 31 974151.538 0.320 + 2 17 15 32 P -n -24304.876 0.040 8464.1203 0.0013 B- 1710.6608 0.0400 31 973907.643 0.042 + 0 16 16 32 S -26015.53714 0.00131 8493.1301 0.0002 B- -12680.8313 0.5617 31 972071.17354 0.00141 + -2 15 17 32 Cl -13334.706 0.562 8072.4058 0.0176 B- -11134.3536 1.8568 31 985684.605 0.603 + -4 14 18 32 Ar x -2200.352 1.770 7700.0089 0.0553 B- -24190# 400# 31 997637.824 1.900 + -6 13 19 32 K x 21990# 400# 6920# 12# B- * 32 023607# 429# +0 13 23 10 33 Ne x 46130# 600# 6436# 18# B- 22350# 750# 33 049523# 644# + 11 22 11 33 Na x 23780.113 449.912 7089.9262 13.6337 B- 18817.2400 449.9195 33 025529.000 483.000 + 9 21 12 33 Mg 4962.873 2.663 7636.4382 0.0807 B- 13460.2550 7.4767 33 005327.862 2.859 + 7 20 13 33 Al x -8497.382 6.986 8020.6172 0.2117 B- 12016.9460 7.0211 32 990877.685 7.500 + 5 19 14 33 Si x -20514.328 0.699 8361.0596 0.0212 B- 5823.0223 1.2947 32 977976.964 0.750 + 3 18 15 33 P + -26337.350 1.090 8513.8073 0.0330 B- 248.5079 1.0900 32 971725.692 1.170 + 1 17 16 33 S -26585.85830 0.00134 8497.6304 0.0002 B- -5582.5182 0.3908 32 971458.90862 0.00144 + -1 16 17 33 Cl -21003.340 0.391 8304.7557 0.0118 B- -11619.0452 0.5596 32 977451.988 0.419 + -3 15 18 33 Ar x -9384.295 0.401 7928.9559 0.0121 B- -16925# 200# 32 989925.545 0.430 + -5 14 19 33 K x 7540# 200# 7392# 6# B- -23489# 447# 33 008095# 215# + -7 13 20 33 Ca x 31030# 400# 6657# 12# B- * 33 033312# 429# +0 14 24 10 34 Ne -nn 52842# 513# 6287# 15# B- 21161# 789# 34 056728# 551# + 12 23 11 34 Na x 31680.114 599.416 6886.4377 17.6299 B- 23356.7903 599.4561 34 034010.000 643.500 + 10 22 12 34 Mg x 8323.324 6.893 7550.3919 0.2027 B- 11320.9428 7.2072 34 008935.455 7.400 + 8 21 13 34 Al x -2997.619 2.105 7860.3506 0.0619 B- 16994.0653 2.2519 33 996781.924 2.259 + 6 20 14 34 Si x -19991.684 0.801 8337.1659 0.0236 B- 4557.0175 1.1395 33 978538.045 0.860 + 4 19 15 34 P x -24548.702 0.810 8448.1856 0.0238 B- 5382.9879 0.8116 33 973645.886 0.870 + 2 18 16 34 S -29931.689 0.045 8583.4986 0.0013 B- -5491.6037 0.0378 33 967867.011 0.047 + 0 17 17 34 Cl -24440.086 0.049 8398.9706 0.0014 B- -6061.7930 0.0631 33 973762.490 0.052 + -2 16 18 34 Ar -18378.293 0.078 8197.6724 0.0023 B- -17158# 196# 33 980270.092 0.083 + -4 15 19 34 K x -1220# 196# 7670# 6# B- -16110# 358# 33 998690# 210# + -6 14 20 34 Ca x 14890# 300# 7173# 9# B- * 34 015985# 322# +0 13 24 11 35 Na -n 37831# 670# 6745# 19# B- 22192# 723# 35 040614# 720# + 11 23 12 35 Mg x 15639.786 269.668 7356.2338 7.7048 B- 15863.5156 269.7679 35 016790.000 289.500 + 9 22 13 35 Al x -223.730 7.359 7787.1243 0.2103 B- 14167.7504 36.6047 34 999759.816 7.900 + 7 21 14 35 Si 2p-n -14391.480 35.857 8169.5644 1.0245 B- 10466.3291 35.9049 34 984550.111 38.494 + 5 20 15 35 P +p -24857.809 1.866 8446.2496 0.0533 B- 3988.4006 1.8667 34 973314.045 2.003 + 3 19 16 35 S -28846.210 0.040 8537.8511 0.0012 B- 167.3218 0.0257 34 969032.321 0.043 + 1 18 17 35 Cl -29013.532 0.035 8520.2790 0.0010 B- -5966.2429 0.6794 34 968852.694 0.038 + -1 17 18 35 Ar - -23047.289 0.680 8327.4621 0.0194 B- -11874.3955 0.8516 34 975257.719 0.730 + -3 16 19 35 K 4n -11172.893 0.512 7965.8409 0.0146 B- -16363# 200# 34 988005.406 0.550 + -5 15 20 35 Ca x 5190# 200# 7476# 6# B- -21910# 447# 35 005572# 215# + -7 14 21 35 Sc x 27100# 400# 6828# 11# B- * 35 029093# 429# +0 14 25 11 36 Na -n 45903# 687# 6557# 19# B- 25523# 974# 36 049279# 737# + 12 24 12 36 Mg x 20380.159 690.237 7244.4202 19.1733 B- 14429.7751 706.2429 36 021879.000 741.000 + 10 23 13 36 Al x 5950.384 149.505 7623.5154 4.1529 B- 18386.5096 165.8510 36 006388.000 160.500 + 8 22 14 36 Si x -12436.125 71.797 8112.5199 1.9944 B- 7814.9194 72.9852 35 986649.271 77.077 + 6 21 15 36 P + -20251.045 13.114 8307.8692 0.3643 B- 10413.0962 13.1124 35 978259.610 14.078 + 4 20 16 36 S -30664.141 0.188 8575.3900 0.0052 B- -1142.1329 0.1893 35 967080.692 0.201 + 2 19 17 36 Cl -29522.008 0.036 8521.9322 0.0010 B- 709.5343 0.0449 35 968306.822 0.038 + 0 18 18 36 Ar -30231.542 0.027 8519.9096 0.0008 B- -12814.3607 0.3259 35 967545.106 0.028 + -2 17 19 36 K -17417.182 0.325 8142.2233 0.0090 B- -10966.0155 40.0013 35 981301.887 0.349 + -4 16 20 36 Ca 4n -6451.166 40.000 7815.8799 1.1111 B- -22601# 303# 35 993074.388 42.941 + -6 15 21 36 Sc x 16150# 300# 7166# 8# B- * 36 017338# 322# +0 15 26 11 37 Na -nn 53134# 687# 6403# 19# B- 24923# 980# 37 057042# 737# + 13 25 12 37 Mg -n 28211.478 698.947 7055.1115 18.8905 B- 18401.9132 721.8139 37 030286.265 750.350 + 11 24 13 37 Al x 9809.564 180.244 7531.3160 4.8715 B- 16381.0765 213.1678 37 010531.000 193.500 + 9 23 14 37 Si x -6571.512 113.809 7952.9033 3.0759 B- 12424.5003 119.9691 36 992945.191 122.179 + 7 22 15 37 P p-2n -18996.012 37.948 8267.5561 1.0256 B- 7900.4135 37.9474 36 979606.942 40.738 + 5 21 16 37 S -n -26896.426 0.198 8459.9363 0.0054 B- 4865.1258 0.1965 36 971125.500 0.212 + 3 20 17 37 Cl -31761.552 0.052 8570.2816 0.0014 B- -813.8729 0.2000 36 965902.573 0.055 + 1 19 18 37 Ar - -30947.679 0.207 8527.1406 0.0056 B- -6147.4775 0.2270 36 966776.301 0.221 + -1 18 19 37 K -p -24800.201 0.094 8339.8480 0.0025 B- -11664.1314 0.6412 36 973375.890 0.100 + -3 17 20 37 Ca x -13136.070 0.634 8003.4567 0.0171 B- -16916# 300# 36 985897.849 0.680 + -5 16 21 37 Sc x 3780# 300# 7525# 8# B- -21390# 500# 37 004058# 322# + -7 15 22 37 Ti x 25170# 400# 6926# 11# B- * 37 027021# 429# +0 16 27 11 38 Na -n 61905# 715# 6216# 19# B- 27831# 875# 38 066458# 768# + 14 26 12 38 Mg x 34074# 503# 6928# 13# B- 17604# 525# 38 036580# 540# + 12 25 13 38 Al x 16470# 150# 7370# 4# B- 20640# 183# 38 017681# 161# + 10 24 14 38 Si x -4170.299 104.793 7892.8297 2.7577 B- 10451.2656 127.4736 37 995523.000 112.500 + 8 23 15 38 P x -14621.565 72.581 8147.2749 1.9100 B- 12239.6512 72.9340 37 984303.105 77.918 + 6 22 16 38 S + -26861.216 7.172 8448.7829 0.1887 B- 2936.9000 7.1714 37 971163.300 7.699 + 4 21 17 38 Cl -n -29798.116 0.098 8505.4817 0.0026 B- 4916.7109 0.2182 37 968010.408 0.105 + 2 20 18 38 Ar -34714.827 0.195 8614.2807 0.0051 B- -5914.0671 0.0448 37 962732.102 0.209 + 0 19 19 38 K -28800.760 0.195 8438.0593 0.0051 B- -6742.2563 0.0626 37 969081.114 0.209 + -2 18 20 38 Ca -22058.503 0.194 8240.0434 0.0051 B- -17809# 200# 37 976319.223 0.208 + -4 17 21 38 Sc x -4249# 200# 7751# 5# B- -15619# 361# 37 995438# 215# + -6 16 22 38 Ti x 11370# 300# 7319# 8# B- * 38 012206# 322# +0 17 28 11 39 Na -n 69977# 743# 6056# 19# B- 27201# 903# 39 075123# 797# + 15 27 12 39 Mg -n 42775# 513# 6734# 13# B- 21286# 594# 39 045921# 551# + 13 26 13 39 Al x 21490# 300# 7260# 8# B- 19169# 329# 39 023070# 322# + 11 25 14 39 Si x 2320.352 135.532 7730.9793 3.4752 B- 15094.9874 176.2324 39 002491.000 145.500 + 9 24 15 39 P x -12774.636 112.645 8097.9701 2.8883 B- 10388.0361 123.2430 38 986285.865 120.929 + 7 23 16 39 S 2p-n -23162.672 50.000 8344.2698 1.2821 B- 6637.5463 50.0300 38 975133.850 53.677 + 5 22 17 39 Cl -nn -29800.218 1.732 8494.4032 0.0444 B- 3441.9774 5.2915 38 968008.151 1.859 + 3 21 18 39 Ar + -33242.195 5.000 8562.5988 0.1282 B- 565.0000 5.0000 38 964313.037 5.367 + 1 20 19 39 K -33807.19535 0.00456 8557.0258 0.0003 B- -6524.4888 0.5962 38 963706.48482 0.00489 + -1 19 20 39 Ca -27282.707 0.596 8369.6711 0.0153 B- -13109.9804 24.0074 38 970710.811 0.640 + -3 18 21 39 Sc 2n-p -14172.726 24.000 8013.4575 0.6154 B- -16673# 202# 38 984784.953 25.765 + -5 17 22 39 Ti x 2500# 200# 7566# 5# B- -20070# 447# 39 002684# 215# + -7 16 23 39 V x 22570# 400# 7031# 10# B- * 39 024230# 429# +0 16 28 12 40 Mg x 49550# 500# 6598# 13# B- 20729# 583# 40 053194# 537# + 14 27 13 40 Al x 28820# 300# 7097# 7# B- 23154# 324# 40 030940# 322# + 12 26 14 40 Si x 5666.876 121.991 7655.8247 3.0498 B- 13806.0653 147.8912 40 006083.641 130.962 + 10 25 15 40 P x -8139.189 83.607 7981.4177 2.0902 B- 14698.6603 83.7017 39 991262.221 89.755 + 8 24 16 40 S -22837.850 3.982 8329.3255 0.0996 B- 4719.9687 32.3118 39 975482.561 4.274 + 6 23 17 40 Cl + -27557.818 32.066 8427.7660 0.8016 B- 7482.0816 32.0655 39 970415.466 34.423 + 4 22 18 40 Ar -35039.89997 0.00218 8595.2594 0.0002 B- -1504.4031 0.0559 39 962383.12204 0.00234 + 2 21 19 40 K -33535.497 0.056 8538.0907 0.0014 B- 1310.9051 0.0596 39 963998.165 0.060 + 0 20 20 40 Ca -34846.402 0.020 8551.3046 0.0006 B- -14323.0493 2.8281 39 962590.850 0.022 + -2 19 21 40 Sc - -20523.353 2.828 8173.6697 0.0707 B- -11529.9139 68.3023 39 977967.275 3.036 + -4 18 22 40 Ti -8993.439 68.244 7865.8632 1.7061 B- -21463# 308# 39 990345.146 73.262 + -6 17 23 40 V x 12470# 300# 7310# 7# B- * 40 013387# 322# +0 17 29 12 41 Mg x 58100# 500# 6425# 12# B- 23510# 640# 41 062373# 537# + 15 28 13 41 Al x 34590# 400# 6980# 10# B- 21390# 500# 41 037134# 429# + 13 27 14 41 Si x 13200# 300# 7482# 7# B- 18180# 323# 41 014171# 322# + 11 26 15 41 P x -4979.767 120.163 7906.5513 2.9308 B- 14028.8125 120.2326 40 994654.000 129.000 + 9 25 16 41 S x -19008.580 4.099 8229.6358 0.1000 B- 8298.6116 68.8456 40 979593.451 4.400 + 7 24 17 41 Cl x -27307.192 68.723 8412.9593 1.6762 B- 5760.3180 68.7243 40 970684.525 73.777 + 5 23 18 41 Ar -n -33067.510 0.347 8534.3733 0.0085 B- 2492.0392 0.3473 40 964500.570 0.372 + 3 22 19 41 K -35559.54880 0.00376 8576.0731 0.0003 B- -421.6406 0.1377 40 961825.25611 0.00403 + 1 21 20 41 Ca -35137.908 0.138 8546.7075 0.0034 B- -6495.5482 0.1553 40 962277.905 0.147 + -1 20 21 41 Sc -28642.360 0.077 8369.1979 0.0019 B- -12944.8214 27.9449 40 969251.163 0.083 + -3 19 22 41 Ti x -15697.539 27.945 8034.3889 0.6816 B- -16008# 202# 40 983148.000 30.000 + -5 18 23 41 V x 310# 200# 7625# 5# B- -20100# 447# 41 000333# 215# + -7 17 24 41 Cr x 20410# 400# 7116# 10# B- * 41 021911# 429# +0 16 29 13 42 Al x 41990# 500# 6829# 12# B- 25150# 583# 42 045078# 537# + 14 28 14 42 Si x 16840# 300# 7410# 7# B- 15748# 315# 42 018078# 322# + 12 27 15 42 P x 1091.842 95.009 7765.9122 2.2621 B- 18729.5899 95.0504 42 001172.140 101.996 + 10 26 16 42 S x -17637.748 2.794 8193.2275 0.0665 B- 7194.0221 59.6811 41 981065.100 3.000 + 8 25 17 42 Cl x -24831.770 59.616 8345.8864 1.4194 B- 9590.9082 59.8947 41 973342.000 64.000 + 6 24 18 42 Ar x -34422.678 5.775 8555.6141 0.1375 B- 599.3527 5.7763 41 963045.737 6.200 + 4 23 19 42 K -n -35022.031 0.106 8551.2571 0.0025 B- 3525.2626 0.1825 41 962402.305 0.113 + 2 22 20 42 Ca -38547.293 0.148 8616.5646 0.0035 B- -6426.2904 0.0485 41 958617.780 0.159 + 0 21 21 42 Sc -32121.003 0.154 8444.9303 0.0037 B- -7016.6496 0.2239 41 965516.686 0.165 + -2 20 22 42 Ti -25104.353 0.269 8259.2400 0.0064 B- -17485# 196# 41 973049.369 0.289 + -4 19 23 42 V x -7620# 196# 7824# 5# B- -14679# 358# 41 991820# 210# + -6 18 24 42 Cr x 7060# 300# 7456# 7# B- * 42 007579# 322# +0 17 30 13 43 Al x 48270# 600# 6712# 14# B- 23940# 721# 43 051820# 644# + 15 29 14 43 Si x 24330# 400# 7251# 9# B- 19289# 500# 43 026119# 429# + 13 28 15 43 P x 5040# 300# 7681# 7# B- 17236# 300# 43 005411# 322# + 11 27 16 43 S x -12195.461 4.970 8063.8276 0.1156 B- 11964.0500 62.0579 42 986907.635 5.335 + 9 26 17 43 Cl x -24159.510 61.859 8323.8672 1.4386 B- 7850.3002 62.0860 42 974063.700 66.407 + 7 25 18 43 Ar x -32009.811 5.310 8488.2382 0.1235 B- 4565.5836 5.3254 42 965636.056 5.700 + 5 24 19 43 K -4n -36575.394 0.410 8576.2204 0.0095 B- 1833.4783 0.4687 42 960734.701 0.440 + 3 23 20 43 Ca -38408.873 0.227 8600.6653 0.0053 B- -2220.7227 1.8650 42 958766.381 0.244 + 1 22 21 43 Sc -p -36188.150 1.863 8530.8265 0.0433 B- -6872.5591 6.0147 42 961150.425 1.999 + -1 21 22 43 Ti -29315.591 5.719 8352.8054 0.1330 B- -11399.2333 43.2287 42 968528.420 6.139 + -3 20 23 43 V x -17916.358 42.849 8069.5129 0.9965 B- -15946# 205# 42 980766.000 46.000 + -5 19 24 43 Cr x -1970# 200# 7680# 5# B- -19340# 447# 42 997885# 215# + -7 18 25 43 Mn x 17370# 400# 7213# 9# B- * 43 018647# 429# +0 16 30 14 44 Si x 29310# 500# 7156# 11# B- 18200# 640# 44 031466# 537# + 14 29 15 44 P x 11110# 400# 7552# 9# B- 20314# 400# 44 011927# 429# + 12 28 16 44 S x -9204.236 5.216 7996.0154 0.1186 B- 11274.7373 85.7253 43 990118.846 5.600 + 10 27 17 44 Cl x -20478.973 85.566 8234.4788 1.9447 B- 12194.2869 85.5811 43 978014.918 91.859 + 8 26 18 44 Ar x -32673.260 1.584 8493.8411 0.0360 B- 3108.2375 1.6381 43 964923.814 1.700 + 6 25 19 44 K x -35781.498 0.419 8546.7023 0.0095 B- 5687.2319 0.5303 43 961586.984 0.450 + 4 24 20 44 Ca -41468.730 0.325 8658.1769 0.0074 B- -3652.6948 1.7565 43 955481.489 0.348 + 2 23 21 44 Sc -p -37816.035 1.756 8557.3805 0.0399 B- -267.4488 1.8899 43 959402.818 1.884 + 0 22 22 44 Ti -a -37548.586 0.700 8533.5215 0.0159 B- -13740.5071 7.2986 43 959689.936 0.751 + -2 21 23 44 V -23808.079 7.265 8203.4567 0.1651 B- -10386.1805 51.7447 43 974440.977 7.799 + -4 20 24 44 Cr x -13421.899 51.232 7949.6265 1.1644 B- -20882# 304# 43 985591.000 55.000 + -6 19 25 44 Mn x 7460# 300# 7457# 7# B- * 44 008009# 322# +0 17 31 14 45 Si x 37090# 600# 7004# 13# B- 21130# 781# 45 039818# 644# + 15 30 15 45 P x 15960# 500# 7456# 11# B- 19301# 583# 45 017134# 537# + 13 29 16 45 S x -3340# 300# 7867# 7# B- 14922# 329# 44 996414# 322# + 11 28 17 45 Cl x -18262.544 136.163 8181.5991 3.0259 B- 11508.2568 136.1643 44 980394.353 146.177 + 9 27 18 45 Ar x -29770.801 0.512 8419.9526 0.0114 B- 6844.8422 0.7311 44 968039.731 0.550 + 7 26 19 45 K x -36615.643 0.522 8554.6747 0.0116 B- 4196.5868 0.6369 44 960691.491 0.560 + 5 25 20 45 Ca -40812.230 0.365 8630.5467 0.0081 B- 260.0910 0.7377 44 956186.270 0.392 + 3 24 21 45 Sc -41072.321 0.663 8618.9410 0.0147 B- -2062.0551 0.5086 44 955907.051 0.712 + 1 23 22 45 Ti -39010.266 0.836 8555.7321 0.0186 B- -7123.8247 0.2142 44 958120.758 0.897 + -1 22 23 45 V -31886.441 0.863 8380.0394 0.0192 B- -12371.6400 35.4073 44 965768.498 0.926 + -3 21 24 45 Cr x -19514.801 35.397 8087.7286 0.7866 B- -14535# 302# 44 979050.000 38.000 + -5 20 25 45 Mn x -4980# 300# 7747# 7# B- -19388# 412# 44 994654# 322# + -7 19 26 45 Fe -pp 14408# 283# 7299# 6# B- * 45 015467# 304# +0 16 31 15 46 P x 22840# 500# 7320# 11# B- 22200# 640# 46 024520# 537# + 14 30 16 46 S x 640# 400# 7785# 9# B- 14375# 411# 46 000687# 429# + 12 29 17 46 Cl x -13734.949 97.249 8080.7757 2.1141 B- 16036.3062 97.2766 45 985254.926 104.400 + 10 28 18 46 Ar x -29771.255 2.329 8412.3835 0.0506 B- 5642.6746 2.4394 45 968039.244 2.500 + 8 27 19 46 K x -35413.929 0.727 8518.0428 0.0158 B- 7725.6802 2.3490 45 961981.584 0.780 + 6 26 20 46 Ca -43139.610 2.234 8668.9848 0.0486 B- -1377.9665 2.3305 45 953687.726 2.398 + 4 25 21 46 Sc -n -41761.643 0.671 8622.0215 0.0146 B- 2366.6260 0.6666 45 955167.034 0.720 + 2 24 22 46 Ti -44128.269 0.090 8656.4623 0.0020 B- -7052.3723 0.0923 45 952626.356 0.097 + 0 23 23 46 V -37075.897 0.134 8486.1423 0.0029 B- -7604.3264 11.4538 45 960197.389 0.143 + -2 22 24 46 Cr -29471.570 11.453 8303.8233 0.2490 B- -17053.8226 87.3828 45 968360.969 12.295 + -4 21 25 46 Mn x -12417.748 86.629 7916.0805 1.8832 B- -13628# 312# 45 986669.000 93.000 + -6 20 26 46 Fe x 1210# 300# 7603# 7# B- * 46 001299# 322# +0 17 32 15 47 P x 28810# 600# 7209# 13# B- 21610# 721# 47 030929# 644# + 15 31 16 47 S x 7200# 400# 7652# 9# B- 16781# 447# 47 007730# 429# + 13 30 17 47 Cl x -9580# 200# 7992# 4# B- 15787# 200# 46 989715# 215# + 11 29 18 47 Ar x -25367.274 1.211 8311.4250 0.0258 B- 10344.7078 1.8490 46 972767.112 1.300 + 9 28 19 47 K x -35711.982 1.397 8514.8795 0.0297 B- 6632.6837 2.6237 46 961661.612 1.500 + 7 27 20 47 Ca -42344.665 2.221 8639.3548 0.0473 B- 1992.1770 1.1849 46 954541.134 2.384 + 5 26 21 47 Sc -44336.842 1.931 8665.0958 0.0411 B- 600.7694 1.9292 46 952402.444 2.072 + 3 25 22 47 Ti -44937.612 0.080 8661.2325 0.0017 B- -2930.5422 0.0879 46 951757.491 0.085 + 1 24 23 47 V -42007.070 0.110 8582.2348 0.0024 B- -7443.9769 5.1976 46 954903.558 0.118 + -1 23 24 47 Cr -34563.093 5.197 8407.2067 0.1106 B- -11996.7167 32.0943 46 962894.995 5.578 + -3 22 25 47 Mn x -22566.376 31.671 8135.3117 0.6738 B- -15437# 501# 46 975774.000 34.000 + -5 21 26 47 Fe x -7130# 500# 7790# 11# B- -17750# 781# 46 992346# 537# + -7 20 27 47 Co x 10620# 600# 7396# 13# B- * 47 011401# 644# +0 16 32 16 48 S x 12390# 500# 7552# 10# B- 16670# 707# 48 013301# 537# + 14 31 17 48 Cl x -4280# 500# 7883# 10# B- 18075# 500# 47 995405# 537# + 12 30 18 48 Ar x -22354.927 16.767 8243.6656 0.3493 B- 9929.5550 16.7847 47 976001.000 18.000 + 10 29 19 48 K x -32284.482 0.773 8434.2324 0.0161 B- 11940.3857 0.7734 47 965341.184 0.830 + 8 28 20 48 Ca -44224.868 0.018 8666.6916 0.0004 B- 279.2155 4.9499 47 952522.654 0.018 + 6 27 21 48 Sc -44504.083 4.950 8656.2097 0.1031 B- 3988.8685 4.9499 47 952222.903 5.313 + 4 26 22 48 Ti -48492.952 0.074 8723.0122 0.0016 B- -4014.9467 0.9691 47 947940.677 0.079 + 2 25 23 48 V -44478.005 0.972 8623.0686 0.0202 B- -1656.6918 7.3746 47 952250.900 1.043 + 0 24 24 48 Cr +nn -42821.313 7.311 8572.2553 0.1523 B- -13524.6692 9.9157 47 954029.431 7.848 + -2 23 25 48 Mn -29296.644 6.699 8274.1924 0.1396 B- -11288.0685 92.4609 47 968548.760 7.191 + -4 22 26 48 Fe x -18008.575 92.218 8022.7254 1.9212 B- -19738# 509# 47 980667.000 99.000 + -6 21 27 48 Co x 1730# 500# 7595# 10# B- -16448# 656# 48 001857# 537# + -8 20 28 48 Ni -pp 18178# 424# 7236# 9# B- * 48 019515# 455# +0 17 33 16 49 S -n 20391# 583# 7400# 12# B- 19652# 707# 49 021891# 626# + 15 32 17 49 Cl x 740# 400# 7785# 8# B- 17800# 565# 49 000794# 429# + 13 31 18 49 Ar x -17060# 400# 8132# 8# B- 12551# 400# 48 981685# 429# + 11 30 19 49 K x -29611.496 0.801 8372.2753 0.0164 B- 11688.5069 0.8205 48 968210.753 0.860 + 9 29 20 49 Ca -n -41300.003 0.178 8594.8500 0.0036 B- 5262.4445 2.2745 48 955662.625 0.190 + 7 28 21 49 Sc -46562.447 2.268 8686.2805 0.0463 B- 2001.5652 2.2684 48 950013.159 2.434 + 5 27 22 49 Ti -48564.012 0.078 8711.1625 0.0016 B- -601.8555 0.8203 48 947864.391 0.084 + 3 26 23 49 V - -47962.157 0.824 8682.9135 0.0168 B- -2629.8047 2.3487 48 948510.509 0.884 + 1 25 24 49 Cr -45332.352 2.202 8613.2777 0.0449 B- -7712.4265 0.2329 48 951333.720 2.363 + -1 24 25 49 Mn -37619.925 2.214 8439.9150 0.0452 B- -12869.1957 24.3199 48 959613.350 2.377 + -3 23 26 49 Fe x -24750.730 24.219 8161.3121 0.4943 B- -14971# 501# 48 973429.000 26.000 + -5 22 27 49 Co x -9780# 500# 7840# 10# B- -18309# 781# 48 989501# 537# + -7 21 28 49 Ni x 8530# 600# 7450# 12# B- * 49 009157# 644# +0 16 33 17 50 Cl x 7700# 400# 7651# 8# B- 20930# 640# 50 008266# 429# + 14 32 18 50 Ar x -13230# 500# 8054# 10# B- 12498# 500# 49 985797# 537# + 12 31 19 50 K x -25727.853 7.731 8288.5833 0.1546 B- 13861.3774 7.8919 49 972380.015 8.300 + 10 30 20 50 Ca x -39589.230 1.584 8550.1639 0.0317 B- 4947.8903 2.9723 49 957499.215 1.700 + 8 29 21 50 Sc -44537.120 2.515 8633.4747 0.0503 B- 6894.7470 2.5166 49 952187.437 2.700 + 6 28 22 50 Ti -51431.867 0.082 8755.7227 0.0017 B- -2208.6274 0.0579 49 944785.622 0.088 + 4 27 23 50 V -49223.240 0.093 8695.9032 0.0019 B- 1038.1240 0.0551 49 947156.681 0.099 + 2 26 24 50 Cr -50261.364 0.094 8701.0188 0.0019 B- -7634.4776 0.0672 49 946042.209 0.100 + 0 25 25 50 Mn -42626.886 0.115 8532.6823 0.0023 B- -8150.4267 8.3842 49 954238.157 0.123 + -2 24 26 50 Fe x -34476.460 8.383 8354.0268 0.1677 B- -16887.0566 126.0308 49 962988.000 9.000 + -4 23 27 50 Co x -17589.403 125.752 8000.6387 2.5150 B- -14130# 516# 49 981117.000 135.000 + -6 22 28 50 Ni x -3460# 500# 7702# 10# B- * 49 996286# 537# +0 17 34 17 51 Cl x 14290# 700# 7530# 14# B- 20780# 806# 51 015341# 751# + 15 33 18 51 Ar x -6490# 400# 7922# 8# B- 16026# 400# 50 993033# 429# + 13 32 19 51 K x -22515.457 13.041 8221.3350 0.2557 B- 13816.8529 13.0517 50 975828.664 14.000 + 11 31 20 51 Ca x -36332.310 0.522 8476.9135 0.0102 B- 6918.0432 2.5686 50 960995.663 0.560 + 9 30 21 51 Sc -43250.353 2.515 8597.2213 0.0493 B- 6482.6122 2.5612 50 953568.838 2.700 + 7 29 22 51 Ti -49732.965 0.484 8708.9912 0.0095 B- 2470.1402 0.4820 50 946609.468 0.519 + 5 28 23 51 V -52203.105 0.097 8742.0852 0.0019 B- -752.3907 0.1494 50 943957.664 0.104 + 3 27 24 51 Cr -51450.715 0.167 8711.9923 0.0033 B- -3207.4893 0.3256 50 944765.388 0.178 + 1 26 25 51 Mn -48243.225 0.304 8633.7602 0.0060 B- -8054.0400 1.4309 50 948208.770 0.326 + -1 25 26 51 Fe x -40189.185 1.398 8460.4977 0.0274 B- -12847.0389 48.4579 50 956855.137 1.501 + -3 24 27 51 Co x -27342.146 48.438 8193.2549 0.9498 B- -15692# 503# 50 970647.000 52.000 + -5 23 28 51 Ni x -11650# 500# 7870# 10# B- * 50 987493# 537# +0 18 35 17 52 Cl x 22360# 700# 7386# 13# B- 23739# 922# 52 024004# 751# + 16 34 18 52 Ar x -1380# 600# 7827# 12# B- 15758# 601# 51 998519# 644# + 14 33 19 52 K x -17137.628 33.534 8115.0303 0.6449 B- 17128.6431 33.5405 51 981602.000 36.000 + 12 32 20 52 Ca x -34266.272 0.671 8429.3821 0.0129 B- 6257.2889 3.1463 51 963213.646 0.720 + 10 31 21 52 Sc -40523.560 3.074 8534.6695 0.0591 B- 8954.1372 4.1223 51 956496.170 3.300 + 8 30 22 52 Ti -49477.698 2.747 8691.8193 0.0528 B- 1965.3340 2.7510 51 946883.509 2.948 + 6 29 23 52 V -n -51443.032 0.159 8714.5690 0.0031 B- 3976.4763 0.1601 51 944773.636 0.170 + 4 28 24 52 Cr -55419.508 0.112 8775.9946 0.0022 B- -4708.1214 0.0633 51 940504.714 0.120 + 2 27 25 52 Mn - -50711.387 0.129 8670.4087 0.0025 B- -2379.2912 0.1534 51 945559.090 0.138 + 0 26 26 52 Fe -- -48332.095 0.179 8609.6079 0.0035 B- -13988.1167 5.2831 51 948113.364 0.192 + -2 25 27 52 Co -34343.979 5.282 8325.5606 0.1016 B- -11784.1230 83.0710 51 963130.224 5.669 + -4 24 28 52 Ni x -22559.856 82.903 8083.8977 1.5943 B- -20680# 606# 51 975781.000 89.000 + -6 23 29 52 Cu x -1880# 600# 7671# 12# B- * 51 997982# 644# +0 17 35 18 53 Ar x 6791# 699# 7677# 13# B- 19086# 708# 53 007290# 750# + 15 34 19 53 K x -12295.722 111.779 8022.8488 2.1090 B- 17091.9853 120.0471 52 986800.000 120.000 + 13 33 20 53 Ca x -29387.707 43.780 8330.5778 0.8260 B- 9381.8471 47.2223 52 968451.000 47.000 + 11 32 21 53 Sc -38769.555 17.698 8492.8325 0.3339 B- 8111.8785 17.9325 52 958379.173 19.000 + 9 31 22 53 Ti x -46881.433 2.888 8631.1256 0.0545 B- 4970.2419 4.2386 52 949670.714 3.100 + 7 30 23 53 V +p -51851.675 3.103 8710.1425 0.0585 B- 3435.9426 3.1017 52 944334.940 3.331 + 5 29 24 53 Cr -55287.618 0.116 8760.2103 0.0022 B- -597.2679 0.3430 52 940646.304 0.124 + 3 28 25 53 Mn -54690.350 0.346 8734.1798 0.0065 B- -3742.8664 1.6971 52 941287.497 0.371 + 1 27 26 53 Fe -50947.483 1.669 8648.7985 0.0315 B- -8288.1073 0.4431 52 945305.629 1.792 + -1 26 27 53 Co -42659.376 1.727 8477.6578 0.0326 B- -13028.5485 25.2096 52 954203.278 1.854 + -3 25 28 53 Ni x -29630.827 25.150 8217.0749 0.4745 B- -16491# 501# 52 968190.000 27.000 + -5 24 29 53 Cu x -13140# 500# 7891# 9# B- * 52 985894# 537# +0 18 36 18 54 Ar x 12560# 800# 7578# 15# B- 17710# 894# 54 013484# 859# + 16 35 19 54 K x -5150# 400# 7891# 7# B- 20010# 403# 53 994471# 429# + 14 34 20 54 Ca x -25160.587 48.438 8247.4967 0.8970 B- 9277.3463 50.4129 53 972989.000 52.000 + 12 33 21 54 Sc x -34437.934 13.973 8404.8115 0.2588 B- 11305.8789 21.1188 53 963029.359 15.000 + 10 32 22 54 Ti x -45743.812 15.835 8599.6917 0.2932 B- 4154.4548 19.3840 53 950892.000 17.000 + 8 31 23 54 V -49898.267 11.180 8662.1382 0.2070 B- 7037.1118 11.1794 53 946432.009 12.001 + 6 30 24 54 Cr -56935.379 0.132 8777.9672 0.0025 B- -1377.1325 1.0051 53 938877.359 0.142 + 4 29 25 54 Mn -p -55558.247 1.007 8737.9768 0.0186 B- 696.3688 1.0587 53 940355.772 1.080 + 2 28 26 54 Fe -56254.615 0.343 8736.3846 0.0064 B- -8244.5478 0.0893 53 939608.189 0.368 + 0 27 27 54 Co -48010.068 0.355 8569.2199 0.0066 B- -8731.7558 4.6710 53 948459.075 0.380 + -2 26 28 54 Ni x -39278.312 4.657 8393.0328 0.0862 B- -18038# 400# 53 957833.000 5.000 + -4 25 29 54 Cu x -21240# 400# 8045# 7# B- -15538# 454# 53 977198# 429# + -6 24 30 54 Zn -pp -5702# 217# 7742# 4# B- * 53 993879# 232# +0 17 36 19 55 K x 470# 500# 7792# 9# B- 19121# 525# 55 000505# 537# + 15 35 20 55 Ca x -18650.375 160.217 8125.9260 2.9130 B- 12191.7329 171.9435 54 979978.000 172.000 + 13 34 21 55 Sc x -30842.108 62.411 8333.3694 1.1347 B- 10990.3608 68.7671 54 966889.637 67.000 + 11 33 22 55 Ti x -41832.469 28.876 8518.9696 0.5250 B- 7292.6673 39.5419 54 955091.000 31.000 + 9 32 23 55 V x -49125.136 27.013 8637.3391 0.4912 B- 5985.1877 27.0143 54 947262.000 29.000 + 7 31 24 55 Cr -n -55110.324 0.228 8731.9362 0.0042 B- 2602.2183 0.3219 54 940836.637 0.245 + 5 30 25 55 Mn -57712.542 0.260 8765.0247 0.0047 B- -231.1204 0.1786 54 938043.040 0.279 + 3 29 26 55 Fe -57481.422 0.308 8746.5981 0.0056 B- -3451.4254 0.3241 54 938291.158 0.330 + 1 28 27 55 Co -54029.996 0.405 8669.6204 0.0074 B- -8694.0350 0.5775 54 941996.416 0.434 + -1 27 28 55 Ni - -45335.961 0.705 8497.3225 0.0128 B- -13700.5585 155.5611 54 951329.846 0.757 + -3 26 29 55 Cu x -31635.403 155.560 8233.9970 2.8284 B- -17366# 429# 54 966038.000 167.000 + -5 25 30 55 Zn x -14270# 400# 7904# 7# B- * 54 984681# 429# +0 18 37 19 56 K x 7980# 600# 7663# 11# B- 21491# 650# 56 008567# 644# + 16 36 20 56 Ca x -13510.390 249.640 8033.1654 4.4579 B- 12005.4580 360.2029 55 985496.000 268.000 + 14 35 21 56 Sc x -25515.848 259.665 8233.5781 4.6369 B- 13907.1470 278.3271 55 972607.611 278.761 + 12 34 22 56 Ti -39422.995 100.201 8467.9495 1.7893 B- 6760.4051 188.1842 55 957677.675 107.569 + 10 33 23 56 V -46183.401 175.884 8574.7006 3.1408 B- 9101.7270 175.8854 55 950420.082 188.819 + 8 32 24 56 Cr ++ -55285.128 0.578 8723.2609 0.0103 B- 1626.5384 0.5524 55 940648.977 0.620 + 6 31 25 56 Mn -n -56911.666 0.293 8738.3358 0.0052 B- 3695.4973 0.2065 55 938902.816 0.314 + 4 30 26 56 Fe -60607.163 0.268 8790.3563 0.0048 B- -4566.6455 0.4104 55 934935.537 0.287 + 2 29 27 56 Co -56040.518 0.475 8694.8386 0.0085 B- -2132.8689 0.3735 55 939838.032 0.510 + 0 28 28 56 Ni -53907.649 0.399 8642.7811 0.0071 B- -15277.9163 6.4070 55 942127.761 0.428 + -2 27 29 56 Cu x -38629.733 6.395 8355.9907 0.1142 B- -13240# 400# 55 958529.278 6.864 + -4 26 30 56 Zn x -25390# 400# 8106# 7# B- -21550# 640# 55 972743# 429# + -6 25 31 56 Ga x -3840# 500# 7707# 9# B- * 55 995878# 537# +0 19 38 19 57 K x 14130# 600# 7563# 11# B- 20689# 721# 57 015169# 644# + 17 37 20 57 Ca x -6560# 400# 7912# 7# B- 14820# 438# 56 992958# 429# + 15 36 21 57 Sc x -21379.653 179.778 8158.1666 3.1540 B- 13022.1957 273.3249 56 977048.000 193.000 + 13 35 22 57 Ti x -34401.848 205.879 8372.9008 3.6119 B- 10033.2148 222.6466 56 963068.098 221.020 + 11 34 23 57 V x -44435.063 84.766 8535.1967 1.4871 B- 8089.9214 84.7864 56 952297.000 91.000 + 9 33 24 57 Cr x -52524.985 1.863 8663.3998 0.0327 B- 4961.2946 2.3948 56 943612.112 2.000 + 7 32 25 57 Mn -57486.279 1.505 8736.7146 0.0264 B- 2695.7375 1.5219 56 938285.944 1.615 + 5 31 26 57 Fe -60182.017 0.268 8770.2829 0.0047 B- -836.3589 0.4493 56 935391.950 0.287 + 3 30 27 57 Co -59345.658 0.516 8741.8845 0.0090 B- -3261.6970 0.6417 56 936289.819 0.553 + 1 29 28 57 Ni -56083.961 0.566 8670.9364 0.0099 B- -8774.9466 0.4393 56 939791.394 0.608 + -1 28 29 57 Cu -47309.014 0.501 8503.2646 0.0088 B- -14759# 200# 56 949211.686 0.537 + -3 27 30 57 Zn x -32550# 200# 8231# 4# B- -17140# 447# 56 965056# 215# + -5 26 31 57 Ga x -15410# 400# 7916# 7# B- * 56 983457# 429# +0 20 39 19 58 K x 21930# 700# 7437# 12# B- 23461# 860# 58 023543# 751# + 18 38 20 58 Ca x -1530# 500# 7828# 9# B- 13949# 535# 57 998357# 537# + 16 37 21 58 Sc x -15479.569 190.025 8054.9436 3.2763 B- 15438.0995 264.0509 57 983382.000 204.000 + 14 36 22 58 Ti x -30917.668 183.340 8307.6290 3.1610 B- 9512.9152 206.8675 57 966808.519 196.823 + 12 35 23 58 V x -40430.584 95.816 8458.1560 1.6520 B- 11561.2240 95.8625 57 956595.985 102.862 + 10 34 24 58 Cr x -51991.808 2.981 8643.9987 0.0514 B- 3835.7607 4.0227 57 944184.501 3.200 + 8 33 25 58 Mn x -55827.568 2.701 8696.6438 0.0466 B- 6327.7027 2.7198 57 940066.643 2.900 + 6 32 26 58 Fe -62155.271 0.316 8792.2534 0.0055 B- -2307.9785 1.1389 57 933273.575 0.339 + 4 31 27 58 Co -59847.292 1.153 8738.9719 0.0199 B- 381.5789 1.1071 57 935751.292 1.237 + 2 30 28 58 Ni -60228.871 0.349 8732.0621 0.0060 B- -8561.0204 0.4425 57 935341.650 0.374 + 0 29 29 58 Cu -51667.851 0.564 8570.9696 0.0097 B- -9368.9796 50.0020 57 944532.283 0.604 + -2 28 30 58 Zn -- -42298.871 50.001 8395.9467 0.8621 B- -18759# 304# 57 954590.296 53.678 + -4 27 31 58 Ga x -23540# 300# 8059# 5# B- -15960# 583# 57 974729# 322# + -6 26 32 58 Ge x -7580# 500# 7770# 9# B- * 57 991863# 537# +0 21 40 19 59 K x 28750# 800# 7332# 14# B- 22940# 1000# 59 030864# 859# + 19 39 20 59 Ca x 5810# 600# 7708# 10# B- 16639# 650# 59 006237# 644# + 17 38 21 59 Sc x -10829.550 249.640 7976.4073 4.2312 B- 15050# 390# 58 988374.000 268.000 + 15 37 22 59 Ti x -25880# 300# 8218# 5# B- 11731# 330# 58 972217# 322# + 13 36 23 59 V x -37610.617 137.400 8403.8034 2.3288 B- 10505.3137 137.4021 58 959623.343 147.505 + 11 35 24 59 Cr x -48115.931 0.671 8568.5995 0.0114 B- 7409.3977 2.4234 58 948345.426 0.720 + 9 34 25 59 Mn x -55525.328 2.329 8680.9224 0.0395 B- 5139.6290 2.3520 58 940391.111 2.500 + 7 33 26 59 Fe -60664.957 0.330 8754.7746 0.0056 B- 1564.8804 0.3690 58 934873.492 0.354 + 5 32 27 59 Co -62229.838 0.397 8768.0379 0.0067 B- -1073.0050 0.1944 58 933193.524 0.426 + 3 31 28 59 Ni -61156.833 0.351 8736.5912 0.0060 B- -4798.3786 0.3973 58 934345.442 0.376 + 1 30 29 59 Cu -56358.454 0.528 8642.0027 0.0090 B- -9142.7760 0.6018 58 939496.713 0.566 + -1 29 30 59 Zn -47215.678 0.759 8473.7802 0.0129 B- -13456# 170# 58 949311.886 0.814 + -3 28 31 59 Ga x -33760# 170# 8232# 3# B- -17390# 434# 58 963757# 183# + -5 27 32 59 Ge x -16370# 400# 7924# 7# B- * 58 982426# 429# +0 20 40 20 60 Ca x 11000# 700# 7627# 12# B- 15550# 860# 60 011809# 751# + 18 39 21 60 Sc x -4550# 500# 7873# 8# B- 17549# 555# 59 995115# 537# + 16 38 22 60 Ti x -22099.698 240.325 8152.7858 4.0054 B- 10987.7040 301.4311 59 976275.000 258.000 + 14 37 23 60 V x -33087.402 181.946 8322.8751 3.0324 B- 13821.0986 181.9496 59 964479.215 195.327 + 12 36 24 60 Cr x -46908.500 1.118 8540.1876 0.0186 B- 6059.4457 2.5831 59 949641.656 1.200 + 10 35 25 60 Mn x -52967.946 2.329 8628.1392 0.0388 B- 8445.2283 4.1261 59 943136.574 2.500 + 8 34 26 60 Fe -nn -61413.174 3.406 8755.8539 0.0568 B- 237.2633 3.4106 59 934070.249 3.656 + 6 33 27 60 Co -n -61650.437 0.403 8746.7692 0.0067 B- 2822.8058 0.2124 59 933815.536 0.433 + 4 32 28 60 Ni -64473.243 0.353 8780.7769 0.0059 B- -6127.9810 1.5735 59 930785.129 0.378 + 2 31 29 60 Cu - -58345.262 1.613 8665.6047 0.0269 B- -4170.7922 1.6286 59 937363.787 1.731 + 0 30 30 60 Zn -54174.470 0.548 8583.0524 0.0091 B- -14584# 200# 59 941841.317 0.588 + -2 29 31 60 Ga x -39590# 200# 8327# 3# B- -12060# 361# 59 957498# 215# + -4 28 32 60 Ge x -27530# 300# 8113# 5# B- -21890# 500# 59 970445# 322# + -6 27 33 60 As x -5640# 400# 7735# 7# B- * 59 993945# 429# +0 21 41 20 61 Ca x 19010# 800# 7503# 13# B- 18510# 1000# 61 020408# 859# + 19 40 21 61 Sc x 500# 600# 7794# 10# B- 16870# 671# 61 000537# 644# + 17 39 22 61 Ti x -16370# 300# 8058# 5# B- 13807# 381# 60 982426# 322# + 15 38 23 61 V x -30177.121 234.920 8271.0417 3.8511 B- 12319.3807 234.9272 60 967603.529 252.196 + 13 37 24 61 Cr x -42496.502 1.863 8460.1734 0.0305 B- 9245.6277 2.9822 60 954378.130 2.000 + 11 36 25 61 Mn x -51742.130 2.329 8598.9157 0.0382 B- 7178.3730 3.4965 60 944452.541 2.500 + 9 35 26 61 Fe x -58920.503 2.608 8703.7686 0.0428 B- 3977.6759 2.7399 60 936746.241 2.800 + 7 34 27 61 Co p2n -62898.179 0.839 8756.1510 0.0138 B- 1323.8504 0.7899 60 932476.031 0.901 + 5 33 28 61 Ni -64222.029 0.355 8765.0281 0.0058 B- -2237.9663 0.9617 60 931054.819 0.381 + 3 32 29 61 Cu p2n -61984.063 0.951 8715.5148 0.0156 B- -5635.1565 15.9028 60 933457.375 1.020 + 1 31 30 61 Zn -56348.906 15.899 8610.3098 0.2606 B- -9214.2438 37.6786 60 939506.964 17.068 + -1 30 31 61 Ga -47134.662 37.994 8446.4313 0.6228 B- -13345# 302# 60 949398.861 40.787 + -3 29 32 61 Ge x -33790# 300# 8215# 5# B- -16590# 424# 60 963725# 322# + -5 28 33 61 As x -17200# 300# 7930# 5# B- * 60 981535# 322# +0 20 41 21 62 Sc x 7310# 600# 7688# 10# B- 19510# 721# 62 007848# 644# + 18 40 22 62 Ti x -12200# 400# 7990# 6# B- 13013# 479# 61 986903# 429# + 16 39 23 62 V x -25213.164 264.287 8187.7565 4.2627 B- 15639.4478 264.3094 61 972932.556 283.723 + 14 38 24 62 Cr x -40852.611 3.447 8427.3871 0.0556 B- 7671.3532 7.3947 61 956142.920 3.700 + 12 37 25 62 Mn IT -48523.965 6.542 8538.5002 0.1055 B- 10354.0920 7.1142 61 947907.384 7.023 + 10 36 26 62 Fe x -58878.057 2.794 8692.8831 0.0451 B- 2546.3427 18.7834 61 936791.809 3.000 + 8 35 27 62 Co + -61424.399 18.574 8721.3347 0.2996 B- 5322.0404 18.5695 61 934058.198 19.940 + 6 34 28 62 Ni -66746.440 0.425 8794.5555 0.0069 B- -3958.8965 0.4751 61 928344.753 0.455 + 4 33 29 62 Cu - -62787.543 0.637 8718.0839 0.0103 B- -1619.4548 0.6507 61 932594.803 0.683 + 2 32 30 62 Zn -61168.088 0.615 8679.3451 0.0099 B- -9181.0666 0.3763 61 934333.359 0.660 + 0 31 31 62 Ga -51987.022 0.637 8518.6449 0.0103 B- -9847# 140# 61 944189.639 0.684 + -2 30 32 62 Ge x -42140# 140# 8347# 2# B- -17720# 331# 61 954761# 150# + -4 29 33 62 As x -24420# 300# 8049# 5# B- * 61 973784# 322# +0 21 42 21 63 Sc x 13070# 700# 7603# 11# B- 18930# 860# 63 014031# 751# + 19 41 22 63 Ti x -5860# 500# 7891# 8# B- 15880# 605# 62 993709# 537# + 17 40 23 63 V x -21740.141 339.995 8130.7809 5.3968 B- 14438.1586 347.6720 62 976661.000 365.000 + 15 39 24 63 Cr x -36178.299 72.657 8347.5398 1.1533 B- 10708.7611 72.7520 62 961161.000 78.000 + 13 38 25 63 Mn x -46887.061 3.726 8505.1020 0.0591 B- 8748.5685 5.6915 62 949664.672 4.000 + 11 37 26 63 Fe -55635.629 4.302 8631.5499 0.0683 B- 6215.9238 19.0668 62 940272.698 4.618 + 9 36 27 63 Co -61851.553 18.575 8717.7972 0.2948 B- 3661.3385 18.5704 62 933599.630 19.941 + 7 35 28 63 Ni -65512.891 0.426 8763.4955 0.0068 B- 66.9768 0.0149 62 929669.021 0.457 + 5 34 29 63 Cu -65579.868 0.426 8752.1404 0.0068 B- -3366.4392 1.5450 62 929597.119 0.457 + 3 33 30 63 Zn -62213.429 1.560 8686.2866 0.0248 B- -5666.3294 2.0330 62 933211.140 1.674 + 1 32 31 63 Ga x -56547.100 1.304 8583.9267 0.0207 B- -9625.8787 37.2826 62 939294.194 1.400 + -1 31 32 63 Ge x -46921.221 37.260 8418.7167 0.5914 B- -13421# 204# 62 949628.000 40.000 + -3 30 33 63 As x -33500# 200# 8193# 3# B- -16650# 539# 62 964036# 215# + -5 29 34 63 Se x -16850# 500# 7917# 8# B- * 62 981911# 537# +0 20 42 22 64 Ti x -1480# 600# 7826# 9# B- 14840# 721# 63 998411# 644# + 18 41 23 64 V x -16320# 400# 8045# 6# B- 17320# 500# 63 982480# 429# + 16 40 24 64 Cr x -33639.978 299.941 8303.5626 4.6866 B- 9349.0622 299.9620 63 963886.000 322.000 + 14 39 25 64 Mn x -42989.040 3.540 8437.4175 0.0553 B- 11980.5117 6.1402 63 953849.369 3.800 + 12 38 26 64 Fe x -54969.552 5.017 8612.3888 0.0784 B- 4822.8898 20.6249 63 940987.761 5.386 + 10 37 27 64 Co + -59792.442 20.005 8675.5223 0.3126 B- 7306.5921 20.0000 63 935810.176 21.476 + 8 36 28 64 Ni -67099.034 0.463 8777.4637 0.0072 B- -1674.6156 0.2055 63 927966.228 0.497 + 6 35 29 64 Cu -65424.418 0.427 8739.0736 0.0067 B- 579.5996 0.6447 63 929764.001 0.458 + 4 34 30 64 Zn -66004.018 0.644 8735.9057 0.0101 B- -7171.1912 1.4825 63 929141.776 0.690 + 2 33 31 64 Ga -58832.827 1.429 8611.6317 0.0223 B- -4517.3237 3.9905 63 936840.366 1.533 + 0 32 32 64 Ge x -54315.503 3.726 8528.8243 0.0582 B- -14783# 203# 63 941689.912 4.000 + -2 31 33 64 As -p -39532# 203# 8286# 3# B- -12673# 540# 63 957560# 218# + -4 30 34 64 Se x -26860# 500# 8075# 8# B- * 63 971165# 537# +0 21 43 22 65 Ti x 5210# 700# 7726# 11# B- 17320# 860# 65 005593# 751# + 19 42 23 65 V x -12110# 500# 7981# 8# B- 16200# 539# 64 986999# 537# + 17 41 24 65 Cr x -28310# 200# 8218# 3# B- 12657# 200# 64 969608# 215# + 15 40 25 65 Mn x -40967.344 3.726 8400.6822 0.0573 B- 10250.5576 6.3257 64 956019.749 4.000 + 13 39 26 65 Fe x -51217.902 5.112 8546.3470 0.0786 B- 7967.3036 5.5198 64 945015.323 5.487 + 11 38 27 65 Co x -59185.205 2.083 8656.8848 0.0320 B- 5940.5911 2.1379 64 936462.071 2.235 + 9 37 28 65 Ni -n -65125.796 0.483 8736.2424 0.0074 B- 2137.8808 0.6997 64 930084.585 0.518 + 7 36 29 65 Cu -67263.677 0.643 8757.0967 0.0099 B- -1351.6527 0.3557 64 927789.476 0.690 + 5 35 30 65 Zn -65912.024 0.646 8724.2660 0.0099 B- -3254.5380 0.6305 64 929240.534 0.693 + 3 34 31 65 Ga -62657.486 0.791 8662.1601 0.0122 B- -6179.2631 2.3046 64 932734.424 0.849 + 1 33 32 65 Ge -56478.223 2.165 8555.0584 0.0333 B- -9541.1670 84.7936 64 939368.136 2.323 + -1 32 33 65 As x -46937.056 84.766 8396.2351 1.3041 B- -13917# 312# 64 949611.000 91.000 + -3 31 34 65 Se x -33020# 300# 8170# 5# B- -16529# 583# 64 964552# 322# + -5 30 35 65 Br x -16490# 500# 7904# 8# B- * 64 982297# 537# +0 20 43 23 66 V x -6300# 500# 7894# 8# B- 18840# 583# 65 993237# 537# + 18 42 24 66 Cr x -25140# 300# 8168# 5# B- 11610# 300# 65 973011# 322# + 16 41 25 66 Mn x -36750.392 11.178 8331.7986 0.1694 B- 13317.4543 11.9056 65 960546.833 12.000 + 14 40 26 66 Fe x -50067.847 4.099 8521.7245 0.0621 B- 6340.6944 14.5611 65 946249.958 4.400 + 12 39 27 66 Co x -56408.541 13.972 8605.9419 0.2117 B- 9597.7522 14.0421 65 939442.943 15.000 + 10 38 28 66 Ni x -66006.293 1.397 8739.5086 0.0212 B- 251.9958 1.5405 65 929139.333 1.500 + 8 37 29 66 Cu -66258.289 0.649 8731.4730 0.0098 B- 2640.9396 0.9255 65 928868.804 0.696 + 6 36 30 66 Zn -68899.229 0.744 8759.6335 0.0113 B- -5175.5000 0.8000 65 926033.639 0.798 + 4 35 31 66 Ga - -63723.729 1.092 8669.3631 0.0166 B- -2116.6879 2.6376 65 931589.766 1.172 + 2 34 32 66 Ge x -61607.041 2.401 8625.4383 0.0364 B- -9581.9570 6.1685 65 933862.124 2.577 + 0 33 33 66 As x -52025.084 5.682 8468.4034 0.0861 B- -10365# 200# 65 944148.778 6.100 + -2 32 34 66 Se x -41660# 200# 8300# 3# B- -18091# 447# 65 955276# 215# + -4 31 35 66 Br x -23570# 400# 8014# 6# B- * 65 974697# 429# +0 21 44 23 67 V x -1744# 600# 7829# 9# B- 17526# 721# 66 998128# 644# + 19 43 24 67 Cr x -19270# 400# 8079# 6# B- 14311# 447# 66 979313# 429# + 17 42 25 67 Mn x -33580# 200# 8281# 3# B- 12128# 200# 66 963950# 215# + 15 41 26 67 Fe x -45708.416 3.819 8449.9359 0.0570 B- 9613.3678 7.4900 66 950930.000 4.100 + 13 40 27 67 Co x -55321.783 6.443 8581.7422 0.0962 B- 8420.9047 7.0607 66 940609.625 6.917 + 11 39 28 67 Ni x -63742.688 2.888 8695.7505 0.0431 B- 3576.8654 3.0223 66 931569.413 3.100 + 9 38 29 67 Cu -67319.553 0.892 8737.4597 0.0133 B- 560.8226 0.8296 66 927729.490 0.957 + 7 37 30 67 Zn -67880.376 0.755 8734.1534 0.0113 B- -1001.2201 1.1196 66 927127.422 0.810 + 5 36 31 67 Ga -66879.156 1.176 8707.5330 0.0176 B- -4205.4380 4.4066 66 928202.276 1.262 + 3 35 32 67 Ge -62673.718 4.319 8633.0884 0.0645 B- -6086.4858 4.3418 66 932716.999 4.636 + 1 34 33 67 As -56587.232 0.443 8530.5685 0.0066 B- -10006.9381 67.0690 66 939251.110 0.475 + -1 33 34 67 Se x -46580.294 67.068 8369.5344 1.0010 B- -14051# 307# 66 949994.000 72.000 + -3 32 35 67 Br x -32530# 300# 8148# 4# B- -16978# 520# 66 965078# 322# + -5 31 36 67 Kr -pp -15552# 424# 7883# 6# B- * 66 983305# 455# +0 20 44 24 68 Cr x -15690# 500# 8026# 7# B- 13230# 583# 67 983156# 537# + 18 43 25 68 Mn x -28920# 300# 8209# 4# B- 14977# 356# 67 968953# 322# + 16 42 26 68 Fe x -43897# 193# 8418# 3# B- 7746# 193# 67 952875# 207# + 14 41 27 68 Co -51642.591 3.859 8520.1301 0.0567 B- 11821.2318 4.8760 67 944559.401 4.142 + 12 40 28 68 Ni x -63463.822 2.981 8682.4667 0.0438 B- 2103.2205 3.3753 67 931868.787 3.200 + 10 39 29 68 Cu x -65567.043 1.584 8701.8913 0.0233 B- 4440.1115 1.7645 67 929610.887 1.700 + 8 38 30 68 Zn -70007.154 0.778 8755.6820 0.0115 B- -2921.1000 1.2000 67 924844.232 0.835 + 6 37 31 68 Ga - -67086.054 1.430 8701.2195 0.0210 B- -107.2555 2.3594 67 927980.161 1.535 + 4 36 32 68 Ge x -66978.799 1.876 8688.1371 0.0276 B- -8084.2715 2.6320 67 928095.305 2.014 + 2 35 33 68 As -58894.527 1.846 8557.7457 0.0271 B- -4705.0786 1.9112 67 936774.127 1.981 + 0 34 34 68 Se x -54189.449 0.496 8477.0482 0.0073 B- -15398# 259# 67 941825.236 0.532 + -2 33 35 68 Br -p -38791# 259# 8239# 4# B- -13165# 563# 67 958356# 278# + -4 32 36 68 Kr x -25626# 500# 8034# 7# B- * 67 972489# 537# +0 21 45 24 69 Cr x -9630# 500# 7939# 7# B- 15730# 640# 68 989662# 537# + 19 44 25 69 Mn x -25360# 400# 8155# 6# B- 13839# 447# 68 972775# 429# + 17 43 26 69 Fe x -39199# 200# 8345# 3# B- 11186# 218# 68 957918# 215# + 15 42 27 69 Co x -50385.447 85.697 8495.4062 1.2420 B- 9593.2084 85.7784 68 945909.000 92.000 + 13 41 28 69 Ni x -59978.656 3.726 8623.0998 0.0540 B- 5757.5650 3.9793 68 935610.267 4.000 + 11 40 29 69 Cu x -65736.221 1.397 8695.2044 0.0203 B- 2681.6854 1.6075 68 929429.267 1.500 + 9 39 30 69 Zn -n -68417.906 0.795 8722.7311 0.0115 B- 909.9134 1.4234 68 926550.360 0.853 + 7 38 31 69 Ga -69327.820 1.197 8724.5798 0.0174 B- -2227.1455 0.5500 68 925573.528 1.285 + 5 37 32 69 Ge -67100.674 1.318 8680.9640 0.0191 B- -3988.4927 31.9822 68 927964.467 1.414 + 3 36 33 69 As -63112.181 31.999 8611.8214 0.4638 B- -6677.4672 32.0215 68 932246.289 34.352 + 1 35 34 69 Se -56434.714 1.490 8503.7082 0.0216 B- -10175.2364 42.0293 68 939414.845 1.599 + -1 34 35 69 Br -p -46259.478 42.003 8344.9026 0.6087 B- -14119# 303# 68 950338.410 45.091 + -3 33 36 69 Kr x -32140# 300# 8129# 4# B- * 68 965496# 322# +0 22 46 24 70 Cr x -5640# 600# 7884# 9# B- 14810# 781# 69 993945# 644# + 20 45 25 70 Mn x -20450# 500# 8084# 7# B- 16440# 583# 69 978046# 537# + 18 44 26 70 Fe x -36890# 300# 8308# 4# B- 9635# 300# 69 960397# 322# + 16 43 27 70 Co x -46524.963 10.992 8434.1980 0.1570 B- 12688.9049 11.1987 69 950053.400 11.800 + 14 42 28 70 Ni x -59213.868 2.144 8604.2917 0.0306 B- 3762.5123 2.4011 69 936431.300 2.301 + 12 41 29 70 Cu x -62976.381 1.082 8646.8655 0.0155 B- 6588.3675 2.2018 69 932392.078 1.161 + 10 40 30 70 Zn -69564.748 1.918 8729.8086 0.0274 B- -654.5979 1.5737 69 925319.175 2.058 + 8 39 31 70 Ga -68910.150 1.201 8709.2808 0.0172 B- 1651.8861 1.4520 69 926021.914 1.289 + 6 38 32 70 Ge -70562.036 0.820 8721.7028 0.0117 B- -6228.0630 1.6200 69 924248.542 0.880 + 4 37 33 70 As x -64333.973 1.397 8621.5541 0.0200 B- -2404.0737 2.1118 69 930934.642 1.500 + 2 36 34 70 Se x -61929.900 1.584 8576.0338 0.0226 B- -10504.2727 14.9878 69 933515.521 1.700 + 0 35 35 70 Br x -51425.627 14.904 8414.7964 0.2129 B- -10325# 201# 69 944792.321 16.000 + -2 34 36 70 Kr x -41100# 200# 8256# 3# B- * 69 955877# 215# +0 21 46 25 71 Mn x -16620# 500# 8030# 7# B- 15310# 640# 70 982158# 537# + 19 45 26 71 Fe x -31930# 400# 8235# 6# B- 12440# 613# 70 965722# 429# + 17 44 27 71 Co x -44369.930 465.030 8398.7344 6.5497 B- 11036.3053 465.0353 70 952366.923 499.230 + 15 43 28 71 Ni x -55406.236 2.237 8543.1564 0.0315 B- 7304.8989 2.6879 70 940518.962 2.401 + 13 42 29 71 Cu x -62711.134 1.490 8635.0233 0.0210 B- 4617.6517 3.0437 70 932676.831 1.600 + 11 41 30 71 Zn -67328.786 2.654 8689.0417 0.0374 B- 2810.3405 2.7748 70 927719.578 2.849 + 9 40 31 71 Ga -70139.127 0.811 8717.6050 0.0114 B- -232.4698 0.0934 70 924702.554 0.870 + 7 39 32 71 Ge -69906.657 0.815 8703.3118 0.0115 B- -2013.4000 4.0825 70 924952.120 0.874 + 5 38 33 71 As - -67893.257 4.163 8663.9350 0.0586 B- -4746.7420 5.0140 70 927113.594 4.469 + 3 37 34 71 Se x -63146.515 2.794 8586.0606 0.0394 B- -6644.0883 6.0820 70 932209.431 3.000 + 1 36 35 71 Br -56502.426 5.402 8481.4629 0.0761 B- -10175.2155 128.8452 70 939342.153 5.799 + -1 35 36 71 Kr -46327.211 128.769 8327.1310 1.8136 B- -14037# 420# 70 950265.695 138.238 + -3 34 37 71 Rb x -32290# 400# 8118# 6# B- * 70 965335# 429# +0 22 47 25 72 Mn x -11170# 600# 7955# 8# B- 18080# 781# 71 988009# 644# + 20 46 26 72 Fe x -29250# 500# 8195# 7# B- 11050# 583# 71 968599# 537# + 18 45 27 72 Co x -40300# 300# 8338# 4# B- 13926# 300# 71 956736# 322# + 16 44 28 72 Ni x -54226.068 2.237 8520.2118 0.0311 B- 5556.9381 2.6374 71 941785.924 2.401 + 14 43 29 72 Cu x -59783.006 1.397 8586.5256 0.0194 B- 8362.4883 2.5578 71 935820.306 1.500 + 12 42 30 72 Zn x -68145.495 2.142 8691.8053 0.0298 B- 442.7892 2.2934 71 926842.806 2.300 + 10 41 31 72 Ga -68588.284 0.818 8687.0893 0.0114 B- 3997.6263 0.8217 71 926367.452 0.878 + 8 40 32 72 Ge -72585.910 0.076 8731.7459 0.0011 B- -4356.1019 4.0825 71 922075.824 0.081 + 6 39 33 72 As - -68229.808 4.083 8660.3786 0.0567 B- -361.6194 4.5276 71 926752.291 4.383 + 4 38 34 72 Se x -67868.189 1.956 8644.4902 0.0272 B- -8806.4384 2.2083 71 927140.506 2.100 + 2 37 35 72 Br x -59061.750 1.025 8511.3126 0.0142 B- -5121.1683 8.0761 71 936594.606 1.100 + 0 36 36 72 Kr x -53940.582 8.011 8429.3193 0.1113 B- -15611# 500# 71 942092.406 8.600 + -2 35 37 72 Rb x -38330# 500# 8202# 7# B- * 71 958851# 537# +0 23 48 25 73 Mn x -6700# 600# 7895# 8# B- 17289# 781# 72 992807# 644# + 21 47 26 73 Fe x -23990# 500# 8121# 7# B- 13980# 583# 72 974246# 537# + 19 46 27 73 Co x -37970# 300# 8302# 4# B- 12139# 300# 72 959238# 322# + 17 45 28 73 Ni x -50108.159 2.423 8457.6529 0.0332 B- 8879.2856 3.1038 72 946206.681 2.601 + 15 44 29 73 Cu -58987.445 1.942 8568.5699 0.0266 B- 6605.9659 2.6910 72 936674.376 2.084 + 13 43 30 73 Zn x -65593.411 1.863 8648.3455 0.0255 B- 4105.9329 2.5064 72 929582.580 2.000 + 11 42 31 73 Ga x -69699.343 1.677 8693.8740 0.0230 B- 1598.1889 1.6777 72 925174.680 1.800 + 9 41 32 73 Ge -71297.532 0.057 8705.0500 0.0008 B- -344.7759 3.8528 72 923458.954 0.061 + 7 40 33 73 As -70952.757 3.853 8689.6099 0.0528 B- -2725.3604 7.3993 72 923829.086 4.136 + 5 39 34 73 Se -68227.396 7.424 8641.5591 0.1017 B- -4581.6095 10.0278 72 926754.881 7.969 + 3 38 35 73 Br -63645.787 6.741 8568.0803 0.0923 B- -7094.0287 9.4187 72 931673.441 7.237 + 1 37 36 73 Kr x -56551.758 6.578 8460.1847 0.0901 B- -10540.1468 41.3212 72 939289.193 7.061 + -1 36 37 73 Rb -p -46011.611 40.794 8305.0821 0.5588 B- -14061# 403# 72 950604.506 43.794 + -3 35 38 73 Sr x -31950# 401# 8102# 5# B- * 72 965700# 430# +0 22 48 26 74 Fe x -20660# 500# 8076# 7# B- 12881# 640# 73 977821# 537# + 20 47 27 74 Co x -33540# 400# 8239# 5# B- 15160# 447# 73 963993# 429# + 18 46 28 74 Ni x -48700# 200# 8433# 3# B- 7306# 200# 73 947718# 215# + 16 45 29 74 Cu x -56006.213 6.148 8521.5633 0.0831 B- 9750.5077 6.6424 73 939874.860 6.600 + 14 44 30 74 Zn x -65756.720 2.515 8642.7547 0.0340 B- 2292.9057 3.9102 73 929407.260 2.700 + 12 43 31 74 Ga x -68049.626 2.994 8663.1676 0.0405 B- 5372.8249 2.9941 73 926945.725 3.214 + 10 42 32 74 Ge -73422.451 0.013 8725.2011 0.0003 B- -2562.3871 1.6931 73 921177.760 0.013 + 8 41 33 74 As -70860.064 1.693 8680.0020 0.0229 B- 1353.1467 1.6931 73 923928.596 1.817 + 6 40 34 74 Se -72213.210 0.015 8687.7155 0.0003 B- -6925.0492 5.8354 73 922475.933 0.015 + 4 39 35 74 Br -65288.161 5.835 8583.5615 0.0789 B- -2956.3173 6.1730 73 929910.279 6.264 + 2 38 36 74 Kr -62331.844 2.013 8533.0390 0.0272 B- -10415.8280 3.4240 73 933084.016 2.161 + 0 37 37 74 Rb -51916.016 3.027 8381.7123 0.0409 B- -11089# 100# 73 944265.867 3.249 + -2 36 38 74 Sr x -40827# 100# 8221# 1# B- * 73 956170# 107# +0 23 49 26 75 Fe x -14700# 600# 7996# 8# B- 15861# 721# 74 984219# 644# + 21 48 27 75 Co x -30560# 400# 8197# 5# B- 13680# 447# 74 967192# 429# + 19 47 28 75 Ni x -44240# 200# 8369# 3# B- 10230# 200# 74 952506# 215# + 17 46 29 75 Cu -54470.219 0.718 8495.0801 0.0096 B- 8088.6967 2.0837 74 941523.817 0.770 + 15 45 30 75 Zn x -62558.916 1.956 8592.4981 0.0261 B- 5901.7231 2.0679 74 932840.244 2.100 + 13 44 31 75 Ga x -68460.639 0.671 8660.7565 0.0089 B- 3396.3337 0.6727 74 926504.484 0.720 + 11 43 32 75 Ge -n -71856.973 0.052 8695.6096 0.0007 B- 1177.2301 0.8851 74 922858.370 0.055 + 9 42 33 75 As -73034.203 0.884 8700.8748 0.0118 B- -864.7139 0.8816 74 921594.562 0.948 + 7 41 34 75 Se -72169.489 0.073 8678.9139 0.0010 B- -3062.4694 4.2855 74 922522.870 0.078 + 5 40 35 75 Br x -69107.020 4.285 8627.6497 0.0571 B- -4783.3880 9.1671 74 925810.566 4.600 + 3 39 36 75 Kr x -64323.632 8.104 8553.4399 0.1081 B- -7104.9299 8.1895 74 930945.744 8.700 + 1 38 37 75 Rb x -57218.702 1.180 8448.2762 0.0157 B- -10600.0000 220.0000 74 938573.200 1.266 + -1 37 38 75 Sr - -46618.702 220.003 8296.5116 2.9334 B- -14799# 372# 74 949952.767 236.183 + -3 36 39 75 Y x -31820# 300# 8089# 4# B- * 74 965840# 322# +0 24 50 26 76 Fe x -10590# 600# 7943# 8# B- 15070# 781# 75 988631# 644# + 22 49 27 76 Co x -25660# 500# 8131# 7# B- 16530# 583# 75 972453# 537# + 20 48 28 76 Ni x -42190# 300# 8338# 4# B- 8791# 300# 75 954707# 322# + 18 47 29 76 Cu x -50981.627 0.913 8443.6018 0.0120 B- 11321.3964 1.7183 75 945268.974 0.980 + 16 46 30 76 Zn -62303.024 1.456 8582.2735 0.0192 B- 3993.6241 2.4384 75 933114.956 1.562 + 14 45 31 76 Ga x -66296.648 1.956 8624.5272 0.0257 B- 6916.2501 1.9562 75 928827.624 2.100 + 12 44 32 76 Ge -73212.898 0.018 8705.2364 0.0003 B- -921.5145 0.8864 75 921402.725 0.019 + 10 43 33 76 As -n -72291.384 0.886 8682.8172 0.0117 B- 2960.5756 0.8864 75 922392.011 0.951 + 8 42 34 76 Se -75251.959 0.016 8711.4781 0.0003 B- -4962.8810 9.3218 75 919213.702 0.017 + 6 41 35 76 Br - -70289.078 9.322 8635.8830 0.1227 B- -1275.3724 10.1490 75 924541.574 10.007 + 4 40 36 76 Kr -69013.706 4.013 8608.8077 0.0528 B- -8534.6172 4.1214 75 925910.743 4.308 + 2 39 37 76 Rb x -60479.089 0.938 8486.2161 0.0123 B- -6231.4432 34.4780 75 935073.031 1.006 + 0 38 38 76 Sr x -54247.645 34.465 8393.9294 0.4535 B- -15998# 302# 75 941762.760 37.000 + -2 37 39 76 Y x -38250# 300# 8173# 4# B- * 75 958937# 322# +0 23 50 27 77 Co x -21910# 600# 8082# 8# B- 15440# 721# 76 976479# 644# + 21 49 28 77 Ni x -37350# 400# 8272# 5# B- 11513# 400# 76 959903# 429# + 19 48 29 77 Cu x -48862.828 1.211 8411.2501 0.0157 B- 9926.3750 2.3148 76 947543.599 1.300 + 17 47 30 77 Zn -58789.203 1.973 8530.0037 0.0256 B- 7203.1495 3.1237 76 936887.197 2.117 + 15 46 31 77 Ga x -65992.352 2.422 8613.3907 0.0315 B- 5220.5176 2.4225 76 929154.299 2.600 + 13 45 32 77 Ge -n -71212.870 0.053 8671.0293 0.0007 B- 2703.4642 1.6926 76 923549.843 0.056 + 11 44 33 77 As -73916.334 1.692 8695.9789 0.0220 B- 683.1627 1.6920 76 920647.555 1.816 + 9 43 34 77 Se -74599.497 0.062 8694.6908 0.0008 B- -1364.6792 2.8099 76 919914.150 0.067 + 7 42 35 77 Br - -73234.818 2.811 8666.8073 0.0365 B- -3065.3663 3.4244 76 921379.193 3.017 + 5 41 36 77 Kr x -70169.451 1.956 8616.8370 0.0254 B- -5338.9516 2.3510 76 924669.999 2.100 + 3 40 37 77 Rb x -64830.500 1.304 8537.3396 0.0169 B- -7027.0566 8.0244 76 930401.599 1.400 + 1 39 38 77 Sr x -57803.443 7.918 8435.9188 0.1028 B- -11365# 203# 76 937945.454 8.500 + -1 38 39 77 Y -p -46439# 203# 8278# 3# B- -14839# 448# 76 950146# 218# + -3 37 40 77 Zr x -31600# 400# 8075# 5# B- * 76 966076# 429# +0 24 51 27 78 Co x -15320# 700# 7997# 9# B- 19560# 806# 77 983553# 751# + 22 50 28 78 Ni x -34880# 400# 8238# 5# B- 9910# 400# 77 962555# 429# + 20 49 29 78 Cu -44789.474 13.332 8354.6695 0.1709 B- 12693.7680 13.4727 77 951916.524 14.312 + 18 48 30 78 Zn -57483.242 1.944 8507.3800 0.0249 B- 6220.8433 2.2088 77 938289.204 2.086 + 16 47 31 78 Ga -63704.085 1.051 8577.1043 0.0135 B- 8157.9729 3.6884 77 931610.854 1.127 + 14 46 32 78 Ge -nn -71862.058 3.536 8671.6636 0.0453 B- 954.9114 10.3987 77 922852.911 3.795 + 12 45 33 78 As +pn -72816.970 9.779 8673.8760 0.1254 B- 4208.9819 9.7801 77 921827.771 10.498 + 10 44 34 78 Se -77025.952 0.179 8717.8072 0.0023 B- -3573.7836 3.5750 77 917309.244 0.191 + 8 43 35 78 Br - -73452.168 3.580 8661.9594 0.0459 B- 726.1153 3.5845 77 921145.858 3.842 + 6 42 36 78 Kr -74178.283 0.307 8661.2385 0.0039 B- -7242.8560 3.2520 77 920366.341 0.329 + 4 41 37 78 Rb x -66935.427 3.237 8558.3512 0.0415 B- -3761.4779 8.1248 77 928141.866 3.475 + 2 40 38 78 Sr x -63173.949 7.452 8500.0971 0.0955 B- -11001# 298# 77 932179.979 8.000 + 0 39 39 78 Y x -52173# 298# 8349# 4# B- -11323# 499# 77 943990# 320# + -2 38 40 78 Zr x -40850# 400# 8194# 5# B- * 77 956146# 429# +0 23 51 28 79 Ni x -28160# 500# 8150# 6# B- 14248# 511# 78 969769# 537# + 21 50 29 79 Cu x -42408.039 104.979 8320.9380 1.3289 B- 11024.2629 105.0030 78 954473.100 112.700 + 19 49 30 79 Zn -53432.302 2.225 8450.5825 0.0282 B- 9116.0536 2.5295 78 942638.067 2.388 + 17 48 31 79 Ga -62548.355 1.208 8556.0725 0.0153 B- 6978.8242 37.1467 78 932851.582 1.296 + 15 47 32 79 Ge -69527.180 37.161 8634.5089 0.4704 B- 4108.9014 37.4361 78 925359.506 39.893 + 13 46 33 79 As -73636.081 5.325 8676.6172 0.0674 B- 2281.3849 5.3284 78 920948.419 5.716 + 11 45 34 79 Se -n -75917.466 0.223 8695.5923 0.0028 B- 150.6016 1.0186 78 918499.252 0.238 + 9 44 35 79 Br -76068.067 1.001 8687.5956 0.0127 B- -1625.7778 3.3333 78 918337.574 1.074 + 7 43 36 79 Kr - -74442.290 3.480 8657.1130 0.0441 B- -3639.5114 3.9423 78 920082.919 3.736 + 5 42 37 79 Rb -70802.778 1.943 8601.1401 0.0246 B- -5323.1140 7.5630 78 923990.095 2.085 + 3 41 38 79 Sr -65479.664 7.421 8523.8558 0.0939 B- -7676.7291 80.4515 78 929704.692 7.967 + 1 40 39 79 Y x -57802.935 80.108 8416.7788 1.0140 B- -11033# 310# 78 937946.000 86.000 + -1 39 40 79 Zr x -46770# 300# 8267# 4# B- -15120# 583# 78 949790# 322# + -3 38 41 79 Nb x -31650# 500# 8066# 6# B- * 78 966022# 537# +0 24 52 28 80 Ni x -23240# 600# 8088# 7# B- 13440# 671# 79 975051# 644# + 22 51 29 80 Cu x -36679# 300# 8246# 4# B- 14969# 300# 79 960623# 322# + 20 50 30 80 Zn -51648.619 2.585 8423.5457 0.0323 B- 7575.0553 3.8774 79 944552.929 2.774 + 18 49 31 80 Ga x -59223.675 2.891 8508.4545 0.0361 B- 10311.6397 3.5409 79 936420.773 3.103 + 16 48 32 80 Ge x -69535.314 2.054 8627.5707 0.0257 B- 2679.2869 3.9156 79 925350.773 2.205 + 14 47 33 80 As x -72214.601 3.333 8651.2824 0.0417 B- 5544.8861 3.4412 79 922474.440 3.578 + 12 46 34 80 Se -77759.487 0.947 8710.8142 0.0118 B- -1870.4623 0.3095 79 916521.761 1.016 + 10 45 35 80 Br -75889.025 0.993 8677.6541 0.0124 B- 2004.4299 1.1413 79 918529.784 1.065 + 8 44 36 80 Kr -77893.455 0.695 8692.9301 0.0087 B- -5717.9785 1.9883 79 916377.940 0.745 + 6 43 37 80 Rb x -72175.476 1.863 8611.6760 0.0233 B- -1864.0090 3.9331 79 922516.442 2.000 + 4 42 38 80 Sr x -70311.467 3.464 8578.5966 0.0433 B- -9163.3050 7.1389 79 924517.538 3.718 + 2 41 39 80 Y x -61148.162 6.242 8454.2759 0.0780 B- -6388# 300# 79 934354.750 6.701 + 0 40 40 80 Zr x -54760# 300# 8365# 4# B- -16339# 500# 79 941213# 322# + -2 39 41 80 Nb x -38420# 400# 8151# 5# B- * 79 958754# 429# +0 25 53 28 81 Ni x -16090# 700# 8000# 9# B- 15820# 761# 80 982727# 751# + 23 52 29 81 Cu x -31910# 300# 8185# 4# B- 14289# 300# 80 965743# 322# + 21 51 30 81 Zn x -46199.669 5.030 8351.9262 0.0621 B- 11428.2924 5.9960 80 950402.617 5.400 + 19 50 31 81 Ga x -57627.962 3.264 8483.3576 0.0403 B- 8663.7335 3.8508 80 938133.841 3.503 + 17 49 32 81 Ge x -66291.695 2.055 8580.6587 0.0254 B- 6241.6189 3.3436 80 928832.941 2.205 + 15 48 33 81 As -72533.314 2.644 8648.0571 0.0326 B- 3855.7050 2.8072 80 922132.288 2.838 + 13 47 34 81 Se -76389.019 0.977 8685.9998 0.0121 B- 1588.0317 1.3787 80 917993.019 1.049 + 11 46 35 81 Br -77977.051 0.978 8695.9465 0.0121 B- -280.8517 0.4713 80 916288.197 1.049 + 9 45 36 81 Kr -77696.199 1.074 8682.8206 0.0133 B- -2239.4954 5.0188 80 916589.703 1.152 + 7 44 37 81 Rb -75456.704 4.904 8645.5139 0.0605 B- -3928.5695 5.8170 80 918993.900 5.265 + 5 43 38 81 Sr x -71528.134 3.128 8587.3545 0.0386 B- -5815.2156 6.2451 80 923211.393 3.358 + 3 42 39 81 Y x -65712.919 5.405 8505.9031 0.0667 B- -8188.5003 92.3762 80 929454.283 5.802 + 1 41 40 81 Zr x -57524.418 92.218 8395.1519 1.1385 B- -11164# 410# 80 938245.000 99.000 + -1 40 41 81 Nb x -46360# 400# 8248# 5# B- -14900# 640# 80 950230# 429# + -3 39 42 81 Mo x -31460# 500# 8054# 6# B- * 80 966226# 537# +0 26 54 28 82 Ni x -10720# 800# 7935# 10# B- 15010# 894# 81 988492# 859# + 24 53 29 82 Cu x -25730# 400# 8108# 5# B- 16584# 400# 81 972378# 429# + 22 52 30 82 Zn x -42313.960 3.074 8301.1175 0.0375 B- 10616.7652 3.9162 81 954574.097 3.300 + 20 51 31 82 Ga x -52930.725 2.426 8421.0494 0.0296 B- 12484.3497 3.2960 81 943176.531 2.604 + 18 50 32 82 Ge x -65415.075 2.240 8563.7567 0.0273 B- 4690.3523 4.3452 81 929774.031 2.405 + 16 49 33 82 As x -70105.427 3.729 8611.4153 0.0455 B- 7488.4677 3.7579 81 924738.731 4.003 + 14 48 34 82 Se -77593.895 0.466 8693.1973 0.0057 B- -95.2184 1.0767 81 916699.531 0.500 + 12 47 35 82 Br -77498.677 0.971 8682.4953 0.0118 B- 3093.1185 0.9714 81 916801.752 1.042 + 10 46 36 82 Kr -80591.79509 0.00551 8710.6754 0.0003 B- -4403.9824 3.0088 81 913481.15368 0.00591 + 8 45 37 82 Rb IT -76187.813 3.009 8647.4275 0.0367 B- -177.7503 6.7048 81 918209.023 3.230 + 6 44 38 82 Sr -76010.062 5.992 8635.7190 0.0731 B- -7945.9650 8.1324 81 918399.845 6.432 + 4 43 39 82 Y x -68064.097 5.499 8529.2762 0.0671 B- -4450.0341 5.7221 81 926930.189 5.902 + 2 42 40 82 Zr x -63614.063 1.584 8465.4666 0.0193 B- -11804# 300# 81 931707.497 1.700 + 0 41 41 82 Nb x -51810# 300# 8312# 4# B- -11440# 500# 81 944380# 322# + -2 40 42 82 Mo x -40370# 400# 8163# 5# B- * 81 956661# 429# +0 25 54 29 83 Cu x -20390# 500# 8044# 6# B- 15900# 583# 82 978110# 537# + 23 53 30 83 Zn x -36290# 300# 8226# 4# B- 12967# 300# 82 961041# 322# + 21 52 31 83 Ga x -49257.129 2.612 8372.5756 0.0315 B- 11719.3136 3.5592 82 947120.300 2.804 + 19 51 32 83 Ge x -60976.442 2.427 8504.3462 0.0292 B- 8692.8893 3.6979 82 934539.100 2.604 + 17 50 33 83 As x -69669.331 2.799 8599.6540 0.0337 B- 5671.2117 4.1290 82 925206.900 3.004 + 15 49 34 83 Se -n -75340.543 3.036 8658.5560 0.0366 B- 3673.1780 4.8392 82 919118.604 3.259 + 13 48 35 83 Br -79013.721 3.795 8693.3852 0.0457 B- 976.9222 3.7947 82 915175.285 4.073 + 11 47 36 83 Kr -79990.643 0.009 8695.7295 0.0003 B- -920.0039 2.3288 82 914126.516 0.009 + 9 46 37 83 Rb -79070.639 2.329 8675.2193 0.0281 B- -2273.0239 6.4245 82 915114.181 2.500 + 7 45 38 83 Sr -76797.616 6.834 8638.4076 0.0823 B- -4591.9435 19.8444 82 917554.372 7.336 + 5 44 39 83 Y x -72205.672 18.631 8573.6571 0.2245 B- -6294.0125 19.7074 82 922484.026 20.000 + 3 43 40 83 Zr x -65911.659 6.430 8488.3997 0.0775 B- -8298.7493 162.2075 82 929240.926 6.902 + 1 42 41 83 Nb x -57612.910 162.080 8378.9889 1.9528 B- -11273# 432# 82 938150.000 174.000 + -1 41 42 83 Mo x -46340# 401# 8234# 5# B- -15020# 641# 82 950252# 430# + -3 40 43 83 Tc x -31320# 500# 8043# 6# B- * 82 966377# 537# +0 26 55 29 84 Cu x -13720# 500# 7965# 6# B- 18110# 640# 83 985271# 537# + 24 54 30 84 Zn x -31830# 400# 8171# 5# B- 12264# 401# 83 965829# 429# + 22 53 31 84 Ga x -44094.136 29.808 8307.5250 0.3549 B- 14054.2989 29.9760 83 952663.000 32.000 + 20 52 32 84 Ge x -58148.435 3.171 8465.5244 0.0377 B- 7705.1329 4.4789 83 937575.090 3.403 + 18 51 33 84 As x -65853.568 3.171 8547.9385 0.0377 B- 10094.1624 3.7219 83 929303.290 3.403 + 16 50 34 84 Se -75947.731 1.961 8658.7935 0.0233 B- 1835.3638 25.7652 83 918466.761 2.105 + 14 49 35 84 Br -77783.094 25.730 8671.3294 0.3063 B- 4656.2510 25.7300 83 916496.417 27.622 + 12 48 36 84 Kr -82439.34527 0.00382 8717.4473 0.0003 B- -2680.3708 2.1940 83 911497.72708 0.00410 + 10 47 37 84 Rb -79758.975 2.194 8676.2244 0.0261 B- 890.6058 2.3356 83 914375.223 2.355 + 8 46 38 84 Sr -80649.580 1.243 8677.5132 0.0148 B- -6755.1411 4.4114 83 913419.118 1.334 + 6 45 39 84 Y -73894.439 4.299 8587.7812 0.0512 B- -2472.7471 6.9767 83 920671.060 4.615 + 4 44 40 84 Zr x -71421.692 5.499 8549.0301 0.0655 B- -10227.8497 5.5133 83 923325.663 5.903 + 2 43 41 84 Nb x -61193.842 0.401 8417.9563 0.0048 B- -7024# 298# 83 934305.711 0.430 + 0 42 42 84 Mo x -54170# 298# 8325# 4# B- -16470# 499# 83 941846# 320# + -2 41 43 84 Tc x -37700# 400# 8120# 5# B- * 83 959527# 429# +0 25 55 30 85 Zn x -25100# 500# 8090# 6# B- 14644# 502# 84 973054# 537# + 23 54 31 85 Ga x -39744.059 37.260 8253.5687 0.4384 B- 13379.3679 37.4459 84 957333.000 40.000 + 21 53 32 85 Ge x -53123.427 3.729 8401.7689 0.0439 B- 10065.7253 4.8303 84 942969.658 4.003 + 19 52 33 85 As x -63189.152 3.078 8510.9851 0.0362 B- 9224.4929 4.0313 84 932163.658 3.304 + 17 51 34 85 Se +3p -72413.645 2.613 8610.3045 0.0307 B- 6161.8335 4.0313 84 922260.758 2.804 + 15 50 35 85 Br +n2p -78575.478 3.078 8673.5926 0.0362 B- 2904.8622 3.6705 84 915645.758 3.304 + 13 49 36 85 Kr + -81480.341 2.000 8698.5633 0.0235 B- 687.0000 2.0000 84 912527.260 2.147 + 11 48 37 85 Rb -82167.34065 0.00500 8697.4416 0.0003 B- -1064.0510 2.8132 84 911789.73604 0.00537 + 9 47 38 85 Sr -81103.290 2.813 8675.7193 0.0331 B- -3261.1584 19.1729 84 912932.041 3.020 + 7 46 39 85 Y x -77842.131 18.965 8628.1486 0.2231 B- -4666.9352 20.0257 84 916433.039 20.360 + 5 45 40 85 Zr x -73175.196 6.430 8564.0394 0.0756 B- -6895.5120 7.6250 84 921443.199 6.902 + 3 44 41 85 Nb x -66279.684 4.099 8473.7117 0.0482 B- -8769.9238 16.3572 84 928845.836 4.400 + 1 43 42 85 Mo x -57509.760 15.835 8361.3320 0.1863 B- -11660# 400# 84 938260.736 17.000 + -1 42 43 85 Tc x -45850# 400# 8215# 5# B- -15220# 640# 84 950778# 429# + -3 41 44 85 Ru x -30630# 500# 8027# 6# B- * 84 967117# 537# +0 26 56 30 86 Zn x -20062# 500# 8032# 6# B- 13699# 640# 85 978463# 537# + 24 55 31 86 Ga x -33760# 400# 8182# 5# B- 15640# 593# 85 963757# 429# + 22 54 32 86 Ge x -49399.927 437.802 8354.6300 5.0907 B- 9562.2229 437.8158 85 946967.000 470.000 + 20 53 33 86 As x -58962.150 3.450 8456.7215 0.0401 B- 11541.0256 4.2666 85 936701.532 3.703 + 18 52 34 86 Se x -70503.175 2.520 8581.8224 0.0293 B- 5129.0860 3.9717 85 924311.732 2.705 + 16 51 35 86 Br +pp -75632.261 3.078 8632.3659 0.0358 B- 7633.4147 3.0779 85 918805.432 3.304 + 14 50 36 86 Kr -83265.67593 0.00372 8712.0295 0.0003 B- -518.6734 0.2000 85 910610.62468 0.00399 + 12 49 37 86 Rb -n -82747.003 0.200 8696.9014 0.0023 B- 1776.0972 0.2001 85 911167.443 0.214 + 10 48 38 86 Sr -84523.09977 0.00524 8708.4566 0.0003 B- -5240.0000 14.1421 85 909260.72473 0.00563 + 8 47 39 86 Y - -79283.100 14.142 8638.4293 0.1644 B- -1314.0763 14.5847 85 914886.095 15.182 + 6 46 40 86 Zr -77969.023 3.566 8614.0523 0.0415 B- -8834.9627 6.5521 85 916296.814 3.827 + 4 45 41 86 Nb x -69134.061 5.499 8502.2231 0.0639 B- -5023.1337 6.2316 85 925781.536 5.903 + 2 44 42 86 Mo x -64110.927 2.932 8434.7175 0.0341 B- -12541# 300# 85 931174.092 3.147 + 0 43 43 86 Tc x -51570# 300# 8280# 3# B- -11800# 500# 85 944637# 322# + -2 42 44 86 Ru x -39770# 400# 8133# 5# B- * 85 957305# 429# +0 25 56 31 87 Ga x -28870# 500# 8124# 6# B- 14720# 583# 86 969007# 537# + 23 55 32 87 Ge x -43590# 300# 8285# 3# B- 12028# 300# 86 953204# 322# + 21 54 33 87 As x -55617.914 2.985 8413.8521 0.0343 B- 10808.2192 3.7260 86 940291.716 3.204 + 19 53 34 87 Se x -66426.133 2.241 8529.0920 0.0258 B- 7465.5526 3.8766 86 928688.616 2.405 + 17 52 35 87 Br 2p-n -73891.685 3.171 8605.9105 0.0364 B- 6817.8455 3.1805 86 920674.016 3.404 + 15 51 36 87 Kr -n -80709.531 0.246 8675.2840 0.0028 B- 3888.2706 0.2463 86 913354.759 0.264 + 13 50 37 87 Rb -84597.802 0.006 8710.9843 0.0003 B- 282.2749 0.0063 86 909180.529 0.006 + 11 49 38 87 Sr -84880.07643 0.00513 8705.2363 0.0003 B- -1861.6894 1.1278 86 908877.49454 0.00550 + 9 48 39 87 Y - -83018.387 1.128 8674.8451 0.0130 B- -3671.2405 4.2962 86 910876.100 1.210 + 7 47 40 87 Zr -79347.147 4.146 8623.6545 0.0477 B- -5472.6536 7.9633 86 914817.338 4.450 + 5 46 41 87 Nb x -73874.493 6.802 8551.7579 0.0782 B- -6989.6757 7.3781 86 920692.473 7.302 + 3 45 42 87 Mo -66884.817 2.857 8462.4243 0.0328 B- -9194.7656 5.0729 86 928196.198 3.067 + 1 44 43 87 Tc x -57690.052 4.192 8347.7449 0.0482 B- -11960# 400# 86 938067.185 4.500 + -1 43 44 87 Ru x -45730# 400# 8201# 5# B- * 86 950907# 429# +0 26 57 31 88 Ga x -22390# 500# 8050# 6# B- 17129# 640# 87 975963# 537# + 24 56 32 88 Ge x -39520# 400# 8236# 5# B- 10930# 447# 87 957574# 429# + 22 55 33 88 As x -50450# 200# 8351# 2# B- 13434# 200# 87 945840# 215# + 20 54 34 88 Se x -63884.203 3.357 8495.0045 0.0382 B- 6831.7640 4.6125 87 931417.490 3.604 + 18 53 35 88 Br ++ -70715.967 3.171 8563.7479 0.0360 B- 8975.3282 4.1059 87 924083.290 3.404 + 16 52 36 88 Kr x -79691.295 2.608 8656.8499 0.0296 B- 2917.7090 2.6130 87 914447.879 2.800 + 14 51 37 88 Rb -82609.004 0.159 8681.1154 0.0018 B- 5312.6243 0.1590 87 911315.590 0.170 + 12 50 38 88 Sr -87921.62876 0.00561 8732.5958 0.0003 B- -3622.6000 1.5000 87 905612.253 0.006 + 10 49 39 88 Y - -84299.029 1.500 8682.5396 0.0170 B- -670.1549 5.6076 87 909501.274 1.610 + 8 48 40 88 Zr -83628.874 5.403 8666.0339 0.0614 B- -7457.3187 57.8921 87 910220.715 5.800 + 6 47 41 88 Nb -76171.555 57.808 8572.4013 0.6569 B- -3485.0021 57.9345 87 918226.476 62.059 + 4 46 42 88 Mo x -72686.553 3.819 8523.9087 0.0434 B- -11016.2515 5.6021 87 921967.779 4.100 + 2 45 43 88 Tc x -61670.301 4.099 8389.8338 0.0466 B- -7331# 300# 87 933794.211 4.400 + 0 44 44 88 Ru x -54340# 300# 8298# 3# B- -17479# 500# 87 941664# 322# + -2 43 45 88 Rh x -36860# 400# 8090# 5# B- * 87 960429# 429# +0 25 57 32 89 Ge x -33040# 400# 8161# 4# B- 13490# 500# 88 964530# 429# + 23 56 33 89 As x -46530# 300# 8304# 3# B- 12462# 300# 88 950048# 322# + 21 55 34 89 Se x -58992.398 3.729 8435.2799 0.0419 B- 9281.8730 4.9510 88 936669.058 4.003 + 19 54 35 89 Br x -68274.271 3.264 8530.7802 0.0367 B- 8261.5231 3.9045 88 926704.558 3.504 + 17 53 36 89 Kr x -76535.795 2.142 8614.8158 0.0241 B- 5176.6042 5.8342 88 917835.449 2.300 + 15 52 37 89 Rb -81712.399 5.427 8664.1895 0.0610 B- 4496.6278 5.4265 88 912278.136 5.825 + 13 51 38 89 Sr -86209.026 0.092 8705.9230 0.0011 B- 1502.1757 0.3510 88 907450.808 0.098 + 11 50 39 89 Y -87711.202 0.339 8714.0110 0.0038 B- -2833.2285 2.7652 88 905838.156 0.363 + 9 49 40 89 Zr -84877.974 2.780 8673.3865 0.0312 B- -4252.2191 23.7199 88 908879.751 2.983 + 7 48 41 89 Nb -80625.755 23.630 8616.8184 0.2655 B- -5610.8105 23.9513 88 913444.696 25.367 + 5 47 42 89 Mo x -75014.944 3.912 8544.9851 0.0440 B- -7620.0875 5.4673 88 919468.149 4.200 + 3 46 43 89 Tc x -67394.857 3.819 8450.5758 0.0429 B- -9025.4327 24.5181 88 927648.649 4.100 + 1 45 44 89 Ru x -58369.424 24.219 8340.3760 0.2721 B- -12719# 361# 88 937337.849 26.000 + -1 44 45 89 Rh -p -45651# 361# 8189# 4# B- * 88 950992# 387# +0 26 58 32 90 Ge x -28470# 500# 8109# 6# B- 12520# 640# 89 969436# 537# + 24 57 33 90 As x -40990# 400# 8240# 4# B- 14810# 518# 89 955995# 429# + 22 56 34 90 Se x -55800.223 329.749 8395.7672 3.6639 B- 8200.0834 329.7660 89 940096.000 354.000 + 20 55 35 90 Br x -64000.306 3.357 8478.1865 0.0373 B- 10958.9533 3.8396 89 931292.848 3.604 + 18 54 36 90 Kr x -74959.259 1.863 8591.2599 0.0207 B- 4406.3133 6.7158 89 919527.929 2.000 + 16 53 37 90 Rb -79365.573 6.452 8631.5262 0.0717 B- 6585.3721 6.4806 89 914797.557 6.926 + 14 52 38 90 Sr -85950.945 1.449 8696.0043 0.0161 B- 545.9674 1.4060 89 907727.870 1.555 + 12 51 39 90 Y -86496.912 0.354 8693.3778 0.0039 B- 2275.6350 0.3726 89 907141.749 0.379 + 10 50 40 90 Zr -88772.547 0.118 8709.9699 0.0013 B- -6111.0165 3.3163 89 904698.755 0.126 + 8 49 41 90 Nb -82661.531 3.317 8633.3770 0.0369 B- -2489.0165 3.3163 89 911259.201 3.561 + 6 48 42 90 Mo -80172.514 3.463 8597.0285 0.0385 B- -9447.8181 3.6110 89 913931.270 3.717 + 4 47 43 90 Tc x -70724.696 1.025 8483.3600 0.0114 B- -5840.8951 3.8685 89 924073.919 1.100 + 2 46 44 90 Ru -64883.801 3.730 8409.7684 0.0414 B- -13250# 200# 89 930344.378 4.004 + 0 45 45 90 Rh - -51634# 200# 8254# 2# B- -11924# 447# 89 944569# 215# + -2 44 46 90 Pd x -39710# 400# 8113# 4# B- * 89 957370# 429# +0 25 58 33 91 As x -36500# 400# 8189# 4# B- 14080# 589# 90 960816# 429# + 23 57 34 91 Se x -50580.130 433.145 8334.8382 4.7598 B- 10527.1716 433.1593 90 945700.000 465.000 + 21 56 35 91 Br -n2p -61107.301 3.543 8441.9242 0.0389 B- 9866.6724 4.1898 90 934398.617 3.804 + 19 55 36 91 Kr x -70973.974 2.236 8541.7519 0.0246 B- 6771.0748 8.1153 90 923806.309 2.400 + 17 54 37 91 Rb -77745.049 7.801 8607.5621 0.0857 B- 5906.9010 8.8732 90 916537.261 8.375 + 15 53 38 91 Sr -83651.950 5.453 8663.8759 0.0599 B- 2699.3714 5.2468 90 910195.942 5.853 + 13 52 39 91 Y -86351.321 1.843 8684.9421 0.0203 B- 1544.2710 1.8403 90 907298.048 1.978 + 11 51 40 91 Zr -87895.592 0.095 8693.3149 0.0011 B- -1257.5644 2.9243 90 905640.205 0.101 + 9 50 41 91 Nb -86638.028 2.926 8670.8983 0.0322 B- -4429.1934 6.7439 90 906990.256 3.140 + 7 49 42 91 Mo -82208.834 6.238 8613.6286 0.0686 B- -6222.1768 6.6706 90 911745.190 6.696 + 5 48 43 91 Tc -75986.657 2.363 8536.6558 0.0260 B- -7746.8246 3.2422 90 918424.972 2.536 + 3 47 44 91 Ru -68239.833 2.221 8442.9287 0.0244 B- -9670# 298# 90 926741.530 2.384 + 1 46 45 91 Rh x -58570# 298# 8328# 3# B- -12400# 300# 90 937123# 320# + -1 45 46 91 Pd - -46170# 423# 8183# 5# B- * 90 950435# 454# +0 26 59 33 92 As x -30380# 500# 8121# 5# B- 16344# 640# 91 967386# 537# + 24 58 34 92 Se x -46724# 400# 8290# 4# B- 9509# 400# 91 949840# 429# + 22 57 35 92 Br x -56232.812 6.709 8384.9123 0.0729 B- 12536.5161 7.2322 91 939631.595 7.202 + 20 56 36 92 Kr x -68769.329 2.701 8512.6750 0.0294 B- 6003.1210 6.6924 91 926173.092 2.900 + 18 55 37 92 Rb -74772.450 6.123 8569.4225 0.0666 B- 8094.9212 6.4187 91 919728.477 6.573 + 16 54 38 92 Sr -82867.371 3.423 8648.9070 0.0372 B- 1949.1237 9.3841 91 911038.222 3.675 + 14 53 39 92 Y -84816.494 9.127 8661.5894 0.0992 B- 3642.5294 9.1271 91 908945.752 9.798 + 12 52 40 92 Zr -88459.024 0.094 8692.6783 0.0011 B- -2005.7335 1.7823 91 905035.336 0.101 + 10 51 41 92 Nb -86453.290 1.784 8662.3731 0.0194 B- 355.2968 1.7911 91 907188.580 1.915 + 8 50 42 92 Mo -86808.587 0.157 8657.7312 0.0017 B- -7882.8841 3.1063 91 906807.153 0.168 + 6 49 43 92 Tc -78925.703 3.102 8563.5440 0.0337 B- -4624.4922 4.1246 91 915269.777 3.330 + 4 48 44 92 Ru -74301.211 2.718 8504.7740 0.0295 B- -11302.1155 5.1531 91 920234.373 2.917 + 2 47 45 92 Rh x -62999.095 4.378 8373.4211 0.0476 B- -8220.0000 345.0000 91 932367.692 4.700 + 0 46 46 92 Pd - -54779.095 345.028 8275.5695 3.7503 B- -17249# 528# 91 941192.225 370.402 + -2 45 47 92 Ag x -37530# 400# 8080# 4# B- * 91 959710# 429# +0 25 59 34 93 Se x -40860# 400# 8225# 4# B- 12030# 588# 92 956135# 429# + 23 58 35 93 Br x -52890.235 430.816 8345.5986 4.6324 B- 11245.7673 430.8234 92 943220.000 462.500 + 21 57 36 93 Kr x -64136.002 2.515 8458.1085 0.0270 B- 8483.8977 8.2243 92 931147.172 2.700 + 19 56 37 93 Rb -72619.900 7.830 8540.9209 0.0842 B- 7465.9434 8.8761 92 922039.334 8.406 + 17 55 38 93 Sr -80085.844 7.554 8612.7875 0.0812 B- 4141.3118 11.6972 92 914024.314 8.109 + 15 54 39 93 Y -84227.155 10.488 8648.9054 0.1128 B- 2894.8723 10.4830 92 909578.434 11.259 + 13 53 40 93 Zr -87122.028 0.456 8671.6207 0.0049 B- 90.8123 1.4838 92 906470.661 0.489 + 11 52 41 93 Nb -87212.840 1.490 8664.1849 0.0160 B- -405.7609 1.5012 92 906373.170 1.599 + 9 51 42 93 Mo -n -86807.079 0.181 8651.4095 0.0020 B- -3200.9629 1.0040 92 906808.772 0.193 + 7 50 43 93 Tc -p -83606.116 1.012 8608.5782 0.0109 B- -6389.3929 2.2995 92 910245.147 1.086 + 5 49 44 93 Ru -77216.723 2.065 8531.4627 0.0222 B- -8204.9136 3.3425 92 917104.442 2.216 + 3 48 45 93 Rh -69011.810 2.629 8434.8255 0.0283 B- -10030.0000 370.0000 92 925912.778 2.821 + 1 47 46 93 Pd - -58981.810 370.009 8318.5637 3.9786 B- -12582# 545# 92 936680.426 397.221 + -1 46 47 93 Ag x -46400# 401# 8175# 4# B- * 92 950188# 430# +0 26 60 34 94 Se x -36803# 500# 8180# 5# B- 10846# 539# 93 960490# 537# + 24 59 35 94 Br x -47650# 200# 8287# 2# B- 13698# 201# 93 948846# 215# + 22 58 36 94 Kr x -61347.780 12.109 8424.3318 0.1288 B- 7215.0114 12.2782 93 934140.452 13.000 + 20 57 37 94 Rb -68562.791 2.029 8492.7644 0.0216 B- 10282.9297 2.6230 93 926394.819 2.177 + 18 56 38 94 Sr -78845.721 1.663 8593.8344 0.0177 B- 3505.7517 6.4220 93 915355.641 1.785 + 16 55 39 94 Y -82351.473 6.380 8622.8068 0.0679 B- 4917.8589 6.3799 93 911592.062 6.849 + 14 54 40 94 Zr -87269.332 0.164 8666.8016 0.0018 B- -900.2684 1.5000 93 906312.523 0.175 + 12 53 41 94 Nb -86369.063 1.491 8648.9014 0.0159 B- 2045.0163 1.4937 93 907279.001 1.600 + 10 52 42 94 Mo -88414.079 0.141 8662.3341 0.0015 B- -4255.7476 4.0687 93 905083.586 0.151 + 8 51 43 94 Tc - -84158.332 4.071 8608.7373 0.0433 B- -1574.7296 5.1433 93 909652.319 4.370 + 6 50 44 94 Ru -82583.602 3.143 8583.6620 0.0334 B- -9675.9789 4.6150 93 911342.860 3.374 + 4 49 45 94 Rh -72907.623 3.379 8472.4033 0.0359 B- -6805.3428 5.4588 93 921730.450 3.627 + 2 48 46 94 Pd x -66102.281 4.287 8391.6832 0.0456 B- -13700# 400# 93 929036.286 4.602 + 0 47 47 94 Ag - -52402# 400# 8238# 4# B- -11962# 640# 93 943744# 429# + -2 46 48 94 Cd x -40440# 500# 8102# 5# B- * 93 956586# 537# +0 27 61 34 95 Se x -30460# 500# 8112# 5# B- 13390# 583# 94 967300# 537# + 25 60 35 95 Br x -43850# 300# 8245# 3# B- 12309# 301# 94 952925# 322# + 23 59 36 95 Kr x -56158.920 18.630 8365.9963 0.1961 B- 9731.3868 27.5124 94 939710.922 20.000 + 21 58 37 95 Rb -65890.307 20.245 8460.1967 0.2131 B- 9226.9772 20.2036 94 929263.849 21.733 + 19 57 38 95 Sr -75117.284 5.810 8549.0875 0.0612 B- 6090.6528 7.2395 94 919358.282 6.237 + 17 56 39 95 Y -81207.937 6.779 8604.9644 0.0714 B- 4452.0031 6.7718 94 912819.697 7.277 + 15 55 40 95 Zr -85659.940 0.869 8643.5924 0.0092 B- 1126.3312 0.9854 94 908040.276 0.933 + 13 54 41 95 Nb -86786.272 0.508 8647.2133 0.0054 B- 925.6009 0.4938 94 906831.110 0.545 + 11 53 42 95 Mo -87711.872 0.123 8648.7212 0.0013 B- -1690.5175 5.0782 94 905837.436 0.132 + 9 52 43 95 Tc -86021.355 5.080 8622.6911 0.0535 B- -2563.5961 10.5310 94 907652.281 5.453 + 7 51 44 95 Ru -83457.759 9.502 8587.4706 0.1000 B- -5117.1423 10.2656 94 910404.415 10.200 + 5 50 45 95 Rh -78340.616 3.886 8525.3707 0.0409 B- -8374.7035 4.9281 94 915897.893 4.171 + 3 49 46 95 Pd x -69965.913 3.031 8428.9807 0.0319 B- -10060# 400# 94 924888.506 3.253 + 1 48 47 95 Ag - -59906# 400# 8315# 4# B- -12850# 400# 94 935688# 429# + -1 47 48 95 Cd - -47056# 566# 8171# 6# B- * 94 949483# 607# +0 26 61 35 96 Br x -38210# 300# 8184# 3# B- 14872# 301# 95 958980# 322# + 24 60 36 96 Kr -53081.682 19.277 8330.8721 0.2008 B- 8272.6693 19.5669 95 943014.473 20.695 + 22 59 37 96 Rb -61354.351 3.353 8408.8963 0.0349 B- 11563.8970 9.1062 95 934133.398 3.599 + 20 58 38 96 Sr -72918.248 8.466 8521.2041 0.0882 B- 5411.7380 9.7257 95 921719.045 9.089 + 18 57 39 96 Y -78329.986 6.075 8569.4269 0.0633 B- 7108.8741 6.0740 95 915909.305 6.521 + 16 56 40 96 Zr -85438.860 0.114 8635.3283 0.0012 B- 163.9704 0.1000 95 908277.615 0.122 + 14 55 41 96 Nb -85602.830 0.147 8628.8868 0.0015 B- 3192.0590 0.1070 95 908101.586 0.157 + 12 54 42 96 Mo -88794.889 0.120 8653.9880 0.0013 B- -2973.2411 5.1450 95 904674.770 0.128 + 10 53 43 96 Tc - -85821.648 5.146 8614.8673 0.0536 B- 258.7369 5.1464 95 907866.675 5.524 + 8 52 44 96 Ru -86080.385 0.170 8609.4130 0.0018 B- -6392.6529 10.0000 95 907588.910 0.182 + 6 51 45 96 Rh - -79687.732 10.001 8534.6735 0.1042 B- -3504.3127 10.8442 95 914451.705 10.737 + 4 50 46 96 Pd x -76183.420 4.194 8490.0207 0.0437 B- -11671.7741 90.1814 95 918213.739 4.502 + 2 49 47 96 Ag ep -64511.645 90.084 8360.2903 0.9384 B- -8940# 400# 95 930743.903 96.708 + 0 48 48 96 Cd - -55572# 410# 8259# 4# B- -17482# 647# 95 940341# 440# + -2 47 49 96 In x -38090# 500# 8069# 5# B- * 95 959109# 537# +0 27 62 35 97 Br x -34000# 400# 8140# 4# B- 13423# 420# 96 963499# 429# + 25 61 36 97 Kr x -47423.499 130.409 8269.8645 1.3444 B- 11095.6460 130.4232 96 949088.782 140.000 + 23 60 37 97 Rb -58519.145 1.912 8376.1872 0.0197 B- 10061.5295 3.8872 96 937177.117 2.052 + 21 59 38 97 Sr -68580.674 3.385 8471.8489 0.0349 B- 7534.7807 7.5131 96 926375.621 3.633 + 19 58 39 97 Y + -76115.455 6.708 8541.4616 0.0692 B- 6821.2382 6.7068 96 918286.702 7.201 + 17 57 40 97 Zr -82936.693 0.121 8603.7182 0.0013 B- 2666.1038 4.2435 96 910963.802 0.130 + 15 56 41 97 Nb -85602.797 4.244 8623.1384 0.0438 B- 1941.9038 4.2435 96 908101.622 4.556 + 13 55 42 97 Mo -87544.700 0.165 8635.0926 0.0017 B- -320.2640 4.1169 96 906016.903 0.176 + 11 54 43 97 Tc -87224.436 4.118 8623.7254 0.0425 B- -1103.8722 4.9563 96 906360.720 4.420 + 9 53 44 97 Ru -n -86120.564 2.763 8604.2799 0.0285 B- -3523.0000 35.3553 96 907545.776 2.965 + 7 52 45 97 Rh - -82597.564 35.463 8559.8949 0.3656 B- -4791.7118 35.7924 96 911327.872 38.071 + 5 51 46 97 Pd x -77805.852 4.844 8502.4303 0.0499 B- -6901.8255 12.9558 96 916471.985 5.200 + 3 50 47 97 Ag x -70904.027 12.016 8423.2121 0.1239 B- -10170.0000 420.0000 96 923881.400 12.900 + 1 49 48 97 Cd - -60734.027 420.172 8310.3013 4.3317 B- -13344# 580# 96 934799.343 451.073 + -1 48 49 97 In x -47390# 401# 8165# 4# B- * 96 949125# 430# +0 28 63 35 98 Br x -28050# 400# 8078# 4# B- 16070# 500# 97 969887# 429# + 26 62 36 98 Kr x -44120# 300# 8234# 3# B- 10249# 300# 97 952635# 322# + 24 61 37 98 Rb -54369.152 16.083 8330.7294 0.1641 B- 12053.2361 16.4029 97 941632.317 17.265 + 22 60 38 98 Sr -66422.389 3.226 8445.7385 0.0329 B- 5866.3591 8.5504 97 928692.636 3.463 + 20 59 39 98 Y p-2n -72288.748 7.919 8497.6162 0.0808 B- 8993.0098 11.5755 97 922394.841 8.501 + 18 58 40 98 Zr -81281.757 8.445 8581.3984 0.0862 B- 2242.8547 9.8134 97 912740.448 9.065 + 16 57 41 98 Nb -pn -83524.612 5.001 8596.3016 0.0510 B- 4591.3681 5.0032 97 910332.645 5.369 + 14 56 42 98 Mo -88115.980 0.174 8635.1691 0.0018 B- -1683.7664 3.3768 97 905403.609 0.186 + 12 55 43 98 Tc -86432.214 3.380 8610.0047 0.0345 B- 1792.6575 7.1568 97 907211.206 3.628 + 10 54 44 98 Ru -88224.871 6.463 8620.3140 0.0659 B- -5049.6529 10.0000 97 905286.709 6.937 + 8 53 45 98 Rh - -83175.219 11.906 8560.8038 0.1215 B- -1854.2331 12.8161 97 910707.734 12.782 + 6 52 46 98 Pd -81320.985 4.742 8533.8999 0.0484 B- -8254.5607 33.0975 97 912698.335 5.090 + 4 51 47 98 Ag -73066.425 32.907 8441.6866 0.3358 B- -5430.0000 40.0000 97 921559.970 35.327 + 2 50 48 98 Cd - -67636.425 51.797 8378.2953 0.5285 B- -13730# 300# 97 927389.315 55.605 + 0 49 49 98 In - -53906# 304# 8230# 3# B- * 97 942129# 327# +0 27 63 36 99 Kr x -38400# 400# 8175# 4# B- 12721# 400# 98 958776# 429# + 25 62 37 99 Rb x -51121.150 4.031 8295.3010 0.0407 B- 11397.3767 6.2201 98 945119.190 4.327 + 23 61 38 99 Sr -62518.527 4.737 8402.5235 0.0479 B- 8125.2037 8.1353 98 932883.604 5.085 + 21 60 39 99 Y x -70643.730 6.615 8476.6938 0.0668 B- 6972.9398 12.4082 98 924160.839 7.101 + 19 59 40 99 Zr -77616.670 10.499 8539.2250 0.1061 B- 4718.6736 15.9474 98 916675.081 11.271 + 17 58 41 99 Nb +p -82335.344 12.004 8578.9859 0.1213 B- 3634.7623 12.0059 98 911609.377 12.886 + 15 57 42 99 Mo -85970.106 0.229 8607.7982 0.0023 B- 1357.7631 0.8905 98 907707.299 0.245 + 13 56 43 99 Tc -87327.869 0.908 8613.6105 0.0092 B- 297.5156 0.9453 98 906249.681 0.974 + 11 55 44 99 Ru -87625.385 0.343 8608.7132 0.0035 B- -2040.8632 19.4529 98 905930.284 0.368 + 9 54 45 99 Rh -85584.522 19.451 8580.1959 0.1965 B- -3401.6603 18.9153 98 908121.241 20.881 + 7 53 46 99 Pd -82182.861 5.107 8537.9332 0.0516 B- -5470.3785 8.0829 98 911773.073 5.482 + 5 52 47 99 Ag x -76712.483 6.265 8474.7744 0.0633 B- -6781.3511 6.4622 98 917645.766 6.725 + 3 51 48 99 Cd x -69931.132 1.584 8398.3734 0.0160 B- -8555# 298# 98 924925.845 1.700 + 1 50 49 99 In x -61376# 298# 8304# 3# B- -13400# 500# 98 934110# 320# + -1 49 50 99 Sn - -47976# 582# 8161# 6# B- * 98 948495# 625# +0 28 64 36 100 Kr x -34470# 400# 8134# 4# B- 11796# 400# 99 962995# 429# + 26 63 37 100 Rb -46265.884 13.124 8244.5085 0.1312 B- 13551.6204 14.8355 99 950331.532 14.089 + 24 62 38 100 Sr -59817.505 6.918 8372.2012 0.0692 B- 7503.7365 13.1453 99 935783.270 7.426 + 22 61 39 100 Y x -67321.241 11.179 8439.4151 0.1118 B- 9051.4949 13.8293 99 927727.678 12.000 + 20 60 40 100 Zr -76372.736 8.143 8522.1066 0.0814 B- 3418.5098 11.3976 99 918010.499 8.742 + 18 59 41 100 Nb IT -79791.246 7.976 8548.4683 0.0798 B- 6401.7829 7.9817 99 914340.578 8.562 + 16 58 42 100 Mo -86193.029 0.301 8604.6626 0.0030 B- -172.0776 1.3704 99 907467.982 0.322 + 14 57 43 100 Tc -n -86020.951 1.351 8595.1184 0.0135 B- 3206.4401 1.3760 99 907652.715 1.450 + 12 56 44 100 Ru -89227.391 0.342 8619.3593 0.0034 B- -3636.2612 18.1231 99 904210.460 0.367 + 10 55 45 100 Rh -85591.130 18.125 8575.1732 0.1813 B- -378.4577 25.2879 99 908114.147 19.458 + 8 54 46 100 Pd -85212.672 17.637 8563.5652 0.1764 B- -7074.7030 18.3319 99 908520.438 18.934 + 6 53 47 100 Ag x -78137.969 5.000 8484.9947 0.0500 B- -3943.3740 5.2735 99 916115.443 5.367 + 4 52 48 100 Cd x -74194.595 1.677 8437.7375 0.0168 B- -10016.4492 2.7945 99 920348.829 1.800 + 2 51 49 100 In x -64178.146 2.236 8329.7495 0.0224 B- -7030.0000 240.0000 99 931101.929 2.400 + 0 50 50 100 Sn - -57148.146 240.010 8251.6260 2.4001 B- * 99 938648.944 257.661 +0 29 65 36 101 Kr x -28580# 500# 8075# 5# B- 13987# 501# 100 969318# 537# + 27 64 37 101 Rb x -42567.417 20.493 8206.1753 0.2029 B- 12757.4969 22.1781 100 954302.000 22.000 + 25 63 38 101 Sr x -55324.914 8.480 8324.7411 0.0840 B- 9729.8721 11.0473 100 940606.264 9.103 + 23 62 39 101 Y x -65054.787 7.080 8413.3305 0.0701 B- 8106.2003 10.9331 100 930160.817 7.601 + 21 61 40 101 Zr -73160.987 8.332 8485.8439 0.0825 B- 5730.5011 9.1366 100 921458.454 8.944 + 19 60 41 101 Nb x -78891.488 3.749 8534.8355 0.0371 B- 4628.4637 3.7378 100 915306.508 4.024 + 17 59 42 101 Mo -n -83519.952 0.308 8572.9159 0.0031 B- 2824.6411 24.0018 100 910337.648 0.331 + 15 58 43 101 Tc + -86344.593 24.004 8593.1366 0.2377 B- 1613.5200 24.0000 100 907305.271 25.768 + 13 57 44 101 Ru -87958.113 0.413 8601.3660 0.0041 B- -545.6846 5.8518 100 905573.086 0.443 + 11 56 45 101 Rh -87412.428 5.841 8588.2172 0.0578 B- -1980.2833 3.9027 100 906158.903 6.270 + 9 55 46 101 Pd -85432.145 4.588 8560.8644 0.0454 B- -4097.7606 6.6679 100 908284.824 4.925 + 7 54 47 101 Ag x -81334.384 4.838 8512.5465 0.0479 B- -5497.9186 5.0625 100 912683.951 5.193 + 5 53 48 101 Cd x -75836.466 1.490 8450.3657 0.0148 B- -7291.5642 11.7569 100 918586.209 1.600 + 3 52 49 101 In x -68544.901 11.662 8370.4260 0.1155 B- -8239.2770 300.2313 100 926414.025 12.519 + 1 51 50 101 Sn ep -60305.624 300.005 8281.1030 2.9703 B- * 100 935259.252 322.068 +0 28 65 37 102 Rb x -37252.312 82.903 8152.7443 0.8128 B- 14906.9991 106.6347 101 960008.000 89.000 + 26 64 38 102 Sr x -52159.311 67.068 8291.2213 0.6575 B- 9013.3301 67.1916 101 944004.679 72.000 + 24 63 39 102 Y x -61172.641 4.081 8371.9172 0.0400 B- 10408.7856 9.6618 101 934328.471 4.381 + 22 62 40 102 Zr -71581.427 8.758 8466.2940 0.0859 B- 4716.8380 9.0530 101 923154.181 9.401 + 20 61 41 102 Nb -76298.265 2.511 8504.8675 0.0246 B- 7262.6008 8.6750 101 918090.447 2.695 + 18 60 42 102 Mo -83560.866 8.305 8568.3994 0.0814 B- 1012.0557 12.3682 101 910293.725 8.916 + 16 59 43 102 Tc -84572.921 9.166 8570.6514 0.0899 B- 4533.5134 9.1646 101 909207.239 9.840 + 14 58 44 102 Ru -89106.435 0.416 8607.4275 0.0041 B- -2323.1187 6.3960 101 904340.312 0.446 + 12 57 45 102 Rh - -86783.316 6.410 8576.9818 0.0628 B- 1119.6470 6.3962 101 906834.282 6.880 + 10 56 46 102 Pd -87902.963 0.419 8580.2887 0.0041 B- -5656.2615 8.1816 101 905632.292 0.449 + 8 55 47 102 Ag + -82246.702 8.171 8517.1650 0.0801 B- -2587.0000 8.0000 101 911704.538 8.771 + 6 54 48 102 Cd -79659.702 1.662 8484.1322 0.0163 B- -8964.8059 4.8654 101 914481.797 1.784 + 4 53 49 102 In -70694.896 4.573 8388.5719 0.0448 B- -5760.0000 100.0000 101 924105.911 4.909 + 2 52 50 102 Sn - -64934.896 100.105 8324.4313 0.9814 B- -13835# 412# 101 930289.525 107.466 + 0 51 51 102 Sb x -51100# 400# 8181# 4# B- * 101 945142# 429# +0 29 66 37 103 Rb x -33160# 400# 8112# 4# B- 14120# 447# 102 964401# 429# + 27 65 38 103 Sr x -47280# 200# 8242# 2# B- 11177# 201# 102 949243# 215# + 25 64 39 103 Y x -58457.034 11.206 8342.6336 0.1088 B- 9351.9600 14.5130 102 937243.796 12.029 + 23 63 40 103 Zr x -67808.993 9.223 8425.8337 0.0895 B- 7219.6740 10.0270 102 927204.054 9.900 + 21 62 41 103 Nb x -75028.667 3.935 8488.3320 0.0382 B- 5925.6639 10.0270 102 919453.416 4.224 + 19 61 42 103 Mo x -80954.331 9.223 8538.2672 0.0895 B- 3649.5889 13.4648 102 913091.954 9.900 + 17 60 43 103 Tc +p -84603.920 9.810 8566.1045 0.0952 B- 2663.2474 9.8086 102 909173.960 10.531 + 15 59 44 103 Ru -87267.168 0.441 8584.3656 0.0043 B- 764.5378 2.2598 102 906314.846 0.473 + 13 58 45 103 Rh -88031.705 2.301 8584.1927 0.0223 B- -574.7252 2.3928 102 905494.081 2.470 + 11 57 46 103 Pd -n -87456.980 0.878 8571.0173 0.0085 B- -2654.2778 4.1916 102 906111.074 0.942 + 9 56 47 103 Ag x -84802.702 4.099 8537.6520 0.0398 B- -4151.0761 4.4806 102 908960.558 4.400 + 7 55 48 103 Cd -80651.626 1.811 8489.7547 0.0176 B- -6019.2293 9.1242 102 913416.922 1.943 + 5 54 49 103 In -74632.397 8.980 8423.7199 0.0872 B- -7540# 100# 102 919878.830 9.640 + 3 53 50 103 Sn - -67092# 100# 8343# 1# B- -10422# 316# 102 927973# 108# + 1 52 51 103 Sb x -56670# 300# 8234# 3# B- * 102 939162# 322# +0 30 67 37 104 Rb x -27450# 500# 8057# 5# B- 16310# 583# 103 970531# 537# + 28 66 38 104 Sr x -43760# 300# 8206# 3# B- 10320# 361# 103 953022# 322# + 26 65 39 104 Y x -54080# 200# 8298# 2# B- 11638# 200# 103 941943# 215# + 24 64 40 104 Zr x -65717.660 9.316 8402.3159 0.0896 B- 6093.3367 9.4851 103 929449.193 10.000 + 22 63 41 104 Nb x -71810.997 1.784 8453.3832 0.0172 B- 8532.7512 9.0879 103 922907.728 1.915 + 20 62 42 104 Mo -80343.748 8.911 8527.9063 0.0857 B- 2155.2212 24.1665 103 913747.443 9.566 + 18 61 43 104 Tc -82498.969 24.886 8541.1070 0.2393 B- 5596.7945 24.9370 103 911433.718 26.716 + 16 60 44 104 Ru -88095.763 2.498 8587.3998 0.0240 B- -1136.4195 3.3643 103 905425.312 2.682 + 14 59 45 104 Rh -n -86959.344 2.303 8568.9501 0.0221 B- 2435.7789 2.6595 103 906645.309 2.471 + 12 58 46 104 Pd +n -89395.123 1.336 8584.8485 0.0129 B- -4278.6529 4.0000 103 904030.393 1.434 + 10 57 47 104 Ag - -85116.470 4.217 8536.1850 0.0406 B- -1148.0787 4.5370 103 908623.715 4.527 + 8 56 48 104 Cd -83968.391 1.673 8517.6232 0.0161 B- -7785.7166 6.0127 103 909856.228 1.795 + 6 55 49 104 In x -76182.675 5.775 8435.2380 0.0555 B- -4555.6174 8.1461 103 918214.538 6.200 + 4 54 50 104 Sn -71627.057 5.745 8383.9114 0.0552 B- -12332# 102# 103 923105.195 6.167 + 2 53 51 104 Sb +a -59295# 101# 8258# 1# B- -9668# 333# 103 936344# 109# + 0 52 52 104 Te -a -49626.831 317.609 8157.3256 3.0539 B- * 103 946723.408 340.967 +0 29 67 38 105 Sr x -38190# 500# 8152# 5# B- 12380# 640# 104 959001# 537# + 27 66 39 105 Y x -50570# 400# 8262# 4# B- 10888# 400# 104 945711# 429# + 25 65 40 105 Zr x -61458.274 12.110 8358.5980 0.1153 B- 8457.2728 12.7625 104 934021.832 13.000 + 23 64 41 105 Nb x -69915.547 4.028 8431.6925 0.0384 B- 7415.2411 9.9106 104 924942.577 4.324 + 21 63 42 105 Mo -77330.788 9.055 8494.8630 0.0862 B- 4955.5157 35.0307 104 916981.989 9.721 + 19 62 43 105 Tc -82286.303 35.263 8534.6074 0.3358 B- 3648.2396 35.2787 104 911662.024 37.856 + 17 61 44 105 Ru -85934.543 2.499 8561.9016 0.0238 B- 1916.7271 2.8508 104 907745.478 2.683 + 15 60 45 105 Rh -87851.270 2.502 8572.7053 0.0238 B- 566.6347 2.3459 104 905687.787 2.685 + 13 59 46 105 Pd -88417.905 1.138 8570.6509 0.0108 B- -1347.0564 4.6695 104 905079.479 1.222 + 11 58 47 105 Ag -87070.848 4.544 8550.3708 0.0433 B- -2736.9989 4.3618 104 906525.604 4.877 + 9 57 48 105 Cd -84333.849 1.392 8516.8532 0.0133 B- -4693.2673 10.3405 104 909463.893 1.494 + 7 56 49 105 In x -79640.582 10.246 8464.7045 0.0976 B- -6302.5807 10.9891 104 914502.322 11.000 + 5 55 50 105 Sn -73338.001 3.971 8397.2290 0.0378 B- -9322.5103 22.1849 104 921268.421 4.263 + 3 54 51 105 Sb +a -64015.491 21.827 8300.9923 0.2079 B- -11203.9825 300.8126 104 931276.547 23.431 + 1 53 52 105 Te -a -52811.509 300.020 8186.8368 2.8573 B- * 104 943304.516 322.084 +0 30 68 38 106 Sr x -34300# 600# 8114# 6# B- 11490# 781# 105 963177# 644# + 28 67 39 106 Y x -45790# 500# 8215# 5# B- 12959# 539# 105 950842# 537# + 26 66 40 106 Zr x -58749# 200# 8330# 2# B- 7453# 200# 105 936930# 215# + 24 65 41 106 Nb -66202.678 1.416 8393.2657 0.0134 B- 9925.3249 9.2388 105 928928.505 1.520 + 22 64 42 106 Mo x -76128.003 9.130 8479.5202 0.0861 B- 3648.2494 15.2778 105 918273.231 9.801 + 20 63 43 106 Tc + -79776.253 12.250 8506.5570 0.1156 B- 6547.0000 11.0000 105 914356.674 13.150 + 18 62 44 106 Ru -86323.253 5.391 8560.9406 0.0509 B- 39.4038 0.2121 105 907328.181 5.787 + 16 61 45 106 Rh -86362.656 5.390 8553.9317 0.0508 B- 3544.8865 5.3348 105 907285.879 5.786 + 14 60 46 106 Pd -89907.543 1.106 8579.9934 0.0104 B- -2965.1434 2.8172 105 903480.287 1.186 + 12 59 47 106 Ag -86942.399 3.016 8544.6397 0.0285 B- 189.7534 2.8190 105 906663.499 3.237 + 10 58 48 106 Cd -87132.153 1.104 8539.0492 0.0104 B- -6524.0031 12.1765 105 906459.791 1.184 + 8 57 49 106 In - -80608.150 12.226 8470.1213 0.1153 B- -3254.4521 13.2439 105 913463.596 13.125 + 6 56 50 106 Sn -77353.698 5.091 8432.0383 0.0480 B- -10880.3964 9.0249 105 916957.394 5.465 + 4 55 51 106 Sb x -66473.301 7.452 8322.0124 0.0703 B- -8253.5423 100.8163 105 928637.979 8.000 + 2 54 52 106 Te -a -58219.759 100.541 8236.7682 0.9485 B- -14920# 412# 105 937498.521 107.934 + 0 53 53 106 I x -43300# 400# 8089# 4# B- * 105 953516# 429# +0 31 69 38 107 Sr x -28250# 700# 8057# 7# B- 13720# 860# 106 969672# 751# + 29 68 39 107 Y x -41970# 500# 8178# 5# B- 12050# 583# 106 954943# 537# + 27 67 40 107 Zr x -54020# 300# 8284# 3# B- 9704# 300# 106 942007# 322# + 25 66 41 107 Nb x -63723.805 8.023 8367.0898 0.0750 B- 8821.1703 12.2239 106 931589.685 8.612 + 23 65 42 107 Mo x -72544.975 9.223 8442.2190 0.0862 B- 6204.9921 12.6599 106 922119.770 9.901 + 21 64 43 107 Tc x -78749.967 8.673 8492.8979 0.0811 B- 5112.5985 11.7243 106 915458.437 9.310 + 19 63 44 107 Ru -nn -83862.565 8.673 8533.3676 0.0811 B- 3001.1457 14.8473 106 909969.837 9.310 + 17 62 45 107 Rh +p -86863.711 12.051 8554.1040 0.1126 B- 1508.9427 12.1108 106 906747.975 12.937 + 15 61 46 107 Pd -88372.654 1.201 8560.8946 0.0112 B- 34.0458 2.3174 106 905128.058 1.289 + 13 60 47 107 Ag -88406.700 2.382 8553.9012 0.0223 B- -1416.3741 2.5654 106 905091.509 2.556 + 11 59 48 107 Cd -86990.325 1.660 8533.3524 0.0155 B- -3423.6586 9.5800 106 906612.049 1.782 + 9 58 49 107 In -83566.667 9.654 8494.0439 0.0902 B- -5054.4281 11.0175 106 910287.497 10.363 + 7 57 50 107 Sn x -78512.239 5.310 8439.4946 0.0496 B- -7858.9903 6.7377 106 915713.649 5.700 + 5 56 51 107 Sb -70653.248 4.148 8358.7344 0.0388 B- -9996# 101# 106 924150.621 4.452 + 3 55 52 107 Te -a -60657# 101# 8258# 1# B- -11227# 316# 106 934882# 108# + 1 54 53 107 I x -49430# 300# 8146# 3# B- * 106 946935# 322# +0 30 69 39 108 Y x -36780# 600# 8129# 6# B- 14170# 721# 107 960515# 644# + 28 68 40 108 Zr x -50950# 400# 8253# 4# B- 8595# 400# 107 945303# 429# + 26 67 41 108 Nb x -59545.198 8.239 8325.6604 0.0763 B- 11204.0998 12.3668 107 936075.604 8.844 + 24 66 42 108 Mo x -70749.297 9.223 8422.1581 0.0854 B- 5173.5330 12.7262 107 924047.508 9.901 + 22 65 43 108 Tc x -75922.831 8.769 8462.8172 0.0812 B- 7738.5736 11.7903 107 918493.493 9.413 + 20 64 44 108 Ru -3n -83661.404 8.680 8527.2267 0.0804 B- 1369.7517 16.4699 107 910185.793 9.318 + 18 63 45 108 Rh x -85031.156 13.997 8532.6657 0.1296 B- 4493.0596 14.0405 107 908715.304 15.026 + 16 62 46 108 Pd -89524.215 1.108 8567.0241 0.0103 B- -1917.4238 2.6323 107 903891.806 1.189 + 14 61 47 108 Ag -n -87606.792 2.388 8542.0262 0.0221 B- 1645.6311 2.6386 107 905950.245 2.563 + 12 60 48 108 Cd -89252.423 1.123 8550.0196 0.0104 B- -5132.5944 8.5845 107 904183.588 1.205 + 10 59 49 108 In -84119.828 8.641 8495.2516 0.0800 B- -2049.8794 9.8365 107 909693.654 9.276 + 8 58 50 108 Sn -82069.949 5.382 8469.0273 0.0498 B- -9624.6079 7.6925 107 911894.290 5.778 + 6 57 51 108 Sb x -72445.341 5.496 8372.6666 0.0509 B- -6663.6646 7.7125 107 922226.731 5.900 + 4 56 52 108 Te -65781.676 5.411 8303.7221 0.0501 B- -13011# 101# 107 929380.469 5.808 + 2 55 53 108 I -p -52771# 101# 8176# 1# B- -10139# 393# 107 943348# 109# + 0 54 54 108 Xe -a -42632.357 379.497 8074.8886 3.5139 B- * 107 954232.285 407.406 +0 31 70 39 109 Y x -32480# 700# 8089# 6# B- 13250# 860# 108 965131# 751# + 29 69 40 109 Zr x -45730# 500# 8204# 5# B- 10960# 660# 108 950907# 537# + 27 68 41 109 Nb x -56689.800 430.816 8297.1307 3.9524 B- 9969.4851 430.9610 108 939141.000 462.500 + 25 67 42 109 Mo x -66659.285 11.179 8381.4163 0.1026 B- 7623.5438 14.7805 108 928438.318 12.000 + 23 66 43 109 Tc x -74282.828 9.669 8444.1796 0.0887 B- 6455.6267 12.6574 108 920254.107 10.380 + 21 65 44 109 Ru -4n -80738.455 8.954 8496.2280 0.0821 B- 4260.7958 9.8229 108 913323.707 9.612 + 19 64 45 109 Rh -84999.251 4.040 8528.1404 0.0371 B- 2607.2327 4.1874 108 908749.555 4.336 + 17 63 46 109 Pd -87606.484 1.114 8544.8825 0.0102 B- 1112.9469 1.4024 108 905950.576 1.195 + 15 62 47 109 Ag -88719.431 1.287 8547.9155 0.0118 B- -215.1002 1.7795 108 904755.778 1.381 + 13 61 48 109 Cd -88504.330 1.536 8538.7646 0.0141 B- -2014.8047 4.0662 108 904986.697 1.649 + 11 60 49 109 In -86489.526 3.969 8513.1027 0.0364 B- -3859.3453 8.8866 108 907149.679 4.261 + 9 59 50 109 Sn -82630.180 7.949 8470.5183 0.0729 B- -6379.1940 8.8074 108 911292.857 8.533 + 7 58 51 109 Sb -76250.986 5.265 8404.8161 0.0483 B- -8535.5871 6.8502 108 918141.203 5.652 + 5 57 52 109 Te -67715.399 4.382 8319.3305 0.0402 B- -10042.8941 8.0301 108 927304.532 4.704 + 3 56 53 109 I -p -57672.505 6.729 8220.0164 0.0617 B- -11502.9589 300.1831 108 938086.022 7.223 + 1 55 54 109 Xe -a -46169.546 300.108 8107.3071 2.7533 B- * 108 950434.955 322.178 +0 30 70 40 110 Zr x -42220# 500# 8171# 5# B- 10090# 976# 109 954675# 537# + 28 69 41 110 Nb x -52309.914 838.345 8255.2607 7.6213 B- 12225.9002 838.6945 109 943843.000 900.000 + 26 68 42 110 Mo x -64535.814 24.219 8359.2930 0.2202 B- 6498.7491 26.0147 109 930717.956 26.000 + 24 67 43 110 Tc x -71034.564 9.497 8411.2603 0.0863 B- 9038.0654 12.5086 109 923741.263 10.195 + 22 66 44 110 Ru -80072.629 8.924 8486.3123 0.0811 B- 2756.0638 19.4044 109 914038.501 9.580 + 20 65 45 110 Rh -82828.693 17.805 8504.2551 0.1619 B- 5502.2116 17.7967 109 911079.745 19.114 + 18 64 46 110 Pd -88330.904 0.612 8547.1630 0.0056 B- -873.5982 1.3777 109 905172.878 0.657 + 16 63 47 110 Ag -87457.306 1.286 8532.1089 0.0117 B- 2890.6633 1.2771 109 906110.724 1.380 + 14 62 48 110 Cd -90347.969 0.380 8551.2755 0.0035 B- -3878.0000 11.5470 109 903007.470 0.407 + 12 61 49 110 In - -86469.969 11.553 8508.9087 0.1050 B- -627.9769 17.9802 109 907170.674 12.402 + 10 60 50 110 Sn x -85841.993 13.777 8496.0875 0.1252 B- -8392.2480 15.0117 109 907844.835 14.790 + 8 59 51 110 Sb x -77449.745 5.962 8412.6821 0.0542 B- -5219.9240 8.8753 109 916854.283 6.400 + 6 58 52 110 Te -72229.821 6.575 8358.1160 0.0598 B- -11761.9766 62.2875 109 922458.102 7.058 + 4 57 53 110 I -a -60467.844 61.940 8244.0767 0.5631 B- -8545.2075 118.4700 109 935085.102 66.494 + 2 56 54 110 Xe -a -51922.636 100.988 8159.2808 0.9181 B- * 109 944258.759 108.415 +0 31 71 40 111 Zr x -36480# 600# 8118# 5# B- 12480# 671# 110 960837# 644# + 29 70 41 111 Nb x -48960# 300# 8223# 3# B- 10980# 300# 110 947439# 322# + 27 69 42 111 Mo + -59939.813 12.578 8315.2932 0.1133 B- 9084.8620 6.7999 110 935651.966 13.503 + 25 68 43 111 Tc x -69024.675 10.582 8390.0906 0.0953 B- 7760.6500 13.8477 110 925898.966 11.359 + 23 67 44 111 Ru x -76785.325 9.682 8452.9582 0.0872 B- 5518.5456 11.8621 110 917567.566 10.394 + 21 66 45 111 Rh -82303.871 6.853 8495.6267 0.0617 B- 3682.0153 6.8899 110 911643.164 7.356 + 19 65 46 111 Pd -n -85985.886 0.731 8521.7498 0.0066 B- 2229.5607 1.5721 110 907690.358 0.785 + 17 64 47 111 Ag + -88215.447 1.459 8534.7878 0.0131 B- 1036.8000 1.4142 110 905296.827 1.565 + 15 63 48 111 Cd -89252.247 0.357 8537.0801 0.0032 B- -860.1972 3.4170 110 904183.776 0.383 + 13 62 49 111 In -88392.050 3.424 8522.2824 0.0308 B- -2453.4692 6.3368 110 905107.236 3.675 + 11 61 50 111 Sn +n -85938.581 5.336 8493.1310 0.0481 B- -5101.8340 10.3337 110 907741.143 5.728 + 9 60 51 111 Sb x -80836.747 8.849 8440.1203 0.0797 B- -7249.2597 10.9370 110 913218.187 9.500 + 7 59 52 111 Te x -73587.487 6.427 8367.7635 0.0579 B- -8633.6922 7.9943 110 921000.587 6.900 + 5 58 53 111 I -64953.795 4.754 8282.9343 0.0428 B- -10434# 116# 110 930269.236 5.103 + 3 57 54 111 Xe -a -54520# 115# 8182# 1# B- -11620# 231# 110 941470# 124# + 1 56 55 111 Cs x -42900# 200# 8070# 2# B- * 110 953945# 215# +0 32 72 40 112 Zr x -32420# 700# 8081# 6# B- 11650# 761# 111 965196# 751# + 30 71 41 112 Nb x -44070# 300# 8178# 3# B- 13410# 361# 111 952689# 322# + 28 70 42 112 Mo x -57480# 200# 8291# 2# B- 7779# 200# 111 938293# 215# + 26 69 43 112 Tc x -65258.932 5.515 8353.6217 0.0492 B- 10371.9409 11.0602 111 929941.658 5.920 + 24 68 44 112 Ru x -75630.873 9.600 8439.2431 0.0857 B- 4100.1790 45.1185 111 918806.922 10.305 + 22 67 45 112 Rh -79731.052 44.085 8468.8666 0.3936 B- 6589.9874 43.9269 111 914405.199 47.327 + 20 66 46 112 Pd -86321.039 6.546 8520.7205 0.0584 B- 262.6897 6.9799 111 907330.557 7.027 + 18 65 47 112 Ag x -86583.729 2.422 8516.0807 0.0216 B- 3991.1283 2.4348 111 907048.548 2.600 + 16 64 48 112 Cd -90574.857 0.250 8544.7306 0.0022 B- -2584.7306 4.2434 111 902763.896 0.268 + 14 63 49 112 In -87990.127 4.251 8514.6674 0.0380 B- 664.9224 4.2434 111 905538.718 4.563 + 12 62 50 112 Sn -88655.049 0.294 8513.6189 0.0026 B- -7056.0760 17.8317 111 904824.894 0.315 + 10 61 51 112 Sb x -81598.973 17.829 8443.6330 0.1592 B- -4031.4550 19.7019 111 912399.903 19.140 + 8 60 52 112 Te x -77567.518 8.383 8400.6527 0.0749 B- -10504.1795 13.2390 111 916727.848 9.000 + 6 59 53 112 I x -67063.339 10.246 8299.8801 0.0915 B- -7036.9910 13.1754 111 928004.548 11.000 + 4 58 54 112 Xe -a -60026.348 8.283 8230.0646 0.0740 B- -13612# 116# 111 935559.068 8.891 + 2 57 55 112 Cs -p -46415# 116# 8102# 1# B- * 111 950172# 124# +0 33 73 40 113 Zr x -26340# 300# 8027# 3# B- 13870# 500# 112 971723# 322# + 31 72 41 113 Nb x -40210# 400# 8143# 4# B- 12440# 500# 112 956833# 429# + 29 71 42 113 Mo x -52650# 300# 8246# 3# B- 10162# 300# 112 943478# 322# + 27 70 43 113 Tc x -62811.549 3.353 8329.4652 0.0297 B- 9056.2674 38.4285 112 932569.032 3.600 + 25 69 44 113 Ru -71867.816 38.282 8402.6857 0.3388 B- 6899.1276 38.9406 112 922846.729 41.097 + 23 68 45 113 Rh x -78766.944 7.132 8456.8165 0.0631 B- 4823.5559 9.8809 112 915440.212 7.656 + 21 67 46 113 Pd x -83590.500 6.947 8492.5795 0.0615 B- 3436.3252 18.0341 112 910261.912 7.458 + 19 66 47 113 Ag + -87026.825 16.643 8516.0660 0.1473 B- 2016.4615 16.6410 112 906572.865 17.866 + 17 65 48 113 Cd -89043.286 0.245 8526.9874 0.0022 B- 323.8370 0.2653 112 904408.105 0.262 + 15 64 49 113 In -89367.123 0.188 8522.9297 0.0017 B- -1038.9941 1.5733 112 904060.451 0.202 + 13 63 50 113 Sn -88328.129 1.575 8506.8117 0.0139 B- -3911.1637 17.1206 112 905175.857 1.690 + 11 62 51 113 Sb - -84416.966 17.193 8465.2762 0.1521 B- -6069.9281 32.8102 112 909374.664 18.457 + 9 61 52 113 Te x -78347.037 27.945 8404.6366 0.2473 B- -7227.5210 29.0704 112 915891.000 30.000 + 7 60 53 113 I x -71119.517 8.011 8333.7528 0.0709 B- -8915.8902 10.5334 112 923650.062 8.600 + 5 59 54 113 Xe -62203.626 6.840 8247.9277 0.0605 B- -10439.0876 10.9702 112 933221.663 7.342 + 3 58 55 113 Cs -p -51764.539 8.577 8148.6230 0.0759 B- -12055# 300# 112 944428.484 9.207 + 1 57 56 113 Ba x -39710# 300# 8035# 3# B- * 112 957370# 322# +0 32 73 41 114 Nb x -34960# 500# 8097# 4# B- 14720# 583# 113 962469# 537# + 30 72 42 114 Mo x -49680# 300# 8219# 3# B- 8920# 527# 113 946666# 322# + 28 71 43 114 Tc x -58600.294 433.145 8290.2599 3.7995 B- 11620.9190 433.1594 113 937090.000 465.000 + 26 70 44 114 Ru x -70221.213 3.556 8385.3351 0.0312 B- 5489.0622 71.6432 113 924614.430 3.817 + 24 69 45 114 Rh -75710.275 71.561 8426.6221 0.6277 B- 7780.0712 71.8915 113 918721.680 76.824 + 22 68 46 114 Pd x -83490.346 6.948 8488.0056 0.0610 B- 1440.4642 8.3133 113 910369.430 7.459 + 20 67 47 114 Ag x -84930.811 4.564 8493.7786 0.0400 B- 5084.1233 4.5727 113 908823.029 4.900 + 18 66 48 114 Cd -90014.934 0.276 8531.5135 0.0024 B- -1445.1268 0.3817 113 903364.998 0.296 + 16 65 49 114 In -88569.807 0.301 8511.9742 0.0027 B- 1989.9281 0.3018 113 904916.405 0.323 + 14 64 50 114 Sn -90559.735 0.029 8522.5671 0.0004 B- -6063.1189 19.7724 113 902780.130 0.031 + 12 63 51 114 Sb -84496.616 19.772 8462.5191 0.1734 B- -2606.9398 31.4275 113 909289.155 21.226 + 10 62 52 114 Te x -81889.676 24.428 8432.7885 0.2143 B- -9250.7417 31.5883 113 912087.820 26.224 + 8 61 53 114 I x -72638.935 20.027 8344.7790 0.1757 B- -5553.0360 22.9354 113 922018.900 21.500 + 6 60 54 114 Xe x -67085.899 11.178 8289.2054 0.0981 B- -12399.9706 85.7989 113 927980.329 12.000 + 4 59 55 114 Cs -a -54685.928 85.068 8173.5711 0.7462 B- -8780.4915 133.3375 113 941292.244 91.323 + 2 58 56 114 Ba -a -45905.437 102.676 8089.6865 0.9007 B- * 113 950718.489 110.227 +0 33 74 41 115 Nb x -30880# 500# 8061# 4# B- 13670# 640# 114 966849# 537# + 31 73 42 115 Mo x -44550# 400# 8173# 3# B- 11247# 445# 114 952174# 429# + 29 72 43 115 Tc x -55796# 196# 8264# 2# B- 10309# 197# 114 940100# 210# + 27 71 44 115 Ru x -66105.296 25.166 8346.8140 0.2188 B- 8123.9327 26.1788 114 929033.049 27.016 + 25 70 45 115 Rh x -74229.228 7.319 8410.6538 0.0636 B- 6196.5938 15.3503 114 920311.649 7.857 + 23 69 46 115 Pd -80425.822 13.547 8457.7342 0.1178 B- 4556.7647 21.6496 114 913659.333 14.543 + 21 68 47 115 Ag -84982.587 18.268 8490.5553 0.1589 B- 3101.8930 18.2744 114 908767.445 19.611 + 19 67 48 115 Cd -88084.480 0.651 8510.7252 0.0057 B- 1451.8768 0.6514 114 905437.426 0.699 + 17 66 49 115 In -89536.357 0.012 8516.5472 0.0003 B- 497.4892 0.0097 114 903878.772 0.012 + 15 65 50 115 Sn -90033.846 0.015 8514.0702 0.0003 B- -3030.4336 16.0253 114 903344.695 0.016 + 13 64 51 115 Sb x -87003.412 16.025 8480.9156 0.1394 B- -4940.6447 32.2137 114 906598.000 17.203 + 11 63 52 115 Te x -82062.767 27.945 8431.1504 0.2430 B- -5724.9628 40.1840 114 911902.000 30.000 + 9 62 53 115 I x -76337.805 28.876 8374.5651 0.2511 B- -7681.0475 31.3126 114 918048.000 31.000 + 7 61 54 115 Xe x -68656.757 12.109 8300.9704 0.1053 B- -8957# 103# 114 926293.943 13.000 + 5 60 55 115 Cs x -59699# 102# 8216# 1# B- -10779# 225# 114 935910# 110# + 3 59 56 115 Ba x -48920# 200# 8116# 2# B- * 114 947482# 215# +0 34 75 41 116 Nb x -25230# 300# 8012# 3# B- 15980# 583# 115 972914# 322# + 32 74 42 116 Mo x -41210# 500# 8143# 4# B- 10003# 582# 115 955759# 537# + 30 73 43 116 Tc x -51214# 298# 8223# 3# B- 12855# 298# 115 945020# 320# + 28 72 44 116 Ru x -64068.917 3.726 8326.8840 0.0321 B- 6666.8252 73.9257 115 931219.191 4.000 + 26 71 45 116 Rh -70735.742 73.832 8377.6123 0.6365 B- 9095.2839 74.1690 115 924062.060 79.261 + 24 70 46 116 Pd x -79831.026 7.135 8449.2755 0.0615 B- 2711.6378 7.8446 115 914297.872 7.659 + 22 69 47 116 Ag x -82542.664 3.260 8465.9073 0.0281 B- 6169.8248 3.2642 115 911386.809 3.500 + 20 68 48 116 Cd -88712.489 0.160 8512.3511 0.0014 B- -462.7305 0.2720 115 904763.230 0.172 + 18 67 49 116 In -n -88249.758 0.220 8501.6177 0.0019 B- 3276.2204 0.2397 115 905259.992 0.236 + 16 66 50 116 Sn -91525.979 0.096 8523.1166 0.0009 B- -4703.9591 5.1540 115 901742.825 0.103 + 14 65 51 116 Sb -86822.020 5.154 8475.8208 0.0444 B- -1558.2272 24.7485 115 906792.732 5.533 + 12 64 52 116 Te -85263.793 24.206 8455.6435 0.2087 B- -7843.1388 75.3230 115 908465.558 25.986 + 10 63 53 116 I -77420.654 75.037 8381.2858 0.6469 B- -4373.7764 75.8444 115 916885.513 80.555 + 8 62 54 116 Xe -73046.877 13.017 8336.8365 0.1122 B- -11004# 101# 115 921580.955 13.974 + 6 61 55 116 Cs ea -62043# 100# 8235# 1# B- -7663# 224# 115 933395# 108# + 4 60 56 116 Ba x -54380# 200# 8162# 2# B- -14330# 379# 115 941621# 215# + 2 59 57 116 La -a -40050# 321# 8032# 3# B- * 115 957005# 345# +0 33 75 42 117 Mo x -35689# 500# 8096# 4# B- 12450# 640# 116 961686# 537# + 31 74 43 117 Tc x -48140# 400# 8195# 3# B- 11350# 589# 116 948320# 429# + 29 73 44 117 Ru x -59489.871 433.145 8285.5625 3.7021 B- 9406.8875 433.2361 116 936135.000 465.000 + 27 72 45 117 Rh x -68896.758 8.895 8359.2766 0.0760 B- 7527.1313 11.4108 116 926036.291 9.548 + 25 71 46 117 Pd -76423.890 7.255 8416.9243 0.0620 B- 5758.0284 14.7674 116 917955.584 7.788 + 23 70 47 117 Ag -82181.918 13.572 8459.4515 0.1160 B- 4236.4790 13.6099 116 911774.086 14.570 + 21 69 48 117 Cd -n -86418.397 1.013 8488.9740 0.0087 B- 2524.6381 4.9829 116 907226.039 1.087 + 19 68 49 117 In -88943.035 4.881 8503.8653 0.0417 B- 1454.7073 4.8567 116 904515.729 5.239 + 17 67 50 117 Sn -90397.742 0.483 8509.6120 0.0041 B- -1758.1788 8.4449 116 902954.036 0.518 + 15 66 51 117 Sb -88639.564 8.437 8487.8981 0.0721 B- -3544.0634 13.0785 116 904841.519 9.057 + 13 65 52 117 Te -85095.500 13.455 8450.9203 0.1150 B- -4656.9321 28.1284 116 908646.227 14.444 + 11 64 53 117 I -80438.568 25.558 8404.4307 0.2184 B- -6253.2213 27.5845 116 913645.649 27.437 + 9 63 54 117 Xe x -74185.347 10.378 8344.2976 0.0887 B- -7692.2462 63.2672 116 920358.758 11.141 + 7 62 55 117 Cs x -66493.101 62.410 8271.8652 0.5334 B- -9035.1943 258.0009 116 928616.723 67.000 + 5 61 56 117 Ba ep -57457.906 250.339 8187.9546 2.1396 B- -11187# 321# 116 938316.403 268.749 + 3 60 57 117 La -p -46271# 200# 8086# 2# B- * 116 950326# 215# +0 34 76 42 118 Mo x -32370# 500# 8067# 4# B- 10920# 640# 117 965249# 537# + 32 75 43 118 Tc x -43290# 400# 8153# 3# B- 13710# 447# 117 953526# 429# + 30 74 44 118 Ru x -57000# 200# 8263# 2# B- 7887# 202# 117 938808# 215# + 28 73 45 118 Rh x -64886.840 24.236 8322.8539 0.2054 B- 10501.5182 24.3424 117 930341.116 26.018 + 26 72 46 118 Pd -75388.358 2.494 8405.2197 0.0211 B- 4165.4444 3.5419 117 919067.273 2.677 + 24 71 47 118 Ag x -79553.802 2.515 8433.8900 0.0213 B- 7147.8469 20.1582 117 914595.484 2.700 + 22 70 48 118 Cd -nn -86701.649 20.001 8487.8350 0.1695 B- 526.5277 21.4501 117 906921.956 21.471 + 20 69 49 118 In -87228.177 7.752 8485.6670 0.0657 B- 4424.6664 7.7396 117 906356.705 8.322 + 18 68 50 118 Sn -91652.843 0.499 8516.5341 0.0042 B- -3656.6393 2.9745 117 901606.630 0.536 + 16 67 51 118 Sb - -87996.204 3.016 8478.9156 0.0256 B- -305.4459 18.5521 117 905532.194 3.237 + 14 66 52 118 Te +nn -87690.758 18.306 8469.6970 0.1551 B- -6719.7015 26.9364 117 905860.104 19.652 + 12 65 53 118 I x -80971.056 19.760 8406.1203 0.1675 B- -2891.9893 22.3197 117 913074.000 21.213 + 10 64 54 118 Xe x -78079.067 10.378 8374.9819 0.0880 B- -9669.6905 16.4423 117 916178.678 11.141 + 8 63 55 118 Cs IT -68409.377 12.753 8286.4053 0.1081 B- -6210# 201# 117 926559.517 13.690 + 6 62 56 118 Ba x -62200# 200# 8227# 2# B- -12580# 361# 117 933226# 215# + 4 61 57 118 La x -49620# 300# 8114# 3# B- * 117 946731# 322# +0 35 77 42 119 Mo x -26580# 300# 8019# 3# B- 13590# 583# 118 971465# 322# + 33 76 43 119 Tc x -40170# 500# 8126# 4# B- 11910# 583# 118 956876# 537# + 31 75 44 119 Ru x -52080# 300# 8220# 3# B- 10743# 300# 118 944090# 322# + 29 74 45 119 Rh x -62822.802 9.315 8303.3953 0.0783 B- 8584.4751 12.4416 118 932556.951 10.000 + 27 73 46 119 Pd x -71407.277 8.248 8368.9594 0.0693 B- 7238.4816 16.8566 118 923341.138 8.854 + 25 72 47 119 Ag -78645.759 14.703 8423.2126 0.1236 B- 5331.1799 35.9259 118 915570.309 15.783 + 23 71 48 119 Cd -83976.939 37.695 8461.4381 0.3168 B- 3721.7197 38.0880 118 909847.052 40.467 + 21 70 49 119 In -87698.658 7.310 8486.1387 0.0614 B- 2366.3263 7.3381 118 905851.622 7.847 + 19 69 50 119 Sn -90064.985 0.725 8499.4494 0.0061 B- -589.4452 6.9937 118 903311.266 0.778 + 17 68 51 119 Sb -89475.539 6.998 8487.9218 0.0588 B- -2293.0000 2.0000 118 903944.062 7.512 + 15 67 52 119 Te - -87182.539 7.278 8462.0785 0.0612 B- -3404.8080 22.8941 118 906405.699 7.813 + 13 66 53 119 I x -83777.731 21.706 8426.8924 0.1824 B- -4983.2433 24.0598 118 910060.910 23.302 + 11 65 54 119 Xe -78794.488 10.378 8378.4420 0.0872 B- -6489.4269 17.3790 118 915410.641 11.141 + 9 64 55 119 Cs IT -72305.061 13.940 8317.3347 0.1171 B- -7714.9651 200.7537 118 922377.327 14.965 + 7 63 56 119 Ba ep -64590.096 200.269 8245.9287 1.6829 B- -9570# 361# 118 930659.683 214.997 + 5 62 57 119 La x -55020# 300# 8159# 3# B- -11199# 583# 118 940934# 322# + 3 61 58 119 Ce x -43820# 500# 8058# 4# B- * 118 952957# 537# +0 34 77 43 120 Tc x -35000# 500# 8083# 4# B- 14720# 640# 119 962426# 537# + 32 76 44 120 Ru x -49720# 400# 8199# 3# B- 8899# 447# 119 946623# 429# + 30 75 45 120 Rh x -58620# 200# 8266# 2# B- 11660# 200# 119 937069# 215# + 28 74 46 120 Pd -70279.604 2.296 8357.0817 0.0191 B- 5371.9076 5.0261 119 924551.745 2.464 + 26 73 47 120 Ag x -75651.512 4.471 8395.3281 0.0373 B- 8305.8535 5.8202 119 918784.765 4.800 + 24 72 48 120 Cd x -83957.365 3.726 8458.0240 0.0311 B- 1770.3754 40.1837 119 909868.065 4.000 + 22 71 49 120 In + -85727.741 40.011 8466.2575 0.3334 B- 5370.0000 40.0000 119 907967.489 42.953 + 20 70 50 120 Sn -91097.741 0.920 8504.4880 0.0077 B- -2680.6076 7.1399 119 902202.557 0.987 + 18 69 51 120 Sb - -88417.133 7.199 8475.6300 0.0600 B- 945.0271 7.3530 119 905080.308 7.728 + 16 68 52 120 Te -89362.160 1.751 8476.9857 0.0146 B- -5615.0000 15.0000 119 904065.779 1.880 + 14 67 53 120 I - -83747.160 15.102 8423.6745 0.1258 B- -1574.7260 19.1760 119 910093.729 16.212 + 12 66 54 120 Xe x -82172.434 11.817 8404.0322 0.0985 B- -8283.7857 15.4611 119 911784.267 12.686 + 10 65 55 120 Cs IT -73888.649 9.970 8328.4811 0.0831 B- -5000.0000 300.0000 119 920677.277 10.702 + 8 64 56 120 Ba - -68888.649 300.166 8280.2949 2.5014 B- -11319# 424# 119 926044.997 322.241 + 6 63 57 120 La x -57570# 300# 8179# 2# B- -7840# 583# 119 938196# 322# + 4 62 58 120 Ce x -49730# 500# 8108# 4# B- * 119 946613# 537# +0 35 78 43 121 Tc x -31540# 500# 8054# 4# B- 13080# 640# 120 966140# 537# + 33 77 44 121 Ru x -44620# 400# 8156# 3# B- 11630# 737# 120 952098# 429# + 31 76 45 121 Rh x -56250.134 619.444 8245.2397 5.1194 B- 9932.2030 619.4527 120 939613.000 665.000 + 29 75 46 121 Pd x -66182.337 3.353 8320.8584 0.0277 B- 8220.4934 12.5652 120 928950.342 3.600 + 27 74 47 121 Ag x -74402.831 12.109 8382.3306 0.1001 B- 6671.0057 12.2642 120 920125.279 13.000 + 25 73 48 121 Cd x -81073.837 1.942 8430.9972 0.0161 B- 4760.7564 27.4876 120 912963.660 2.085 + 23 72 49 121 In +p -85834.593 27.419 8463.8767 0.2266 B- 3362.0331 27.4098 120 907852.778 29.435 + 21 71 50 121 Sn -89196.626 0.978 8485.1964 0.0081 B- 402.5306 2.5239 120 904243.488 1.050 + 19 70 51 121 Sb -89599.157 2.506 8482.0574 0.0207 B- -1056.0462 25.7587 120 903811.353 2.690 + 17 69 52 121 Te -88543.111 25.835 8466.8641 0.2135 B- -2297.4615 25.9856 120 904945.065 27.734 + 15 68 53 121 I -86245.649 4.723 8441.4111 0.0390 B- -3764.6525 11.2790 120 907411.492 5.070 + 13 67 54 121 Xe -82480.997 10.243 8403.8326 0.0847 B- -5378.6549 13.9791 120 911453.012 10.995 + 11 66 55 121 Cs -77102.342 14.290 8352.9152 0.1181 B- -6357.4948 141.1765 120 917227.235 15.340 + 9 65 56 121 Ba - -70744.847 141.898 8293.9083 1.1727 B- -8555# 332# 120 924052.286 152.333 + 7 64 57 121 La x -62190# 300# 8217# 2# B- -9500# 500# 120 933236# 322# + 5 63 58 121 Ce x -52690# 401# 8132# 3# B- -11139# 641# 120 943435# 430# + 3 62 59 121 Pr -p -41551# 500# 8033# 4# B- * 120 955393# 537# +0 36 79 43 122 Tc x -26305# 300# 8011# 2# B- 15475# 583# 121 971760# 322# + 34 78 44 122 Ru x -41780# 500# 8132# 4# B- 10099# 583# 121 955147# 537# + 32 77 45 122 Rh x -51880# 300# 8208# 2# B- 12737# 301# 121 944305# 322# + 30 76 46 122 Pd x -64616.169 19.561 8305.9755 0.1603 B- 6489.9492 42.9094 121 930631.693 21.000 + 28 75 47 122 Ag x -71106.118 38.191 8352.7591 0.3130 B- 9506.2662 38.2604 121 923664.446 41.000 + 26 74 48 122 Cd -80612.384 2.299 8424.2667 0.0188 B- 2958.9765 50.1126 121 913459.050 2.468 + 24 73 49 122 In + -83571.361 50.060 8442.1079 0.4103 B- 6368.5921 50.0000 121 910282.458 53.741 + 22 72 50 122 Sn -89939.953 2.448 8487.8968 0.0201 B- -1605.7483 3.2135 121 903445.494 2.627 + 20 71 51 122 Sb -88334.205 2.503 8468.3222 0.0205 B- 1979.0772 2.1265 121 905169.335 2.687 + 18 70 52 122 Te -90313.282 1.357 8478.1315 0.0111 B- -4234.0000 5.0000 121 903044.708 1.456 + 16 69 53 122 I - -86079.282 5.181 8437.0139 0.0425 B- -724.2937 12.2596 121 907590.094 5.561 + 14 68 54 122 Xe x -85354.988 11.111 8424.6644 0.0911 B- -7210.2195 35.4720 121 908367.655 11.928 + 12 67 55 122 Cs -78144.769 33.687 8359.1515 0.2761 B- -3535.8170 43.7690 121 916108.144 36.164 + 10 66 56 122 Ba x -74608.952 27.945 8323.7567 0.2291 B- -10066# 299# 121 919904.000 30.000 + 8 65 57 122 La x -64543# 298# 8235# 2# B- -6669# 499# 121 930710# 320# + 6 64 58 122 Ce x -57874# 401# 8174# 3# B- -13094# 641# 121 937870# 430# + 4 63 59 122 Pr x -44780# 500# 8060# 4# B- * 121 951927# 537# +0 35 79 44 123 Ru x -36550# 500# 8089# 4# B- 12640# 640# 122 960762# 537# + 33 78 45 123 Rh x -49190# 400# 8185# 3# B- 11239# 885# 122 947192# 429# + 31 77 46 123 Pd x -60429.748 789.441 8270.0318 6.4182 B- 9138.8323 790.1142 122 935126.000 847.500 + 29 76 47 123 Ag x -69568.581 32.602 8337.9707 0.2651 B- 7845.6026 32.7136 122 925315.060 35.000 + 27 75 48 123 Cd -77414.183 2.696 8395.3955 0.0219 B- 6014.8503 19.8980 122 916892.460 2.894 + 25 74 49 123 In -83429.034 19.832 8437.9362 0.1612 B- 4385.6489 19.8392 122 910435.252 21.290 + 23 73 50 123 Sn -87814.683 2.479 8467.2313 0.0202 B- 1408.2079 2.4203 122 905727.065 2.661 + 21 72 51 123 Sb -89222.890 1.356 8472.3196 0.0110 B- -51.9128 0.0661 122 904215.292 1.456 + 19 71 52 123 Te -89170.978 1.355 8465.5370 0.0110 B- -1228.3898 3.4448 122 904271.022 1.454 + 17 70 53 123 I -87942.588 3.686 8449.1896 0.0300 B- -2694.3302 9.6829 122 905589.753 3.956 + 15 69 54 123 Xe -85248.258 9.534 8420.9239 0.0775 B- -4204.6012 15.4121 122 908482.235 10.234 + 13 68 55 123 Cs x -81043.657 12.109 8380.3796 0.0985 B- -5388.6934 17.1253 122 912996.060 13.000 + 11 67 56 123 Ba x -75654.963 12.109 8330.2086 0.0985 B- -7004# 196# 122 918781.060 13.000 + 9 66 57 123 La x -68651# 196# 8267# 2# B- -8365# 357# 122 926300# 210# + 7 65 58 123 Ce x -60286# 298# 8193# 2# B- -10056# 499# 122 935280# 320# + 5 64 59 123 Pr x -50230# 400# 8104# 3# B- * 122 946076# 429# +0 36 80 44 124 Ru x -33590# 600# 8065# 5# B- 11120# 721# 123 963940# 644# + 34 79 45 124 Rh x -44710# 400# 8148# 3# B- 13690# 500# 123 952002# 429# + 32 78 46 124 Pd x -58400# 300# 8252# 2# B- 7830# 391# 123 937305# 322# + 30 77 47 124 Ag x -66229.951 251.503 8308.8958 2.0283 B- 10469.4858 251.5169 123 928899.227 270.000 + 28 76 48 124 Cd -76699.436 2.609 8387.0179 0.0210 B- 4168.3420 30.5355 123 917659.772 2.800 + 26 75 49 124 In -80867.778 30.561 8414.3243 0.2465 B- 7363.6970 30.5668 123 913184.873 32.808 + 24 74 50 124 Sn -88231.475 1.314 8467.3997 0.0106 B- -612.4067 0.4101 123 905279.619 1.410 + 22 73 51 124 Sb -n -87619.069 1.358 8456.1517 0.0110 B- 2905.0730 0.1317 123 905937.065 1.457 + 20 72 52 124 Te -90524.142 1.352 8473.2705 0.0109 B- -3159.5870 1.8593 123 902818.341 1.451 + 18 71 53 124 I - -87364.555 2.299 8441.4807 0.0185 B- 302.8501 1.8639 123 906210.297 2.467 + 16 70 54 124 Xe -87667.405 1.358 8437.6138 0.0110 B- -5926.3445 9.2512 123 905885.174 1.457 + 14 69 55 124 Cs x -81741.060 9.151 8383.5114 0.0738 B- -2651.2748 15.4894 123 912247.366 9.823 + 12 68 56 124 Ba x -79089.786 12.497 8355.8209 0.1008 B- -8831.1685 58.0305 123 915093.627 13.416 + 10 67 57 124 La x -70258.617 56.669 8278.2926 0.4570 B- -5343# 303# 123 924574.275 60.836 + 8 66 58 124 Ce x -64916# 298# 8229# 2# B- -11765# 499# 123 930310# 320# + 6 65 59 124 Pr x -53151# 401# 8128# 3# B- -8321# 641# 123 942940# 430# + 4 64 60 124 Nd x -44830# 500# 8054# 4# B- * 123 951873# 537# +0 37 81 44 125 Ru x -28370# 300# 8023# 2# B- 13460# 583# 124 969544# 322# + 35 80 45 125 Rh x -41830# 500# 8124# 4# B- 12130# 640# 124 955094# 537# + 33 79 46 125 Pd x -53960# 400# 8215# 3# B- 10560# 589# 124 942072# 429# + 31 78 47 125 Ag x -64519.939 433.145 8293.3151 3.4652 B- 8828.1511 433.1544 124 930735.000 465.000 + 29 77 48 125 Cd x -73348.090 2.888 8357.6815 0.0231 B- 7064.2177 3.3869 124 921257.590 3.100 + 27 76 49 125 In x -80412.308 1.770 8407.9365 0.0142 B- 5481.3495 2.2131 124 913673.841 1.900 + 25 75 50 125 Sn -n -85893.657 1.329 8445.5285 0.0106 B- 2361.4366 2.1661 124 907789.370 1.426 + 23 74 51 125 Sb + -88255.094 2.515 8458.1612 0.0201 B- 766.7000 2.1213 124 905254.264 2.700 + 21 73 52 125 Te -89021.794 1.352 8458.0361 0.0108 B- -185.7700 0.0600 124 904431.178 1.451 + 19 72 53 125 I - -88836.024 1.353 8450.2911 0.0108 B- -1636.6632 0.4259 124 904630.610 1.452 + 17 71 54 125 Xe -87199.361 1.415 8430.9390 0.0113 B- -3109.6184 7.7879 124 906387.640 1.518 + 15 70 55 125 Cs -84089.742 7.736 8399.8033 0.0619 B- -4420.7663 13.4415 124 909725.953 8.304 + 13 69 56 125 Ba -79668.976 10.992 8358.1784 0.0879 B- -5909.4836 27.6308 124 914471.840 11.800 + 11 68 57 125 La -73759.492 25.997 8304.6438 0.2080 B- -7102# 197# 124 920815.931 27.909 + 9 67 58 125 Ce x -66658# 196# 8242# 2# B- -8587# 358# 124 928440# 210# + 7 66 59 125 Pr x -58070# 300# 8167# 2# B- -10001# 500# 124 937659# 322# + 5 65 60 125 Nd x -48070# 400# 8080# 3# B- * 124 948395# 429# +0 36 81 45 126 Rh x -37200# 500# 8087# 4# B- 14590# 640# 125 960064# 537# + 34 80 46 126 Pd x -51790# 400# 8197# 3# B- 8930# 447# 125 944401# 429# + 32 79 47 126 Ag x -60720# 200# 8261# 2# B- 11535# 200# 125 934814# 215# + 30 78 48 126 Cd -72255.727 2.304 8346.7393 0.0183 B- 5553.6500 4.7831 125 922430.290 2.473 + 28 77 49 126 In x -77809.377 4.192 8384.6067 0.0333 B- 8205.7585 11.4802 125 916468.202 4.500 + 26 76 50 126 Sn -nn -86015.135 10.688 8443.5227 0.0848 B- 378.0000 30.0000 125 907658.958 11.473 + 24 75 51 126 Sb - -86393.135 31.847 8440.3136 0.2528 B- 3671.0321 31.8223 125 907253.158 34.189 + 22 74 52 126 Te -90064.168 1.354 8463.2397 0.0107 B- -2153.6712 3.6717 125 903312.144 1.453 + 20 73 53 126 I -87910.496 3.778 8439.9379 0.0300 B- 1235.8904 3.7779 125 905624.205 4.055 + 18 72 54 126 Xe -89146.38687 0.00562 8443.5375 0.0003 B- -4795.7039 10.3587 125 904297.422 0.006 + 16 71 55 126 Cs -84350.683 10.359 8399.2673 0.0822 B- -1680.7697 16.2322 125 909445.821 11.120 + 14 70 56 126 Ba x -82669.913 12.497 8379.7187 0.0992 B- -7696.4376 91.3663 125 911250.202 13.416 + 12 69 57 126 La x -74973.476 90.508 8312.4268 0.7183 B- -4152.9106 94.7235 125 919512.667 97.163 + 10 68 58 126 Ce x -70820.565 27.945 8273.2581 0.2218 B- -10497# 198# 125 923971.000 30.000 + 8 67 59 126 Pr x -60324# 196# 8184# 2# B- -6943# 358# 125 935240# 210# + 6 66 60 126 Nd x -53380# 300# 8122# 2# B- -13631# 583# 125 942694# 322# + 4 65 61 126 Pm x -39750# 500# 8008# 4# B- * 125 957327# 537# +0 37 82 45 127 Rh x -33730# 600# 8060# 5# B- 13490# 781# 126 963789# 644# + 35 81 46 127 Pd x -47220# 500# 8160# 4# B- 11429# 539# 126 949307# 537# + 33 80 47 127 Ag x -58650# 200# 8244# 2# B- 10092# 200# 126 937037# 215# + 31 79 48 127 Cd x -68741.199 6.200 8316.8971 0.0488 B- 8138.6978 11.7675 126 926203.291 6.656 + 29 78 49 127 In -76879.897 10.001 8374.8212 0.0788 B- 6589.6810 12.0260 126 917466.040 10.736 + 27 77 50 127 Sn -83469.578 9.226 8420.5482 0.0726 B- 3228.7160 10.1668 126 910391.726 9.904 + 25 76 51 127 Sb -86698.294 5.083 8439.8110 0.0400 B- 1582.2030 4.9102 126 906925.557 5.457 + 23 75 52 127 Te -88280.497 1.365 8446.1090 0.0108 B- 702.7199 3.5652 126 905226.993 1.465 + 21 74 53 127 I -88983.217 3.621 8445.4820 0.0285 B- -662.3336 2.0442 126 904472.592 3.887 + 19 73 54 127 Xe -88320.883 4.088 8434.1066 0.0322 B- -2080.8562 6.4115 126 905183.636 4.388 + 17 72 55 127 Cs -86240.027 5.578 8411.5617 0.0439 B- -3422.0719 12.6525 126 907417.527 5.987 + 15 71 56 127 Ba -82817.955 11.357 8378.4560 0.0894 B- -4921.8386 27.7403 126 911091.272 12.192 + 13 70 57 127 La -77896.116 26.000 8333.5412 0.2047 B- -5916.7727 38.8567 126 916375.083 27.912 + 11 69 58 127 Ce x -71979.344 28.876 8280.7922 0.2274 B- -7436# 198# 126 922727.000 31.000 + 9 68 59 127 Pr x -64543# 196# 8216# 2# B- -8633# 358# 126 930710# 210# + 7 67 60 127 Nd x -55910# 300# 8142# 2# B- -10600# 500# 126 939978# 322# + 5 66 61 127 Pm x -45310# 400# 8052# 3# B- * 126 951358# 429# +0 38 83 45 128 Rh x -27340# 300# 8010# 2# B- 17050# 583# 127 970649# 322# + 36 82 46 128 Pd x -44390# 500# 8137# 4# B- 10320# 583# 127 952345# 537# + 34 81 47 128 Ag x -54710# 300# 8211# 2# B- 12528# 300# 127 941266# 322# + 32 80 48 128 Cd -67238.245 6.432 8303.2367 0.0503 B- 6951.8716 6.5665 127 927816.778 6.905 + 30 79 49 128 In x -74190.117 1.322 8351.4361 0.0103 B- 9171.3131 17.7194 127 920353.637 1.419 + 28 78 50 128 Sn -83361.430 17.682 8416.9749 0.1381 B- 1268.4219 13.3175 127 910507.828 18.982 + 26 77 51 128 Sb IT -84629.852 18.788 8420.7724 0.1468 B- 4363.9419 18.7862 127 909146.121 20.169 + 24 76 52 128 Te -88993.794 0.706 8448.7536 0.0055 B- -1255.7634 3.6807 127 904461.237 0.758 + 22 75 53 128 I -87738.030 3.621 8432.8309 0.0283 B- 2122.5041 3.6211 127 905809.355 3.887 + 20 74 54 128 Xe -89860.53427 0.00520 8443.3008 0.0003 B- -3928.7617 5.3762 127 903530.75341 0.00558 + 18 73 55 128 Cs -85931.773 5.376 8406.4953 0.0420 B- -562.6171 5.6122 127 907748.452 5.771 + 16 72 56 128 Ba -85369.156 1.610 8395.9878 0.0126 B- -6743.7167 54.4716 127 908352.446 1.728 + 14 71 57 128 La x -78625.439 54.448 8337.1904 0.4254 B- -3091.5136 61.2003 127 915592.123 58.452 + 12 70 58 128 Ce x -75533.925 27.945 8306.9259 0.2183 B- -9203.1617 40.8585 127 918911.000 30.000 + 10 69 59 128 Pr x -66330.764 29.808 8228.9141 0.2329 B- -5800# 202# 127 928791.000 32.000 + 8 68 60 128 Nd x -60530# 200# 8177# 2# B- -12311# 361# 127 935018# 215# + 6 67 61 128 Pm x -48220# 300# 8075# 2# B- -9070# 583# 127 948234# 322# + 4 66 62 128 Sm x -39150# 500# 7998# 4# B- * 127 957971# 537# +0 37 83 46 129 Pd x -37880# 600# 8086# 5# B- 13990# 721# 128 959334# 644# + 35 82 47 129 Ag x -51870# 400# 8188# 3# B- 11252# 400# 128 944315# 429# + 33 81 48 129 Cd x -63122.142 5.310 8269.5311 0.0412 B- 9712.7471 5.6637 128 932235.597 5.700 + 31 80 49 129 In -72834.889 1.971 8338.7590 0.0153 B- 7755.7081 17.2376 128 921808.534 2.116 + 29 79 50 129 Sn -80590.597 17.270 8392.8161 0.1339 B- 4038.7874 27.3634 128 913482.440 18.540 + 27 78 51 129 Sb + -84629.384 21.225 8418.0598 0.1645 B- 2375.5000 21.2132 128 909146.623 22.786 + 25 77 52 129 Te -87004.884 0.711 8430.4098 0.0055 B- 1502.2919 3.1358 128 906596.419 0.763 + 23 76 53 129 I -88507.176 3.153 8435.9908 0.0244 B- 188.8936 3.1534 128 904983.643 3.385 + 21 75 54 129 Xe -88696.06975 0.00505 8431.3904 0.0003 B- -1197.0197 4.5532 128 904780.85742 0.00542 + 19 74 55 129 Cs -87499.050 4.553 8416.0465 0.0353 B- -2438.1843 10.5627 128 906065.910 4.888 + 17 73 56 129 Ba -85060.866 10.504 8391.0811 0.0814 B- -3737.3247 21.6280 128 908683.409 11.276 + 15 72 57 129 La -81323.541 21.343 8356.0449 0.1655 B- -5036.0370 35.1633 128 912695.592 22.913 + 13 71 58 129 Ce x -76287.504 27.945 8310.9411 0.2166 B- -6513.9383 40.8585 128 918102.000 30.000 + 11 70 59 129 Pr x -69773.566 29.808 8254.3808 0.2311 B- -7399# 204# 128 925095.000 32.000 + 9 69 60 129 Nd ep -62375# 202# 8191# 2# B- -9195# 362# 128 933038# 217# + 7 68 61 129 Pm x -53180# 300# 8114# 2# B- -10850# 583# 128 942909# 322# + 5 67 62 129 Sm x -42330# 500# 8023# 4# B- * 128 954557# 537# +0 38 84 46 130 Pd x -32730# 300# 8046# 2# B- 13168# 520# 129 964863# 322# + 36 83 47 130 Ag -nn -45898# 424# 8142# 3# B- 15220# 425# 129 950727# 455# + 34 82 48 130 Cd x -61117.597 22.356 8252.5868 0.1720 B- 8788.9322 22.4274 129 934387.563 24.000 + 32 81 49 130 In -69906.530 1.790 8314.1760 0.0138 B- 10225.6870 2.5905 129 924952.257 1.921 + 30 80 50 130 Sn -80132.217 1.873 8386.8170 0.0144 B- 2153.4702 14.1129 129 913974.531 2.010 + 28 79 51 130 Sb -82285.687 14.212 8397.3641 0.1093 B- 5067.2728 14.2124 129 911662.686 15.257 + 26 78 52 130 Te -87352.960 0.011 8430.3251 0.0003 B- -416.7716 3.1537 129 906222.745 0.011 + 24 77 53 130 I -n -86936.188 3.154 8421.1011 0.0243 B- 2944.2864 3.1537 129 906670.168 3.385 + 22 76 54 130 Xe -89880.474 0.009 8437.7314 0.0003 B- -2980.7199 8.3567 129 903509.346 0.010 + 20 75 55 130 Cs -86899.754 8.357 8408.7848 0.0643 B- 357.0219 8.3617 129 906709.281 8.971 + 18 74 56 130 Ba -87256.776 0.287 8405.5130 0.0022 B- -5629.4021 25.9477 129 906326.002 0.308 + 16 73 57 130 La x -81627.374 25.946 8356.1919 0.1996 B- -2204.4611 38.1328 129 912369.413 27.854 + 14 72 58 130 Ce x -79422.913 27.945 8333.2164 0.2150 B- -8247.4488 70.0853 129 914736.000 30.000 + 12 71 59 130 Pr x -71175.464 64.273 8263.7565 0.4944 B- -4579.2250 70.0853 129 923590.000 69.000 + 10 70 60 130 Nd x -66596.239 27.945 8222.5136 0.2150 B- -11127# 202# 129 928506.000 30.000 + 8 69 61 130 Pm x -55470# 200# 8131# 2# B- -7770# 447# 129 940451# 215# + 6 68 62 130 Sm x -47700# 400# 8065# 3# B- -14187# 671# 129 948792# 429# + 4 67 63 130 Eu -p -33513# 539# 7950# 4# B- * 129 964022# 578# +0 39 85 46 131 Pd x -25740# 300# 7993# 2# B- 15010# 583# 130 972367# 322# + 37 84 47 131 Ag x -40750# 500# 8102# 4# B- 14462# 501# 130 956253# 537# + 35 83 48 131 Cd -55211.760 19.238 8206.1204 0.1469 B- 12812.6089 19.3644 130 940727.740 20.653 + 33 82 49 131 In -68024.369 2.205 8297.9544 0.0168 B- 9240.2095 4.2397 130 926972.839 2.367 + 31 81 50 131 Sn -77264.579 3.621 8362.5183 0.0276 B- 4716.8328 3.9621 130 917053.067 3.887 + 29 80 51 131 Sb -81981.412 2.084 8392.5525 0.0159 B- 3229.6099 2.0845 130 911989.339 2.236 + 27 79 52 131 Te -n -85211.022 0.061 8411.2339 0.0005 B- 2231.7057 0.6077 130 908522.210 0.065 + 25 78 53 131 I + -87442.727 0.605 8422.2977 0.0046 B- 970.8477 0.6046 130 906126.375 0.649 + 23 77 54 131 Xe -88413.57492 0.00512 8423.7367 0.0003 B- -358.0009 0.1771 130 905084.12808 0.00549 + 21 76 55 131 Cs +nn -88055.574 0.177 8415.0317 0.0014 B- -1376.6158 0.4515 130 905468.457 0.190 + 19 75 56 131 Ba -n -86678.958 0.415 8398.5511 0.0032 B- -2909.6936 27.9479 130 906946.315 0.445 + 17 74 57 131 La x -83769.265 27.945 8370.3676 0.2133 B- -4060.8167 43.0918 130 910070.000 30.000 + 15 73 58 131 Ce -79708.448 32.802 8333.3969 0.2504 B- -5407.7842 55.4462 130 914429.465 35.214 + 13 72 59 131 Pr -74300.664 46.995 8286.1439 0.3587 B- -6532.6235 53.0809 130 920234.960 50.451 + 11 71 60 131 Nd -67768.040 27.517 8230.3045 0.2101 B- -7998# 202# 130 927248.020 29.541 + 9 70 61 131 Pm x -59770# 200# 8163# 2# B- -9490# 447# 130 935834# 215# + 7 69 62 131 Sm x -50280# 400# 8085# 3# B- -10816# 565# 130 946022# 429# + 5 68 63 131 Eu -p -39464# 400# 7996# 3# B- * 130 957634# 429# +0 38 85 47 132 Ag x -34400# 500# 8053# 4# B- 16065# 504# 131 963070# 537# + 36 84 48 132 Cd x -50465.429 60.068 8169.1421 0.4551 B- 11946.1243 84.9236 131 945823.136 64.485 + 34 83 49 132 In + -62411.554 60.033 8253.7162 0.4548 B- 14135.0000 60.0000 131 932998.444 64.447 + 32 82 50 132 Sn -76546.554 1.976 8354.8726 0.0150 B- 3088.7280 3.1606 131 917823.898 2.121 + 30 81 51 132 Sb -79635.282 2.467 8372.3452 0.0187 B- 5552.9155 4.2708 131 914508.013 2.648 + 28 80 52 132 Te -85188.197 3.486 8408.4859 0.0264 B- 515.3046 3.4830 131 908546.713 3.742 + 26 79 53 132 I -85703.502 4.065 8406.4628 0.0308 B- 3575.4729 4.0654 131 907993.511 4.364 + 24 78 54 132 Xe -89278.97451 0.00507 8427.6229 0.0003 B- -2126.2813 1.0359 131 904155.08346 0.00544 + 22 77 55 132 Cs -87152.693 1.036 8405.5878 0.0079 B- 1282.2099 1.4773 131 906437.740 1.112 + 20 76 56 132 Ba -88434.903 1.053 8409.3747 0.0080 B- -4711.3256 36.3537 131 905061.231 1.130 + 18 75 57 132 La -83723.578 36.359 8367.7559 0.2754 B- -1254.8898 41.7025 131 910119.047 39.032 + 16 74 58 132 Ce -82468.688 20.407 8352.3223 0.1546 B- -7241.2240 35.3594 131 911466.226 21.907 + 14 73 59 132 Pr x -75227.464 28.876 8291.5377 0.2188 B- -3801.6487 37.6795 131 919240.000 31.000 + 12 72 60 132 Nd x -71425.815 24.205 8256.8104 0.1834 B- -9798# 151# 131 923321.237 25.985 + 10 71 61 132 Pm x -61628# 149# 8177# 1# B- -6488# 335# 131 933840# 160# + 8 70 62 132 Sm x -55140# 300# 8122# 2# B- -12939# 500# 131 940805# 322# + 6 69 63 132 Eu x -42200# 400# 8018# 3# B- * 131 954696# 429# +0 39 86 47 133 Ag x -29080# 500# 8013# 4# B- 15059# 539# 132 968781# 537# + 37 85 48 133 Cd x -44140# 200# 8121# 2# B- 13550# 283# 132 952614# 215# + 35 84 49 133 In x -57690# 200# 8217# 2# B- 13184# 200# 132 938067# 215# + 33 83 50 133 Sn -70873.890 1.904 8310.0890 0.0143 B- 8049.6228 3.6617 132 923913.753 2.043 + 31 82 51 133 Sb -78923.513 3.128 8364.7302 0.0235 B- 4013.6198 3.5179 132 915272.128 3.357 + 29 81 52 133 Te -82937.133 2.066 8389.0255 0.0155 B- 2920.1690 6.2531 132 910963.330 2.218 + 27 80 53 133 I -85857.302 5.902 8405.0993 0.0444 B- 1786.2812 6.3712 132 907828.400 6.335 + 25 79 54 133 Xe + -87643.583 2.400 8412.6477 0.0180 B- 427.3600 2.4000 132 905910.748 2.576 + 23 78 55 133 Cs -88070.943 0.008 8409.9786 0.0003 B- -517.4310 0.9920 132 905451.958 0.008 + 21 77 56 133 Ba -87553.512 0.992 8400.2059 0.0075 B- -2059.1203 27.9624 132 906007.443 1.065 + 19 76 57 133 La x -85494.392 27.945 8378.8415 0.2101 B- -3076.1685 32.3786 132 908218.000 30.000 + 17 75 58 133 Ce x -82418.223 16.354 8349.8301 0.1230 B- -4480.6319 20.5826 132 911520.402 17.557 + 15 74 59 133 Pr x -77937.591 12.497 8310.2588 0.0940 B- -5605.2112 48.2223 132 916330.558 13.416 + 13 73 60 133 Nd x -72332.380 46.575 8262.2320 0.3502 B- -6924.7272 68.5519 132 922348.000 50.000 + 11 72 61 133 Pm x -65407.653 50.301 8204.2841 0.3782 B- -8177# 302# 132 929782.000 54.000 + 9 71 62 133 Sm x -57231# 298# 8137# 2# B- -9995# 422# 132 938560# 320# + 7 70 63 133 Eu x -47236# 298# 8056# 2# B- -11176# 582# 132 949290# 320# + 5 69 64 133 Gd x -36060# 500# 7966# 4# B- * 132 961288# 537# +0 38 86 48 134 Cd x -39460# 300# 8086# 2# B- 12510# 361# 133 957638# 322# + 36 85 49 134 In x -51970# 200# 8173# 1# B- 14464# 200# 133 944208# 215# + 34 84 50 134 Sn x -66433.759 3.167 8275.1719 0.0236 B- 7585.2453 4.4136 133 928680.430 3.400 + 32 83 51 134 Sb x -74019.004 3.074 8325.9398 0.0229 B- 8514.7483 4.1221 133 920537.334 3.300 + 30 82 52 134 Te -82533.752 2.746 8383.6442 0.0205 B- 1509.6875 4.9335 133 911396.376 2.948 + 28 81 53 134 I -84043.440 4.857 8389.0722 0.0362 B- 4082.3946 4.8567 133 909775.660 5.213 + 26 80 54 134 Xe -88125.83443 0.00577 8413.6994 0.0003 B- -1234.6691 0.0160 133 905393.030 0.006 + 24 79 55 134 Cs -86891.165 0.016 8398.6470 0.0003 B- 2058.8368 0.2508 133 906718.501 0.017 + 22 78 56 134 Ba -88950.002 0.251 8408.1731 0.0019 B- -3731.3434 19.9312 133 904508.249 0.269 + 20 77 57 134 La x -85218.659 19.930 8374.4888 0.1487 B- -385.7605 28.5098 133 908514.011 21.395 + 18 76 58 134 Ce x -84832.898 20.387 8365.7716 0.1521 B- -6304.8987 28.7814 133 908928.142 21.886 + 16 75 59 134 Pr x -78528.000 20.316 8312.8817 0.1516 B- -2881.5569 23.5032 133 915696.729 21.810 + 14 74 60 134 Nd x -75646.443 11.817 8285.5391 0.0882 B- -8882.5343 43.5512 133 918790.207 12.686 + 12 73 61 134 Pm x -66763.908 41.917 8213.4131 0.3128 B- -5388# 200# 133 928326.000 45.000 + 10 72 62 134 Sm x -61376# 196# 8167# 1# B- -11576# 358# 133 934110# 210# + 8 71 63 134 Eu x -49800# 300# 8075# 2# B- -8271# 500# 133 946537# 322# + 6 70 64 134 Gd x -41530# 400# 8008# 3# B- * 133 955416# 429# +0 39 87 48 135 Cd x -32820# 400# 8036# 3# B- 14290# 500# 134 964766# 429# + 37 86 49 135 In x -47110# 300# 8136# 2# B- 13522# 300# 134 949425# 322# + 35 85 50 135 Sn x -60632.252 3.074 8230.6877 0.0228 B- 9058.0800 4.0522 134 934908.603 3.300 + 33 84 51 135 Sb -69690.332 2.640 8291.9894 0.0196 B- 8038.4581 3.1524 134 925184.354 2.834 + 31 83 52 135 Te -77728.790 1.722 8345.7384 0.0128 B- 6050.3894 2.6850 134 916554.715 1.848 + 29 82 53 135 I -83779.180 2.060 8384.7609 0.0153 B- 2634.1851 3.8284 134 910059.355 2.211 + 27 81 54 135 Xe -86413.365 3.668 8398.4783 0.0272 B- 1168.5917 3.6623 134 907231.441 3.938 + 25 80 55 135 Cs -87581.956 0.364 8401.3393 0.0027 B- 268.6983 0.2862 134 905976.907 0.390 + 23 79 56 135 Ba -87850.655 0.245 8397.5345 0.0018 B- -1207.1973 9.4299 134 905688.447 0.263 + 21 78 57 135 La -86643.458 9.432 8382.7972 0.0699 B- -2027.1499 4.6101 134 906984.427 10.126 + 19 77 58 135 Ce -84616.308 10.266 8361.9861 0.0760 B- -3680.4357 15.6540 134 909160.662 11.021 + 17 76 59 135 Pr x -80935.872 11.817 8328.9284 0.0875 B- -4722.2522 22.4837 134 913111.772 12.686 + 15 75 60 135 Nd x -76213.620 19.128 8288.1536 0.1417 B- -6151.2907 85.0809 134 918181.318 20.534 + 13 74 61 135 Pm x -70062.329 82.903 8236.7933 0.6141 B- -7205.1069 175.4501 134 924785.000 89.000 + 11 73 62 135 Sm x -62857.222 154.628 8177.6270 1.1454 B- -8709# 249# 134 932520.000 166.000 + 9 72 63 135 Eu x -54148# 196# 8107# 1# B- -9898# 445# 134 941870# 210# + 7 71 64 135 Gd x -44250# 400# 8028# 3# B- -11197# 565# 134 952496# 429# + 5 70 65 135 Tb -p -33053# 400# 7939# 3# B- * 134 964516# 429# +0 38 87 49 136 In x -40970# 300# 8091# 2# B- 15200# 361# 135 956017# 322# + 36 86 50 136 Sn x -56170# 200# 8197# 1# B- 8337# 200# 135 939699# 215# + 34 85 51 136 Sb -64506.890 5.830 8252.2533 0.0429 B- 9918.3897 6.2599 135 930749.009 6.258 + 32 84 52 136 Te -74425.279 2.281 8319.4301 0.0168 B- 5119.9455 14.1880 135 920101.180 2.448 + 30 83 53 136 I -79545.225 14.188 8351.3242 0.1043 B- 6883.9455 14.1880 135 914604.693 15.231 + 28 82 54 136 Xe -86429.170 0.007 8396.1889 0.0003 B- -90.3151 1.8730 135 907214.474 0.007 + 26 81 55 136 Cs + -86338.855 1.873 8389.7723 0.0138 B- 2548.2241 1.8570 135 907311.431 2.010 + 24 80 56 136 Ba -88887.079 0.245 8402.7566 0.0018 B- -2849.5915 53.1715 135 904575.800 0.262 + 22 79 57 136 La x -86037.488 53.171 8376.0512 0.3910 B- 471.0621 53.1720 135 907634.962 57.081 + 20 78 58 136 Ce -86508.550 0.324 8373.7624 0.0024 B- -5168.1290 11.4561 135 907129.256 0.348 + 18 77 59 136 Pr -81340.421 11.455 8330.0089 0.0842 B- -2141.1234 16.4578 135 912677.470 12.296 + 16 76 60 136 Nd x -79199.297 11.817 8308.5127 0.0869 B- -8029.3747 70.0764 135 914976.061 12.686 + 14 75 61 136 Pm x -71169.923 69.073 8243.7207 0.5079 B- -4359.0237 70.1942 135 923595.949 74.152 + 12 74 62 136 Sm x -66810.899 12.497 8205.9165 0.0919 B- -10567# 196# 135 928275.553 13.416 + 10 73 63 136 Eu x -56244# 196# 8122# 1# B- -7154# 357# 135 939620# 210# + 8 72 64 136 Gd x -49090# 298# 8064# 2# B- -13190# 582# 135 947300# 320# + 6 71 65 136 Tb x -35900# 500# 7961# 4# B- * 135 961460# 537# +0 39 88 49 137 In x -35830# 400# 8053# 3# B- 14320# 500# 136 961535# 429# + 37 87 50 137 Sn x -50150# 300# 8152# 2# B- 9911# 304# 136 946162# 322# + 35 86 51 137 Sb x -60060.392 52.164 8218.4764 0.3808 B- 9243.3698 52.2059 136 935522.519 56.000 + 33 85 52 137 Te -69303.762 2.100 8280.2357 0.0153 B- 7052.5063 8.6426 136 925599.354 2.254 + 31 84 53 137 I p-2n -76356.268 8.383 8326.0033 0.0612 B- 6027.1455 8.3841 136 918028.178 9.000 + 29 83 54 137 Xe -n -82383.414 0.104 8364.2865 0.0008 B- 4162.3582 0.3193 136 911557.771 0.111 + 27 82 55 137 Cs + -86545.772 0.302 8388.9581 0.0022 B- 1175.6285 0.1723 136 907089.296 0.324 + 25 81 56 137 Ba -87721.401 0.248 8391.8288 0.0018 B- -580.5356 1.6231 136 905827.207 0.266 + 23 80 57 137 La + -87140.865 1.640 8381.8807 0.0120 B- -1222.1000 1.6000 136 906450.438 1.760 + 21 79 58 137 Ce -85918.765 0.360 8367.2497 0.0026 B- -2716.9515 8.1328 136 907762.416 0.386 + 19 78 59 137 Pr -83201.814 8.135 8341.7074 0.0594 B- -3617.8447 14.2706 136 910679.183 8.733 + 17 77 60 137 Nd -79583.969 11.725 8309.5892 0.0856 B- -5511.1108 17.5366 136 914563.099 12.586 + 15 76 61 137 Pm x -74072.858 13.041 8263.6516 0.0952 B- -6081.2029 31.4460 136 920479.519 14.000 + 13 75 62 137 Sm -67991.655 28.614 8213.5527 0.2089 B- -7845.7518 28.9474 136 927007.959 30.718 + 11 74 63 137 Eu x -60145.904 4.378 8150.5738 0.0320 B- -8932# 298# 136 935430.719 4.700 + 9 73 64 137 Gd x -51214# 298# 8080# 2# B- -10246# 499# 136 945020# 320# + 7 72 65 137 Tb x -40967# 401# 7999# 3# B- * 136 956020# 430# +0 38 88 50 138 Sn x -45510# 400# 8118# 3# B- 9140# 500# 137 951143# 429# + 36 87 51 138 Sb x -54650# 300# 8178# 2# B- 11046# 300# 137 941331# 322# + 34 86 52 138 Te -65695.995 3.787 8252.5786 0.0274 B- 6283.9149 7.0625 137 929472.452 4.065 + 32 85 53 138 I x -71979.910 5.962 8292.4450 0.0432 B- 7992.3346 6.5881 137 922726.392 6.400 + 30 84 54 138 Xe -79972.244 2.804 8344.6913 0.0203 B- 2914.7839 9.5780 137 914146.268 3.010 + 28 83 55 138 Cs -82887.028 9.158 8360.1437 0.0664 B- 5374.7776 9.1584 137 911017.119 9.831 + 26 82 56 138 Ba -88261.806 0.249 8393.4222 0.0018 B- -1748.3977 0.3384 137 905247.059 0.267 + 24 81 57 138 La -86513.408 0.416 8375.0835 0.0030 B- 1052.4585 0.4018 137 907124.041 0.446 + 22 80 58 138 Ce -87565.867 0.499 8377.0408 0.0036 B- -4437.0000 10.0000 137 905994.180 0.536 + 20 79 59 138 Pr - -83128.867 10.012 8339.2195 0.0726 B- -1111.6847 15.3256 137 910757.495 10.748 + 18 78 60 138 Nd -82017.182 11.603 8325.4946 0.0841 B- -7102.8119 16.0995 137 911950.938 12.456 + 16 77 61 138 Pm -74914.370 11.603 8268.3558 0.0841 B- -3416.5976 16.5613 137 919576.119 12.456 + 14 76 62 138 Sm x -71497.772 11.817 8237.9286 0.0856 B- -9748.0968 30.3408 137 923243.988 12.686 + 12 75 63 138 Eu x -61749.676 27.945 8161.6211 0.2025 B- -6090# 202# 137 933709.000 30.000 + 10 74 64 138 Gd x -55660# 200# 8112# 1# B- -12059# 361# 137 940247# 215# + 8 73 65 138 Tb x -43600# 300# 8019# 2# B- -8669# 586# 137 953193# 322# + 6 72 66 138 Dy x -34931# 503# 7950# 4# B- * 137 962500# 540# +0 39 89 50 139 Sn x -39310# 400# 8073# 3# B- 10740# 565# 138 957799# 429# + 37 88 51 139 Sb x -50050# 400# 8144# 3# B- 10155# 400# 138 946269# 429# + 35 87 52 139 Te x -60205.080 3.540 8211.7716 0.0255 B- 8265.8835 5.3454 138 935367.191 3.800 + 33 86 53 139 I x -68470.964 4.005 8265.6100 0.0288 B- 7173.6224 4.5424 138 926493.400 4.300 + 31 85 54 139 Xe x -75644.586 2.142 8311.5904 0.0154 B- 5056.5023 3.7960 138 918792.200 2.300 + 29 84 55 139 Cs + -80701.088 3.134 8342.3397 0.0225 B- 4212.8293 3.1235 138 913363.822 3.364 + 27 83 56 139 Ba -n -84913.918 0.253 8367.0194 0.0018 B- 2308.4632 0.6571 138 908841.164 0.271 + 25 82 57 139 La -87222.381 0.607 8377.9987 0.0044 B- -264.6396 1.9989 138 906362.927 0.651 + 23 81 58 139 Ce -86957.741 2.089 8370.4664 0.0150 B- -2129.0890 2.9962 138 906647.029 2.242 + 21 80 59 139 Pr -84828.652 3.649 8349.5208 0.0263 B- -2811.7226 27.6166 138 908932.700 3.917 + 19 79 60 139 Nd -82016.930 27.521 8323.6642 0.1980 B- -4515.9014 25.8700 138 911951.208 29.545 + 17 78 61 139 Pm -77501.028 13.588 8285.5473 0.0978 B- -5120.7993 17.4098 138 916799.228 14.587 + 15 77 62 139 Sm x -72380.229 10.884 8243.0786 0.0783 B- -6982.1777 17.0705 138 922296.631 11.684 + 13 76 63 139 Eu x -65398.051 13.151 8187.2187 0.0946 B- -7767# 196# 138 929792.307 14.117 + 11 75 64 139 Gd x -57632# 196# 8126# 1# B- -9501# 357# 138 938130# 210# + 9 74 65 139 Tb x -48130# 298# 8052# 2# B- -10430# 582# 138 948330# 320# + 7 73 66 139 Dy x -37700# 500# 7971# 4# B- * 138 959527# 537# +0 40 90 50 140 Sn x -34490# 300# 8038# 2# B- 9900# 671# 139 962973# 322# + 38 89 51 140 Sb x -44390# 600# 8103# 4# B- 11977# 600# 139 952345# 644# + 36 88 52 140 Te -56367.449 14.377 8183.3567 0.1027 B- 7238.7734 18.7976 139 939487.057 15.434 + 34 87 53 140 I x -63606.223 12.109 8229.4740 0.0865 B- 9380.2388 12.3313 139 931715.914 13.000 + 32 86 54 140 Xe x -72986.461 2.329 8290.8875 0.0166 B- 4063.2768 8.5232 139 921645.814 2.500 + 30 85 55 140 Cs -77049.738 8.199 8314.3227 0.0586 B- 6218.1669 9.8671 139 917283.707 8.801 + 28 84 56 140 Ba -83267.905 7.900 8353.1500 0.0564 B- 1044.1542 7.9051 139 910608.231 8.480 + 26 83 57 140 La -84312.059 0.607 8355.0201 0.0043 B- 3762.1676 1.3364 139 909487.285 0.651 + 24 82 58 140 Ce -88074.227 1.313 8376.3045 0.0094 B- -3388.0000 6.0000 139 905448.433 1.409 + 22 81 59 140 Pr - -84686.227 6.142 8346.5163 0.0439 B- -428.9806 6.9536 139 909085.600 6.593 + 20 80 60 140 Nd x -84257.246 3.260 8337.8640 0.0233 B- -6045.2000 24.0000 139 909546.130 3.500 + 18 79 61 140 Pm - -78212.046 24.220 8289.0958 0.1730 B- -2756.1010 27.2546 139 916035.918 26.001 + 16 78 62 140 Sm x -75455.945 12.497 8263.8211 0.0893 B- -8470.0000 50.0000 139 918994.714 13.416 + 14 77 63 140 Eu - -66985.945 51.538 8197.7330 0.3681 B- -5203.6675 58.6268 139 928087.633 55.328 + 12 76 64 140 Gd x -61782.278 27.945 8154.9757 0.1996 B- -11300.0000 800.0000 139 933674.000 30.000 + 10 75 65 140 Tb - -50482.278 800.488 8068.6732 5.7178 B- -7652# 895# 139 945805.048 859.359 + 8 74 66 140 Dy x -42830# 401# 8008# 3# B- -13513# 641# 139 954020# 430# + 6 73 67 140 Ho -p -29317# 500# 7906# 4# B- * 139 968526# 537# +0 39 90 51 141 Sb x -39540# 500# 8069# 4# B- 11129# 640# 140 957552# 537# + 37 89 52 141 Te x -50670# 400# 8142# 3# B- 9257# 400# 140 945604# 429# + 35 88 53 141 I x -59926.666 15.835 8202.2562 0.1123 B- 8270.6430 16.0965 140 935666.081 17.000 + 33 87 54 141 Xe x -68197.309 2.888 8255.3647 0.0205 B- 6280.0423 9.6378 140 926787.181 3.100 + 31 86 55 141 Cs -74477.351 9.195 8294.3554 0.0652 B- 5255.1410 9.6174 140 920045.279 9.871 + 29 85 56 141 Ba -79732.492 5.318 8326.0774 0.0377 B- 3197.3515 6.5510 140 914403.653 5.709 + 27 84 57 141 La -82929.844 4.127 8343.2050 0.0293 B- 2501.2141 3.9279 140 910971.155 4.430 + 25 83 58 141 Ce -85431.058 1.315 8355.3956 0.0093 B- 583.4758 1.1784 140 908285.991 1.411 + 23 82 59 141 Pr -86014.533 1.498 8353.9852 0.0106 B- -1823.0137 2.8090 140 907659.604 1.607 + 21 81 60 141 Nd - -84191.520 3.183 8335.5074 0.0226 B- -3668.5879 14.3304 140 909616.690 3.417 + 19 80 61 141 Pm x -80522.932 13.972 8303.9405 0.0991 B- -4588.9724 16.3730 140 913555.081 15.000 + 17 79 62 141 Sm -75933.959 8.535 8265.8460 0.0605 B- -6008.3127 14.2829 140 918481.545 9.162 + 15 78 63 141 Eu -69925.647 12.639 8217.6853 0.0896 B- -6701.4161 23.4562 140 924931.734 13.568 + 13 77 64 141 Gd x -63224.231 19.760 8164.6090 0.1401 B- -8683.3880 107.0975 140 932126.000 21.213 + 11 76 65 141 Tb x -54540.843 105.259 8097.4761 0.7465 B- -9158# 316# 140 941448.000 113.000 + 9 75 66 141 Dy x -45382# 298# 8027# 2# B- -11018# 499# 140 951280# 320# + 7 74 67 141 Ho -p -34364# 401# 7943# 3# B- * 140 963108# 430# +0 40 91 51 142 Sb x -33610# 300# 8027# 2# B- 12939# 583# 141 963918# 322# + 38 90 52 142 Te x -46550# 500# 8113# 4# B- 8253# 500# 141 950027# 537# + 36 89 53 142 I x -54802.969 4.937 8165.2517 0.0348 B- 10426.6792 5.6276 141 941166.595 5.300 + 34 88 54 142 Xe x -65229.648 2.701 8233.1695 0.0190 B- 5284.9078 7.5655 141 929973.095 2.900 + 32 87 55 142 Cs -70514.556 7.067 8264.8777 0.0498 B- 7327.7007 8.3627 141 924299.514 7.586 + 30 86 56 142 Ba -77842.257 5.920 8310.9718 0.0417 B- 2181.6932 8.3754 141 916432.904 6.355 + 28 85 57 142 La -80023.950 6.286 8320.8263 0.0443 B- 4508.9455 5.8446 141 914090.760 6.748 + 26 84 58 142 Ce -84532.896 2.443 8347.0700 0.0172 B- -746.5288 2.4868 141 909250.208 2.623 + 24 83 59 142 Pr -83786.367 1.498 8336.3032 0.0106 B- 2163.6885 1.3656 141 910051.640 1.607 + 22 82 60 142 Nd -85950.055 1.256 8346.0310 0.0088 B- -4808.5196 23.6216 141 907728.824 1.348 + 20 81 61 142 Pm -81141.536 23.596 8306.6587 0.1662 B- -2159.6062 23.6524 141 912890.982 25.330 + 18 80 62 142 Sm -78981.930 1.866 8285.9407 0.0131 B- -7673.0000 30.0000 141 915209.415 2.002 + 16 79 63 142 Eu - -71308.930 30.058 8226.3960 0.2117 B- -4349.4074 41.0414 141 923446.719 32.268 + 14 78 64 142 Gd x -66959.522 27.945 8190.2569 0.1968 B- -10400.0000 700.0000 141 928116.000 30.000 + 12 77 65 142 Tb - -56559.522 700.558 8111.5080 4.9335 B- -6440# 200# 141 939280.858 752.079 + 10 76 66 142 Dy - -50120# 729# 8061# 5# B- -12869# 831# 141 946194# 782# + 8 75 67 142 Ho x -37250# 401# 7965# 3# B- -9321# 641# 141 960010# 430# + 6 74 68 142 Er x -27930# 500# 7893# 4# B- * 141 970016# 537# +0 39 91 52 143 Te x -40530# 500# 8070# 3# B- 10259# 539# 142 956489# 537# + 37 90 53 143 I x -50790# 200# 8137# 1# B- 9413# 200# 142 945475# 215# + 35 89 54 143 Xe x -60202.882 4.657 8196.8855 0.0326 B- 7472.6365 8.8908 142 935369.550 5.000 + 33 88 55 143 Cs -67675.519 7.573 8243.6707 0.0530 B- 6261.6865 9.7303 142 927347.346 8.130 + 31 87 56 143 Ba -73937.205 6.756 8281.9878 0.0472 B- 4234.2623 9.9682 142 920625.149 7.253 + 29 86 57 143 La -78171.467 7.329 8306.1271 0.0513 B- 3434.9108 7.5812 142 916079.482 7.868 + 27 85 58 143 Ce -81606.378 2.442 8324.6765 0.0171 B- 1461.8214 1.8649 142 912391.953 2.621 + 25 84 59 143 Pr -83068.200 1.816 8329.4280 0.0127 B- 934.1107 1.3673 142 910822.624 1.949 + 23 83 60 143 Nd -84002.310 1.255 8330.4893 0.0088 B- -1041.6463 2.6830 142 909819.815 1.347 + 21 82 61 143 Pm -82960.664 2.944 8317.7341 0.0206 B- -3443.5291 3.5604 142 910938.068 3.160 + 19 81 62 143 Sm -79517.135 2.750 8288.1825 0.0192 B- -5275.8240 11.3245 142 914634.848 2.951 + 17 80 63 143 Eu x -74241.311 10.986 8245.8177 0.0768 B- -6010.0000 200.0000 142 920298.678 11.793 + 15 79 64 143 Gd - -68231.311 200.301 8198.3188 1.4007 B- -7812.1185 206.7497 142 926750.678 215.032 + 13 78 65 143 Tb x -60419.192 51.232 8138.2176 0.3583 B- -8250.2433 52.8659 142 935137.332 55.000 + 11 77 66 143 Dy x -52168.949 13.041 8075.0527 0.0912 B- -10121# 298# 142 943994.332 14.000 + 9 76 67 143 Ho x -42048# 298# 7999# 2# B- -10887# 499# 142 954860# 320# + 7 75 68 143 Er x -31160# 400# 7917# 3# B- * 142 966548# 429# +0 40 92 52 144 Te x -36220# 300# 8040# 2# B- 9110# 500# 143 961116# 322# + 38 91 53 144 I x -45330# 400# 8098# 3# B- 11542# 400# 143 951336# 429# + 36 90 54 144 Xe x -56872.301 5.310 8172.8845 0.0369 B- 6399.0606 20.8203 143 938945.076 5.700 + 34 89 55 144 Cs -63271.362 20.132 8211.8894 0.1398 B- 8495.7679 20.4163 143 932075.402 21.612 + 32 88 56 144 Ba -71767.130 7.136 8265.4549 0.0496 B- 3082.5300 14.7744 143 922954.821 7.661 + 30 87 57 144 La x -74849.660 12.937 8281.4283 0.0898 B- 5582.2823 13.2432 143 919645.589 13.888 + 28 86 58 144 Ce + -80431.942 2.833 8314.7612 0.0197 B- 318.6462 0.8321 143 913652.763 3.041 + 26 85 59 144 Pr + -80750.588 2.708 8311.5411 0.0188 B- 2997.4400 2.4000 143 913310.682 2.907 + 24 84 60 144 Nd -83748.028 1.255 8326.9237 0.0087 B- -2331.9117 2.6464 143 910092.798 1.346 + 22 83 61 144 Pm -81416.116 2.912 8305.2969 0.0202 B- 549.5096 2.6679 143 912596.208 3.126 + 20 82 62 144 Sm -81965.626 1.459 8303.6800 0.0101 B- -6346.4515 10.8092 143 912006.285 1.566 + 18 81 63 144 Eu -75619.175 10.787 8254.1744 0.0749 B- -3859.6633 29.9546 143 918819.481 11.580 + 16 80 64 144 Gd x -71759.511 27.945 8221.9382 0.1941 B- -9391.3235 39.5199 143 922963.000 30.000 + 14 79 65 144 Tb x -62368.188 27.945 8151.2877 0.1941 B- -5798.0965 28.8506 143 933045.000 30.000 + 12 78 66 144 Dy x -56570.091 7.173 8105.5902 0.0498 B- -11960.5706 11.1039 143 939269.512 7.700 + 10 77 67 144 Ho x -44609.521 8.477 8017.0977 0.0589 B- -8002# 196# 143 952109.712 9.100 + 8 76 68 144 Er x -36608# 196# 7956# 1# B- -14448# 445# 143 960700# 210# + 6 75 69 144 Tm -p -22159# 400# 7850# 3# B- * 143 976211# 429# +0 41 93 52 145 Te x -30010# 300# 7998# 2# B- 11120# 583# 144 967783# 322# + 39 92 53 145 I x -41130# 500# 8069# 3# B- 10363# 500# 144 955845# 537# + 37 91 54 145 Xe x -51493.337 11.178 8135.0877 0.0771 B- 8561.0867 14.3929 144 944719.631 12.000 + 35 90 55 145 Cs -60054.424 9.067 8188.7342 0.0625 B- 7461.7591 12.4121 144 935528.927 9.733 + 33 89 56 145 Ba x -67516.183 8.477 8234.7991 0.0585 B- 5319.1425 14.9122 144 927518.400 9.100 + 31 88 57 145 La -72835.325 12.269 8266.0873 0.0846 B- 4231.7331 35.2977 144 921808.065 13.170 + 29 87 58 145 Ce -77067.059 33.900 8289.8762 0.2338 B- 2558.9318 33.6347 144 917265.113 36.393 + 27 86 59 145 Pr -79625.990 7.149 8302.1285 0.0493 B- 1806.0140 7.0370 144 914517.987 7.674 + 25 85 60 145 Nd -81432.004 1.271 8309.1883 0.0088 B- -164.4982 2.5361 144 912579.151 1.364 + 23 84 61 145 Pm -81267.506 2.805 8302.6583 0.0193 B- -616.0990 2.5390 144 912755.748 3.011 + 21 83 62 145 Sm -80651.407 1.485 8293.0139 0.0102 B- -2659.8832 2.7224 144 913417.157 1.594 + 19 82 63 145 Eu -77991.524 3.060 8269.2744 0.0211 B- -5064.8984 19.9521 144 916272.659 3.285 + 17 81 64 145 Gd -72926.626 19.716 8228.9485 0.1360 B- -6526.9329 109.7144 144 921710.051 21.165 + 15 80 65 145 Tb -66399.693 110.896 8178.5397 0.7648 B- -8157.0853 111.0872 144 928717.001 119.051 + 13 79 66 145 Dy x -58242.607 6.520 8116.8884 0.0450 B- -9122.4943 9.9019 144 937473.992 7.000 + 11 78 67 145 Ho x -49120.113 7.452 8048.5792 0.0514 B- -9880# 200# 144 947267.392 8.000 + 9 77 68 145 Er x -39240# 200# 7975# 1# B- -11657# 280# 144 957874# 215# + 7 76 69 145 Tm -p -27583# 196# 7889# 1# B- * 144 970389# 210# +0 40 93 53 146 I x -35540# 300# 8031# 2# B- 12415# 301# 145 961846# 322# + 38 92 54 146 Xe x -47954.950 24.219 8110.4154 0.1659 B- 7355.4298 24.3911 145 948518.245 26.000 + 36 91 55 146 Cs x -55310.380 2.893 8155.4365 0.0198 B- 9555.8882 3.3918 145 940621.867 3.106 + 34 90 56 146 Ba x -64866.269 1.770 8215.5293 0.0121 B- 4354.9052 2.4366 145 930363.200 1.900 + 32 89 57 146 La -69221.174 1.675 8239.9988 0.0115 B- 6404.6947 14.7146 145 925688.017 1.797 + 30 88 58 146 Ce -75625.868 14.665 8278.5081 0.1004 B- 1047.6178 32.4842 145 918812.294 15.743 + 28 87 59 146 Pr -76673.486 34.356 8280.3250 0.2353 B- 4252.4300 34.3686 145 917687.630 36.882 + 26 86 60 146 Nd -80925.916 1.273 8304.0927 0.0087 B- -1471.5560 4.1194 145 913122.459 1.366 + 24 85 61 146 Pm + -79454.360 4.275 8288.6550 0.0293 B- 1542.0000 3.0000 145 914702.240 4.589 + 22 84 62 146 Sm -80996.360 3.045 8293.8581 0.0209 B- -3878.7573 5.8685 145 913046.835 3.269 + 20 83 63 146 Eu -77117.603 6.009 8261.9327 0.0412 B- -1031.7798 7.0750 145 917210.852 6.451 + 18 82 64 146 Gd -76085.823 4.076 8249.5072 0.0279 B- -8322.1791 44.7488 145 918318.513 4.376 + 16 81 65 146 Tb -67763.644 44.860 8187.1474 0.3073 B- -5208.7165 45.1580 145 927252.739 48.159 + 14 80 66 146 Dy -62554.928 6.695 8146.1128 0.0459 B- -11316.7007 9.3917 145 932844.526 7.187 + 12 79 67 146 Ho -51238.227 6.587 8063.2426 0.0451 B- -6916.2074 9.3987 145 944993.503 7.071 + 10 78 68 146 Er -44322.019 6.705 8010.5127 0.0459 B- -13267# 200# 145 952418.357 7.197 + 8 77 69 146 Tm -p -31055# 200# 7914# 1# B- * 145 966661# 215# +0 41 94 53 147 I x -31200# 300# 8001# 2# B- 11199# 361# 146 966505# 322# + 39 93 54 147 Xe x -42400# 200# 8072# 1# B- 9520# 200# 146 954482# 215# + 37 92 55 147 Cs x -51920.073 8.383 8131.8010 0.0570 B- 8343.9631 21.4535 146 944261.512 9.000 + 35 91 56 147 Ba x -60264.036 19.748 8183.2405 0.1343 B- 6414.3615 22.4660 146 935303.900 21.200 + 33 90 57 147 La x -66678.397 10.712 8221.5536 0.0729 B- 5335.5046 13.7248 146 928417.800 11.500 + 31 89 58 147 Ce -72013.902 8.580 8252.5274 0.0584 B- 3430.1913 15.5317 146 922689.900 9.211 + 29 88 59 147 Pr -75444.093 15.855 8270.5400 0.1079 B- 2702.7013 15.8571 146 919007.438 17.020 + 27 87 60 147 Nd -78146.794 1.275 8283.6036 0.0087 B- 895.1896 0.5664 146 916105.969 1.368 + 25 86 61 147 Pm -79041.984 1.288 8284.3712 0.0088 B- 224.0638 0.2940 146 915144.944 1.382 + 23 85 62 147 Sm -79266.048 1.262 8280.5734 0.0086 B- -1721.4367 2.2832 146 914904.401 1.354 + 21 84 63 147 Eu -77544.611 2.569 8263.5409 0.0175 B- -2187.6833 2.5273 146 916752.440 2.758 + 19 83 64 147 Gd -75356.928 1.887 8243.3366 0.0128 B- -4614.2543 8.1414 146 919101.014 2.025 + 17 82 65 147 Tb -70742.674 8.096 8206.6250 0.0551 B- -6546.6266 11.9939 146 924054.620 8.691 + 15 81 66 147 Dy x -64196.047 8.849 8156.7680 0.0602 B- -8438.9460 10.1644 146 931082.712 9.500 + 13 80 67 147 Ho -55757.101 5.001 8094.0381 0.0340 B- -9149.2869 38.5173 146 940142.293 5.368 + 11 79 68 147 Er x -46607.814 38.191 8026.4760 0.2598 B- -10633.4071 38.7987 146 949964.456 41.000 + 9 78 69 147 Tm -35974.407 6.839 7948.8178 0.0465 B- * 146 961379.887 7.341 +0 40 94 54 148 Xe x -38650# 300# 8047# 2# B- 8261# 300# 147 958508# 322# + 38 93 55 148 Cs x -46910.950 13.041 8097.5469 0.0881 B- 10633.9614 13.1258 147 949639.026 14.000 + 36 92 56 148 Ba x -57544.911 1.490 8164.1118 0.0101 B- 5163.8307 19.5252 147 938223.000 1.600 + 34 91 57 148 La x -62708.742 19.468 8193.7165 0.1315 B- 7689.6824 22.4573 147 932679.400 20.900 + 32 90 58 148 Ce -70398.424 11.195 8240.3876 0.0756 B- 2137.0282 12.5663 147 924424.186 12.017 + 30 89 59 148 Pr -72535.452 15.041 8249.5409 0.1016 B- 4872.6133 15.0858 147 922129.992 16.147 + 28 88 60 148 Nd -77408.066 2.053 8277.1778 0.0139 B- -542.1891 5.8755 147 916899.027 2.203 + 26 87 61 148 Pm +p -76865.877 5.690 8268.2283 0.0384 B- 2470.1898 5.6409 147 917481.091 6.108 + 24 86 62 148 Sm -79336.067 1.246 8279.6326 0.0084 B- -3038.5844 9.9657 147 914829.233 1.337 + 22 85 63 148 Eu -76297.482 9.961 8253.8155 0.0673 B- -28.0630 9.9633 147 918091.288 10.693 + 20 84 64 148 Gd -76269.419 1.460 8248.3398 0.0099 B- -5732.4723 12.5208 147 918121.414 1.566 + 18 83 65 148 Tb -70536.947 12.463 8204.3207 0.0842 B- -2677.5501 9.5961 147 924275.476 13.379 + 16 82 66 148 Dy -67859.397 8.724 8180.9430 0.0589 B- -9868.2304 84.2872 147 927149.944 9.365 + 14 81 67 148 Ho x -57991.166 83.834 8108.9797 0.5664 B- -6512.1694 84.4583 147 937743.925 90.000 + 12 80 68 148 Er x -51478.997 10.246 8059.6924 0.0692 B- -12713.9630 14.4906 147 944735.026 11.000 + 10 79 69 148 Tm x -38765.034 10.246 7968.5011 0.0692 B- -8535# 400# 147 958384.026 11.000 + 8 78 70 148 Yb x -30230# 400# 7906# 3# B- * 147 967547# 429# +0 41 95 54 149 Xe x -33000# 300# 8009# 2# B- 10300# 500# 148 964573# 322# + 39 94 55 149 Cs x -43300# 400# 8073# 3# B- 9531# 400# 148 953516# 429# + 37 93 56 149 Ba x -52830.620 2.515 8131.8495 0.0169 B- 7389.3010 200.2781 148 943284.000 2.700 + 35 92 57 149 La + -60219.921 200.262 8176.1915 1.3440 B- 6450.0000 200.0000 148 935351.259 214.990 + 33 91 58 149 Ce x -66669.921 10.246 8214.2294 0.0688 B- 4369.4525 14.2296 148 928426.900 11.000 + 31 90 59 149 Pr x -71039.373 9.874 8238.3040 0.0663 B- 3336.1617 10.0853 148 923736.100 10.600 + 29 89 60 149 Nd -n -74375.535 2.054 8255.4437 0.0138 B- 1688.8697 2.4588 148 920154.583 2.205 + 27 88 61 149 Pm -76064.404 2.184 8261.5277 0.0147 B- 1071.4936 1.8747 148 918341.507 2.344 + 25 87 62 149 Sm -77135.898 1.157 8263.4683 0.0078 B- -694.5812 3.7881 148 917191.211 1.241 + 23 86 63 149 Eu -76441.317 3.903 8253.5560 0.0262 B- -1314.1437 4.1361 148 917936.875 4.190 + 21 85 64 149 Gd -75127.173 3.310 8239.4856 0.0222 B- -3638.5336 4.3388 148 919347.666 3.553 + 19 84 65 149 Tb -71488.639 3.629 8209.8153 0.0244 B- -3794.6493 9.1335 148 923253.792 3.895 + 17 83 66 149 Dy -67693.990 9.183 8179.0972 0.0616 B- -6048.1366 12.7928 148 927327.516 9.858 + 15 82 67 149 Ho -61645.854 11.985 8133.2550 0.0804 B- -7904.2328 30.4066 148 933820.457 12.866 + 13 81 68 149 Er x -53741.621 27.945 8074.9558 0.1875 B- -9801# 202# 148 942306.000 30.000 + 11 80 69 149 Tm x -43940# 200# 8004# 1# B- -10611# 361# 148 952828# 215# + 9 79 70 149 Yb x -33330# 300# 7927# 2# B- * 148 964219# 322# +0 42 96 54 150 Xe x -28990# 300# 7983# 2# B- 9180# 500# 149 968878# 322# + 40 95 55 150 Cs x -38170# 400# 8039# 3# B- 11720# 400# 149 959023# 429# + 38 94 56 150 Ba x -49889.799 5.682 8111.8405 0.0379 B- 6421.3477 6.2138 149 946441.100 6.100 + 36 93 57 150 La x -56311.147 2.515 8149.4339 0.0168 B- 8535.7155 11.9641 149 939547.500 2.700 + 34 92 58 150 Ce -64846.863 11.697 8201.1230 0.0780 B- 3453.6462 14.2913 149 930384.032 12.556 + 32 91 59 150 Pr -68300.509 9.015 8218.9316 0.0601 B- 5379.4422 9.0682 149 926676.391 9.677 + 30 90 60 150 Nd -73679.951 1.129 8249.5789 0.0075 B- -82.6155 20.0010 149 920901.322 1.211 + 28 89 61 150 Pm + -73597.336 20.031 8243.8125 0.1335 B- 3454.0000 20.0000 149 920990.014 21.504 + 26 88 62 150 Sm -77051.336 1.112 8261.6235 0.0074 B- -2258.9662 6.1803 149 917281.993 1.193 + 24 87 63 150 Eu -74792.369 6.231 8241.3481 0.0415 B- 971.6815 3.5428 149 919707.092 6.688 + 22 86 64 150 Gd -75764.051 6.055 8242.6103 0.0404 B- -4658.2621 8.3784 149 918663.949 6.500 + 20 85 65 150 Tb -71105.789 7.371 8206.3396 0.0491 B- -1796.1707 8.3886 149 923664.799 7.912 + 18 84 66 150 Dy -69309.618 4.319 8189.1495 0.0288 B- -7363.7264 14.4629 149 925593.068 4.636 + 16 83 67 150 Ho -61945.892 14.168 8134.8423 0.0945 B- -4114.5689 13.5910 149 933498.353 15.209 + 14 82 68 150 Er -57831.323 17.194 8102.1962 0.1146 B- -11340# 196# 149 937915.524 18.458 + 12 81 69 150 Tm x -46491# 196# 8021# 1# B- -7661# 358# 149 950090# 210# + 10 80 70 150 Yb x -38830# 300# 7965# 2# B- -14059# 424# 149 958314# 322# + 8 79 71 150 Lu -p -24771# 300# 7866# 2# B- * 149 973407# 322# +0 41 96 55 151 Cs x -34280# 500# 8013# 3# B- 10660# 640# 150 963199# 537# + 39 95 56 151 Ba x -44940# 400# 8079# 3# B- 8370# 591# 150 951755# 429# + 37 94 57 151 La x -53310.339 435.473 8129.0436 2.8839 B- 7914.7191 435.8330 150 942769.000 467.500 + 35 93 58 151 Ce x -61225.058 17.698 8176.2779 0.1172 B- 5554.6233 21.1885 150 934272.200 19.000 + 33 92 59 151 Pr -66779.681 11.650 8207.8824 0.0772 B- 4163.5021 11.6789 150 928309.066 12.506 + 31 91 60 151 Nd -70943.183 1.133 8230.2741 0.0075 B- 2443.0767 4.4734 150 923839.363 1.215 + 29 90 61 151 Pm -73386.260 4.611 8241.2723 0.0305 B- 1190.2198 4.4757 150 921216.613 4.949 + 27 89 62 151 Sm -74576.480 1.110 8243.9735 0.0074 B- 76.6182 0.5375 150 919938.859 1.191 + 25 88 63 151 Eu -74653.098 1.166 8239.2998 0.0077 B- -464.1779 2.7791 150 919856.606 1.251 + 23 87 64 151 Gd -74188.920 2.993 8231.0446 0.0198 B- -2565.3796 3.7615 150 920354.922 3.212 + 21 86 65 151 Tb -71623.541 4.094 8208.8743 0.0271 B- -2871.1525 4.9455 150 923108.970 4.395 + 19 85 66 151 Dy -a -68752.388 3.247 8184.6789 0.0215 B- -5129.6421 8.7507 150 926191.279 3.486 + 17 84 67 151 Ho -a -63622.746 8.298 8145.5267 0.0550 B- -5356.4558 18.4420 150 931698.176 8.908 + 15 83 68 151 Er x -58266.290 16.470 8104.8723 0.1091 B- -7494.6768 25.4292 150 937448.567 17.681 + 13 82 69 151 Tm -50771.613 19.375 8050.0576 0.1283 B- -9229.2615 300.1329 150 945494.433 20.799 + 11 81 70 151 Yb ep -41542.352 300.492 7983.7556 1.9900 B- -11242# 425# 150 955402.453 322.591 + 9 80 71 151 Lu -p -30300# 300# 7904# 2# B- * 150 967471# 322# +0 42 97 55 152 Cs x -29130# 500# 7980# 3# B- 12480# 640# 151 968728# 537# + 40 96 56 152 Ba x -41610# 400# 8057# 3# B- 7680# 500# 151 955330# 429# + 38 95 57 152 La x -49290# 300# 8102# 2# B- 9690# 361# 151 947085# 322# + 36 94 58 152 Ce x -58980# 200# 8161# 1# B- 4778# 201# 151 936682# 215# + 34 93 59 152 Pr x -63758.070 18.537 8187.1049 0.1220 B- 6391.5934 30.7035 151 931552.900 19.900 + 32 92 60 152 Nd -70149.663 24.476 8224.0078 0.1610 B- 1104.8050 18.5011 151 924691.242 26.276 + 30 91 61 152 Pm -71254.468 25.904 8226.1293 0.1704 B- 3508.5089 25.8859 151 923505.185 27.809 + 28 90 62 152 Sm -74762.977 1.016 8244.0645 0.0067 B- -1874.4774 0.6857 151 919738.646 1.090 + 26 89 63 152 Eu -72888.500 1.166 8226.5854 0.0077 B- 1818.8037 0.7002 151 921750.980 1.252 + 24 88 64 152 Gd -74707.304 1.007 8233.4042 0.0066 B- -3990.0000 40.0000 151 919798.414 1.081 + 22 87 65 152 Tb - -70717.304 40.013 8202.0072 0.2632 B- -599.3405 40.2575 151 924081.855 42.955 + 20 86 66 152 Dy -a -70117.963 4.593 8192.9171 0.0302 B- -6513.3275 13.3176 151 924725.274 4.930 + 18 85 67 152 Ho -63604.636 12.528 8144.9193 0.0824 B- -3104.4174 9.8150 151 931717.618 13.449 + 16 84 68 152 Er -60500.218 8.830 8119.3485 0.0581 B- -8779.9397 54.7434 151 935050.347 9.478 + 14 83 69 152 Tm -51720.279 54.027 8056.4387 0.3554 B- -5449.8923 139.6200 151 944476.000 58.000 + 12 82 70 152 Yb -46270.386 149.708 8015.4371 0.9849 B- -12848# 246# 151 950326.699 160.718 + 10 81 71 152 Lu x -33422# 196# 7926# 1# B- * 151 964120# 210# +0 41 97 56 153 Ba x -36470# 400# 8023# 3# B- 9590# 500# 152 960848# 429# + 39 96 57 153 La x -46060# 300# 8081# 2# B- 8850# 361# 152 950553# 322# + 37 95 58 153 Ce x -54910# 200# 8134# 1# B- 6659# 201# 152 941052# 215# + 35 94 59 153 Pr -61568.490 11.882 8172.0371 0.0777 B- 5761.8901 12.1896 152 933903.511 12.755 + 33 93 60 153 Nd -67330.380 2.747 8204.5832 0.0180 B- 3317.6236 9.3521 152 927717.868 2.949 + 31 92 61 153 Pm -70648.003 9.063 8221.1536 0.0592 B- 1912.0559 9.0829 152 924156.252 9.729 + 29 91 62 153 Sm -n -72560.059 1.025 8228.5373 0.0067 B- 807.4073 0.7063 152 922103.576 1.100 + 27 90 63 153 Eu -73367.466 1.171 8228.7011 0.0077 B- -484.5225 0.7150 152 921236.789 1.257 + 25 89 64 153 Gd -72882.944 1.002 8220.4209 0.0066 B- -1569.3340 3.8444 152 921756.945 1.075 + 23 88 65 153 Tb -71313.610 3.947 8205.0504 0.0258 B- -2170.4134 1.9335 152 923441.694 4.237 + 21 87 66 153 Dy -69143.197 4.001 8185.7514 0.0262 B- -4131.1229 6.1571 152 925771.729 4.295 + 19 86 67 153 Ho -a -65012.074 5.066 8153.6372 0.0331 B- -4545.3918 9.8899 152 930206.671 5.438 + 17 85 68 153 Er -60466.682 9.285 8118.8154 0.0607 B- -6494.0723 12.8812 152 935086.350 9.967 + 15 84 69 153 Tm -53972.610 11.979 8071.2571 0.0783 B- -6813# 201# 152 942058.023 12.860 + 13 83 70 153 Yb x -47160# 200# 8022# 1# B- -8784# 250# 152 949372# 215# + 11 82 71 153 Lu +a -38375.462 150.017 7959.0882 0.9805 B- -11075# 335# 152 958802.248 161.050 + 9 81 72 153 Hf x -27300# 300# 7882# 2# B- * 152 970692# 322# +0 42 98 56 154 Ba x -32920# 500# 8001# 3# B- 8610# 583# 153 964659# 537# + 40 97 57 154 La x -41530# 300# 8051# 2# B- 10690# 361# 153 955416# 322# + 38 96 58 154 Ce x -52220# 200# 8116# 1# B- 5640# 224# 153 943940# 215# + 36 95 59 154 Pr + -57859.602 100.005 8147.2994 0.6494 B- 7720.0000 100.0000 153 937885.165 107.360 + 34 94 60 154 Nd x -65579.602 1.025 8192.3491 0.0067 B- 2687.0000 25.0000 153 929597.404 1.100 + 32 93 61 154 Pm - -68266.602 25.021 8204.7170 0.1625 B- 4188.9614 25.0550 153 926712.791 26.861 + 30 92 62 154 Sm -72455.564 1.305 8226.8379 0.0085 B- -717.1969 1.1019 153 922215.756 1.400 + 28 91 63 154 Eu -71738.367 1.188 8217.1006 0.0077 B- 1967.9913 0.7535 153 922985.699 1.275 + 26 90 64 154 Gd -73706.358 0.994 8224.7996 0.0065 B- -3549.6514 45.2983 153 920872.974 1.066 + 24 89 65 154 Tb - -70156.707 45.309 8196.6697 0.2942 B- 237.3080 45.9008 153 924683.681 48.641 + 22 88 66 154 Dy -70394.015 7.431 8193.1305 0.0483 B- -5754.6363 10.1785 153 924428.920 7.977 + 20 87 67 154 Ho -a -64639.378 8.216 8150.6825 0.0534 B- -2034.4045 9.4621 153 930606.776 8.820 + 18 86 68 154 Er -62604.974 4.961 8132.3919 0.0322 B- -8177.8316 14.9113 153 932790.799 5.325 + 16 85 69 154 Tm -a -54427.142 14.412 8074.2090 0.0936 B- -4495.0495 13.9528 153 941570.062 15.471 + 14 84 70 154 Yb -49932.093 17.281 8039.9402 0.1122 B- -10265# 201# 153 946395.696 18.551 + 12 83 71 154 Lu +a -39667# 201# 7968# 1# B- -6937# 361# 153 957416# 216# + 10 82 72 154 Hf x -32730# 300# 7918# 2# B- * 153 964863# 322# +0 41 98 57 155 La x -37930# 400# 8028# 3# B- 9850# 500# 154 959280# 429# + 39 97 58 155 Ce x -47780# 300# 8087# 2# B- 7635# 300# 154 948706# 322# + 37 96 59 155 Pr -55415.335 17.198 8131.0398 0.1110 B- 6868.4609 19.4725 154 940509.193 18.462 + 35 95 60 155 Nd -62283.796 9.154 8170.3050 0.0591 B- 4656.2095 10.2781 154 933135.598 9.826 + 33 94 61 155 Pm -66940.006 4.719 8195.2977 0.0304 B- 3251.1999 4.9024 154 928136.951 5.065 + 31 93 62 155 Sm -n -70191.206 1.332 8211.2258 0.0086 B- 1627.1314 1.2016 154 924646.645 1.429 + 29 92 63 155 Eu -71818.337 1.252 8216.6760 0.0081 B- 251.9612 0.8682 154 922899.847 1.343 + 27 91 64 155 Gd -72070.298 0.983 8213.2541 0.0063 B- -819.8588 9.7884 154 922629.356 1.055 + 25 90 65 155 Tb + -71250.439 9.830 8202.9173 0.0634 B- -2094.5000 1.8974 154 923509.511 10.552 + 23 89 66 155 Dy -69155.939 9.645 8184.3570 0.0622 B- -3116.1405 16.5887 154 925758.049 10.354 + 21 88 67 155 Ho -66039.799 17.470 8159.2055 0.1127 B- -3830.6268 18.4730 154 929103.363 18.754 + 19 87 68 155 Er -a -62209.172 6.074 8129.4444 0.0392 B- -5583.2509 11.5109 154 933215.710 6.520 + 17 86 69 155 Tm -a -56625.921 9.921 8088.3760 0.0640 B- -6123.3072 19.3388 154 939209.576 10.651 + 15 85 70 155 Yb -a -50502.614 16.600 8043.8234 0.1071 B- -7957.5578 25.4153 154 945783.216 17.820 + 13 84 71 155 Lu -42545.056 19.245 7987.4369 0.1242 B- -8235# 301# 154 954326.005 20.660 + 11 83 72 155 Hf x -34310# 300# 7929# 2# B- -10322# 424# 154 963167# 322# + 9 82 73 155 Ta -p -23988# 300# 7858# 2# B- * 154 974248# 322# +0 42 99 57 156 La x -33050# 400# 7997# 3# B- 11769# 500# 155 964519# 429# + 40 98 58 156 Ce x -44820# 300# 8068# 2# B- 6630# 300# 155 951884# 322# + 38 97 59 156 Pr x -51449.307 1.025 8105.2337 0.0066 B- 8752.8229 1.6585 155 944766.900 1.100 + 36 96 60 156 Nd x -60202.130 1.304 8156.3265 0.0084 B- 3964.7175 1.7642 155 935370.358 1.400 + 34 95 61 156 Pm -64166.847 1.188 8176.7263 0.0076 B- 5193.8878 8.6044 155 931114.059 1.275 + 32 94 62 156 Sm -69360.735 8.522 8205.0054 0.0546 B- 722.1090 7.9025 155 925538.191 9.148 + 30 93 63 156 Eu -70082.844 3.532 8204.6192 0.0226 B- 2452.4891 3.4083 155 924762.976 3.791 + 28 92 64 156 Gd -72535.333 0.983 8215.3253 0.0063 B- -2444.3230 3.6774 155 922130.120 1.054 + 26 91 65 156 Tb -70091.010 3.768 8194.6415 0.0242 B- 438.3762 3.6789 155 924754.209 4.044 + 24 90 66 156 Dy -70529.386 0.988 8192.4366 0.0063 B- -4990.9836 38.4111 155 924283.593 1.060 + 22 89 67 156 Ho - -65538.403 38.424 8155.4280 0.2463 B- -1326.7201 45.6391 155 929641.634 41.249 + 20 88 68 156 Er -64211.683 24.629 8141.9084 0.1579 B- -7377.2657 26.7099 155 931065.926 26.440 + 18 87 69 156 Tm -56834.417 14.279 8089.6032 0.0915 B- -3568.8794 12.5484 155 938985.746 15.328 + 16 86 70 156 Yb -53265.538 9.308 8061.7107 0.0597 B- -9565.9880 54.9162 155 942817.096 9.992 + 14 85 71 156 Lu -a -43699.550 54.122 7995.3752 0.3469 B- -5880.0352 139.6908 155 953086.606 58.102 + 12 84 72 156 Hf -37819.514 149.740 7952.6676 0.9599 B- -11819# 335# 155 959399.083 160.752 + 10 83 73 156 Ta -p -26001# 300# 7872# 2# B- * 155 972087# 322# +0 43 100 57 157 La x -29070# 300# 7972# 2# B- 10860# 500# 156 968792# 322# + 41 99 58 157 Ce x -39930# 400# 8037# 3# B- 8504# 400# 156 957133# 429# + 39 98 59 157 Pr x -48434.806 3.167 8085.8170 0.0202 B- 8059.3109 3.8207 156 948003.100 3.400 + 37 97 60 157 Nd -56494.117 2.137 8132.1671 0.0136 B- 5802.9994 7.3246 156 939351.074 2.294 + 35 96 61 157 Pm -62297.116 7.006 8164.1458 0.0446 B- 4380.5376 8.2650 156 933121.298 7.521 + 33 95 62 157 Sm -66677.654 4.434 8187.0642 0.0282 B- 2781.4807 6.1191 156 928418.598 4.759 + 31 94 63 157 Eu -69459.134 4.234 8199.7975 0.0270 B- 1364.7614 4.1973 156 925432.556 4.545 + 29 93 64 157 Gd -70823.896 0.977 8203.5072 0.0062 B- -60.0473 0.2972 156 923967.424 1.048 + 27 92 65 157 Tb -70763.848 1.018 8198.1416 0.0065 B- -1339.1791 5.1297 156 924031.888 1.092 + 25 91 66 157 Dy -69424.669 5.154 8184.6287 0.0328 B- -2591.8068 23.7839 156 925469.555 5.532 + 23 90 67 157 Ho -66832.862 23.469 8163.1373 0.1495 B- -3419.2146 33.6675 156 928251.974 25.194 + 21 89 68 157 Er -63413.648 26.505 8136.3757 0.1688 B- -4704.3690 38.5152 156 931922.652 28.454 + 19 88 69 157 Tm x -58709.279 27.945 8101.4285 0.1780 B- -5289.3667 29.9971 156 936973.000 30.000 + 17 87 70 157 Yb -53419.912 10.905 8062.7551 0.0695 B- -6980.0942 14.2406 156 942651.368 11.706 + 15 86 71 157 Lu -46439.818 12.074 8013.3128 0.0769 B- -7585# 201# 156 950144.807 12.961 + 13 85 72 157 Hf -a -38855# 200# 7960# 1# B- -9259# 250# 156 958288# 215# + 11 84 73 157 Ta IT -29595.948 150.052 7896.0608 0.9557 B- -9906# 427# 156 968227.445 161.087 + 9 83 74 157 W x -19690# 400# 7828# 3# B- * 156 978862# 429# +0 42 100 58 158 Ce x -36540# 400# 8015# 3# B- 7610# 500# 157 960773# 429# + 40 99 59 158 Pr x -44150# 300# 8059# 2# B- 9685# 300# 157 952603# 322# + 38 98 60 158 Nd x -53835.123 1.304 8114.9529 0.0083 B- 5271.0196 1.5776 157 942205.620 1.400 + 36 97 61 158 Pm -59106.143 0.888 8143.3622 0.0056 B- 6145.7062 4.8634 157 936546.948 0.953 + 34 96 62 158 Sm -65251.849 4.782 8177.3075 0.0303 B- 2018.6123 5.1146 157 929949.262 5.133 + 32 95 63 158 Eu -67270.461 2.032 8185.1320 0.0129 B- 3419.5081 2.2547 157 927782.192 2.181 + 30 94 64 158 Gd -70689.969 0.976 8201.8229 0.0062 B- -1219.0862 0.9799 157 924111.200 1.048 + 28 93 65 158 Tb -69470.883 1.268 8189.1556 0.0080 B- 936.2686 2.4750 157 925419.942 1.360 + 26 92 66 158 Dy -70407.152 2.337 8190.1298 0.0148 B- -4219.7555 27.0048 157 924414.817 2.509 + 24 91 67 158 Ho - -66187.396 27.106 8158.4709 0.1716 B- -883.5812 37.0236 157 928944.910 29.099 + 22 90 68 158 Er -65303.815 25.219 8147.9270 0.1596 B- -6600.6151 31.3411 157 929893.474 27.074 + 20 89 69 158 Tm -58703.200 25.219 8101.1994 0.1596 B- -2693.5796 26.4499 157 936979.525 27.074 + 18 88 70 158 Yb -56009.620 7.973 8079.1999 0.0505 B- -8797.4201 16.8655 157 939871.202 8.559 + 16 87 71 158 Lu -a -47212.200 15.125 8018.5685 0.0957 B- -5109.8010 14.9373 157 949315.620 16.236 + 14 86 72 158 Hf -42102.399 17.494 7981.2764 0.1107 B- -10984# 201# 157 954801.217 18.780 + 12 85 73 158 Ta +a -31118# 201# 7907# 1# B- -7426# 361# 157 966593# 215# + 10 84 74 158 W -a -23693# 300# 7855# 2# B- * 157 974565# 322# +0 43 101 58 159 Ce x -31340# 500# 7983# 3# B- 9430# 640# 158 966355# 537# + 41 100 59 159 Pr x -40770# 400# 8037# 3# B- 8954# 401# 158 956232# 429# + 39 99 60 159 Nd x -49724.007 29.808 8088.8224 0.1875 B- 6830.3441 31.4529 158 946619.085 32.000 + 37 98 61 159 Pm -56554.351 10.039 8126.8601 0.0631 B- 5653.4982 11.6438 158 939286.409 10.777 + 35 97 62 159 Sm -62207.849 5.934 8157.4963 0.0373 B- 3835.5363 7.3211 158 933217.130 6.370 + 33 96 63 159 Eu -66043.386 4.320 8176.6987 0.0272 B- 2518.4717 4.3657 158 929099.512 4.637 + 31 95 64 159 Gd -68561.857 0.980 8187.6177 0.0062 B- 970.7242 0.7495 158 926395.822 1.051 + 29 94 65 159 Tb -69532.582 1.103 8188.8025 0.0069 B- -365.3613 1.1573 158 925353.707 1.184 + 27 93 66 159 Dy -69167.220 1.439 8181.5842 0.0091 B- -1837.6000 2.6833 158 925745.938 1.544 + 25 92 67 159 Ho - -67329.620 3.045 8165.1066 0.0192 B- -2768.5000 2.0000 158 927718.683 3.268 + 23 91 68 159 Er - -64561.120 3.643 8142.7742 0.0229 B- -3990.7162 28.1813 158 930690.790 3.910 + 21 90 69 159 Tm x -60570.404 27.945 8112.7549 0.1758 B- -4736.8869 33.0155 158 934975.000 30.000 + 19 89 70 159 Yb x -55833.517 17.582 8078.0428 0.1106 B- -6124.9081 41.5648 158 940060.257 18.874 + 17 88 71 159 Lu x -49708.609 37.663 8034.6009 0.2369 B- -6856.0030 41.2456 158 946635.615 40.433 + 15 87 72 159 Hf -a -42852.606 16.813 7986.5610 0.1057 B- -8413.4492 25.8909 158 953995.837 18.049 + 13 86 73 159 Ta IT -34439.157 19.689 7928.7258 0.1238 B- -9005# 301# 158 963028.046 21.137 + 11 85 74 159 W -a -25434# 300# 7867# 2# B- -10629# 427# 158 972696# 322# + 9 84 75 159 Re IT -14805# 305# 7795# 2# B- * 158 984106# 327# +0 42 101 59 160 Pr x -36200# 400# 8009# 2# B- 10525# 402# 159 961138# 429# + 40 100 60 160 Nd x -46724.515 46.575 8069.9662 0.2911 B- 6170.1238 46.6198 159 949839.172 50.000 + 38 99 61 160 Pm x -52894.639 2.049 8103.6398 0.0128 B- 7338.5338 2.8330 159 943215.272 2.200 + 36 98 62 160 Sm x -60233.172 1.956 8144.6159 0.0122 B- 3260.2763 2.1547 159 935337.032 2.100 + 34 97 63 160 Eu x -63493.449 0.904 8160.1030 0.0057 B- 4448.6112 1.4417 159 931836.982 0.970 + 32 96 64 160 Gd -67942.060 1.123 8183.0171 0.0070 B- -105.5863 1.0207 159 927061.202 1.206 + 30 95 65 160 Tb -67836.474 1.110 8177.4676 0.0069 B- 1835.9516 1.1011 159 927174.553 1.191 + 28 94 66 160 Dy -69672.425 0.700 8184.0526 0.0044 B- -3290.0000 15.0000 159 925203.578 0.751 + 26 93 67 160 Ho - -66382.425 15.016 8158.6004 0.0939 B- -318.2488 28.5197 159 928735.538 16.120 + 24 92 68 160 Er -66064.176 24.246 8151.7217 0.1515 B- -5763.1395 39.1333 159 929077.193 26.029 + 22 91 69 160 Tm -60301.037 32.686 8110.8124 0.2043 B- -2137.8101 33.1447 159 935264.177 35.089 + 20 90 70 160 Yb x -58163.227 5.496 8092.5614 0.0343 B- -7893.2846 57.0863 159 937559.210 5.900 + 18 89 71 160 Lu x -50269.942 56.821 8038.3387 0.3551 B- -4331.1951 57.6165 159 946033.000 61.000 + 16 88 72 160 Hf -45938.747 9.540 8006.3791 0.0596 B- -10115.0475 55.1472 159 950682.728 10.241 + 14 87 73 160 Ta -a -35823.700 54.316 7938.2704 0.3395 B- -6494.6267 139.8413 159 961541.678 58.310 + 12 86 74 160 W -29329.073 149.810 7892.7893 0.9363 B- -12451# 335# 159 968513.946 160.828 + 10 85 75 160 Re -a -16878# 300# 7810# 2# B- * 159 981880# 322# +0 43 102 59 161 Pr x -32490# 500# 7986# 3# B- 9741# 640# 160 965121# 537# + 41 101 60 161 Nd x -42230# 400# 8042# 2# B- 7856# 400# 160 954664# 429# + 39 100 61 161 Pm x -50086.589 9.035 8085.9977 0.0561 B- 6585.4546 11.3187 160 946229.837 9.700 + 37 99 62 161 Sm -56672.043 6.817 8122.0418 0.0423 B- 5119.5577 12.4147 160 939160.062 7.318 + 35 98 63 161 Eu -61791.601 10.400 8148.9810 0.0646 B- 3714.5406 10.5074 160 933663.991 11.164 + 33 97 64 161 Gd -n -65506.142 1.504 8167.1934 0.0093 B- 1955.6357 1.4402 160 929676.267 1.614 + 31 96 65 161 Tb -67461.778 1.218 8174.4809 0.0076 B- 593.7166 1.2010 160 927576.806 1.308 + 29 95 66 161 Dy -68055.494 0.697 8173.3093 0.0043 B- -859.2003 2.1368 160 926939.425 0.748 + 27 94 67 161 Ho -67196.294 2.151 8163.1134 0.0134 B- -1994.9954 9.0041 160 927861.815 2.309 + 25 93 68 161 Er +n -65201.298 8.774 8145.8628 0.0545 B- -3302.5839 29.2899 160 930003.530 9.419 + 23 92 69 161 Tm x -61898.715 27.945 8120.4906 0.1736 B- -4064.4665 31.7640 160 933549.000 30.000 + 21 91 70 161 Yb x -57834.248 15.101 8090.3861 0.0938 B- -5271.8989 31.7640 160 937912.384 16.211 + 19 90 71 161 Lu x -52562.349 27.945 8052.7821 0.1736 B- -6246.5319 36.4805 160 943572.000 30.000 + 17 89 72 161 Hf -46315.817 23.450 8009.1245 0.1457 B- -7537.2421 33.7278 160 950277.927 25.174 + 15 88 73 161 Ta +a -38778.575 24.381 7957.4500 0.1514 B- -8272# 202# 160 958369.489 26.174 + 13 87 74 161 W -a -30507# 200# 7901# 1# B- -9664# 250# 160 967249# 215# + 11 86 75 161 Re -20842.820 149.905 7836.3292 0.9311 B- -10647# 427# 160 977624.313 160.930 + 9 85 76 161 Os -a -10196# 400# 7765# 2# B- * 160 989054# 429# +0 42 102 60 162 Nd x -39010# 400# 8022# 2# B- 7030# 500# 161 958121# 429# + 40 101 61 162 Pm x -46040# 300# 8061# 2# B- 8339# 300# 161 950574# 322# + 38 100 62 162 Sm -54379.053 3.523 8107.5745 0.0218 B- 4343.8905 3.7605 161 941621.687 3.782 + 36 99 63 162 Eu -58722.944 1.314 8129.5593 0.0081 B- 5557.7761 4.1748 161 936958.329 1.410 + 34 98 64 162 Gd -nn -64280.720 3.963 8159.0373 0.0245 B- 1598.8278 4.4611 161 930991.812 4.254 + 32 97 65 162 Tb x -65879.548 2.049 8164.0773 0.0127 B- 2301.6217 2.1640 161 929275.400 2.200 + 30 96 66 162 Dy -68181.169 0.695 8173.4555 0.0043 B- -2140.6068 3.0926 161 926804.507 0.746 + 28 95 67 162 Ho -66040.563 3.102 8155.4126 0.0192 B- 293.6478 3.1069 161 929102.543 3.330 + 26 94 68 162 Er -66334.210 0.756 8152.3959 0.0047 B- -4856.7282 26.0475 161 928787.299 0.811 + 24 93 69 162 Tm - -61477.482 26.058 8117.5868 0.1609 B- -1656.3190 30.1127 161 934001.211 27.974 + 22 92 70 162 Yb x -59821.163 15.103 8102.5333 0.0932 B- -6989.4042 76.5406 161 935779.342 16.213 + 20 91 71 162 Lu x -52831.759 75.036 8054.5596 0.4632 B- -3663.3333 75.5679 161 943282.776 80.554 + 18 90 72 162 Hf -49168.426 8.952 8027.1171 0.0553 B- -9387.0211 63.8938 161 947215.526 9.610 + 16 89 73 162 Ta -a -39781.405 63.322 7964.3432 0.3909 B- -5782.1880 63.3235 161 957292.907 67.979 + 14 88 74 162 W -33999.217 17.657 7923.8214 0.1090 B- -11546# 201# 161 963500.341 18.955 + 12 87 75 162 Re +a -22453# 201# 7848# 1# B- -7953# 361# 161 975896# 215# + 10 86 76 162 Os -a -14500# 300# 7794# 2# B- * 161 984434# 322# +0 43 103 60 163 Nd x -34080# 500# 7992# 3# B- 8880# 640# 162 963414# 537# + 41 102 61 163 Pm x -42960# 400# 8042# 2# B- 7640# 400# 162 953881# 429# + 39 101 62 163 Sm x -50599.612 7.359 8084.1653 0.0451 B- 5974.2073 7.4141 162 945679.085 7.900 + 37 100 63 163 Eu x -56573.819 0.904 8116.0172 0.0056 B- 4814.7720 1.2046 162 939265.510 0.970 + 35 99 64 163 Gd -61388.591 0.797 8140.7560 0.0049 B- 3207.1628 4.1374 162 934096.640 0.855 + 33 98 65 163 Tb +p -64595.754 4.060 8155.6322 0.0249 B- 1785.1041 4.0006 162 930653.609 4.358 + 31 97 66 163 Dy -66380.858 0.693 8161.7840 0.0043 B- -2.8309 0.0222 162 928737.221 0.744 + 29 96 67 163 Ho -66378.027 0.693 8156.9670 0.0043 B- -1210.6141 4.5755 162 928740.260 0.744 + 27 95 68 163 Er -65167.413 4.628 8144.7403 0.0284 B- -2439.0000 3.0000 162 930039.908 4.967 + 25 94 69 163 Tm - -62728.413 5.515 8124.9774 0.0338 B- -3434.5345 16.0687 162 932658.282 5.920 + 23 93 70 163 Yb x -59293.878 15.105 8099.1069 0.0927 B- -4502.4636 31.7657 162 936345.406 16.215 + 21 92 71 163 Lu x -54791.415 27.945 8066.6848 0.1714 B- -5522.0944 37.9611 162 941179.000 30.000 + 19 91 72 163 Hf -49269.320 25.693 8028.0072 0.1576 B- -6734.6864 45.9217 162 947107.211 27.582 + 17 90 73 163 Ta -a -42534.634 38.061 7981.8905 0.2335 B- -7626.1952 69.7301 162 954337.194 40.860 + 15 89 74 163 W -a -34908.439 58.426 7930.3043 0.3584 B- -8906.1859 61.2953 162 962524.251 62.722 + 13 88 75 163 Re +a -26002.253 18.534 7870.8655 0.1137 B- -9666# 301# 162 972085.434 19.897 + 11 87 76 163 Os -a -16336# 300# 7807# 2# B- -11026# 500# 162 982462# 322# + 9 86 77 163 Ir x -5310# 400# 7734# 2# B- * 162 994299# 429# +0 42 103 61 164 Pm x -38360# 400# 8014# 2# B- 9565# 400# 163 958819# 429# + 40 102 62 164 Sm x -47925.314 4.099 8067.7803 0.0250 B- 5306.8315 4.5907 163 948550.061 4.400 + 38 101 63 164 Eu -53232.146 2.068 8095.3686 0.0126 B- 6461.5416 2.2969 163 942852.943 2.219 + 36 100 64 164 Gd -59693.688 1.000 8129.9978 0.0061 B- 2411.2959 2.1143 163 935916.193 1.073 + 34 99 65 164 Tb x -62104.983 1.863 8139.9304 0.0114 B- 3862.6653 1.9884 163 933327.561 2.000 + 32 98 66 164 Dy -65967.649 0.695 8158.7129 0.0042 B- -987.1315 1.3710 163 929180.819 0.746 + 30 97 67 164 Ho -64980.517 1.390 8147.9234 0.0085 B- 962.0559 1.3756 163 930240.548 1.492 + 28 96 68 164 Er -65942.573 0.704 8149.0191 0.0043 B- -4033.6302 25.0113 163 929207.739 0.755 + 26 95 69 164 Tm -61908.943 25.006 8119.6534 0.1525 B- -896.7722 29.2135 163 933538.019 26.845 + 24 94 70 164 Yb x -61012.171 15.106 8109.4149 0.0921 B- -6369.7952 31.7666 163 934500.743 16.217 + 22 93 71 164 Lu x -54642.376 27.945 8065.8043 0.1704 B- -2824.0194 32.1083 163 941339.000 30.000 + 20 92 72 164 Hf -51818.356 15.812 8043.8142 0.0964 B- -8535.5511 32.1083 163 944370.709 16.975 + 18 91 73 164 Ta x -43282.805 27.945 7986.9978 0.1704 B- -5047.2500 29.5717 163 953534.000 30.000 + 16 90 74 164 W -38235.555 9.673 7951.4515 0.0590 B- -10763.1138 55.4055 163 958952.445 10.384 + 14 89 75 164 Re -a -27472.441 54.555 7881.0523 0.3326 B- -7047.7180 140.0330 163 970507.122 58.566 + 12 88 76 164 Os -20424.723 149.903 7833.3080 0.9140 B- -12941# 350# 163 978073.158 160.927 + 10 87 77 164 Ir -a -7483# 316# 7750# 2# B- * 163 991966# 339# +0 43 104 61 165 Pm x -34670# 500# 7992# 3# B- 8840# 640# 164 962780# 537# + 41 103 62 165 Sm x -43510# 400# 8041# 2# B- 7219# 400# 164 953290# 429# + 39 102 63 165 Eu -50729.103 5.213 8080.0529 0.0316 B- 5796.6788 5.3733 164 945540.070 5.596 + 37 101 64 165 Gd -56525.782 1.304 8110.4428 0.0079 B- 4063.0674 2.0189 164 939317.080 1.400 + 35 100 65 165 Tb -60588.849 1.541 8130.3259 0.0093 B- 3023.4392 1.6915 164 934955.198 1.654 + 33 99 66 165 Dy -n -63612.289 0.697 8143.9083 0.0042 B- 1285.7287 0.7502 164 931709.402 0.748 + 31 98 67 165 Ho -64898.017 0.786 8146.9591 0.0048 B- -376.6648 0.9575 164 930329.116 0.844 + 29 97 68 165 Er -64521.353 0.918 8139.9348 0.0056 B- -1591.3282 1.4891 164 930733.482 0.985 + 27 96 69 165 Tm -62930.024 1.658 8125.5489 0.0101 B- -2634.6364 26.5907 164 932441.843 1.779 + 25 95 70 165 Yb -60295.388 26.539 8104.8399 0.1608 B- -3853.1403 35.4324 164 935270.241 28.490 + 23 94 71 165 Lu -56442.248 26.539 8076.7460 0.1608 B- -4806.7350 38.5387 164 939406.758 28.490 + 21 93 72 165 Hf x -51635.513 27.945 8042.8728 0.1694 B- -5787.6410 31.0668 164 944567.000 30.000 + 19 92 73 165 Ta -45847.872 13.573 8003.0547 0.0823 B- -6986.5555 29.1133 164 950780.287 14.571 + 17 91 74 165 W -38861.316 25.756 7955.9704 0.1561 B- -8201.9627 34.9116 164 958280.663 27.649 + 15 90 75 165 Re +a -30659.353 23.593 7901.5201 0.1430 B- -8913# 202# 164 967085.831 25.328 + 13 89 76 165 Os -a -21747# 200# 7843# 1# B- -10151# 255# 164 976654# 215# + 11 88 77 165 Ir IT -11595# 158# 7776# 1# B- -11277# 430# 164 987552# 170# + 9 87 78 165 Pt -a -318# 400# 7703# 2# B- * 164 999658# 429# +0 42 104 62 166 Sm x -40450# 400# 8023# 2# B- 6299# 412# 165 956575# 429# + 40 103 63 166 Eu + -46749# 100# 8056# 1# B- 7622# 100# 165 949813# 107# + 38 102 64 166 Gd x -54370.926 1.584 8097.2260 0.0095 B- 3437.8515 2.1558 165 941630.413 1.700 + 36 101 65 166 Tb -57808.778 1.463 8113.2230 0.0088 B- 4775.6930 1.6690 165 937939.727 1.570 + 34 100 66 166 Dy -n -62584.471 0.804 8137.2793 0.0048 B- 485.8684 0.8502 165 932812.810 0.862 + 32 99 67 166 Ho -63070.339 0.786 8135.4933 0.0047 B- 1853.8057 0.7792 165 932291.209 0.844 + 30 98 68 166 Er -64924.145 0.334 8141.9479 0.0020 B- -3037.6667 11.5470 165 930301.067 0.358 + 28 97 69 166 Tm - -61886.478 11.552 8118.9357 0.0696 B- -292.7714 13.5069 165 933562.136 12.401 + 26 96 70 166 Yb +nn -61593.706 7.001 8112.4591 0.0422 B- -5572.7197 30.6189 165 933876.439 7.515 + 24 95 71 166 Lu x -56020.987 29.808 8074.1756 0.1796 B- -2161.9978 40.8585 165 939859.000 32.000 + 22 94 72 166 Hf x -53858.989 27.945 8056.4386 0.1683 B- -7761.2089 39.5199 165 942180.000 30.000 + 20 93 73 166 Ta x -46097.780 27.945 8004.9714 0.1683 B- -4210.3092 29.5036 165 950512.000 30.000 + 18 92 74 166 W -41887.471 9.463 7974.8951 0.0570 B- -10050.1358 88.7072 165 955031.952 10.159 + 16 91 75 166 Re -a -31837.335 88.242 7909.6392 0.5316 B- -6405.8090 88.3046 165 965821.216 94.731 + 14 90 76 166 Os -25431.526 17.966 7866.3371 0.1082 B- -12126# 201# 165 972698.135 19.287 + 12 89 77 166 Ir -p -13306# 201# 7789# 1# B- -8523# 361# 165 985716# 215# + 10 88 78 166 Pt -a -4783# 300# 7733# 2# B- * 165 994866# 322# +0 43 105 62 167 Sm x -35330# 500# 7992# 3# B- 8440# 640# 166 962072# 537# + 41 104 63 167 Eu x -43770# 400# 8038# 2# B- 7006# 400# 166 953011# 429# + 39 103 64 167 Gd -50775.732 5.213 8075.5428 0.0312 B- 5107.3505 5.5584 166 945490.012 5.596 + 37 102 65 167 Tb -55883.082 1.929 8101.4410 0.0116 B- 4028.3686 4.4459 166 940007.046 2.071 + 35 101 66 167 Dy x -59911.451 4.005 8120.8782 0.0240 B- 2368.0076 6.5549 166 935682.415 4.300 + 33 100 67 167 Ho p2n -62279.459 5.189 8130.3732 0.0311 B- 1009.7971 5.1890 166 933140.254 5.570 + 31 99 68 167 Er -63289.256 0.286 8131.7352 0.0017 B- -746.1392 1.2633 166 932056.192 0.306 + 29 98 69 167 Tm -62543.117 1.258 8122.5826 0.0075 B- -1953.2162 3.7968 166 932857.206 1.350 + 27 97 70 167 Yb -60589.900 3.960 8106.2020 0.0237 B- -3063.6190 37.4696 166 934954.069 4.251 + 25 96 71 167 Lu x -57526.281 37.260 8083.1722 0.2231 B- -4058.5198 46.5747 166 938243.000 40.000 + 23 95 72 167 Hf x -53467.761 27.945 8054.1850 0.1673 B- -5116.6971 39.5199 166 942600.000 30.000 + 21 94 73 167 Ta x -48351.064 27.945 8018.8614 0.1673 B- -6257.8523 33.6262 166 948093.000 30.000 + 19 93 74 167 W -42093.212 18.703 7976.7045 0.1120 B- -7259# 44# 166 954811.080 20.078 + 17 92 75 167 Re +a -34834# 40# 7929# 0# B- -8335# 90# 166 962604# 43# + 15 91 76 167 Os -a -26498.860 80.892 7873.9557 0.4844 B- -9426.4121 82.9464 166 971552.304 86.841 + 13 90 77 167 Ir -17072.448 18.346 7812.8254 0.1099 B- -10319# 307# 166 981671.973 19.694 + 11 89 78 167 Pt -a -6753# 306# 7746# 2# B- * 166 992750# 329# +0 44 106 62 168 Sm x -31640# 300# 7971# 2# B- 7610# 500# 167 966033# 322# + 42 105 63 168 Eu x -39250# 400# 8012# 2# B- 8899# 500# 167 957863# 429# + 40 104 64 168 Gd x -48150# 300# 8060# 2# B- 4631# 300# 167 948309# 322# + 38 103 65 168 Tb x -52781.181 4.192 8082.7980 0.0250 B- 5777.2167 140.0696 167 943337.074 4.500 + 36 102 66 168 Dy +pp -58558.398 140.007 8112.5293 0.8334 B- 1500.8333 143.1849 167 937134.977 150.303 + 34 101 67 168 Ho + -60059.231 30.001 8116.8061 0.1786 B- 2930.0000 30.0000 167 935523.766 32.207 + 32 100 68 168 Er -62989.231 0.262 8129.5897 0.0016 B- -1676.8526 1.6858 167 932378.282 0.280 + 30 99 69 168 Tm -61312.379 1.677 8114.9516 0.0100 B- 267.4880 1.6782 167 934178.457 1.800 + 28 98 70 168 Yb -61579.867 0.093 8111.8870 0.0006 B- -4507.0351 37.9735 167 933891.297 0.100 + 26 97 71 168 Lu -57072.832 37.973 8080.4026 0.2260 B- -1712.2740 47.1476 167 938729.798 40.766 + 24 96 72 168 Hf x -55360.557 27.945 8065.5536 0.1663 B- -6966.6444 39.5199 167 940568.000 30.000 + 22 95 73 168 Ta x -48393.913 27.945 8019.4287 0.1663 B- -3500.9828 30.9307 167 948047.000 30.000 + 20 94 74 168 W -44892.930 13.259 7993.9327 0.0789 B- -9098.0411 33.5515 167 951805.459 14.233 + 18 93 75 168 Re -a -35794.889 30.821 7935.1208 0.1835 B- -5799.8944 32.3727 167 961572.607 33.087 + 16 92 76 168 Os -29994.995 9.903 7895.9408 0.0589 B- -11328.7644 56.0975 167 967799.050 10.631 + 14 91 77 168 Ir -a -18666.230 55.216 7823.8509 0.3287 B- -7656.1526 140.3258 167 979960.978 59.277 + 12 90 78 168 Pt -a -11010.078 149.934 7773.6217 0.8925 B- -13540# 427# 167 988180.196 160.960 + 10 89 79 168 Au x 2530# 400# 7688# 2# B- * 168 002716# 429# +0 43 106 63 169 Eu x -35660# 500# 7991# 3# B- 8230# 640# 168 961717# 537# + 41 105 64 169 Gd x -43890# 400# 8035# 2# B- 6590# 500# 168 952882# 429# + 39 104 65 169 Tb x -50480# 300# 8069# 2# B- 5116# 425# 168 945807# 322# + 37 103 66 169 Dy + -55596.010 300.669 8094.7566 1.7791 B- 3200.0000 300.0000 168 940315.231 322.781 + 35 102 67 169 Ho +p -58796.010 20.048 8109.0622 0.1186 B- 2125.1534 20.0483 168 936879.890 21.522 + 33 101 68 169 Er -n -60921.163 0.304 8117.0078 0.0018 B- 353.4910 0.7729 168 934598.444 0.326 + 31 100 69 169 Tm -61274.654 0.738 8114.4702 0.0044 B- -899.1270 0.7563 168 934218.956 0.792 + 29 99 70 169 Yb -n -60375.527 0.178 8104.5206 0.0011 B- -2293.0000 3.0000 168 935184.208 0.191 + 27 98 71 169 Lu - -58082.527 3.005 8086.3233 0.0178 B- -3365.6321 28.1060 168 937645.845 3.226 + 25 97 72 169 Hf x -54716.895 27.945 8061.7791 0.1654 B- -4426.4600 39.5199 168 941259.000 30.000 + 23 96 73 169 Ta x -50290.435 27.945 8030.9577 0.1654 B- -5372.5686 31.9246 168 946011.000 30.000 + 21 95 74 169 W -44917.866 15.436 7994.5381 0.0913 B- -6508.6195 19.1703 168 951778.689 16.571 + 19 94 75 169 Re +a -38409.247 11.369 7951.3963 0.0673 B- -7686.2626 28.3218 168 958765.979 12.204 + 17 93 76 169 Os -a -30722.984 25.940 7901.2862 0.1535 B- -8629.5684 34.8557 168 967017.521 27.847 + 15 92 77 169 Ir +a -22093.416 23.307 7845.5944 0.1379 B- -9629# 202# 168 976281.743 25.020 + 13 91 78 169 Pt -a -12464# 200# 7784# 1# B- -10676# 359# 168 986619# 215# + 11 90 79 169 Au x -1788# 298# 7716# 2# B- * 168 998080# 320# +0 44 107 63 170 Eu x -30860# 500# 7963# 3# B- 9989# 707# 169 966870# 537# + 42 106 64 170 Gd x -40850# 500# 8017# 3# B- 5860# 583# 169 956146# 537# + 40 105 65 170 Tb x -46710# 300# 8047# 2# B- 7000# 361# 169 949855# 322# + 38 104 66 170 Dy x -53710# 200# 8084# 1# B- 2528# 206# 169 942340# 215# + 36 103 67 170 Ho + -56237.514 50.019 8093.7902 0.2942 B- 3870.0000 50.0000 169 939626.548 53.697 + 34 102 68 170 Er -60107.514 1.387 8111.9529 0.0082 B- -312.1997 1.7852 169 935471.933 1.488 + 32 101 69 170 Tm -59795.314 0.732 8105.5144 0.0043 B- 968.6148 0.7319 169 935807.093 0.785 + 30 100 70 170 Yb -60763.929 0.010 8106.6101 0.0003 B- -3457.6950 16.8430 169 934767.242 0.011 + 28 99 71 170 Lu - -57306.234 16.843 8081.6686 0.0991 B- -1052.3734 32.6282 169 938479.230 18.081 + 26 98 72 170 Hf x -56253.860 27.945 8070.8762 0.1644 B- -6116.1903 39.5199 169 939609.000 30.000 + 24 97 73 170 Ta x -50137.670 27.945 8030.2965 0.1644 B- -2846.8656 30.9035 169 946175.000 30.000 + 22 96 74 170 W -47290.804 13.195 8008.9482 0.0776 B- -8386.8083 17.4557 169 949231.235 14.165 + 20 95 75 170 Re -38903.996 11.427 7955.0120 0.0672 B- -4978.3046 15.0274 169 958234.844 12.267 + 18 94 76 170 Os -33925.692 9.759 7921.1258 0.0574 B- -10743# 102# 169 963579.273 10.476 + 16 93 77 170 Ir -a -23182# 101# 7853# 1# B- -6883# 102# 169 975113# 109# + 14 92 78 170 Pt -16299.202 18.246 7808.2365 0.1073 B- -12596# 202# 169 982502.087 19.588 + 12 91 79 170 Au -p -3703# 201# 7730# 1# B- -9119# 362# 169 996024# 216# + 10 90 80 170 Hg -a 5415# 302# 7671# 2# B- * 170 005814# 324# +0 43 107 64 171 Gd x -36210# 500# 7990# 3# B- 7560# 640# 170 961127# 537# + 41 106 65 171 Tb x -43770# 400# 8030# 2# B- 6240# 447# 170 953011# 429# + 39 105 66 171 Dy x -50010# 200# 8062# 1# B- 4508# 633# 170 946312# 215# + 37 104 67 171 Ho + -54517.822 600.002 8083.6021 3.5088 B- 3200.0000 600.0000 170 941472.713 644.128 + 35 103 68 171 Er -57717.822 1.408 8097.7404 0.0082 B- 1492.4490 1.0788 170 938037.372 1.511 + 33 102 69 171 Tm -59210.271 0.972 8101.8931 0.0057 B- 96.5468 0.9715 170 936435.162 1.043 + 31 101 70 171 Yb -59306.818 0.013 8097.8826 0.0003 B- -1478.3526 1.8621 170 936331.515 0.013 + 29 100 71 171 Lu -57828.465 1.862 8084.6621 0.0109 B- -2397.1144 28.9363 170 937918.591 1.999 + 27 99 72 171 Hf x -55431.351 28.876 8066.0687 0.1689 B- -3711.0725 40.1840 170 940492.000 31.000 + 25 98 73 171 Ta x -51720.279 27.945 8039.7914 0.1634 B- -4634.1832 39.5199 170 944476.000 30.000 + 23 97 74 171 W x -47086.095 27.945 8008.1158 0.1634 B- -5835.8106 39.5199 170 949451.000 30.000 + 21 96 75 171 Re x -41250.285 27.945 7969.4131 0.1634 B- -6953.0469 33.3748 170 955716.000 30.000 + 19 95 76 171 Os -34297.238 18.247 7924.1769 0.1067 B- -7885.2079 42.5749 170 963180.402 19.589 + 17 94 77 171 Ir -a -26412.030 38.466 7873.4895 0.2249 B- -8945.4613 89.6251 170 971645.520 41.295 + 15 93 78 171 Pt -a -17466.569 80.951 7816.6017 0.4734 B- -9904.2655 83.5586 170 981248.868 86.904 + 13 92 79 171 Au -p -7562.303 20.713 7754.1069 0.1211 B- -10901# 307# 170 991881.533 22.236 + 11 91 80 171 Hg -a 3339# 307# 7686# 2# B- * 171 003585# 329# +0 44 108 64 172 Gd x -32970# 300# 7972# 2# B- 6720# 583# 171 964605# 322# + 42 107 65 172 Tb x -39690# 500# 8006# 3# B- 8070# 583# 171 957391# 537# + 40 106 66 172 Dy x -47760# 300# 8049# 2# B- 3724# 358# 171 948728# 322# + 38 105 67 172 Ho x -51484# 196# 8066# 1# B- 4999# 196# 171 944730# 210# + 36 104 68 172 Er -56482.578 3.962 8090.4052 0.0230 B- 890.9756 4.5418 171 939363.461 4.253 + 34 103 69 172 Tm -57373.554 5.481 8091.0367 0.0319 B- 1881.9024 5.4814 171 938406.959 5.884 + 32 102 70 172 Yb -59255.456 0.014 8097.4295 0.0003 B- -2519.3805 2.3360 171 936386.654 0.014 + 30 101 71 172 Lu -56736.076 2.336 8078.2334 0.0136 B- -333.8443 24.5396 171 939091.320 2.507 + 28 100 72 172 Hf x -56402.232 24.428 8071.7439 0.1420 B- -5072.2490 37.1167 171 939449.716 26.224 + 26 99 73 172 Ta x -51329.983 27.945 8037.7056 0.1625 B- -2232.7914 39.5199 171 944895.000 30.000 + 24 98 74 172 W x -49097.191 27.945 8020.1757 0.1625 B- -7530.3525 45.2323 171 947292.000 30.000 + 22 97 75 172 Re -41566.839 35.568 7971.8460 0.2068 B- -4323.1979 37.7890 171 955376.165 38.183 + 20 96 76 172 Os -37243.641 12.766 7942.1626 0.0742 B- -9864.2673 34.8263 171 960017.309 13.704 + 18 95 77 172 Ir -a -27379.373 32.402 7880.2637 0.1884 B- -6272.7035 34.0232 171 970607.035 34.785 + 16 94 78 172 Pt -21106.670 10.376 7839.2460 0.0603 B- -11788.6592 57.1082 171 977341.059 11.139 + 14 93 79 172 Au -a -9318.011 56.158 7766.1587 0.3265 B- -8256.6495 140.8350 171 989996.704 60.287 + 12 92 80 172 Hg -a -1061.361 150.062 7713.6064 0.8725 B- * 171 998860.581 161.098 +0 43 108 65 173 Tb x -36510# 500# 7988# 3# B- 7230# 640# 172 960805# 537# + 41 107 66 173 Dy x -43740# 400# 8026# 2# B- 5610# 499# 172 953043# 429# + 39 106 67 173 Ho x -49351# 298# 8054# 2# B- 4304# 357# 172 947020# 320# + 37 105 68 173 Er x -53654# 196# 8074# 1# B- 2602# 196# 172 942400# 210# + 35 104 69 173 Tm p2n -56256.067 4.400 8084.4633 0.0254 B- 1295.1669 4.4000 172 939606.630 4.723 + 33 103 70 173 Yb -57551.234 0.011 8087.4276 0.0003 B- -670.2201 1.5674 172 938216.211 0.012 + 31 102 71 173 Lu -56881.014 1.567 8079.0312 0.0091 B- -1469.2244 27.9887 172 938935.722 1.682 + 29 101 72 173 Hf x -55411.790 27.945 8066.0164 0.1615 B- -3015.2464 39.5199 172 940513.000 30.000 + 27 100 73 173 Ta x -52396.543 27.945 8044.0650 0.1615 B- -3669.1553 39.5199 172 943750.000 30.000 + 25 99 74 173 W x -48727.388 27.945 8018.3337 0.1615 B- -5173.5182 39.5199 172 947689.000 30.000 + 23 98 75 173 Re x -43553.870 27.945 7983.9068 0.1615 B- -6115.6196 31.6968 172 953243.000 30.000 + 21 97 76 173 Os -37438.250 14.959 7944.0341 0.0865 B- -7169.7944 18.2998 172 959808.387 16.059 + 19 96 77 173 Ir -30268.456 10.542 7898.0680 0.0609 B- -8331.6974 64.3014 172 967505.477 11.316 + 17 95 78 173 Pt -a -21936.758 63.431 7845.3856 0.3667 B- -9104.7415 67.3903 172 976449.922 68.096 + 15 94 79 173 Au +a -12832.017 22.783 7788.2348 0.1317 B- -10171# 202# 172 986224.263 24.458 + 13 93 80 173 Hg -a -2661# 201# 7725# 1# B- * 172 997143# 215# +0 44 109 65 174 Tb x -31970# 500# 7963# 3# B- 9160# 707# 173 965679# 537# + 42 108 66 174 Dy x -41130# 500# 8011# 3# B- 4739# 583# 173 955845# 537# + 40 107 67 174 Ho x -45870# 300# 8034# 2# B- 6080# 423# 173 950757# 322# + 38 106 68 174 Er x -51949# 298# 8064# 2# B- 1915# 301# 173 944230# 320# + 36 105 69 174 Tm + -53864.521 44.721 8070.6432 0.2570 B- 3080.0000 44.7214 173 942174.061 48.010 + 34 104 70 174 Yb -56944.521 0.011 8083.8481 0.0003 B- -1374.2287 1.5675 173 938867.545 0.011 + 32 103 71 174 Lu -55570.292 1.567 8071.4540 0.0090 B- 274.2911 2.1686 173 940342.840 1.682 + 30 102 72 174 Hf -55844.583 2.259 8068.5341 0.0130 B- -4103.8117 28.0360 173 940048.377 2.425 + 28 101 73 174 Ta x -51740.771 27.945 8040.4528 0.1606 B- -1513.6779 39.5199 173 944454.000 30.000 + 26 100 74 174 W x -50227.093 27.945 8027.2572 0.1606 B- -6553.9925 39.5199 173 946079.000 30.000 + 24 99 75 174 Re x -43673.101 27.945 7985.0944 0.1606 B- -3677.7177 29.7667 173 953115.000 30.000 + 22 98 76 174 Os -39995.383 10.254 7959.4618 0.0589 B- -9209.4466 15.2008 173 957063.192 11.008 + 20 97 77 174 Ir +a -30785.937 11.221 7902.0377 0.0645 B- -5468.3293 15.2578 173 966949.939 12.046 + 18 96 78 174 Pt -a -25317.608 10.338 7866.1143 0.0594 B- -11259# 102# 173 972820.431 11.098 + 16 95 79 174 Au -a -14058# 102# 7797# 1# B- -7417# 102# 173 984908# 109# + 14 94 80 174 Hg -a -6641.017 19.211 7749.7851 0.1104 B- * 173 992870.575 20.623 +0 43 109 66 175 Dy x -36730# 500# 7986# 3# B- 6570# 640# 174 960569# 537# + 41 108 67 175 Ho x -43300# 400# 8019# 2# B- 5352# 566# 174 953516# 429# + 39 107 68 175 Er x -48652# 401# 8045# 2# B- 3659# 404# 174 947770# 430# + 37 106 69 175 Tm + -52310.556 50.000 8061.7673 0.2857 B- 2385.0000 50.0000 174 943842.310 53.677 + 35 105 70 175 Yb -54695.556 0.071 8070.9253 0.0005 B- 470.1219 1.2068 174 941281.907 0.076 + 33 104 71 175 Lu -55165.678 1.207 8069.1412 0.0069 B- -683.9154 1.9516 174 940777.211 1.295 + 31 103 72 175 Hf -54481.763 2.283 8060.7625 0.0130 B- -2073.1103 28.0379 174 941511.424 2.450 + 29 102 73 175 Ta x -52408.653 27.945 8044.4456 0.1597 B- -2775.8524 39.5199 174 943737.000 30.000 + 27 101 74 175 W x -49632.800 27.945 8024.1130 0.1597 B- -4344.4885 39.5199 174 946717.000 30.000 + 25 100 75 175 Re x -45288.312 27.945 7994.8168 0.1597 B- -5182.9513 30.3243 174 951381.000 30.000 + 23 99 76 175 Os -40105.360 11.775 7960.7294 0.0673 B- -6710.8496 17.0887 174 956945.126 12.640 + 21 98 77 175 Ir -33394.511 12.384 7917.9112 0.0708 B- -7685.8265 22.3572 174 964149.519 13.295 + 19 97 78 175 Pt -25708.684 18.614 7869.5216 0.1064 B- -8304.9980 42.8203 174 972400.593 19.982 + 17 96 79 175 Au -a -17403.686 38.563 7817.5939 0.2204 B- -9434.2436 89.7876 174 981316.375 41.399 + 15 95 80 175 Hg -a -7969.443 81.085 7759.2134 0.4633 B- * 174 991444.451 87.047 +0 44 110 66 176 Dy x -33610# 500# 7969# 3# B- 5780# 707# 175 963918# 537# + 42 109 67 176 Ho x -39390# 500# 7997# 3# B- 7241# 641# 175 957713# 537# + 40 108 68 176 Er x -46631# 401# 8034# 2# B- 2741# 413# 175 949940# 430# + 38 107 69 176 Tm + -49371.322 100.000 8045.1214 0.5682 B- 4120.0000 100.0000 175 946997.707 107.354 + 36 106 70 176 Yb -53491.322 0.014 8064.0853 0.0003 B- -108.9895 1.2124 175 942574.706 0.015 + 34 105 71 176 Lu -53382.333 1.212 8059.0209 0.0069 B- 1194.0947 0.8744 175 942691.711 1.301 + 32 104 72 176 Hf -54576.428 1.482 8061.3604 0.0084 B- -3211.0484 30.7750 175 941409.797 1.591 + 30 103 73 176 Ta x -51365.379 30.739 8038.6706 0.1747 B- -723.7709 41.5430 175 944857.000 33.000 + 28 102 74 176 W x -50641.608 27.945 8030.1131 0.1588 B- -5578.7182 39.5199 175 945634.000 30.000 + 26 101 75 176 Re x -45062.890 27.945 7993.9707 0.1588 B- -2931.7058 30.0134 175 951623.000 30.000 + 24 100 76 176 Os -42131.184 10.949 7972.8681 0.0622 B- -8249.2618 13.6110 175 954770.315 11.754 + 22 99 77 176 Ir -33881.923 8.085 7921.5522 0.0459 B- -4948.0041 15.0657 175 963626.261 8.679 + 20 98 78 176 Pt -28933.918 12.712 7888.9934 0.0722 B- -10412.9516 35.5363 175 968938.162 13.647 + 18 97 79 176 Au -a -18520.967 33.185 7825.3837 0.1885 B- -6736.3283 34.9978 175 980116.925 35.625 + 16 96 80 176 Hg -11784.639 11.118 7782.6640 0.0632 B- -12369.3668 83.7993 175 987348.670 11.936 + 14 95 81 176 Tl -p 584.728 83.058 7707.9383 0.4719 B- * 176 000627.731 89.166 +0 43 110 67 177 Ho x -36280# 500# 7980# 3# B- 6578# 709# 176 961052# 537# + 41 109 68 177 Er x -42858# 503# 8013# 3# B- 4711# 541# 176 953990# 540# + 39 108 69 177 Tm x -47570# 200# 8035# 1# B- 3417# 200# 176 948932# 215# + 37 107 70 177 Yb -n -50986.404 0.220 8049.9741 0.0013 B- 1397.4983 1.2406 176 945263.846 0.236 + 35 106 71 177 Lu -52383.903 1.221 8053.4495 0.0069 B- 496.8425 0.7921 176 943763.570 1.310 + 33 105 72 177 Hf -52880.745 1.410 8051.8365 0.0080 B- -1166.0000 3.0000 176 943230.187 1.514 + 31 104 73 177 Ta - -51714.745 3.315 8040.8289 0.0187 B- -2013.0144 28.1408 176 944481.940 3.558 + 29 103 74 177 W x -49701.731 27.945 8025.0359 0.1579 B- -3432.5558 39.5199 176 946643.000 30.000 + 27 102 75 177 Re x -46269.175 27.945 8001.2229 0.1579 B- -4312.7271 31.5349 176 950328.000 30.000 + 25 101 76 177 Os +a -41956.448 14.613 7972.4371 0.0826 B- -5909.0234 24.5763 176 954957.902 15.687 + 23 100 77 177 Ir x -36047.425 19.760 7934.6328 0.1116 B- -6676.9880 24.8010 176 961301.500 21.213 + 21 99 78 177 Pt -29370.437 14.988 7892.4896 0.0847 B- -7824.7003 17.9991 176 968469.541 16.090 + 19 98 79 177 Au -21545.736 9.968 7843.8623 0.0563 B- -8769.9135 85.3061 176 976869.701 10.700 + 17 97 80 177 Hg -a -12775.823 84.722 7789.8947 0.4787 B- -9435.7198 87.4322 176 986284.590 90.952 + 15 96 81 177 Tl IT -3340.103 21.628 7732.1655 0.1222 B- * 176 996414.252 23.218 +0 44 111 67 178 Ho x -32130# 500# 7957# 3# B- 8130# 778# 177 965507# 537# + 42 110 68 178 Er x -40260# 596# 7999# 3# B- 3980# 667# 177 956779# 640# + 40 109 69 178 Tm x -44240# 300# 8017# 2# B- 5437# 300# 177 952506# 322# + 38 108 70 178 Yb -49677.139 6.588 8042.7386 0.0370 B- 660.7415 6.9617 177 946669.400 7.072 + 36 107 71 178 Lu -50337.881 2.251 8042.0554 0.0127 B- 2097.4851 2.0569 177 945960.065 2.416 + 34 106 72 178 Hf -52435.366 1.415 8049.4438 0.0080 B- -1837# 52# 177 943708.322 1.519 + 32 105 73 178 Ta IT -50598# 52# 8035# 0# B- -191# 50# 177 945680# 56# + 30 104 74 178 W - -50407.066 15.199 8029.2584 0.0854 B- -4753.6082 31.8106 177 945885.791 16.316 + 28 103 75 178 Re x -45653.457 27.945 7998.1576 0.1570 B- -2109.2143 31.0925 177 950989.000 30.000 + 26 102 76 178 Os -43544.243 13.632 7981.9128 0.0766 B- -7289.9295 23.2390 177 953253.334 14.634 + 24 101 77 178 Ir -36254.314 18.821 7936.5630 0.1057 B- -4256.8282 21.3752 177 961079.395 20.204 + 22 100 78 178 Pt -31997.486 10.133 7908.2530 0.0569 B- -9694.4567 14.4108 177 965649.288 10.878 + 20 99 79 178 Au x -22303.029 10.246 7849.3946 0.0576 B- -5987.6832 14.8566 177 976056.714 11.000 + 18 98 80 178 Hg -a -16315.346 10.758 7811.3607 0.0604 B- -11702# 103# 177 982484.756 11.548 + 16 97 81 178 Tl -a -4613# 102# 7741# 1# B- -8187# 103# 177 995047# 110# + 14 96 82 178 Pb -a 3573.371 23.184 7690.8359 0.1302 B- * 178 003836.171 24.889 +0 43 111 68 179 Er x -36080# 500# 7976# 3# B- 5821# 640# 178 961267# 537# + 41 110 69 179 Tm x -41900# 400# 8004# 2# B- 4739# 447# 178 955018# 429# + 39 109 70 179 Yb x -46640# 200# 8026# 1# B- 2419# 200# 178 949930# 215# + 37 108 71 179 Lu -49059.013 5.150 8035.0744 0.0288 B- 1404.0231 5.0672 178 947332.985 5.528 + 35 107 72 179 Hf -50463.036 1.416 8038.5474 0.0079 B- -105.5801 0.4088 178 945825.705 1.520 + 33 106 73 179 Ta -50357.456 1.466 8033.5869 0.0082 B- -1062.2093 14.5197 178 945939.050 1.574 + 31 105 74 179 W -49295.247 14.573 8023.2821 0.0814 B- -2710.9347 26.8021 178 947079.378 15.644 + 29 104 75 179 Re -46584.312 24.639 8003.7666 0.1376 B- -3564.1747 29.1116 178 949989.686 26.450 + 27 103 76 179 Os -43020.137 15.505 7979.4844 0.0866 B- -4938.4182 18.3271 178 953815.985 16.645 + 25 102 77 179 Ir -38081.719 9.771 7947.5248 0.0546 B- -5813.5920 12.6133 178 959117.594 10.489 + 23 101 78 179 Pt -32268.127 7.977 7910.6759 0.0446 B- -7279.5556 14.1567 178 965358.742 8.563 + 21 100 79 179 Au -24988.572 11.696 7865.6374 0.0653 B- -8055.6480 30.4559 178 973173.666 12.555 + 19 99 80 179 Hg -16932.924 28.121 7816.2631 0.1571 B- -8663.2918 47.7998 178 981821.759 30.188 + 17 98 81 179 Tl -a -8269.632 38.653 7763.4942 0.2159 B- -10321.2401 89.9571 178 991122.185 41.495 + 15 97 82 179 Pb -a 2051.608 81.229 7701.4630 0.4538 B- * 179 002202.492 87.203 +0 44 112 68 180 Er x -33180# 500# 7960# 3# B- 4990# 640# 179 964380# 537# + 42 111 69 180 Tm x -38170# 400# 7983# 2# B- 6550# 500# 179 959023# 429# + 40 110 70 180 Yb x -44720# 300# 8016# 2# B- 1956# 308# 179 951991# 322# + 38 109 71 180 Lu + -46676.476 70.725 8022.0394 0.3929 B- 3103.0000 70.7107 179 949890.744 75.926 + 36 108 72 180 Hf -49779.476 1.421 8034.9319 0.0079 B- -845.8453 2.3472 179 946559.537 1.525 + 34 107 73 180 Ta +n -48933.631 2.068 8025.8864 0.0115 B- 702.6122 2.3590 179 947467.589 2.219 + 32 106 74 180 W -49636.243 1.439 8025.4434 0.0080 B- -3798.8793 21.4404 179 946713.304 1.545 + 30 105 75 180 Re x -45837.364 21.392 7999.9922 0.1188 B- -1481.1659 26.5482 179 950791.568 22.965 + 28 104 76 180 Os -44356.198 15.722 7987.4171 0.0873 B- -6378.6679 26.8021 179 952381.665 16.878 + 26 103 77 180 Ir x -37977.530 21.706 7947.6337 0.1206 B- -3547.6546 23.9205 179 959229.446 23.302 + 24 102 78 180 Pt -34429.875 10.051 7923.5781 0.0558 B- -8804.2290 11.1207 179 963038.010 10.790 + 22 101 79 180 Au -25625.646 4.759 7870.3194 0.0264 B- -5375.1330 13.5105 179 972489.738 5.108 + 20 100 80 180 Hg -20250.513 12.645 7836.1111 0.0702 B- -10860.0750 71.0509 179 978260.180 13.574 + 18 99 81 180 Tl -a -9390.438 69.917 7771.4310 0.3884 B- -7449.3696 71.0069 179 989918.950 75.058 + 16 98 82 180 Pb -a -1941.069 12.395 7725.6993 0.0689 B- * 179 997916.177 13.306 +0 43 112 69 181 Tm x -35440# 500# 7969# 3# B- 5649# 582# 180 961954# 537# + 41 111 70 181 Yb x -41088# 298# 7996# 2# B- 3709# 324# 180 955890# 320# + 39 110 71 181 Lu x -44797.414 125.752 8011.9301 0.6948 B- 2605.5435 125.7598 180 951908.000 135.000 + 37 109 72 181 Hf -n -47402.958 1.423 8022.0030 0.0079 B- 1036.1061 1.9298 180 949110.834 1.527 + 35 108 73 181 Ta -48439.064 1.576 8023.4050 0.0087 B- -205.1193 1.9495 180 947998.528 1.692 + 33 107 74 181 W -n -48233.945 1.448 8017.9494 0.0080 B- -1716.5331 12.6289 180 948218.733 1.554 + 31 106 75 181 Re 4n -46517.412 12.549 8004.1434 0.0693 B- -2967.4438 28.2755 180 950061.507 13.471 + 29 105 76 181 Os -43549.968 25.338 7983.4263 0.1400 B- -4086.9327 25.8756 180 953247.188 27.201 + 27 104 77 181 Ir +a -39463.035 5.245 7956.5242 0.0290 B- -5081.5379 14.6597 180 957634.691 5.631 + 25 103 78 181 Pt -34381.497 13.689 7924.1271 0.0756 B- -6510.3575 24.2164 180 963089.946 14.695 + 23 102 79 181 Au -a -27871.140 19.976 7883.8359 0.1104 B- -7210.0126 25.2123 180 970079.102 21.445 + 21 101 80 181 Hg -20661.127 15.382 7839.6792 0.0850 B- -7862.3780 17.8730 180 977819.368 16.513 + 19 100 81 181 Tl -12798.749 9.102 7791.9183 0.0503 B- -9688.1182 85.5227 180 986259.978 9.771 + 17 99 82 181 Pb -a -3110.631 85.037 7734.0704 0.4698 B- * 180 996660.600 91.290 +0 44 113 69 182 Tm x -31490# 500# 7948# 3# B- 7410# 640# 181 966194# 537# + 42 112 70 182 Yb x -38900# 400# 7984# 2# B- 2870# 447# 181 958239# 429# + 40 111 71 182 Lu x -41770# 200# 7996# 1# B- 4280# 200# 181 955158# 215# + 38 110 72 182 Hf -nn -46049.636 6.166 8014.8381 0.0339 B- 381.0486 6.3027 181 950563.684 6.619 + 36 109 73 182 Ta -46430.685 1.578 8012.6332 0.0087 B- 1815.4592 1.5276 181 950154.612 1.693 + 34 108 74 182 W -48246.144 0.745 8018.3096 0.0041 B- -2800.0000 101.9804 181 948205.636 0.799 + 32 107 75 182 Re IT -45446.144 101.983 7998.6264 0.5603 B- -837.0348 104.2757 181 951211.560 109.483 + 30 106 76 182 Os -44609.109 21.745 7989.7287 0.1195 B- -5557.4266 30.2074 181 952110.154 23.344 + 28 105 77 182 Ir -39051.682 20.967 7954.8948 0.1152 B- -2883.2620 24.7202 181 958076.296 22.509 + 26 104 78 182 Pt -36168.420 13.095 7934.7541 0.0719 B- -7864.4447 22.8812 181 961171.605 14.057 + 24 103 79 182 Au -28303.976 18.764 7887.2442 0.1031 B- -4727.0905 21.1645 181 969614.433 20.143 + 22 102 80 182 Hg -23576.885 9.790 7856.9726 0.0538 B- -10249.6721 15.4687 181 974689.173 10.510 + 20 101 81 182 Tl -a -13327.213 11.976 7796.3571 0.0658 B- -6502.6567 17.0151 181 985692.649 12.856 + 18 100 82 182 Pb -a -6824.556 12.086 7756.3296 0.0664 B- * 181 992673.537 12.975 +0 43 113 70 183 Yb x -35000# 400# 7963# 2# B- 4716# 408# 182 962426# 429# + 41 112 71 183 Lu x -39716.114 80.108 7984.8125 0.4378 B- 3567.4325 85.5564 182 957363.000 86.000 + 39 111 72 183 Hf + -43283.547 30.042 8000.0315 0.1642 B- 2010.0000 30.0000 182 953533.203 32.251 + 37 110 73 183 Ta -n -45293.547 1.590 8006.7400 0.0087 B- 1072.1161 1.5405 182 951375.380 1.707 + 35 109 74 183 W -46365.663 0.743 8008.3234 0.0041 B- -556.0000 8.0000 182 950224.416 0.798 + 33 108 75 183 Re - -45809.663 8.034 8001.0101 0.0439 B- -2145.9028 50.4129 182 950821.306 8.625 + 31 107 76 183 Os -43663.760 49.769 7985.0087 0.2720 B- -3461.6215 52.8061 182 953125.028 53.428 + 29 106 77 183 Ir -40202.138 24.672 7961.8176 0.1348 B- -4428.9417 28.4743 182 956841.231 26.486 + 27 105 78 183 Pt -35773.197 14.216 7933.3406 0.0777 B- -5581.7092 17.0554 182 961595.895 15.261 + 25 104 79 183 Au -30191.488 9.423 7898.5644 0.0515 B- -6386.8322 11.7891 182 967588.106 10.116 + 23 103 80 183 Hg -23804.655 7.084 7859.3885 0.0387 B- -7217.3941 11.7158 182 974444.652 7.604 + 21 102 81 183 Tl -16587.261 9.331 7815.6741 0.0510 B- -9007.2534 30.4442 182 982192.843 10.017 + 19 101 82 183 Pb -a -7580.008 28.979 7762.1790 0.1584 B- * 182 991862.527 31.110 +0 44 114 70 184 Yb x -32600# 503# 7951# 3# B- 3700# 541# 183 965002# 540# + 42 113 71 184 Lu x -36300# 200# 7967# 1# B- 5199# 204# 183 961030# 215# + 40 112 72 184 Hf + -41499.453 39.706 7990.7228 0.2158 B- 1340.0000 30.0000 183 955448.507 42.625 + 38 111 73 184 Ta + -42839.453 26.010 7993.7535 0.1414 B- 2866.0000 26.0000 183 954009.958 27.923 + 36 110 74 184 W -45705.453 0.738 8005.0777 0.0040 B- -1485.6333 4.1971 183 950933.180 0.792 + 34 109 75 184 Re -44219.819 4.276 7992.7517 0.0232 B- 32.7460 4.1387 183 952528.073 4.590 + 32 108 76 184 Os -44252.565 0.829 7988.6778 0.0045 B- -4641.7101 27.9571 183 952492.919 0.890 + 30 107 77 184 Ir x -39610.855 27.945 7959.1992 0.1519 B- -2278.3688 31.5957 183 957476.000 30.000 + 28 106 78 184 Pt -37332.486 14.744 7942.5649 0.0801 B- -7013.7724 26.7122 183 959921.929 15.828 + 26 105 79 184 Au -a -30318.714 22.275 7900.1947 0.1211 B- -3973.9269 24.2296 183 967451.523 23.912 + 24 104 80 184 Hg -26344.787 9.535 7874.3454 0.0518 B- -9461.4321 13.8254 183 971717.709 10.235 + 22 103 81 184 Tl -16883.355 10.012 7818.6727 0.0544 B- -5831.7688 16.2517 183 981874.973 10.747 + 20 102 82 184 Pb -11051.586 12.802 7782.7264 0.0696 B- -12306# 123# 183 988135.634 13.743 + 18 101 83 184 Bi -a 1254# 122# 7712# 1# B- * 184 001347# 131# +0 45 115 70 185 Yb x -28480# 500# 7929# 3# B- 5480# 583# 184 969425# 537# + 43 114 71 185 Lu x -33960# 300# 7955# 2# B- 4359# 307# 184 963542# 322# + 41 113 72 185 Hf x -38319.804 64.273 7973.9711 0.3474 B- 3074.5667 65.8147 184 958862.000 69.000 + 39 112 73 185 Ta + -41394.371 14.161 7986.3615 0.0765 B- 1993.5000 14.1421 184 955561.317 15.202 + 37 111 74 185 W -43387.871 0.739 7992.9083 0.0040 B- 431.1764 0.6616 184 953421.206 0.793 + 35 110 75 185 Re -43819.047 0.820 7991.0101 0.0044 B- -1013.1393 0.4190 184 952958.320 0.879 + 33 109 76 185 Os -42805.908 0.832 7981.3047 0.0045 B- -2470.3505 27.9572 184 954045.969 0.893 + 31 108 77 185 Ir x -40335.558 27.945 7963.7226 0.1511 B- -3647.4137 38.0554 184 956698.000 30.000 + 29 107 78 185 Pt -36688.144 25.832 7939.7779 0.1396 B- -4829.9942 25.9635 184 960613.659 27.731 + 27 106 79 185 Au x -31858.150 2.608 7909.4410 0.0141 B- -5674.4989 13.8859 184 965798.871 2.800 + 25 105 80 185 Hg -26183.651 13.639 7874.5391 0.0737 B- -6425.9062 24.7674 184 971890.696 14.641 + 23 104 81 185 Tl IT -19757.745 20.674 7835.5756 0.1118 B- -8216.5333 26.2493 184 978789.189 22.194 + 21 103 82 185 Pb -a -11541.211 16.175 7786.9330 0.0874 B- -9305# 83# 184 987610.000 17.364 + 19 102 83 185 Bi IT -2236# 81# 7732# 0# B- * 184 997600# 87# +0 44 115 71 186 Lu x -30320# 400# 7936# 2# B- 6104# 403# 185 967450# 429# + 42 114 72 186 Hf x -36424.214 51.232 7964.3032 0.2754 B- 2183.3883 78.9063 185 960897.000 55.000 + 40 113 73 186 Ta + -38607.602 60.012 7971.8357 0.3226 B- 3901.0000 60.0000 185 958553.036 64.425 + 38 112 74 186 W -42508.602 1.213 7988.6026 0.0065 B- -581.2819 1.2386 185 954365.140 1.302 + 36 111 75 186 Re -41927.320 0.820 7981.2713 0.0044 B- 1072.7114 0.8337 185 954989.172 0.880 + 34 110 76 186 Os -43000.032 0.761 7982.8324 0.0041 B- -3827.6813 16.5430 185 953837.569 0.816 + 32 109 77 186 Ir x -39172.350 16.526 7958.0473 0.0888 B- -1307.9030 27.3122 185 957946.754 17.740 + 30 108 78 186 Pt -37864.447 21.745 7946.8094 0.1169 B- -6149.5913 30.2074 185 959350.845 23.344 + 28 107 79 186 Au -31714.856 20.967 7909.5409 0.1127 B- -3175.7972 23.9866 185 965952.703 22.509 + 26 106 80 186 Hg -28539.059 11.650 7888.2605 0.0626 B- -8656.1190 23.7974 185 969362.061 12.507 + 24 105 81 186 Tl -19882.940 20.751 7837.5161 0.1116 B- -5202.0427 23.4877 185 978654.787 22.276 + 22 104 82 186 Pb -a -14680.897 11.004 7805.3420 0.0592 B- -11535.3999 20.2118 185 984239.409 11.813 + 20 103 83 186 Bi -a -3145.497 16.954 7739.1175 0.0911 B- -7247.0279 24.9305 185 996623.169 18.200 + 18 102 84 186 Po -a 4101.531 18.278 7695.9488 0.0983 B- * 186 004403.174 19.622 +0 45 116 71 187 Lu x -27770# 400# 7923# 2# B- 5230# 447# 186 970188# 429# + 43 115 72 187 Hf x -33000# 200# 7947# 1# B- 3896# 208# 186 964573# 215# + 41 114 73 187 Ta x -36895.550 55.890 7963.2123 0.2989 B- 3008.4937 55.9028 186 960391.000 60.000 + 39 113 74 187 W -39904.044 1.213 7975.1168 0.0065 B- 1312.5048 1.1219 186 957161.249 1.302 + 37 112 75 187 Re -41216.548 0.737 7977.9519 0.0039 B- 2.4667 0.0016 186 955752.217 0.791 + 35 111 76 187 Os -41219.015 0.737 7973.7814 0.0039 B- -1669.6385 27.9545 186 955749.569 0.791 + 33 110 77 187 Ir x -39549.377 27.945 7960.6692 0.1494 B- -2864.0151 36.8802 186 957542.000 30.000 + 31 109 78 187 Pt -36685.361 24.067 7941.1699 0.1287 B- -3656.5811 27.4478 186 960616.646 25.837 + 29 108 79 187 Au -33028.780 22.499 7917.4323 0.1203 B- -4910.2713 25.9171 186 964542.147 24.153 + 27 107 80 187 Hg -28118.509 12.864 7886.9905 0.0688 B- -5673.9170 15.1746 186 969813.540 13.810 + 25 106 81 187 Tl -22444.592 8.048 7852.4650 0.0430 B- -7457.6370 9.5248 186 975904.740 8.640 + 23 105 82 187 Pb -14986.955 5.094 7808.4010 0.0272 B- -8603.6798 11.2268 186 983910.842 5.468 + 21 104 83 187 Bi -a -6383.275 10.005 7758.2083 0.0535 B- -9207.0832 34.1302 186 993147.272 10.740 + 19 103 84 187 Po -a 2823.808 32.631 7704.7889 0.1745 B- * 187 003031.482 35.030 +0 46 117 71 188 Lu x -23820# 400# 7903# 2# B- 7009# 500# 187 974428# 429# + 44 116 72 188 Hf x -30830# 300# 7936# 2# B- 3080# 361# 187 966903# 322# + 42 115 73 188 Ta x -33910# 200# 7948# 1# B- 4758# 200# 187 963596# 215# + 40 114 74 188 W + -38667.880 3.089 7969.0532 0.0164 B- 349.0000 3.0000 187 958488.325 3.316 + 38 113 75 188 Re -n -39016.880 0.738 7966.7481 0.0039 B- 2120.4209 0.1520 187 958113.658 0.792 + 36 112 76 188 Os -41137.301 0.734 7973.8656 0.0039 B- -2792.3457 9.4164 187 955837.292 0.788 + 34 111 77 188 Ir -38344.955 9.423 7954.8512 0.0501 B- -523.9860 8.6863 187 958834.999 10.116 + 32 110 78 188 Pt -37820.970 5.305 7947.9027 0.0282 B- -5449.6549 5.9528 187 959397.521 5.694 + 30 109 79 188 Au x -32371.315 2.701 7914.7537 0.0144 B- -2172.9634 7.3046 187 965247.966 2.900 + 28 108 80 188 Hg -30198.351 6.787 7899.0340 0.0361 B- -7861.9485 30.6643 187 967580.738 7.285 + 26 107 81 188 Tl x -22336.403 29.904 7853.0537 0.1591 B- -4525.3784 31.5712 187 976020.886 32.103 + 24 106 82 188 Pb -a -17811.024 10.124 7824.8211 0.0539 B- -10616.2241 15.0824 187 980879.079 10.868 + 22 105 83 188 Bi -a -7194.800 11.179 7764.1904 0.0595 B- -6650.4226 22.8861 187 992276.064 12.001 + 20 104 84 188 Po -a -544.378 19.970 7724.6544 0.1062 B- * 187 999415.586 21.438 +0 45 117 72 189 Hf x -27150# 300# 7917# 2# B- 4809# 361# 188 970853# 322# + 43 116 73 189 Ta x -31960# 200# 7938# 1# B- 3850# 283# 188 965690# 215# + 41 115 74 189 W + -35809# 200# 7954# 1# B- 2170# 200# 188 961557# 215# + 39 114 75 189 Re +p -37979.097 8.191 7961.8105 0.0433 B- 1007.7049 8.1671 188 959227.764 8.793 + 37 113 76 189 Os -38986.802 0.666 7963.0029 0.0035 B- -537.1494 12.5630 188 958145.949 0.715 + 35 112 77 189 Ir -38449.652 12.576 7956.0214 0.0665 B- -1980.2470 13.6363 188 958722.602 13.500 + 33 111 78 189 Pt -36469.405 10.090 7941.4045 0.0534 B- -2887.4471 22.4737 188 960848.485 10.832 + 31 110 79 189 Au x -33581.958 20.081 7921.9876 0.1063 B- -3955.5800 37.4009 188 963948.286 21.558 + 29 109 80 189 Hg -29626.378 31.553 7896.9192 0.1669 B- -5010.2727 32.6434 188 968194.776 33.873 + 27 108 81 189 Tl -24616.105 8.368 7866.2704 0.0443 B- -6772.0862 16.3636 188 973573.525 8.983 + 25 107 82 189 Pb -17844.019 14.062 7826.2999 0.0744 B- -7779.3555 25.1498 188 980843.658 15.096 + 23 106 83 189 Bi -a -10064.664 20.851 7780.9999 0.1103 B- -8642.6682 30.3542 188 989195.139 22.384 + 21 105 84 189 Po -a -1421.996 22.059 7731.1321 0.1167 B- * 188 998473.425 23.681 +0 46 118 72 190 Hf x -24800# 400# 7905# 2# B- 3920# 447# 189 973376# 429# + 44 117 73 190 Ta x -28720# 200# 7922# 1# B- 5649# 203# 189 969168# 215# + 42 116 74 190 W -34368.832 35.391 7947.5031 0.1863 B- 1214.1824 35.5545 189 963103.542 37.993 + 40 115 75 190 Re -35583.015 4.870 7949.7759 0.0256 B- 3124.8105 4.7864 189 961800.064 5.227 + 38 114 76 190 Os -38707.825 0.650 7962.1047 0.0034 B- -1954.2108 1.2131 189 958445.442 0.697 + 36 113 77 190 Ir +n -36753.614 1.370 7947.7017 0.0072 B- 552.8893 1.2822 189 960543.374 1.470 + 34 112 78 190 Pt -37306.504 0.657 7946.4941 0.0035 B- -4472.9637 3.5086 189 959949.823 0.705 + 32 111 79 190 Au x -32833.540 3.447 7918.8345 0.0181 B- -1462.9150 16.2760 189 964751.746 3.700 + 30 110 80 190 Hg -31370.625 15.907 7907.0174 0.0837 B- -7004.3892 17.4819 189 966322.250 17.076 + 28 109 81 190 Tl +a -24366.236 7.252 7866.0345 0.0382 B- -3949.6288 14.4634 189 973841.771 7.784 + 26 108 82 190 Pb -a -20416.607 12.514 7841.1294 0.0659 B- -9820.7014 24.4226 189 978081.872 13.434 + 24 107 83 190 Bi -a -10595.906 20.973 7785.3239 0.1104 B- -6033.1975 24.7615 189 988624.828 22.515 + 22 106 84 190 Po -a -4562.708 13.163 7749.4526 0.0693 B- * 189 995101.731 14.131 +0 45 118 73 191 Ta x -26520# 300# 7911# 2# B- 4657# 303# 190 971530# 322# + 43 117 74 191 W x -31176.176 41.917 7931.4359 0.2195 B- 3174.2318 43.1556 190 966531.000 45.000 + 41 116 75 191 Re +p -34350.408 10.264 7943.9588 0.0537 B- 2044.8311 10.2443 190 963123.322 11.019 + 39 115 76 191 Os -36395.239 0.659 7950.5687 0.0035 B- 313.5873 1.1410 190 960928.105 0.707 + 37 114 77 191 Ir -36708.826 1.310 7948.1144 0.0069 B- -1010.4903 3.6360 190 960591.455 1.406 + 35 113 78 191 Pt -35698.336 4.127 7938.7279 0.0216 B- -1900.4257 6.4260 190 961676.261 4.430 + 33 112 79 191 Au -33797.910 4.926 7924.6819 0.0258 B- -3206.0616 22.7103 190 963716.452 5.288 + 31 111 80 191 Hg -30591.849 22.280 7903.8002 0.1167 B- -4308.8981 23.4609 190 967158.301 23.918 + 29 110 81 191 Tl +a -26282.951 7.349 7877.1445 0.0385 B- -5991.7073 9.8864 190 971784.093 7.889 + 27 109 82 191 Pb -20291.243 6.613 7841.6782 0.0346 B- -7051.8922 9.9895 190 978216.455 7.099 + 25 108 83 191 Bi -13239.351 7.487 7800.6613 0.0392 B- -8170.6205 10.3201 190 985786.972 8.037 + 23 107 84 191 Po -5068.731 7.103 7753.7871 0.0372 B- -8932.6440 17.5997 190 994558.494 7.624 + 21 106 85 191 At -a 3863.913 16.103 7702.9233 0.0843 B- * 191 004148.081 17.287 +0 46 119 73 192 Ta x -23100# 400# 7894# 2# B- 6520# 447# 191 975201# 429# + 44 118 74 192 W x -29620# 200# 7924# 1# B- 1969# 212# 191 968202# 215# + 42 117 75 192 Re x -31588.828 70.794 7930.2389 0.3687 B- 4293.4750 70.8314 191 966088.000 76.000 + 40 116 76 192 Os -35882.303 2.314 7948.5260 0.0121 B- -1046.6722 2.3962 191 961478.765 2.484 + 38 115 77 192 Ir -34835.631 1.314 7938.9999 0.0068 B- 1452.8946 2.2739 191 962602.414 1.410 + 36 114 78 192 Pt -36288.525 2.570 7942.4923 0.0134 B- -3516.3415 15.6174 191 961042.667 2.758 + 34 113 79 192 Au - -32772.184 15.827 7920.1033 0.0824 B- -760.7028 22.1777 191 964817.615 16.991 + 32 112 80 192 Hg x -32011.481 15.537 7912.0666 0.0809 B- -6139.2324 35.2767 191 965634.263 16.679 + 30 111 81 192 Tl x -25872.249 31.671 7876.0167 0.1650 B- -3320.4029 32.1843 191 972225.000 34.000 + 28 110 82 192 Pb -22551.846 5.726 7854.6482 0.0298 B- -9017.3089 30.6518 191 975789.598 6.147 + 26 109 83 192 Bi -a -13534.537 30.112 7803.6084 0.1568 B- -5468.0534 31.9348 191 985470.077 32.326 + 24 108 84 192 Po -a -8066.483 10.634 7771.0542 0.0554 B- -10992.2252 29.8328 191 991340.274 11.416 + 22 107 85 192 At -a 2925.742 27.873 7709.7283 0.1452 B- * 192 003140.912 29.922 +0 47 120 73 193 Ta x -20810# 400# 7883# 2# B- 5380# 447# 192 977660# 429# + 45 119 74 193 W x -26190# 200# 7907# 1# B- 4042# 204# 192 971884# 215# + 43 118 75 193 Re x -30231.641 39.123 7923.9378 0.2027 B- 3162.7597 39.1915 192 967545.000 42.000 + 41 117 76 193 Os -33394.401 2.320 7936.2716 0.0120 B- 1141.9038 2.4000 192 964149.637 2.490 + 39 116 77 193 Ir -34536.305 1.327 7938.1346 0.0069 B- -56.6276 0.2997 192 962923.753 1.425 + 37 115 78 193 Pt -34479.677 1.359 7933.7875 0.0070 B- -1074.8477 8.7676 192 962984.546 1.458 + 35 114 79 193 Au -33404.829 8.674 7924.1648 0.0449 B- -2342.6641 14.3702 192 964138.442 9.311 + 33 113 80 193 Hg -31062.165 15.505 7907.9730 0.0803 B- -3584.9466 16.8938 192 966653.395 16.645 + 31 112 81 193 Tl x -27477.218 6.707 7885.3445 0.0348 B- -5247.9637 12.2808 192 970501.994 7.200 + 29 111 82 193 Pb -22229.255 10.288 7854.0994 0.0533 B- -6344.6913 12.7761 192 976135.914 11.044 + 27 110 83 193 Bi -15884.563 7.576 7817.1718 0.0393 B- -7559.2614 16.3874 192 982947.220 8.132 + 25 109 84 193 Po -a -8325.302 14.531 7773.9510 0.0753 B- -8257.9789 26.0594 192 991062.421 15.599 + 23 108 85 193 At -a -67.323 21.632 7727.1099 0.1121 B- -9110.2435 33.1444 192 999927.725 23.222 + 21 107 86 193 Rn -a 9042.920 25.112 7675.8530 0.1301 B- * 193 009707.973 26.958 +0 48 121 73 194 Ta x -17130# 500# 7865# 3# B- 7280# 583# 193 981610# 537# + 46 120 74 194 W x -24410# 300# 7899# 2# B- 2850# 361# 193 973795# 322# + 44 119 75 194 Re x -27260# 200# 7909# 1# B- 5175# 200# 193 970735# 215# + 42 118 76 194 Os + -32435.176 2.403 7932.0232 0.0124 B- 96.6000 2.0000 193 965179.407 2.579 + 40 117 77 194 Ir -n -32531.776 1.332 7928.4885 0.0069 B- 2228.3252 1.2569 193 965075.703 1.429 + 38 116 78 194 Pt -34760.101 0.496 7935.9420 0.0026 B- -2548.1518 2.1174 193 962683.498 0.532 + 36 115 79 194 Au +3n -32211.950 2.118 7918.7744 0.0109 B- -27.9978 3.5809 193 965419.051 2.273 + 34 114 80 194 Hg x -32183.952 2.888 7914.5974 0.0149 B- -5246.4542 14.2677 193 965449.108 3.100 + 32 113 81 194 Tl x -26937.498 13.972 7883.5211 0.0720 B- -2729.6315 22.3433 193 971081.408 15.000 + 30 112 82 194 Pb -24207.866 17.435 7865.4181 0.0899 B- -8184.8462 18.2094 193 974011.788 18.717 + 28 111 83 194 Bi +a -16023.020 5.252 7819.1955 0.0271 B- -5018.4029 13.9386 193 982798.581 5.638 + 26 110 84 194 Po -a -11004.617 12.911 7789.2947 0.0666 B- -10288.1273 26.8153 193 988186.058 13.860 + 24 109 85 194 At -a -716.490 23.502 7732.2304 0.1211 B- -6441.1134 28.8079 193 999230.816 25.230 + 22 108 86 194 Rn -a 5724.624 16.659 7694.9961 0.0859 B- * 194 006145.636 17.884 +0 47 121 74 195 W x -20740# 300# 7881# 2# B- 4820# 424# 194 977735# 322# + 45 120 75 195 Re x -25560# 300# 7901# 2# B- 3951# 305# 194 972560# 322# + 43 119 76 195 Os x -29511.596 55.890 7917.7449 0.2866 B- 2180.7220 55.9055 194 968318.000 60.000 + 41 118 77 195 Ir -n -31692.318 1.333 7924.9160 0.0068 B- 1101.5601 1.2637 194 965976.898 1.431 + 39 117 78 195 Pt -32793.878 0.503 7926.5530 0.0026 B- -226.8175 0.9998 194 964794.325 0.540 + 37 116 79 195 Au -32567.061 1.119 7921.3778 0.0057 B- -1553.7190 23.1562 194 965037.823 1.201 + 35 115 80 195 Hg -31013.342 23.142 7909.3980 0.1187 B- -2858.0499 25.6707 194 966705.809 24.843 + 33 114 81 195 Tl -28155.292 11.126 7890.7293 0.0571 B- -4417.2524 12.2333 194 969774.052 11.944 + 31 113 82 195 Pb -23738.039 5.088 7864.0647 0.0261 B- -5712.4729 7.3370 194 974516.167 5.461 + 29 112 83 195 Bi -18025.567 5.287 7830.7579 0.0271 B- -6908.9121 8.0284 194 980648.759 5.675 + 27 111 84 195 Po -11116.655 6.042 7791.3155 0.0310 B- -7646.3554 11.3199 194 988065.781 6.486 + 25 110 85 195 At -a -3470.299 9.573 7748.0914 0.0491 B- -8520.5842 52.5650 194 996274.480 10.276 + 23 109 86 195 Rn -a 5050.285 51.686 7700.3841 0.2651 B- * 195 005421.703 55.487 +0 48 122 74 196 W x -18740# 400# 7872# 2# B- 3620# 500# 195 979882# 429# + 46 121 75 196 Re x -22360# 300# 7886# 2# B- 5918# 303# 195 975996# 322# + 44 120 76 196 Os +pp -28277.123 40.055 7912.2301 0.2044 B- 1158.3989 55.4951 195 969643.261 43.000 + 42 119 77 196 Ir + -29435.522 38.414 7914.1487 0.1960 B- 3209.0164 38.4111 195 968399.669 41.239 + 40 118 78 196 Pt -32644.538 0.510 7926.5297 0.0026 B- -1505.8204 2.9605 195 964954.648 0.547 + 38 117 79 196 Au -31138.718 2.962 7914.8553 0.0151 B- 687.2263 3.1176 195 966571.213 3.179 + 36 116 80 196 Hg -31825.944 2.946 7914.3700 0.0150 B- -4329.3455 12.4627 195 965833.445 3.163 + 34 115 81 196 Tl x -27496.598 12.109 7888.2900 0.0618 B- -2148.3639 14.3556 195 970481.189 13.000 + 32 114 82 196 Pb -25348.234 7.710 7873.3374 0.0393 B- -7339.2020 25.6160 195 972787.552 8.277 + 30 113 83 196 Bi x -18009.032 24.428 7831.9009 0.1246 B- -4540.3012 25.0142 195 980666.509 26.224 + 28 112 84 196 Po -13468.731 5.383 7804.7445 0.0275 B- -9555.5564 30.7105 195 985540.722 5.778 + 26 111 85 196 At -a -3913.175 30.235 7752.0001 0.1543 B- -5888.3439 33.3417 195 995799.034 32.458 + 24 110 86 196 Rn -a 1975.169 14.054 7717.9660 0.0717 B- * 196 002120.431 15.087 +0 49 123 74 197 W x -14870# 400# 7853# 2# B- 5480# 500# 196 984036# 429# + 47 122 75 197 Re x -20350# 300# 7877# 2# B- 4729# 361# 196 978153# 322# + 45 121 76 197 Os x -25080# 200# 7897# 1# B- 3185# 201# 196 973076# 215# + 43 120 77 197 Ir +p -28264.123 20.110 7909.0003 0.1021 B- 2155.6519 20.1061 196 969657.217 21.588 + 41 119 78 197 Pt -30419.775 0.536 7915.9714 0.0027 B- 719.9769 0.5022 196 967343.030 0.575 + 39 118 79 197 Au -31139.751 0.542 7915.6548 0.0028 B- -599.5206 3.2022 196 966570.103 0.581 + 37 117 80 197 Hg -30540.231 3.207 7908.6402 0.0163 B- -2186.0092 13.9478 196 967213.715 3.442 + 35 116 81 197 Tl +a -28354.222 13.575 7893.5725 0.0689 B- -3608.8367 14.3969 196 969560.492 14.573 + 33 115 82 197 Pb -24745.385 4.804 7871.2822 0.0244 B- -5058.1894 9.6190 196 973434.737 5.157 + 31 114 83 197 Bi +a -19687.196 8.333 7841.6348 0.0423 B- -6294.1172 12.9103 196 978864.927 8.946 + 29 113 84 197 Po -13393.078 9.861 7805.7136 0.0501 B- -7037.8235 12.6871 196 985621.939 10.585 + 27 112 85 197 At -6355.255 7.983 7766.0174 0.0405 B- -7865.6231 18.0538 196 993177.353 8.570 + 25 111 86 197 Rn -a 1510.368 16.193 7722.1190 0.0822 B- -8743.5996 58.7110 197 001621.446 17.383 + 23 110 87 197 Fr -a 10253.968 56.434 7673.7640 0.2865 B- * 197 011008.086 60.584 +0 48 123 75 198 Re x -16990# 400# 7861# 2# B- 6610# 447# 197 981760# 429# + 46 122 76 198 Os x -23600# 200# 7890# 1# B- 2110# 283# 197 974664# 215# + 44 121 77 198 Ir x -25710# 200# 7897# 1# B- 4194# 200# 197 972399# 215# + 42 120 78 198 Pt -29904.018 2.100 7914.1512 0.0106 B- -323.2251 2.0595 197 967896.718 2.254 + 40 119 79 198 Au -29580.793 0.540 7908.5675 0.0027 B- 1373.5226 0.4905 197 968243.714 0.579 + 38 118 80 198 Hg -30954.315 0.458 7911.5532 0.0023 B- -3425.5625 7.5590 197 966769.177 0.491 + 36 117 81 198 Tl x -27528.753 7.545 7890.3011 0.0381 B- -1461.3103 11.5537 197 970446.669 8.100 + 34 116 82 198 Pb -26067.443 8.750 7878.9695 0.0442 B- -6693.5916 28.9259 197 972015.450 9.393 + 32 115 83 198 Bi -19373.851 27.571 7841.2123 0.1392 B- -3900.5727 32.6152 197 979201.316 29.598 + 30 114 84 198 Po -15473.278 17.424 7817.5611 0.0880 B- -8764.5316 18.1013 197 983388.753 18.705 + 28 113 85 198 At -6708.747 4.904 7769.3446 0.0248 B- -5478.4271 14.2879 197 992797.864 5.265 + 26 112 86 198 Rn -a -1230.320 13.420 7737.7245 0.0678 B- -10808.0177 33.8991 197 998679.197 14.406 + 24 111 87 198 Fr -a 9577.698 31.130 7679.1873 0.1572 B- * 198 010282.081 33.419 +0 49 124 75 199 Re x -14730# 400# 7850# 2# B- 5541# 447# 198 984187# 429# + 47 123 76 199 Os x -20270# 200# 7874# 1# B- 4128# 204# 198 978239# 215# + 45 122 77 199 Ir p-2n -24398.534 41.054 7891.2066 0.2063 B- 2990.1656 41.0030 198 973807.097 44.073 + 43 121 78 199 Pt -n -27388.700 2.159 7902.3011 0.0109 B- 1705.0525 2.1201 198 970597.022 2.317 + 41 120 79 199 Au -29093.752 0.542 7906.9379 0.0027 B- 452.3142 0.6126 198 968766.573 0.581 + 39 119 80 199 Hg -29546.066 0.526 7905.2794 0.0027 B- -1486.6695 27.9498 198 968280.994 0.564 + 37 118 81 199 Tl x -28059.397 27.945 7893.8773 0.1404 B- -2827.6624 28.7653 198 969877.000 30.000 + 35 117 82 199 Pb +a -25231.734 6.821 7875.7366 0.0343 B- -4434.1181 12.6179 198 972912.620 7.322 + 33 116 83 199 Bi -20797.616 10.615 7849.5232 0.0533 B- -5558.7875 11.9224 198 977672.841 11.395 + 31 115 84 199 Po -a -15238.829 5.429 7817.6582 0.0273 B- -6415.4515 7.6464 198 983640.445 5.828 + 29 114 85 199 At -8823.377 5.384 7781.4883 0.0271 B- -7263.5308 9.0686 198 990527.715 5.780 + 27 113 86 199 Rn -a -1559.846 7.297 7741.0568 0.0367 B- -8331.2349 15.5446 198 998325.436 7.833 + 25 112 87 199 Fr -a 6771.388 13.726 7695.2599 0.0690 B- * 199 007269.384 14.734 +0 48 124 76 200 Os x -18550# 300# 7867# 1# B- 3020# 358# 199 980086# 322# + 46 123 77 200 Ir x -21570# 196# 7878# 1# B- 5030# 197# 199 976844# 210# + 44 122 78 200 Pt -nn -26599.178 20.110 7899.1986 0.1006 B- 640.9158 33.4386 199 971444.609 21.588 + 42 121 79 200 Au -27240.094 26.717 7898.4915 0.1336 B- 2263.1737 26.7188 199 970756.558 28.681 + 40 120 80 200 Hg -29503.267 0.530 7905.8956 0.0027 B- -2456.0403 5.7346 199 968326.941 0.568 + 38 119 81 200 Tl - -27047.227 5.759 7889.7037 0.0288 B- -796.3695 11.5360 199 970963.608 6.182 + 36 118 82 200 Pb -26250.858 10.008 7881.8101 0.0500 B- -5880.2841 24.8094 199 971818.546 10.744 + 34 117 83 200 Bi +a -20370.574 22.701 7848.4969 0.1135 B- -3428.8901 23.9327 199 978131.290 24.370 + 32 116 84 200 Po -16941.683 7.579 7827.4407 0.0379 B- -7953.7897 25.6119 199 981812.355 8.136 + 30 115 85 200 At -a -8987.894 24.465 7783.7601 0.1223 B- -4987.4394 25.1411 199 990351.099 26.264 + 28 114 86 200 Rn -a -4000.454 5.792 7754.9111 0.0290 B- -10134.0327 31.0688 199 995705.335 6.217 + 26 113 87 200 Fr -a 6133.578 30.524 7700.3292 0.1526 B- * 200 006584.666 32.769 +0 49 125 76 201 Os x -14840# 300# 7849# 1# B- 5000# 361# 200 984069# 322# + 47 124 77 201 Ir x -19840# 200# 7870# 1# B- 3901# 206# 200 978701# 215# + 45 123 78 201 Pt + -23740.705 50.103 7885.8337 0.2493 B- 2660.0000 50.0000 200 974513.305 53.788 + 43 122 79 201 Au -26400.705 3.218 7895.1752 0.0160 B- 1261.8237 3.1471 200 971657.678 3.455 + 41 121 80 201 Hg -27662.529 0.712 7897.5607 0.0036 B- -481.7508 14.1815 200 970303.054 0.763 + 39 120 81 201 Tl -27180.778 14.185 7891.2717 0.0706 B- -1909.7458 18.5299 200 970820.235 15.228 + 37 119 82 201 Pb -25271.033 13.747 7877.8782 0.0684 B- -3842.0269 18.3637 200 972870.431 14.758 + 35 118 83 201 Bi +a -21429.006 12.177 7854.8713 0.0606 B- -4907.8397 13.1377 200 976995.017 13.072 + 33 117 84 201 Po -16521.166 4.942 7826.5619 0.0246 B- -5731.7247 9.5606 200 982263.799 5.305 + 31 116 85 201 At +a -10789.441 8.184 7794.1536 0.0407 B- -6682.0288 13.0163 200 988417.058 8.786 + 29 115 86 201 Rn -a -4107.413 10.121 7757.0174 0.0504 B- -7695.9856 13.5972 200 995590.511 10.865 + 27 114 87 201 Fr -a 3588.573 9.080 7714.8367 0.0452 B- -8348.2439 22.2391 201 003852.491 9.747 + 25 113 88 201 Ra -a 11936.817 20.301 7669.4108 0.1010 B- * 201 012814.699 21.794 +0 50 126 76 202 Os x -12530# 400# 7839# 2# B- 4110# 500# 201 986548# 429# + 48 125 77 202 Ir x -16640# 300# 7855# 1# B- 6052# 301# 201 982136# 322# + 46 124 78 202 Pt x -22692.128 25.150 7881.5609 0.1245 B- 1660.8540 34.2759 201 975639.000 27.000 + 44 123 79 202 Au x -24352.982 23.287 7885.9100 0.1153 B- 2992.3278 23.2980 201 973856.000 25.000 + 42 122 80 202 Hg -27345.310 0.705 7896.8505 0.0035 B- -1364.8906 1.8348 201 970643.604 0.757 + 40 121 81 202 Tl -25980.419 1.838 7886.2206 0.0091 B- -39.8110 4.1863 201 972108.874 1.972 + 38 120 82 202 Pb -25940.608 3.796 7882.1505 0.0188 B- -5189.7539 14.5070 201 972151.613 4.075 + 36 119 83 202 Bi -20750.854 14.002 7852.5857 0.0693 B- -2809.2850 16.4660 201 977723.042 15.032 + 34 118 84 202 Po -17941.569 8.670 7834.8053 0.0429 B- -7346.4628 28.9311 201 980738.934 9.307 + 32 117 85 202 At -10595.106 27.601 7794.5637 0.1366 B- -4320.5458 32.6924 201 988625.686 29.631 + 30 116 86 202 Rn -a -6274.561 17.520 7769.3018 0.0867 B- -9376.0984 18.5296 201 993263.982 18.808 + 28 115 87 202 Fr -a 3101.538 6.033 7719.0125 0.0299 B- -5973.3620 16.1841 202 003329.637 6.476 + 26 114 88 202 Ra -a 9074.900 15.018 7685.5684 0.0743 B- * 202 009742.305 16.122 +0 51 127 76 203 Os x -7270# 400# 7814# 2# B- 7100# 565# 202 992195# 429# + 49 126 77 203 Ir x -14370# 400# 7845# 2# B- 5140# 447# 202 984573# 429# + 47 125 78 203 Pt x -19510# 200# 7867# 1# B- 3633# 200# 202 979055# 215# + 45 124 79 203 Au -23143.444 3.083 7880.8650 0.0152 B- 2125.7592 3.4514 202 975154.492 3.309 + 43 123 80 203 Hg -25269.203 1.630 7887.4828 0.0080 B- 492.1062 1.2247 202 972872.396 1.750 + 41 122 81 203 Tl -25761.309 1.171 7886.0530 0.0058 B- -974.8265 6.4609 202 972344.098 1.257 + 39 121 82 203 Pb -24786.483 6.554 7877.3970 0.0323 B- -3261.5896 14.3559 202 973390.617 7.036 + 37 120 83 203 Bi +a -21524.893 12.778 7857.4762 0.0629 B- -4214.0744 13.5942 202 976892.077 13.717 + 35 119 84 203 Po -17310.819 4.640 7832.8632 0.0229 B- -5148.2116 11.5921 202 981416.072 4.981 + 33 118 85 203 At -12162.607 10.623 7803.6487 0.0523 B- -5978.5623 12.1098 202 986942.904 11.404 + 31 117 86 203 Rn -a -6184.045 5.815 7770.3437 0.0286 B- -7060.4572 8.5231 202 993361.155 6.242 + 29 116 87 203 Fr 876.412 6.232 7731.7092 0.0307 B- -7724.9182 11.5191 203 000940.867 6.689 + 27 115 88 203 Ra -a 8601.331 9.688 7689.8015 0.0477 B- * 203 009233.907 10.400 +0 50 127 77 204 Ir x -9570# 400# 7823# 2# B- 8050# 447# 203 989726# 429# + 48 126 78 204 Pt x -17620# 200# 7859# 1# B- 2770# 283# 203 981084# 215# + 46 125 79 204 Au + -20390# 200# 7868# 1# B- 4300# 200# 203 978110# 215# + 44 124 80 204 Hg -24690.148 0.498 7885.5455 0.0025 B- -344.0781 1.1876 203 973494.037 0.534 + 42 123 81 204 Tl -24346.070 1.154 7880.0238 0.0057 B- 763.7453 0.1768 203 973863.420 1.238 + 40 122 82 204 Pb -25109.815 1.147 7879.9326 0.0056 B- -4463.8883 9.2480 203 973043.506 1.231 + 38 121 83 204 Bi +a -20645.927 9.180 7854.2157 0.0450 B- -2304.8814 13.6253 203 977835.687 9.854 + 36 120 84 204 Po -18341.045 10.071 7839.0823 0.0494 B- -6465.7939 24.8047 203 980310.078 10.811 + 34 119 85 204 At -11875.252 22.668 7803.5522 0.1111 B- -3905.1360 23.8592 203 987251.393 24.335 + 32 118 86 204 Rn -7970.115 7.444 7780.5743 0.0365 B- -8577.4239 25.6840 203 991443.729 7.991 + 30 117 87 204 Fr -a 607.308 24.581 7734.6931 0.1205 B- -5453.7889 26.1514 204 000651.972 26.389 + 28 116 88 204 Ra -a 6061.097 8.924 7704.1238 0.0437 B- * 204 006506.855 9.580 +0 51 128 77 205 Ir x -5600# 500# 7805# 2# B- 7220# 583# 204 993988# 537# + 49 127 78 205 Pt x -12820# 300# 7836# 1# B- 5750# 361# 204 986237# 322# + 47 126 79 205 Au x -18570# 200# 7860# 1# B- 3717# 200# 204 980064# 215# + 45 125 80 205 Hg -22287.719 3.655 7874.7325 0.0178 B- 1533.0836 3.7238 204 976073.151 3.923 + 43 124 81 205 Tl -23820.802 1.239 7878.3946 0.0060 B- -50.6402 0.5033 204 974427.318 1.330 + 41 123 82 205 Pb -23770.162 1.145 7874.3313 0.0056 B- -2704.5927 4.8196 204 974481.682 1.228 + 39 122 83 205 Bi -21065.569 4.808 7857.3218 0.0235 B- -3544.1710 11.1458 204 977385.182 5.161 + 37 121 84 205 Po -17521.398 10.059 7836.2168 0.0491 B- -4536.8793 15.6996 204 981190.006 10.798 + 35 120 85 205 At +a -12984.519 12.055 7810.2694 0.0588 B- -5274.7547 13.0777 204 986060.546 12.941 + 33 119 86 205 Rn -7709.764 5.080 7780.7226 0.0248 B- -6399.9479 9.3288 204 991723.228 5.453 + 31 118 87 205 Fr x -1309.816 7.824 7745.6870 0.0382 B- -7113.6693 24.0784 204 998593.854 8.399 + 29 117 88 205 Ra -a 5803.853 22.772 7707.1698 0.1111 B- -8302.8357 63.5402 205 006230.692 24.446 + 27 116 89 205 Ac -a 14106.689 59.320 7662.8519 0.2894 B- * 205 015144.152 63.682 +0 50 128 78 206 Pt x -9240# 300# 7820# 1# B- 4950# 424# 205 990080# 322# + 48 127 79 206 Au x -14190# 300# 7840# 1# B- 6755# 301# 205 984766# 322# + 46 126 80 206 Hg +a -20945.728 20.441 7869.1723 0.0992 B- 1307.5659 20.4100 205 977513.837 21.943 + 44 125 81 206 Tl -22253.293 1.286 7871.7219 0.0062 B- 1532.2128 0.6117 205 976110.108 1.380 + 42 124 82 206 Pb -23785.506 1.144 7875.3620 0.0056 B- -3757.3057 7.5461 205 974465.210 1.228 + 40 123 83 206 Bi - -20028.201 7.632 7853.3249 0.0371 B- -1839.5323 8.6005 205 978498.843 8.193 + 38 122 84 206 Po -a -18188.668 4.012 7840.5973 0.0195 B- -5749.2803 14.1099 205 980473.662 4.306 + 36 121 85 206 At -12439.388 13.529 7808.8904 0.0657 B- -3306.4697 16.0227 205 986645.768 14.523 + 34 120 86 206 Rn -9132.918 8.591 7789.0417 0.0417 B- -7886.0592 29.1075 205 990195.409 9.223 + 32 119 87 206 Fr -1246.859 27.811 7746.9621 0.1350 B- -4812.4721 33.1318 205 998661.441 29.856 + 30 118 88 206 Ra -a 3565.613 18.008 7719.8028 0.0874 B- -9919.1406 67.5328 206 003827.842 19.332 + 28 117 89 206 Ac -a 13484.754 65.088 7667.8538 0.3160 B- * 206 014476.477 69.874 +0 51 129 78 207 Pt x -4140# 400# 7797# 2# B- 6501# 500# 206 995556# 429# + 49 128 79 207 Au x -10640# 300# 7824# 1# B- 5847# 301# 206 988577# 322# + 47 127 80 207 Hg x -16487.446 29.808 7848.6112 0.1440 B- 4546.9906 30.3000 206 982300.000 32.000 + 45 126 81 207 Tl -21034.436 5.439 7866.7979 0.0263 B- 1417.5323 5.4024 206 977418.605 5.839 + 43 125 82 207 Pb -22451.968 1.147 7869.8664 0.0055 B- -2397.4140 2.1175 206 975896.821 1.231 + 41 124 83 207 Bi -20054.554 2.397 7854.5053 0.0116 B- -2908.8541 6.6140 206 978470.551 2.573 + 39 123 84 207 Po -17145.700 6.659 7836.6734 0.0322 B- -3918.2186 14.0754 206 981593.334 7.148 + 37 122 85 207 At +a -13227.482 12.406 7813.9653 0.0599 B- -4592.7400 13.2815 206 985799.715 13.318 + 35 121 86 207 Rn -8634.742 4.742 7787.9987 0.0229 B- -5785.7211 18.1857 206 990730.224 5.090 + 33 120 87 207 Fr -2849.021 17.557 7756.2689 0.0848 B- -6363.0084 60.8726 206 996941.450 18.847 + 31 119 88 207 Ra -a 3513.988 58.286 7721.7503 0.2816 B- -7632.2404 81.0004 207 003772.420 62.572 + 29 118 89 207 Ac -a 11146.228 56.248 7681.1001 0.2717 B- * 207 011965.967 60.384 +0 52 130 78 208 Pt x -500# 400# 7780# 2# B- 5410# 500# 207 999463# 429# + 50 129 79 208 Au x -5910# 300# 7803# 1# B- 7355# 302# 207 993655# 322# + 48 128 80 208 Hg x -13265.408 30.739 7834.1914 0.1478 B- 3484.7131 30.7951 207 985759.000 33.000 + 46 127 81 208 Tl +a -16750.121 1.854 7847.1835 0.0089 B- 4998.3984 1.6693 207 982018.006 1.989 + 44 126 82 208 Pb -21748.519 1.148 7867.4530 0.0055 B- -2878.3680 2.0127 207 976652.005 1.232 + 42 125 83 208 Bi +n -18870.151 2.305 7849.8534 0.0111 B- -1400.9438 2.3787 207 979742.060 2.474 + 40 124 84 208 Po -17469.207 1.672 7839.3568 0.0080 B- -4999.3061 9.0736 207 981246.035 1.795 + 38 123 85 208 At +a -12469.901 8.921 7811.5604 0.0429 B- -2814.5118 13.5215 207 986613.011 9.577 + 36 122 86 208 Rn -9655.389 10.163 7794.2678 0.0489 B- -6990.4614 15.4656 207 989634.513 10.910 + 34 121 87 208 Fr -2664.928 11.657 7756.8985 0.0560 B- -4392.8615 14.7413 207 997139.082 12.514 + 32 120 88 208 Ra -a 1727.934 9.023 7732.0177 0.0434 B- -9032.9208 65.1111 208 001855.012 9.686 + 30 119 89 208 Ac -a 10760.854 64.483 7684.8289 0.3100 B- -5927.1868 71.9264 208 011552.251 69.225 + 28 118 90 208 Th -a 16688.041 31.865 7652.5716 0.1532 B- * 208 017915.348 34.208 +0 51 130 79 209 Au x -2230# 400# 7786# 2# B- 6380# 427# 208 997606# 429# + 49 129 80 209 Hg x -8610# 150# 7813# 1# B- 5035# 150# 208 990757# 161# + 47 128 81 209 Tl +a -13644.793 6.110 7833.3979 0.0292 B- 3969.7809 6.2115 208 985351.713 6.559 + 45 127 82 209 Pb -17614.574 1.747 7848.6488 0.0084 B- 644.0152 1.1462 208 981089.978 1.875 + 43 126 83 209 Bi -18258.589 1.365 7847.9869 0.0065 B- -1892.5741 1.5635 208 980398.599 1.465 + 41 125 84 209 Po -a -16366.015 1.778 7835.1882 0.0085 B- -3482.2417 4.9599 208 982430.361 1.909 + 39 124 85 209 At -12883.773 4.745 7814.7835 0.0227 B- -3942.7237 11.0298 208 986168.701 5.094 + 37 123 86 209 Rn -8941.049 9.960 7792.1755 0.0477 B- -5158.9051 15.2153 208 990401.389 10.692 + 35 122 87 209 Fr -3782.144 11.503 7763.7485 0.0550 B- -5640.3845 12.8549 208 995939.701 12.349 + 33 121 88 209 Ra -a 1858.240 5.747 7733.0177 0.0275 B- -6986.6464 56.1409 209 001994.902 6.169 + 31 120 89 209 Ac -a 8844.887 55.846 7695.8455 0.2672 B- -7550# 117# 209 009495.375 59.953 + 29 119 90 209 Th IT 16395# 103# 7656# 0# B- * 209 017601# 111# +0 52 131 79 210 Au x 2680# 400# 7764# 2# B- 7980# 447# 210 002877# 429# + 50 130 80 210 Hg x -5300# 200# 7799# 1# B- 3947# 201# 209 994310# 215# + 48 129 81 210 Tl +a -9246.996 11.603 7813.5890 0.0553 B- 5481.4334 11.5610 209 990072.942 12.456 + 46 128 82 210 Pb -14728.429 1.448 7835.9656 0.0069 B- 63.4758 0.4992 209 984188.381 1.554 + 44 127 83 210 Bi -14791.905 1.364 7832.5424 0.0065 B- 1161.1549 0.7662 209 984120.237 1.463 + 42 126 84 210 Po -15953.060 1.146 7834.3462 0.0055 B- -3980.9605 7.6101 209 982873.686 1.230 + 40 125 85 210 At -a -11972.099 7.695 7811.6638 0.0366 B- -2367.3352 8.9225 209 987147.423 8.261 + 38 124 86 210 Rn -a -9604.764 4.557 7796.6653 0.0217 B- -6261.2558 14.1720 209 989688.862 4.892 + 36 123 87 210 Fr -3343.508 13.420 7763.1243 0.0639 B- -3786.3467 16.2633 209 996410.596 14.407 + 34 122 88 210 Ra -a 442.839 9.193 7741.3687 0.0438 B- -8321.2403 62.8832 210 000475.406 9.868 + 32 121 89 210 Ac 8764.079 62.208 7698.0182 0.2962 B- -5295.4420 65.0180 210 009408.625 66.782 + 30 120 90 210 Th -a 14059.521 18.909 7669.0764 0.0900 B- * 210 015093.515 20.299 +0 51 131 80 211 Hg x -390# 200# 7777# 1# B- 5688# 205# 210 999581# 215# + 49 130 81 211 Tl x -6077.999 41.917 7799.7915 0.1987 B- 4415.0129 41.9781 210 993475.000 45.000 + 47 129 82 211 Pb -10493.012 2.260 7817.0079 0.0107 B- 1366.1041 5.4713 210 988735.288 2.426 + 45 128 83 211 Bi -11859.116 5.442 7819.7745 0.0258 B- 573.3763 5.4297 210 987268.715 5.842 + 43 127 84 211 Po -a -12432.492 1.255 7818.7842 0.0060 B- -785.3012 2.5385 210 986653.171 1.347 + 41 126 85 211 At -a -11647.191 2.729 7811.3545 0.0129 B- -2891.8615 6.8937 210 987496.226 2.929 + 39 125 86 211 Rn -a -8755.330 6.813 7793.9412 0.0323 B- -4615.0152 13.7862 210 990600.767 7.314 + 37 124 87 211 Fr -4140.314 11.991 7768.3613 0.0568 B- -4972.1844 12.9786 210 995555.189 12.872 + 35 123 88 211 Ra 831.870 4.966 7741.0887 0.0235 B- -6311.6152 53.9818 211 000893.049 5.331 + 33 122 89 211 Ac 7143.485 53.753 7707.4680 0.2548 B- -6732.9111 101.4758 211 007668.846 57.706 + 31 121 90 211 Th -a 13876.396 86.070 7671.8506 0.4079 B- -8175.8296 110.6091 211 014896.923 92.399 + 29 120 91 211 Pa -a 22052.226 69.472 7629.3948 0.3293 B- * 211 023674.036 74.581 +0 52 132 80 212 Hg x 3020# 300# 7762# 1# B- 4571# 361# 212 003242# 322# + 50 131 81 212 Tl +a -1551# 200# 7780# 1# B- 5998# 200# 211 998335# 215# + 48 130 82 212 Pb -7548.929 1.840 7804.3203 0.0087 B- 569.0133 1.8246 211 991895.891 1.975 + 46 129 83 212 Bi -8117.943 1.853 7803.3140 0.0087 B- 2251.4656 1.6671 211 991285.030 1.989 + 44 128 84 212 Po -10369.408 1.153 7810.2438 0.0054 B- -1741.2596 2.1066 211 988867.982 1.237 + 42 127 85 212 At -a -8628.149 2.385 7798.3400 0.0113 B- 31.0705 3.5927 211 990737.301 2.559 + 40 126 86 212 Rn -a -8659.219 3.110 7794.7963 0.0147 B- -5143.2210 9.3064 211 990703.946 3.338 + 38 125 87 212 Fr -3515.998 8.775 7766.8455 0.0414 B- -3317.2355 13.4939 211 996225.420 9.419 + 36 124 88 212 Ra -198.763 10.254 7747.5078 0.0484 B- -7498.3626 24.1666 211 999786.619 11.007 + 34 123 89 212 Ac 7299.600 21.883 7708.4479 0.1032 B- -4811.2864 24.1055 212 007836.442 23.492 + 32 122 90 212 Th -a 12110.886 10.109 7682.0628 0.0477 B- -9485.6366 88.1858 212 013001.570 10.852 + 30 121 91 212 Pa -a 21596.523 87.604 7633.6289 0.4132 B- * 212 023184.819 94.047 +0 53 133 80 213 Hg x 8200# 300# 7739# 1# B- 6416# 301# 213 008803# 322# + 51 132 81 213 Tl x 1783.811 27.013 7765.4311 0.1268 B- 4987.4088 27.8941 213 001915.000 29.000 + 49 131 82 213 Pb +a -3203.598 6.954 7785.1732 0.0326 B- 2028.0730 8.3708 212 996560.796 7.465 + 47 130 83 213 Bi -5231.671 5.082 7791.0217 0.0239 B- 1421.8481 5.4898 212 994383.570 5.455 + 45 129 84 213 Po -6653.519 3.053 7794.0240 0.0143 B- -73.9972 5.4646 212 992857.154 3.277 + 43 128 85 213 At -a -6579.522 4.898 7790.0036 0.0230 B- -883.5727 5.7243 212 992936.593 5.258 + 41 127 86 213 Rn -a -5695.949 3.370 7782.1824 0.0158 B- -2141.7493 5.6996 212 993885.147 3.618 + 39 126 87 213 Fr -3554.199 4.707 7768.4543 0.0221 B- -3899.7568 10.8862 212 996184.410 5.053 + 37 125 88 213 Ra 345.557 9.818 7746.4726 0.0461 B- -5795.4721 15.2463 213 000370.971 10.540 + 35 124 89 213 Ac 6141.029 11.665 7715.5908 0.0548 B- -5979.0781 14.8635 213 006592.665 12.522 + 33 123 90 213 Th -a 12120.108 9.217 7683.8470 0.0433 B- -7534.0866 57.9078 213 013011.470 9.895 + 31 122 91 213 Pa -a 19654.194 57.170 7644.8027 0.2684 B- * 213 021099.644 61.374 +0 54 134 80 214 Hg x 11770# 400# 7724# 2# B- 5306# 445# 214 012636# 429# + 52 133 81 214 Tl x 6465# 196# 7745# 1# B- 6648# 196# 214 006940# 210# + 50 132 82 214 Pb -183.019 1.969 7772.3955 0.0092 B- 1017.7611 11.2559 213 999803.521 2.114 + 48 131 83 214 Bi -1200.780 11.209 7773.4955 0.0524 B- 3269.1925 11.1649 213 998710.909 12.033 + 46 130 84 214 Po -4469.972 1.449 7785.1163 0.0068 B- -1090.8208 3.7750 213 995201.287 1.556 + 44 129 85 214 At -3379.151 3.982 7776.3632 0.0186 B- 940.5125 9.8827 213 996372.331 4.274 + 42 128 86 214 Rn -a -4319.664 9.187 7777.1023 0.0429 B- -3361.3369 12.4238 213 995362.650 9.862 + 40 127 87 214 Fr -a -958.327 8.519 7757.7393 0.0398 B- -1051.0675 9.9879 213 998971.193 9.145 + 38 126 88 214 Ra -a 92.740 5.250 7749.1719 0.0245 B- -6340.5313 14.5317 214 000099.560 5.636 + 36 125 89 214 Ac 6433.272 13.551 7715.8874 0.0633 B- -4261.6599 17.2389 214 006906.400 14.547 + 34 124 90 214 Th -a 10694.932 10.661 7692.3173 0.0498 B- -8764.9630 81.9051 214 011481.480 11.445 + 32 123 91 214 Pa -a 19459.895 81.208 7647.7037 0.3795 B- * 214 020891.055 87.180 +0 55 135 80 215 Hg x 17110# 400# 7701# 2# B- 7079# 500# 215 018368# 429# + 53 134 81 215 Tl x 10030# 300# 7730# 1# B- 5688# 305# 215 010768# 322# + 51 133 82 215 Pb +a 4342.245 52.685 7752.7381 0.2450 B- 2712.9729 52.9848 215 004661.591 56.560 + 49 132 83 215 Bi 1629.272 5.624 7761.7177 0.0262 B- 2171.0426 5.5297 215 001749.095 6.037 + 47 131 84 215 Po -541.771 2.120 7768.1768 0.0099 B- 714.8128 6.6491 214 999418.385 2.276 + 45 130 85 215 At -a -1256.583 6.629 7767.8627 0.0308 B- -87.5935 8.9062 214 998651.002 7.116 + 43 129 86 215 Rn -a -1168.990 6.090 7763.8164 0.0283 B- -1487.1274 9.1890 214 998745.037 6.538 + 41 128 87 215 Fr -a 318.137 7.066 7753.2607 0.0329 B- -2213.8573 9.7691 215 000341.534 7.585 + 39 127 88 215 Ra -a 2531.995 7.201 7739.3249 0.0335 B- -3498.5554 14.3395 215 002718.208 7.730 + 37 126 89 215 Ac -a 6030.550 12.406 7719.4137 0.0577 B- -4890.8833 13.9296 215 006474.061 13.318 + 35 125 90 215 Th -a 10921.434 6.335 7693.0266 0.0295 B- -6883.1037 82.6932 215 011724.640 6.800 + 33 124 91 215 Pa -a 17804.537 82.450 7657.3733 0.3835 B- -7084.7747 132.8246 215 019113.955 88.513 + 31 123 92 215 U -a 24889.312 104.136 7620.7821 0.4844 B- * 215 026719.774 111.794 +0 56 136 80 216 Hg x 20920# 400# 7685# 2# B- 6050# 500# 216 022459# 429# + 54 135 81 216 Tl x 14870# 300# 7709# 1# B- 7361# 361# 216 015964# 322# + 52 134 82 216 Pb x 7510# 200# 7740# 1# B- 1636# 201# 216 008062# 215# + 50 133 83 216 Bi x 5873.988 11.178 7743.4996 0.0518 B- 4091.6520 11.3243 216 006305.985 12.000 + 48 132 84 216 Po 1782.336 1.815 7758.8205 0.0084 B- -474.3423 3.5713 216 001913.416 1.948 + 46 131 85 216 At -a 2256.678 3.575 7753.0024 0.0166 B- 2003.3657 6.6383 216 002422.643 3.837 + 44 130 86 216 Rn -a 253.312 5.768 7758.6553 0.0267 B- -2717.7096 6.9366 216 000271.942 6.192 + 42 129 87 216 Fr -a 2971.022 4.174 7742.4513 0.0193 B- -320.4441 8.8904 216 003189.523 4.480 + 40 128 88 216 Ra -a 3291.466 8.004 7737.3458 0.0371 B- -4858.2701 12.2147 216 003533.534 8.592 + 38 127 89 216 Ac 8149.736 9.230 7711.2319 0.0427 B- -2148.8006 14.4375 216 008749.101 9.908 + 36 126 90 216 Th -a 10298.537 11.104 7697.6617 0.0514 B- -7525.2616 27.0327 216 011055.933 11.920 + 34 125 91 216 Pa -a 17823.799 24.647 7659.2006 0.1141 B- -5242.6308 37.3721 216 019134.633 26.459 + 32 124 92 216 U -a 23066.429 28.093 7631.3072 0.1301 B- * 216 024762.829 30.158 +0 55 136 81 217 Tl x 18660# 400# 7693# 2# B- 6399# 500# 217 020032# 429# + 53 135 82 217 Pb x 12260# 300# 7719# 1# B- 3530# 300# 217 013162# 322# + 51 134 83 217 Bi x 8729.963 17.698 7731.8491 0.0816 B- 2846.5103 18.8695 217 009372.000 19.000 + 49 133 84 217 Po +a 5883.452 6.544 7741.3614 0.0302 B- 1488.8543 7.9791 217 006316.145 7.025 + 47 132 85 217 At 4394.598 5.001 7744.6172 0.0230 B- 736.0320 6.1505 217 004717.794 5.368 + 45 131 86 217 Rn -a 3658.566 4.198 7744.4037 0.0193 B- -656.0967 7.5383 217 003927.632 4.506 + 43 130 87 217 Fr -a 4314.663 6.531 7737.7750 0.0301 B- -1574.8729 9.4723 217 004631.980 7.011 + 41 129 88 217 Ra -a 5889.536 7.047 7726.9122 0.0325 B- -2812.7853 13.2129 217 006322.676 7.564 + 39 128 89 217 Ac -a 8702.321 11.223 7710.3448 0.0517 B- -3503.4590 15.4454 217 009342.325 12.048 + 37 127 90 217 Th -a 12205.780 10.614 7690.5945 0.0489 B- -4848.9680 16.3966 217 013103.443 11.394 + 35 126 91 217 Pa -a 17054.748 12.498 7664.6438 0.0576 B- -5916# 81# 217 018309.024 13.417 + 33 125 92 217 U -a 22971# 81# 7634# 0# B- * 217 024660# 86# +0 56 137 81 218 Tl x 23710# 400# 7672# 2# B- 8081# 500# 218 025454# 429# + 54 136 82 218 Pb x 15630# 300# 7705# 1# B- 2414# 301# 218 016779# 322# + 52 135 83 218 Bi x 13216.038 27.013 7712.8280 0.1239 B- 4859.3866 27.0849 218 014188.000 29.000 + 50 134 84 218 Po 8356.652 1.967 7731.5300 0.0090 B- 256.4334 11.5490 218 008971.234 2.112 + 48 133 85 218 At -a 8100.218 11.503 7729.1175 0.0528 B- 2882.8048 11.6054 218 008695.941 12.349 + 46 132 86 218 Rn 5217.413 2.316 7738.7527 0.0106 B- -1842.0267 4.4418 218 005601.123 2.486 + 44 131 87 218 Fr -a 7059.440 4.235 7726.7143 0.0194 B- 413.8838 10.5603 218 007578.620 4.546 + 42 130 88 218 Ra -a 6645.556 9.807 7725.0241 0.0450 B- -4205.2887 58.4224 218 007134.297 10.528 + 40 129 89 218 Ac -a 10850.845 57.616 7702.1450 0.2643 B- -1515.9019 58.5648 218 011648.860 61.853 + 38 128 90 218 Th -a 12366.747 10.516 7691.6026 0.0482 B- -6282.8212 20.7132 218 013276.248 11.289 + 36 127 91 218 Pa -a 18649.568 17.846 7659.1935 0.0819 B- -3245.0869 22.5042 218 020021.133 19.158 + 34 126 92 218 U -a 21894.655 13.714 7640.7191 0.0629 B- * 218 023504.877 14.722 +0 55 137 82 219 Pb x 20620# 400# 7684# 2# B- 4300# 447# 219 022136# 429# + 53 136 83 219 Bi x 16320# 200# 7700# 1# B- 3638# 201# 219 017520# 215# + 51 135 84 219 Po x 12681.361 15.835 7713.3340 0.0723 B- 2285.3395 16.1628 219 013614.000 17.000 + 49 134 85 219 At 10396.021 3.237 7720.1970 0.0148 B- 1566.6838 2.9473 219 011160.587 3.474 + 47 133 86 219 Rn 8829.337 2.100 7723.7784 0.0096 B- 212.3984 6.8938 219 009478.683 2.254 + 45 132 87 219 Fr -a 8616.939 6.874 7721.1759 0.0314 B- -776.9137 9.5906 219 009250.664 7.380 + 43 131 88 219 Ra -a 9393.853 6.814 7714.0560 0.0311 B- -2175.7005 51.9016 219 010084.715 7.315 + 41 130 89 219 Ac -a 11569.553 51.477 7700.5489 0.2351 B- -2893.2268 76.3627 219 012420.425 55.263 + 39 129 90 219 Th -a 14462.780 56.460 7683.7655 0.2578 B- -4120.4434 89.7010 219 015526.432 60.611 + 37 128 91 219 Pa -a 18583.223 69.705 7661.3783 0.3183 B- -4712.7298 70.9693 219 019949.909 74.831 + 35 127 92 219 U -a 23295.953 13.338 7636.2867 0.0609 B- -6140.9976 92.9309 219 025009.233 14.319 + 33 126 93 219 Np -a 29436.951 91.969 7604.6732 0.4199 B- * 219 031601.865 98.732 +0 56 138 82 220 Pb x 24130# 400# 7670# 2# B- 3171# 500# 220 025905# 429# + 54 137 83 220 Bi x 20960# 300# 7681# 1# B- 5696# 300# 220 022501# 322# + 52 136 84 220 Po x 15263.462 17.698 7703.2244 0.0804 B- 887.7139 22.5491 220 016386.000 19.000 + 50 135 85 220 At x 14375.748 13.972 7703.7033 0.0635 B- 3763.7550 14.0896 220 015433.000 15.000 + 48 134 86 220 Rn 10611.994 1.814 7717.2552 0.0082 B- -870.3384 4.0256 220 011392.443 1.947 + 46 133 87 220 Fr -a 11482.332 4.028 7709.7430 0.0183 B- 1210.2406 8.4809 220 012326.789 4.324 + 44 132 88 220 Ra -a 10272.091 7.595 7711.6879 0.0345 B- -3471.6640 9.6266 220 011027.542 8.153 + 42 131 89 220 Ac -a 13743.755 6.129 7692.3515 0.0279 B- -945.7825 14.9144 220 014754.527 6.579 + 40 130 90 220 Th -a 14689.538 13.687 7684.4964 0.0622 B- -5588.8595 20.0508 220 015769.866 14.693 + 38 129 91 220 Pa -a 20278.397 14.655 7655.5364 0.0666 B- -2735# 102# 220 021769.753 15.732 + 36 128 92 220 U -a 23013# 101# 7640# 0# B- -7462# 105# 220 024706# 108# + 34 127 93 220 Np -a 30475.022 30.718 7602.0758 0.1396 B- * 220 032716.280 32.977 +0 55 138 83 221 Bi x 24200# 300# 7668# 1# B- 4426# 301# 221 025980# 322# + 53 137 84 221 Po x 19773.757 19.561 7684.4814 0.0885 B- 2991.0276 24.0390 221 021228.000 21.000 + 51 136 85 221 At x 16782.729 13.972 7694.4754 0.0632 B- 2311.3750 15.0957 221 018017.000 15.000 + 49 135 86 221 Rn +a 14471.354 5.714 7701.3941 0.0259 B- 1194.1032 7.2312 221 015535.637 6.134 + 47 134 87 221 Fr 13277.251 4.886 7703.2572 0.0221 B- 313.3741 6.3858 221 014253.714 5.245 + 45 133 88 221 Ra -a 12963.877 4.630 7701.1352 0.0210 B- -1567.1715 57.0591 221 013917.293 4.970 + 43 132 89 221 Ac -a 14531.048 56.901 7690.5039 0.2575 B- -2408.8773 57.4376 221 015599.721 61.086 + 41 131 90 221 Th -a 16939.926 7.994 7676.0640 0.0362 B- -3435.0112 59.9069 221 018185.757 8.582 + 39 130 91 221 Pa -a 20374.937 59.380 7656.9809 0.2687 B- -4145.0590 93.4311 221 021873.393 63.746 + 37 129 92 221 U -a 24519.996 72.135 7634.6849 0.3264 B- -5390# 213# 221 026323.297 77.440 + 35 128 93 221 Np x 29910# 200# 7607# 1# B- -6019# 361# 221 032110# 215# + 33 127 94 221 Pu x 35930# 300# 7576# 1# B- * 221 038572# 322# +0 56 139 83 222 Bi x 28950# 300# 7648# 1# B- 6464# 303# 222 031079# 322# + 54 138 84 222 Po x 22486.268 40.054 7674.0054 0.1804 B- 1533.2393 43.0709 222 024140.000 43.000 + 52 137 85 222 At x 20953.028 15.835 7677.3878 0.0713 B- 4581.0714 15.9542 222 022494.000 17.000 + 50 136 86 222 Rn 16371.957 1.944 7694.4991 0.0088 B- -6.1461 7.7013 222 017576.017 2.086 + 48 135 87 222 Fr x 16378.103 7.452 7690.9474 0.0336 B- 2057.8980 8.6816 222 017582.615 8.000 + 46 134 88 222 Ra 14320.205 4.454 7696.6931 0.0201 B- -2301.5922 6.2737 222 015373.371 4.781 + 44 133 89 222 Ac -a 16621.797 4.699 7682.8015 0.0212 B- -581.2415 11.1289 222 017844.232 5.044 + 42 132 90 222 Th -a 17203.039 10.216 7676.6592 0.0460 B- -4861.3220 87.1915 222 018468.220 10.966 + 40 131 91 222 Pa -a 22064.361 86.606 7651.2373 0.3901 B- -2208.4729 101.0129 222 023687.064 92.975 + 38 130 92 222 U -a 24272.834 51.994 7637.7651 0.2342 B- -7001.8072 64.4300 222 026057.957 55.817 + 36 129 93 222 Np -a 31274.641 38.051 7602.7013 0.1714 B- -3785# 302# 222 033574.706 40.849 + 34 128 94 222 Pu x 35060# 300# 7582# 1# B- * 222 037638# 322# +0 57 140 83 223 Bi x 32240# 400# 7636# 2# B- 5161# 445# 223 034611# 429# + 55 139 84 223 Po x 27079# 196# 7655# 1# B- 3651# 196# 223 029070# 210# + 53 138 85 223 At x 23428.008 13.972 7668.0557 0.0627 B- 3038.2698 16.0129 223 025151.000 15.000 + 51 137 86 223 Rn 20389.738 7.822 7678.1720 0.0351 B- 2007.4091 8.0568 223 021889.283 8.397 + 49 136 87 223 Fr 18382.329 1.931 7683.6655 0.0087 B- 1149.0844 0.8476 223 019734.241 2.073 + 47 135 88 223 Ra 17233.245 2.090 7685.3101 0.0094 B- -591.8099 6.9657 223 018500.648 2.243 + 45 134 89 223 Ac -a 17825.055 6.947 7679.1479 0.0312 B- -1560.3471 10.4712 223 019135.982 7.457 + 43 133 90 223 Th -a 19385.402 7.943 7668.6426 0.0356 B- -2952.2124 76.0305 223 020811.083 8.527 + 41 132 91 223 Pa -a 22337.614 75.632 7651.8957 0.3392 B- -3707.6637 95.9225 223 023980.414 81.193 + 39 131 92 223 U -a 26045.278 59.054 7631.7611 0.2648 B- -4613.3046 101.7520 223 027960.754 63.396 + 37 130 93 223 Np -a 30658.583 82.863 7607.5654 0.3716 B- -5462# 311# 223 032913.340 88.956 + 35 129 94 223 Pu x 36121# 300# 7580# 1# B- -6579# 424# 223 038777# 322# + 33 128 95 223 Am x 42700# 300# 7547# 1# B- * 223 045840# 322# +0 58 141 83 224 Bi x 37070# 400# 7616# 2# B- 7159# 445# 224 039796# 429# + 56 140 84 224 Po x 29910# 196# 7644# 1# B- 2199# 197# 224 032110# 210# + 54 139 85 224 At x 27711.018 22.356 7650.7354 0.0998 B- 5265.9197 24.4153 224 029749.000 24.000 + 52 138 86 224 Rn 22445.098 9.814 7670.7514 0.0438 B- 696.4840 14.8750 224 024095.803 10.536 + 50 137 87 224 Fr x 21748.614 11.178 7670.3680 0.0499 B- 2922.7819 11.3237 224 023348.096 12.000 + 48 136 88 224 Ra 18825.832 1.811 7679.9236 0.0081 B- -1408.3152 4.0869 224 020210.361 1.944 + 46 135 89 224 Ac -a 20234.148 4.089 7670.1438 0.0183 B- 238.5672 10.3428 224 021722.249 4.389 + 44 134 90 224 Th -a 19995.581 9.604 7667.7162 0.0429 B- -3866.7705 12.1339 224 021466.137 10.310 + 42 133 91 224 Pa -a 23862.351 7.587 7646.9612 0.0339 B- -1880.3393 16.9711 224 025617.286 8.145 + 40 132 92 224 U -a 25742.690 15.261 7635.0742 0.0681 B- -6289.5572 32.7036 224 027635.913 16.383 + 38 131 93 224 Np 32032.248 28.925 7603.5032 0.1291 B- -3248# 301# 224 034388.030 31.052 + 36 130 94 224 Pu x 35280# 300# 7586# 1# B- -7980# 500# 224 037875# 322# + 34 129 95 224 Am x 43260# 400# 7546# 2# B- * 224 046442# 429# +0 57 141 84 225 Po x 34580# 300# 7626# 1# B- 4280# 424# 225 037123# 322# + 55 140 85 225 At x 30300# 300# 7641# 1# B- 3765# 300# 225 032528# 322# + 53 139 86 225 Rn 26534.143 11.140 7654.3581 0.0495 B- 2713.5412 16.3492 225 028485.572 11.958 + 51 138 87 225 Fr 23820.602 11.967 7662.9412 0.0532 B- 1827.5584 12.1574 225 025572.466 12.847 + 49 137 88 225 Ra 21993.044 2.596 7667.5866 0.0115 B- 355.7386 5.0067 225 023610.502 2.786 + 47 136 89 225 Ac 21637.305 4.758 7665.6906 0.0211 B- -672.8878 6.6576 225 023228.601 5.107 + 45 135 90 225 Th -a 22310.193 5.093 7659.2229 0.0226 B- -2046.4473 82.0038 225 023950.975 5.467 + 43 134 91 225 Pa -a 24356.640 81.867 7646.6504 0.3639 B- -3015.3610 82.4514 225 026147.927 87.887 + 41 133 92 225 U -a 27372.001 9.934 7629.7717 0.0442 B- -4246.0969 92.1491 225 029385.050 10.664 + 39 132 93 225 Np -a 31618.098 91.618 7607.4231 0.4072 B- -4682# 314# 225 033943.422 98.355 + 37 131 94 225 Pu x 36300# 300# 7583# 1# B- -6090# 500# 225 038970# 322# + 35 130 95 225 Am x 42390# 400# 7553# 2# B- * 225 045508# 429# +0 58 142 84 226 Po x 37549# 401# 7614# 2# B- 2889# 500# 226 040310# 430# + 56 141 85 226 At x 34660# 300# 7624# 1# B- 5913# 300# 226 037209# 322# + 54 140 86 226 Rn 28747.194 10.477 7646.4108 0.0464 B- 1226.6542 12.1895 226 030861.380 11.247 + 52 139 87 226 Fr 27520.539 6.230 7648.3768 0.0276 B- 3852.9638 6.5215 226 029544.512 6.688 + 50 138 88 226 Ra 23667.576 1.927 7661.9636 0.0085 B- -641.6252 3.2730 226 025408.186 2.068 + 48 137 89 226 Ac 24309.201 3.100 7655.6628 0.0137 B- 1111.5517 4.5626 226 026096.999 3.327 + 46 136 90 226 Th 23197.649 4.481 7657.1195 0.0198 B- -2835.9504 11.9702 226 024903.699 4.810 + 44 135 91 226 Pa -a 26033.600 11.213 7641.1093 0.0496 B- -1295.1978 15.6747 226 027948.217 12.037 + 42 134 92 226 U -a 27328.797 11.071 7631.9166 0.0490 B- -5488.0792 102.6485 226 029338.669 11.884 + 40 133 93 226 Np -a 32816.877 102.063 7604.1714 0.4516 B- -2813# 225# 226 035230.364 109.568 + 38 132 94 226 Pu x 35630# 200# 7588# 1# B- -7340# 361# 226 038250# 215# + 36 131 95 226 Am x 42970# 300# 7552# 1# B- * 226 046130# 322# +0 59 143 84 227 Po x 42281# 401# 7596# 2# B- 4850# 500# 227 045390# 430# + 57 142 85 227 At x 37430# 300# 7613# 1# B- 4544# 300# 227 040183# 322# + 55 141 86 227 Rn 32885.835 14.091 7630.0508 0.0621 B- 3203.3894 15.2755 227 035304.393 15.127 + 53 140 87 227 Fr 29682.445 5.898 7640.7162 0.0260 B- 2504.9813 6.2112 227 031865.413 6.332 + 51 139 88 227 Ra -n 27177.464 1.946 7648.3048 0.0086 B- 1327.9489 2.2622 227 029176.205 2.089 + 49 138 89 227 Ac 25849.515 1.926 7650.7084 0.0085 B- 44.7559 0.8297 227 027750.594 2.068 + 47 137 90 227 Th 25804.759 2.088 7647.4591 0.0092 B- -1025.6117 7.2815 227 027702.546 2.241 + 45 136 91 227 Pa -a 26830.371 7.263 7639.4945 0.0320 B- -2214.6629 11.1118 227 028803.586 7.797 + 43 135 92 227 U -a 29045.034 8.510 7626.2918 0.0375 B- -3533.9848 77.4417 227 031181.124 9.136 + 41 134 93 227 Np -a 32579.018 76.989 7607.2771 0.3392 B- -4191# 126# 227 034975.012 82.651 + 39 133 94 227 Pu x 36770# 100# 7585# 0# B- -5410# 224# 227 039474# 107# + 37 132 95 227 Am x 42180# 200# 7558# 1# B- * 227 045282# 215# +0 58 143 85 228 At x 41880# 400# 7596# 2# B- 6637# 400# 228 044960# 429# + 56 142 86 228 Rn 35243.466 17.677 7621.6457 0.0775 B- 1859.2451 18.9157 228 037835.415 18.977 + 54 141 87 228 Fr 33384.221 6.732 7626.3689 0.0295 B- 4444.0270 7.0210 228 035839.433 7.226 + 52 140 88 228 Ra +a 28940.194 1.995 7642.4289 0.0088 B- 45.5402 0.6344 228 031068.574 2.141 + 50 139 89 228 Ac - 28894.654 2.093 7639.1973 0.0092 B- 2123.7545 2.6446 228 031019.685 2.247 + 48 138 90 228 Th 26770.899 1.806 7645.0807 0.0079 B- -2152.6993 4.3399 228 028739.741 1.938 + 46 137 91 228 Pa -a 28923.599 4.340 7632.2076 0.0190 B- -296.4020 14.0858 228 031050.758 4.659 + 44 136 92 228 U -a 29220.001 13.474 7627.4763 0.0591 B- -4605# 101# 228 031368.959 14.465 + 42 135 93 228 Np -a 33825# 100# 7604# 0# B- -2283# 103# 228 036313# 108# + 40 134 94 228 Pu -a 36107.809 23.352 7590.4039 0.1024 B- -6742# 202# 228 038763.325 25.069 + 38 133 95 228 Am x 42850# 200# 7557# 1# B- * 228 046001# 215# +0 59 144 85 229 At x 44890# 400# 7585# 2# B- 5527# 400# 229 048191# 429# + 57 143 86 229 Rn x 39362.400 13.041 7605.6227 0.0569 B- 3694.1465 13.9670 229 042257.272 14.000 + 55 142 87 229 Fr 35668.253 5.001 7618.3380 0.0218 B- 3106.2907 16.2305 229 038291.443 5.368 + 53 141 88 229 Ra x 32561.963 15.441 7628.4862 0.0674 B- 1872.0266 19.6229 229 034956.703 16.576 + 51 140 89 229 Ac x 30689.936 12.109 7633.2446 0.0529 B- 1104.4191 12.3458 229 032947.000 13.000 + 49 139 90 229 Th 29585.517 2.404 7634.6510 0.0105 B- -311.3310 3.7152 229 031761.357 2.581 + 47 138 91 229 Pa 29896.848 3.280 7629.8752 0.0143 B- -1313.7716 6.6554 229 032095.585 3.521 + 45 137 92 229 U -a 31210.620 5.938 7620.7218 0.0259 B- -2590.7577 101.3342 229 033505.976 6.374 + 43 136 93 229 Np -a 33801.378 101.177 7605.9921 0.4418 B- -3593.5462 117.9433 229 036287.269 108.618 + 41 135 94 229 Pu -a 37394.924 60.633 7586.8834 0.2648 B- -4785.4899 122.4147 229 040145.099 65.092 + 39 134 95 229 Am -a 42180.414 106.348 7562.5697 0.4644 B- * 229 045282.534 114.169 +0 58 144 86 230 Rn x 42170# 200# 7595# 1# B- 2683# 200# 230 045271# 215# + 56 143 87 230 Fr 39486.769 6.541 7603.7052 0.0284 B- 4970.4627 12.1984 230 042390.787 7.022 + 54 142 88 230 Ra x 34516.306 10.296 7621.9144 0.0448 B- 677.9196 18.8884 230 037054.776 11.053 + 52 141 89 230 Ac x 33838.386 15.835 7621.4604 0.0689 B- 2975.8745 15.8815 230 036327.000 17.000 + 50 140 90 230 Th 30862.512 1.209 7630.9974 0.0053 B- -1311.0313 2.8334 230 033132.267 1.297 + 48 139 91 230 Pa 32173.543 3.038 7621.8958 0.0132 B- 558.5262 4.5919 230 034539.717 3.261 + 46 138 92 230 U -a 31615.017 4.509 7620.9227 0.0196 B- -3621.5986 55.1683 230 033940.114 4.841 + 44 137 93 230 Np -a 35236.615 55.007 7601.7751 0.2392 B- -1695.5543 56.8505 230 037828.060 59.051 + 42 136 94 230 Pu -a 36932.170 14.451 7591.0016 0.0628 B- -5940# 144# 230 039648.313 15.514 + 40 135 95 230 Am -a 42872# 143# 7562# 1# B- * 230 046025# 153# +0 59 145 86 231 Rn x 46550# 300# 7579# 1# B- 4469# 300# 231 049973# 322# + 57 144 87 231 Fr x 42080.575 7.731 7594.5009 0.0335 B- 3864.0868 13.7495 231 045175.353 8.300 + 55 143 88 231 Ra 38216.488 11.370 7607.8418 0.0492 B- 2453.6351 17.3014 231 041027.085 12.206 + 53 142 89 231 Ac x 35762.853 13.041 7615.0768 0.0565 B- 1947.0425 13.0976 231 038393.000 14.000 + 51 141 90 231 Th 33815.811 1.217 7620.1188 0.0053 B- 391.4727 1.4598 231 036302.764 1.306 + 49 140 91 231 Pa 33424.338 1.771 7618.4267 0.0077 B- -381.6138 2.0325 231 035882.500 1.901 + 47 139 92 231 U -a 33805.952 2.670 7613.3879 0.0116 B- -1817.7347 51.1839 231 036292.180 2.866 + 45 138 93 231 Np -a 35623.686 51.154 7602.1321 0.2214 B- -2684.8905 55.6931 231 038243.598 54.916 + 43 137 94 231 Pu -a 38308.577 22.061 7587.1224 0.0955 B- -4101# 301# 231 041125.946 23.683 + 41 136 95 231 Am x 42410# 300# 7566# 1# B- -4860# 424# 231 045529# 322# + 39 135 96 231 Cm x 47270# 300# 7542# 1# B- * 231 050746# 322# +0 58 145 87 232 Fr x 46072.834 13.972 7579.3481 0.0602 B- 5575.8791 16.7023 232 049461.219 15.000 + 56 144 88 232 Ra 40496.955 9.151 7600.0099 0.0394 B- 1342.5322 15.9313 232 043475.267 9.823 + 54 143 89 232 Ac x 39154.423 13.041 7602.4245 0.0562 B- 3707.7131 13.1181 232 042034.000 14.000 + 52 142 90 232 Th 35446.710 1.421 7615.0338 0.0061 B- -499.8388 7.7338 232 038053.606 1.525 + 50 141 91 232 Pa + 35946.549 7.645 7609.5072 0.0330 B- 1337.1034 7.4278 232 038590.205 8.206 + 48 140 92 232 U 34609.445 1.808 7611.8984 0.0078 B- -2750# 100# 232 037154.765 1.941 + 46 139 93 232 Np - 37359# 100# 7597# 0# B- -1001# 101# 232 040107# 107# + 44 138 94 232 Pu -a 38360.915 16.885 7588.9839 0.0728 B- -5059# 300# 232 041182.133 18.126 + 42 137 95 232 Am x 43420# 300# 7564# 1# B- -2913# 361# 232 046613# 322# + 40 136 96 232 Cm -a 46333# 201# 7548# 1# B- * 232 049740# 216# +0 59 146 87 233 Fr x 48920.052 19.561 7569.2398 0.0840 B- 4585.9906 21.3694 233 052517.833 21.000 + 57 145 88 233 Ra 44334.062 8.603 7585.5644 0.0369 B- 3026.0244 15.6228 233 047594.570 9.235 + 55 144 89 233 Ac x 41308.037 13.041 7595.1939 0.0560 B- 2576.3950 13.1184 233 044346.000 14.000 + 53 143 90 233 Th 38731.642 1.424 7602.8937 0.0061 B- 1242.2320 1.1224 233 041580.126 1.528 + 51 142 91 233 Pa 37489.410 1.336 7604.8675 0.0057 B- 570.2993 1.9750 233 040246.535 1.433 + 49 141 92 233 U 36919.111 2.254 7603.9574 0.0097 B- -1029.4197 51.0050 233 039634.294 2.420 + 47 140 93 233 Np -a 37948.531 50.981 7596.1816 0.2188 B- -2103.3047 74.3811 233 040739.421 54.729 + 45 139 94 233 Pu -a 40051.836 54.178 7583.7968 0.2325 B- -3233# 126# 233 042997.411 58.162 + 43 138 95 233 Am -a 43285# 114# 7567# 0# B- -4008# 140# 233 046468# 123# + 41 137 96 233 Cm -a 47293.340 81.095 7546.0020 0.3480 B- -5478# 247# 233 050771.485 87.059 + 39 136 97 233 Bk -a 52771# 233# 7519# 1# B- * 233 056652# 250# +0 58 146 88 234 Ra x 46930.629 8.383 7576.5439 0.0358 B- 2089.4348 16.2945 234 050382.100 9.000 + 56 145 89 234 Ac x 44841.195 13.972 7582.1297 0.0597 B- 4228.2364 14.2103 234 048139.000 15.000 + 54 144 90 234 Th +a 40612.958 2.589 7596.8557 0.0111 B- 274.0882 3.1716 234 043599.801 2.779 + 52 143 91 234 Pa IT 40338.870 4.094 7594.6837 0.0175 B- 2193.9105 3.9998 234 043305.555 4.395 + 50 142 92 234 U 38144.959 1.129 7600.7160 0.0048 B- -1809.8462 8.3205 234 040950.296 1.212 + 48 141 93 234 Np - 39954.806 8.397 7589.6382 0.0359 B- -395.1807 10.7522 234 042893.245 9.014 + 46 140 94 234 Pu -a 40349.986 6.798 7584.6061 0.0291 B- -4112# 160# 234 043317.489 7.298 + 44 139 95 234 Am -a 44462# 160# 7564# 1# B- -2261# 161# 234 047731# 172# + 42 138 96 234 Cm -a 46722.411 17.078 7550.6868 0.0730 B- -6673# 154# 234 050158.568 18.333 + 40 137 97 234 Bk -a 53395# 153# 7519# 1# B- * 234 057322# 164# +0 59 147 88 235 Ra x 51130# 300# 7561# 1# B- 3773# 300# 235 054890# 322# + 57 146 89 235 Ac x 47357.160 13.972 7573.5051 0.0595 B- 3339.4064 19.1127 235 050840.000 15.000 + 55 145 90 235 Th x 44017.754 13.041 7584.3862 0.0555 B- 1728.8531 19.1127 235 047255.000 14.000 + 53 144 91 235 Pa x 42288.901 13.972 7588.4139 0.0595 B- 1370.1184 14.0169 235 045399.000 15.000 + 51 143 92 235 U 40918.782 1.116 7590.9151 0.0048 B- -124.2619 0.8524 235 043928.117 1.198 + 49 142 93 235 Np 41043.044 1.388 7587.0571 0.0059 B- -1139.3021 20.4992 235 044061.518 1.490 + 47 141 94 235 Pu -a 42182.346 20.521 7578.8799 0.0873 B- -2442.2558 56.5932 235 045284.609 22.030 + 45 140 95 235 Am -a 44624.602 52.780 7565.1582 0.2246 B- -3389# 115# 235 047906.478 56.661 + 43 139 96 235 Cm -a 48013# 102# 7547# 0# B- -4757# 413# 235 051545# 110# + 41 138 97 235 Bk x 52770# 401# 7524# 2# B- * 235 056651# 430# +0 58 147 89 236 Ac x 51220.998 38.191 7559.2423 0.1618 B- 4965.7951 40.6669 236 054988.000 41.000 + 56 146 90 236 Th x 46255.203 13.972 7576.9688 0.0592 B- 921.2477 19.7600 236 049657.000 15.000 + 54 145 91 236 Pa x 45333.955 13.972 7577.5573 0.0592 B- 2889.3730 14.0166 236 048668.000 15.000 + 52 144 92 236 U 42444.582 1.112 7586.4854 0.0047 B- -933.5116 50.4152 236 045566.130 1.193 + 50 143 93 236 Np IT 43378.094 50.421 7579.2148 0.2136 B- 476.5854 50.3887 236 046568.296 54.129 + 48 142 94 236 Pu 42901.508 1.810 7577.9192 0.0077 B- -3139# 119# 236 046056.661 1.942 + 46 141 95 236 Am -a 46041# 119# 7561# 1# B- -1812# 120# 236 049427# 127# + 44 140 96 236 Cm -a 47852.820 17.635 7550.3090 0.0747 B- -5689# 361# 236 051372.112 18.931 + 42 139 97 236 Bk -a 53542# 361# 7523# 2# B- * 236 057479# 387# +0 59 148 89 237 Ac x 54020# 400# 7550# 2# B- 4065# 400# 237 057993# 429# + 57 147 90 237 Th x 49955.097 15.835 7563.4433 0.0668 B- 2427.4736 20.5140 237 053629.000 17.000 + 55 146 91 237 Pa x 47527.624 13.041 7570.3847 0.0550 B- 2137.4905 13.0962 237 051023.000 14.000 + 53 145 92 237 U 45390.133 1.202 7576.1026 0.0051 B- 518.5338 0.5200 237 048728.309 1.290 + 51 144 93 237 Np 44871.599 1.120 7574.9895 0.0047 B- -220.0630 1.2944 237 048171.640 1.201 + 49 143 94 237 Pu 45091.662 1.697 7570.7599 0.0072 B- -1478# 59# 237 048407.888 1.821 + 47 142 95 237 Am -a 46570# 59# 7561# 0# B- -2677# 95# 237 049995# 64# + 45 141 96 237 Cm -a 49247.151 74.399 7546.6241 0.3139 B- -3963# 242# 237 052868.988 79.870 + 43 140 97 237 Bk -a 53210# 230# 7527# 1# B- -4728# 250# 237 057123# 247# + 41 139 98 237 Cf -a 57938.255 97.347 7503.3507 0.4107 B- * 237 062199.272 104.506 +0 58 148 90 238 Th +a 52525# 283# 7555# 1# B- 1631# 284# 238 056388# 304# + 56 147 91 238 Pa x 50894.043 15.835 7558.3449 0.0665 B- 3586.3111 15.9056 238 054637.000 17.000 + 54 146 92 238 U 47307.732 1.492 7570.1262 0.0063 B- -146.8652 1.2006 238 050786.936 1.601 + 52 145 93 238 Np -n 47454.597 1.137 7566.2220 0.0048 B- 1291.4491 0.4573 238 050944.603 1.220 + 50 144 94 238 Pu 46163.148 1.138 7568.3611 0.0048 B- -2258.2731 58.9005 238 049558.175 1.221 + 48 143 95 238 Am -a 48421.421 58.911 7555.5853 0.2475 B- -1023.7818 60.1587 238 051982.531 63.243 + 46 142 96 238 Cm -a 49445.203 12.234 7547.9966 0.0514 B- -4771# 256# 238 053081.606 13.133 + 44 141 97 238 Bk -a 54216# 256# 7525# 1# B- -3061# 393# 238 058204# 275# + 42 140 98 238 Cf x 57278# 298# 7509# 1# B- * 238 061490# 320# +0 59 149 90 239 Th x 56500# 400# 7540# 2# B- 3162# 445# 239 060655# 429# + 57 148 91 239 Pa x 53337# 196# 7550# 1# B- 2765# 196# 239 057260# 210# + 55 147 92 239 U -n 50572.668 1.502 7558.5624 0.0063 B- 1261.6634 1.4935 239 054291.989 1.612 + 53 146 93 239 Np 49311.005 1.310 7560.5680 0.0055 B- 722.7849 0.9304 239 052937.538 1.406 + 51 145 94 239 Pu 48588.220 1.112 7560.3187 0.0047 B- -802.1402 1.6635 239 052161.596 1.194 + 49 144 95 239 Am -a 49390.360 1.982 7553.6891 0.0083 B- -1756.6021 150.0740 239 053022.729 2.127 + 47 143 96 239 Cm -a 51146.962 150.070 7543.0659 0.6279 B- -3103# 256# 239 054908.519 161.107 + 45 142 97 239 Bk -a 54250# 207# 7527# 1# B- -3952# 239# 239 058239# 222# + 43 141 98 239 Cf -a 58202# 120# 7507# 1# B- -5429# 323# 239 062482# 129# + 41 140 99 239 Es x 63630# 300# 7481# 1# B- * 239 068310# 322# +0 58 149 91 240 Pa x 57010# 200# 7537# 1# B- 4295# 200# 240 061203# 215# + 56 148 92 240 U 52715.497 2.553 7551.7705 0.0106 B- 399.2685 17.0830 240 056592.411 2.740 + 54 147 93 240 Np 52316.229 17.032 7550.1743 0.0710 B- 2190.9095 17.0151 240 056163.778 18.284 + 52 146 94 240 Pu 50125.319 1.105 7556.0433 0.0046 B- -1384.7902 13.7882 240 053811.740 1.186 + 50 145 95 240 Am +n 51510.110 13.832 7547.0136 0.0576 B- -214.1127 13.8967 240 055298.374 14.849 + 48 144 96 240 Cm 51724.222 1.905 7542.8617 0.0079 B- -3940# 150# 240 055528.233 2.045 + 46 143 97 240 Bk - 55664# 150# 7523# 1# B- -2324# 151# 240 059758# 161# + 44 142 98 240 Cf -a 57988.719 18.034 7510.2400 0.0751 B- -6237# 366# 240 062253.447 19.360 + 42 141 99 240 Es -a 64225# 366# 7481# 2# B- * 240 068949# 393# +0 59 150 91 241 Pa x 59740# 300# 7528# 1# B- 3543# 358# 241 064134# 322# + 57 149 92 241 U x 56197# 196# 7539# 1# B- 1882# 220# 241 060330# 210# + 55 148 93 241 Np + 54315.115 100.006 7544.0426 0.4150 B- 1360.0000 100.0000 241 058309.671 107.360 + 53 147 94 241 Pu 52955.115 1.105 7546.4395 0.0046 B- 20.7799 0.1658 241 056849.651 1.186 + 51 146 95 241 Am 52934.335 1.113 7543.2795 0.0046 B- -767.4346 1.1685 241 056827.343 1.195 + 49 145 96 241 Cm 53701.770 1.607 7536.8488 0.0067 B- -2279# 165# 241 057651.218 1.725 + 47 144 97 241 Bk +a 55981# 165# 7524# 1# B- -3346# 235# 241 060098# 178# + 45 143 98 241 Cf -a 59327# 167# 7507# 1# B- -4567# 285# 241 063690# 180# + 43 142 99 241 Es -a 63893# 231# 7485# 1# B- -5327# 379# 241 068592# 248# + 41 141 100 241 Fm x 69220# 300# 7459# 1# B- * 241 074311# 322# +0 58 150 92 242 U +a 58620# 201# 7532# 1# B- 1203# 283# 242 062931# 215# + 56 149 93 242 Np + 57416.876 200.004 7533.4042 0.8265 B- 2700.0000 200.0000 242 061639.548 214.712 + 54 148 94 242 Pu 54716.876 1.245 7541.3284 0.0052 B- -751.1373 0.7080 242 058740.979 1.336 + 52 147 95 242 Am -n 55468.014 1.118 7534.9917 0.0046 B- 664.3145 0.4143 242 059547.358 1.199 + 50 146 96 242 Cm 54803.699 1.141 7534.5040 0.0047 B- -2948# 135# 242 058834.187 1.224 + 48 145 97 242 Bk IT 57752# 135# 7519# 1# B- -1635# 135# 242 061999# 144# + 46 144 98 242 Cf -a 59386.982 12.892 7509.0991 0.0533 B- -5414# 257# 242 063754.544 13.840 + 44 143 99 242 Es -a 64801# 257# 7483# 1# B- -3598# 476# 242 069567# 276# + 42 142 100 242 Fm x 68400# 401# 7465# 2# B- * 242 073430# 430# +0 59 151 92 243 U x 62480# 300# 7518# 1# B- 2674# 302# 243 067075# 322# + 57 150 93 243 Np IT 59806# 32# 7526# 0# B- 2051# 32# 243 064204# 34# + 55 149 94 243 Pu 57754.561 2.542 7531.0087 0.0105 B- 579.5559 2.6216 243 062002.068 2.728 + 53 148 95 243 Am 57175.005 1.388 7530.1742 0.0057 B- -6.9302 1.5692 243 061379.889 1.490 + 51 147 96 243 Cm -a 57181.936 1.496 7526.9261 0.0062 B- -1507.6936 4.5065 243 061387.329 1.605 + 49 146 97 243 Bk -a 58689.629 4.524 7517.5021 0.0186 B- -2300# 181# 243 063005.905 4.856 + 47 145 98 243 Cf -a 60990# 181# 7505# 1# B- -3757# 275# 243 065475# 194# + 45 144 99 243 Es -a 64747# 207# 7486# 1# B- -4569# 245# 243 069508# 222# + 43 143 100 243 Fm -a 69316# 130# 7464# 1# B- * 243 074414# 140# +0 58 151 93 244 Np x 63240# 100# 7514# 0# B- 3434# 100# 244 067891# 107# + 56 150 94 244 Pu 59806.021 2.346 7524.8154 0.0096 B- -73.1143 2.6856 244 064204.401 2.518 + 54 149 95 244 Am + 59879.135 1.491 7521.3095 0.0061 B- 1427.3000 1.0000 244 064282.892 1.600 + 52 148 96 244 Cm -a 58451.835 1.106 7523.9527 0.0045 B- -2261.9902 14.3567 244 062750.622 1.187 + 50 147 97 244 Bk -a 60713.825 14.399 7511.4759 0.0590 B- -764.2709 14.5724 244 065178.969 15.457 + 48 146 98 244 Cf 61478.096 2.617 7505.1373 0.0107 B- -4547# 181# 244 065999.447 2.809 + 46 145 99 244 Es -a 66026# 181# 7483# 1# B- -2938# 271# 244 070881# 195# + 44 144 100 244 Fm -a 68964# 201# 7468# 1# B- -6634# 425# 244 074036# 216# + 42 143 101 244 Md -a 75597# 374# 7438# 2# B- * 244 081157# 402# +0 59 152 93 245 Np x 65850# 200# 7506# 1# B- 2672# 201# 245 070693# 215# + 57 151 94 245 Pu -n 63178.173 13.620 7513.2822 0.0556 B- 1277.7559 13.7334 245 067824.554 14.621 + 55 150 95 245 Am +a 61900.417 1.886 7515.3043 0.0077 B- 895.8929 1.5491 245 066452.827 2.024 + 53 149 96 245 Cm 61004.524 1.149 7515.7677 0.0047 B- -809.2519 1.4964 245 065491.047 1.233 + 51 148 97 245 Bk -a 61813.776 1.792 7509.2714 0.0073 B- -1571.3755 2.5861 245 066359.814 1.923 + 49 147 98 245 Cf 63385.151 2.428 7499.6644 0.0099 B- -2930# 165# 245 068046.755 2.606 + 47 146 99 245 Es IT 66315# 165# 7485# 1# B- -3877# 256# 245 071192# 178# + 45 145 100 245 Fm -a 70192# 195# 7465# 1# B- -5133# 325# 245 075354# 210# + 43 144 101 245 Md -a 75325# 260# 7441# 1# B- * 245 080864# 279# +0 58 152 94 246 Pu 65394.772 14.985 7506.5401 0.0609 B- 401# 14# 246 070204.172 16.087 + 56 151 95 246 Am IT 64994# 18# 7505# 0# B- 2377# 18# 246 069774# 19# + 54 150 96 246 Cm 62616.912 1.525 7511.4716 0.0062 B- -1350.0000 60.0000 246 067222.016 1.637 + 52 149 97 246 Bk - 63966.912 60.019 7502.8035 0.2440 B- -123.3159 60.0198 246 068671.300 64.433 + 50 148 98 246 Cf 64090.228 1.514 7499.1220 0.0062 B- -3728.5741 89.9373 246 068803.685 1.625 + 48 147 99 246 Es 67818.802 89.925 7480.7849 0.3655 B- -2372.3848 90.9577 246 072806.474 96.538 + 46 146 100 246 Fm -a 70191.187 13.670 7467.9608 0.0556 B- -5924# 260# 246 075353.334 14.675 + 44 145 101 246 Md -a 76115# 260# 7441# 1# B- * 246 081713# 279# +0 59 153 94 247 Pu x 69210# 200# 7493# 1# B- 2057# 224# 247 074300# 215# + 57 152 95 247 Am + 67153# 100# 7499# 0# B- 1620# 100# 247 072092# 107# + 55 151 96 247 Cm 65533.105 3.797 7501.9318 0.0154 B- 43.5841 6.3245 247 070352.678 4.076 + 53 150 97 247 Bk -a 65489.521 5.189 7498.9408 0.0210 B- -619.8711 15.2376 247 070305.889 5.570 + 51 149 98 247 Cf +a 66109.392 14.327 7493.2638 0.0580 B- -2469.0006 24.1495 247 070971.348 15.380 + 49 148 99 247 Es +a 68578.393 19.441 7480.1005 0.0787 B- -3094# 182# 247 073621.929 20.870 + 47 147 100 247 Fm +a 71672# 181# 7464# 1# B- -4263# 275# 247 076944# 194# + 45 146 101 247 Md -a 75936# 207# 7444# 1# B- * 247 081520# 223# +0 58 153 95 248 Am + 70563# 200# 7487# 1# B- 3170# 200# 248 075752# 215# + 56 152 96 248 Cm 67392.748 2.358 7496.7291 0.0095 B- -738.3049 50.0026 248 072349.086 2.531 + 54 151 97 248 Bk +a 68131.053 50.058 7490.5975 0.2018 B- 893.1015 50.3143 248 073141.689 53.739 + 52 150 98 248 Cf -a 67237.951 5.121 7491.0440 0.0207 B- -3061# 53# 248 072182.905 5.497 + 50 149 99 248 Es -a 70299# 52# 7476# 0# B- -1599# 53# 248 075469# 56# + 48 148 100 248 Fm 71897.793 8.497 7465.9451 0.0343 B- -5050# 184# 248 077185.451 9.122 + 46 147 101 248 Md -a 76948# 184# 7442# 1# B- -3741# 290# 248 082607# 198# + 44 146 102 248 No -a 80689# 224# 7424# 1# B- * 248 086623# 241# +0 59 154 95 249 Am x 73104# 298# 7479# 1# B- 2353# 298# 249 078480# 320# + 57 153 96 249 Cm -n 70750.696 2.371 7485.5510 0.0095 B- 904.3630 2.5934 249 075953.992 2.545 + 55 152 97 249 Bk + 69846.333 1.248 7486.0410 0.0050 B- 123.6000 0.4000 249 074983.118 1.339 + 53 151 98 249 Cf 69722.733 1.182 7483.3954 0.0048 B- -1452# 30# 249 074850.428 1.269 + 51 150 99 249 Es -a 71175# 30# 7474# 0# B- -2344# 31# 249 076409# 32# + 49 149 100 249 Fm 73519.143 6.212 7461.8649 0.0249 B- -3661.8091 164.5418 249 078926.042 6.668 + 47 148 101 249 Md 77180.952 164.425 7444.0169 0.6603 B- -4606# 324# 249 082857.155 176.516 + 45 147 102 249 No -a 81787# 279# 7422# 1# B- * 249 087802# 300# +0 58 154 96 250 Cm -nn 72989.588 10.274 7478.9385 0.0411 B- 37.5820 10.6414 250 078357.541 11.029 + 56 153 97 250 Bk +a 72952.006 2.898 7475.9594 0.0116 B- 1781.6696 2.4561 250 078317.195 3.110 + 54 152 98 250 Cf -a 71170.336 1.538 7479.9567 0.0062 B- -2055# 100# 250 076404.494 1.650 + 52 151 99 250 Es - 73225# 100# 7469# 0# B- -847# 100# 250 078611# 107# + 50 150 100 250 Fm 74072.193 7.888 7462.0905 0.0316 B- -4326.9476 91.2615 250 079519.765 8.468 + 48 149 101 250 Md 78399.140 90.920 7441.6533 0.3637 B- -3167# 220# 250 084164.934 97.606 + 46 148 102 250 No -a 81566# 200# 7426# 1# B- * 250 087565# 215# +0 59 155 96 251 Cm + 76647.981 22.698 7466.7233 0.0904 B- 1420.0000 20.0000 251 082284.988 24.367 + 57 154 97 251 Bk + 75227.981 10.734 7469.2637 0.0428 B- 1093.0000 10.0000 251 080760.555 11.523 + 55 153 98 251 Cf -a 74134.981 3.901 7470.5014 0.0155 B- -376.5660 6.4677 251 079587.171 4.187 + 53 152 99 251 Es -a 74511.547 5.288 7465.8842 0.0211 B- -1447.2610 15.2387 251 079991.431 5.676 + 51 151 100 251 Fm 75958.808 14.292 7457.0013 0.0569 B- -3007.9406 23.7108 251 081545.130 15.342 + 49 150 101 251 Md +a 78966.749 18.919 7441.9005 0.0754 B- -3882# 182# 251 084774.287 20.310 + 47 149 102 251 No IT 82849# 181# 7423# 1# B- -4981# 270# 251 088942# 194# + 45 148 103 251 Lr x 87830# 200# 7400# 1# B- * 251 094289# 215# +0 60 156 96 252 Cm x 79056# 298# 7460# 1# B- 521# 359# 252 084870# 320# + 58 155 97 252 Bk + 78535# 200# 7459# 1# B- 2500# 200# 252 084310# 215# + 56 154 98 252 Cf -a 76034.610 2.358 7465.3474 0.0094 B- -1260.0000 50.0000 252 081626.507 2.531 + 54 153 99 252 Es - 77294.610 50.056 7457.2428 0.1986 B- 477.9998 50.3220 252 082979.173 53.736 + 52 152 100 252 Fm -a 76816.611 5.221 7456.0351 0.0207 B- -3650.5075 91.4356 252 082466.019 5.604 + 50 151 101 252 Md x 80467.118 91.286 7438.4444 0.3622 B- -2404.2523 91.7581 252 086385.000 98.000 + 48 150 102 252 No 82871.370 9.292 7425.7992 0.0369 B- -5666# 185# 252 088966.070 9.975 + 46 149 103 252 Lr -a 88537# 185# 7400# 1# B- * 252 095048# 198# +0 59 156 97 253 Bk -a 80929# 359# 7451# 1# B- 1627# 359# 253 086880# 385# + 57 155 98 253 Cf -a 79301.562 4.257 7454.8297 0.0168 B- 291.0753 4.3850 253 085133.723 4.570 + 55 154 99 253 Es -a 79010.486 1.249 7452.8879 0.0049 B- -335.0623 1.0782 253 084821.241 1.341 + 53 153 100 253 Fm -a 79345.549 1.549 7448.4712 0.0061 B- -1827# 31# 253 085180.945 1.662 + 51 152 101 253 Md -a 81173# 31# 7438# 0# B- -3186# 32# 253 087143# 34# + 49 151 102 253 No 84358.696 6.912 7422.4719 0.0273 B- -4164.7752 164.6791 253 090562.780 7.420 + 47 150 103 253 Lr 88523.471 164.534 7402.9180 0.6503 B- -5118# 442# 253 095033.850 176.634 + 45 149 104 253 Rf -a 93642# 410# 7380# 2# B- * 253 100528# 440# +0 60 157 97 254 Bk x 84393# 298# 7440# 1# B- 3052# 298# 254 090600# 320# + 58 156 98 254 Cf -a 81341.395 11.462 7449.2259 0.0451 B- -652.7561 11.8014 254 087323.575 12.304 + 56 155 99 254 Es -a 81994.151 2.936 7443.5759 0.0116 B- 1091.6300 2.2858 254 088024.337 3.152 + 54 154 100 254 Fm -a 80902.521 1.843 7444.7936 0.0073 B- -2550# 100# 254 086852.424 1.978 + 52 153 101 254 Md - 83453# 100# 7432# 0# B- -1271# 100# 254 089590# 107# + 50 152 102 254 No 84723.312 9.658 7423.5909 0.0380 B- -4922.5753 91.8208 254 090954.211 10.367 + 48 151 103 254 Lr -a 89645.887 91.312 7401.1306 0.3595 B- -3555# 298# 254 096238.813 98.026 + 46 150 104 254 Rf -a 93201# 283# 7384# 1# B- * 254 100055# 304# +0 59 157 98 255 Cf + 84809# 200# 7438# 1# B- 720# 200# 255 091046# 215# + 57 156 99 255 Es -a 84089.237 10.817 7437.8216 0.0424 B- 288.7717 10.1024 255 090273.504 11.612 + 55 155 100 255 Fm -a 83800.465 3.934 7435.8860 0.0154 B- -1041.6037 6.7172 255 089963.495 4.223 + 53 154 101 255 Md -a 84842.069 5.567 7428.7333 0.0218 B- -1969.8648 15.1096 255 091081.702 5.976 + 51 153 102 255 No 86811.934 14.047 7417.9403 0.0551 B- -3135.3716 22.5952 255 093196.439 15.079 + 49 152 103 255 Lr x 89947.305 17.698 7402.5767 0.0694 B- -4382# 182# 255 096562.399 19.000 + 47 151 104 255 Rf -a 94329# 181# 7382# 1# B- -5265# 336# 255 101267# 194# + 45 150 105 255 Db -a 99595# 283# 7359# 1# B- * 255 106919# 304# +0 60 158 98 256 Cf -a 87041# 314# 7432# 1# B- -144# 330# 256 093442# 338# + 58 157 99 256 Es + 87185# 100# 7428# 0# B- 1700# 100# 256 093597# 107# + 56 156 100 256 Fm -a 85484.796 3.020 7431.7888 0.0118 B- -1971# 124# 256 091771.699 3.241 + 54 155 101 256 Md IT 87456# 124# 7421# 0# B- -367# 124# 256 093888# 133# + 52 154 102 256 No -a 87823.046 7.548 7416.5429 0.0295 B- -3923.5573 83.2459 256 094281.912 8.103 + 50 153 103 256 Lr x 91746.603 82.903 7398.1605 0.3238 B- -2475.3893 84.8025 256 098494.024 89.000 + 48 152 104 256 Rf -a 94221.992 17.848 7385.4349 0.0697 B- -6076# 188# 256 101151.464 19.160 + 46 151 105 256 Db -a 100298# 187# 7359# 1# B- * 256 107674# 201# +0 59 158 99 257 Es -a 89403# 411# 7422# 2# B- 813# 411# 257 095979# 441# + 57 157 100 257 Fm -a 88590.137 4.350 7422.1942 0.0169 B- -402.3347 4.5748 257 095105.419 4.669 + 55 156 101 257 Md -a 88992.472 1.569 7417.5845 0.0061 B- -1254.5923 6.1695 257 095537.343 1.683 + 53 155 102 257 No -a 90247.064 6.197 7409.6587 0.0241 B- -2418# 45# 257 096884.203 6.652 + 51 154 103 257 Lr -a 92665# 44# 7397# 0# B- -3201# 45# 257 099480# 47# + 49 153 104 257 Rf -a 95866.389 10.817 7381.7053 0.0421 B- -4287.8969 164.9888 257 102916.796 11.612 + 47 152 105 257 Db 100154.285 164.634 7361.9767 0.6406 B- * 257 107520.042 176.741 +0 60 159 99 258 Es x 92702# 401# 7412# 2# B- 2276# 448# 258 099520# 430# + 58 158 100 258 Fm -a 90426# 200# 7418# 1# B- -1264# 200# 258 097077# 215# + 56 157 101 258 Md -a 91690.350 3.474 7409.6615 0.0135 B- 213# 100# 258 098433.634 3.729 + 54 156 102 258 No -a 91477# 100# 7407# 0# B- -3304# 143# 258 098205# 107# + 52 155 103 258 Lr -a 94782# 102# 7392# 0# B- -1562# 103# 258 101753# 109# + 50 154 104 258 Rf -a 96344.338 16.104 7382.5257 0.0624 B- -5163.3651 93.2584 258 103429.895 17.288 + 48 153 105 258 Db -a 101507.703 91.857 7359.4803 0.3560 B- -3788# 423# 258 108972.995 98.613 + 46 152 106 258 Sg -a 105296# 413# 7342# 2# B- * 258 113040# 443# +0 59 159 100 259 Fm -a 93704# 283# 7407# 1# B- 140# 300# 259 100596# 304# + 57 158 101 259 Md -a 93564# 101# 7405# 0# B- -515# 101# 259 100445# 108# + 55 157 102 259 No -a 94079.381 6.362 7399.9714 0.0246 B- -1771# 71# 259 100998.364 6.829 + 53 156 103 259 Lr -a 95851# 71# 7390# 0# B- -2516# 101# 259 102900# 76# + 51 155 104 259 Rf -a 98367# 72# 7377# 0# B- -3624# 92# 259 105601# 78# + 49 154 105 259 Db -a 101991.021 56.685 7360.3626 0.2189 B- -4528# 190# 259 109491.859 60.854 + 47 153 106 259 Sg -a 106519# 181# 7340# 1# B- * 259 114353# 194# +0 60 160 100 260 Fm -a 95766# 435# 7402# 2# B- -784# 537# 260 102809# 467# + 58 159 101 260 Md -a 96550# 316# 7396# 1# B- 940# 374# 260 103650# 339# + 56 158 102 260 No -a 95610# 200# 7397# 1# B- -2667# 236# 260 102641# 215# + 54 157 103 260 Lr -a 98277# 125# 7383# 0# B- -871# 236# 260 105504# 134# + 52 156 104 260 Rf -a 99148# 200# 7377# 1# B- -4525# 221# 260 106440# 215# + 50 155 105 260 Db -a 103673# 93# 7357# 0# B- -2875# 95# 260 111297# 100# + 48 154 106 260 Sg -a 106547.495 20.536 7342.5632 0.0790 B- -6576# 197# 260 114383.435 22.045 + 46 153 107 260 Bh -a 113123# 196# 7314# 1# B- * 260 121443# 211# +0 59 160 101 261 Md -a 98578# 509# 7391# 2# B- 123# 547# 261 105828# 546# + 57 159 102 261 No -a 98455# 200# 7388# 1# B- -1102# 283# 261 105696# 215# + 55 158 103 261 Lr -a 99557# 200# 7381# 1# B- -1761# 211# 261 106879# 215# + 53 157 104 261 Rf -a 101318.233 65.663 7371.3858 0.2516 B- -2990# 128# 261 108769.591 70.492 + 51 156 105 261 Db -a 104308# 110# 7357# 0# B- -3697# 112# 261 111979# 118# + 49 155 106 261 Sg -a 108005.004 18.494 7339.7710 0.0709 B- -5074.4052 180.7519 261 115948.135 19.853 + 47 154 107 261 Bh -a 113079.410 179.803 7317.3313 0.6889 B- * 261 121395.733 193.026 +0 60 161 101 262 Md -a 101667# 448# 7382# 2# B- 1566# 575# 262 109144# 481# + 58 160 102 262 No -a 100101# 361# 7385# 1# B- -2004# 412# 262 107463# 387# + 56 159 103 262 Lr -a 102105# 200# 7374# 1# B- -287# 300# 262 109615# 215# + 54 158 104 262 Rf -a 102392# 224# 7370# 1# B- -3861# 265# 262 109923# 240# + 52 157 105 262 Db -a 106253# 143# 7352# 1# B- -2116# 145# 262 114067# 154# + 50 156 106 262 Sg -a 108369.072 22.167 7341.1736 0.0846 B- -5883.0463 95.6774 262 116338.978 23.797 + 48 155 107 262 Bh -a 114252.119 93.074 7315.7331 0.3552 B- * 262 122654.688 99.919 +0 59 161 102 263 No -a 103129# 490# 7376# 2# B- -540# 539# 263 110714# 526# + 57 160 103 263 Lr -a 103669# 224# 7371# 1# B- -1087# 271# 263 111293# 240# + 55 159 104 263 Rf -a 104757# 153# 7364# 1# B- -2353# 227# 263 112461# 164# + 53 158 105 263 Db -a 107110# 168# 7352# 1# B- -3085# 193# 263 114987# 180# + 51 157 106 263 Sg -a 110195# 95# 7337# 0# B- -4301# 320# 263 118299# 101# + 49 156 107 263 Bh -a 114496# 305# 7318# 1# B- -5182# 363# 263 122916# 328# + 47 155 108 263 Hs -a 119678# 197# 7295# 1# B- * 263 128479# 212# +0 60 162 102 264 No -a 105011# 591# 7371# 2# B- -1364# 734# 264 112734# 634# + 58 161 103 264 Lr -a 106375# 436# 7363# 2# B- 300# 566# 264 114198# 468# + 56 160 104 264 Rf -a 106075# 361# 7361# 1# B- -3187# 431# 264 113876# 387# + 54 159 105 264 Db -a 109262# 236# 7346# 1# B- -1521# 368# 264 117297# 253# + 52 158 106 264 Sg -a 110783# 283# 7338# 1# B- -5175# 334# 264 118930# 304# + 50 157 107 264 Bh -a 115958# 177# 7315# 1# B- -3605# 180# 264 124486# 190# + 48 156 108 264 Hs -a 119563.165 28.881 7298.3762 0.1094 B- * 264 128356.330 31.005 +0 59 162 103 265 Lr -a 108233# 547# 7359# 2# B- -457# 655# 265 116193# 587# + 57 161 104 265 Rf -a 108690# 361# 7354# 1# B- -1692# 424# 265 116683# 387# + 55 160 105 265 Db -a 110382# 224# 7345# 1# B- -2412# 263# 265 118500# 240# + 53 159 106 265 Sg -a 112794# 139# 7333# 1# B- -3601# 277# 265 121089# 149# + 51 158 107 265 Bh -a 116395# 239# 7316# 1# B- -4505# 240# 265 124955# 257# + 49 157 108 265 Hs -a 120900.245 23.958 7296.2474 0.0904 B- -5724# 439# 265 129791.744 25.719 + 47 156 109 265 Mt -a 126624# 439# 7272# 2# B- * 265 135937# 471# +0 60 163 103 266 Lr -a 111662# 539# 7349# 2# B- 1526# 679# 266 119874# 579# + 58 162 104 266 Rf -a 110136# 412# 7351# 2# B- -2604# 500# 266 118236# 443# + 56 161 105 266 Db -a 112740# 283# 7339# 1# B- -877# 374# 266 121032# 304# + 54 160 106 266 Sg -a 113617# 245# 7332# 1# B- -4487# 294# 266 121973# 263# + 52 159 107 266 Bh -a 118104# 163# 7313# 1# B- -3036# 165# 266 126790# 175# + 50 158 108 266 Hs -a 121139.675 27.106 7298.2611 0.1019 B- -6533.0066 100.2087 266 130048.783 29.099 + 48 157 109 266 Mt -a 127672.681 96.473 7270.7598 0.3627 B- * 266 137062.253 103.568 +0 59 163 104 267 Rf -a 113444# 575# 7342# 2# B- -570# 686# 267 121787# 617# + 57 162 105 267 Db -a 114014# 374# 7337# 1# B- -1792# 457# 267 122399# 402# + 55 161 106 267 Sg -a 115806# 261# 7327# 1# B- -2958# 371# 267 124323# 281# + 53 160 107 267 Bh -a 118765# 263# 7313# 1# B- -3893# 279# 267 127499# 282# + 51 159 108 267 Hs -a 122658# 95# 7295# 0# B- -5133# 512# 267 131678# 102# + 49 158 109 267 Mt -a 127791# 503# 7273# 2# B- -6089# 543# 267 137189# 540# + 47 157 110 267 Ds -a 133880# 204# 7248# 1# B- * 267 143726# 219# +0 60 164 104 268 Rf -a 115476# 662# 7337# 2# B- -1584# 848# 268 123968# 711# + 58 163 105 268 Db -a 117060# 529# 7328# 2# B- 260# 707# 268 125669# 568# + 56 162 106 268 Sg -a 116800# 469# 7326# 2# B- -3907# 605# 268 125389# 504# + 54 161 107 268 Bh -a 120707# 382# 7309# 1# B- -2261# 486# 268 129584# 410# + 52 160 108 268 Hs -a 122968# 300# 7297# 1# B- -6183# 380# 268 132011# 322# + 50 159 109 268 Mt -a 129151# 233# 7271# 1# B- -4497# 381# 268 138649# 250# + 48 158 110 268 Ds -a 133648# 301# 7252# 1# B- * 268 143477# 324# +0 59 164 105 269 Db -a 119148# 624# 7323# 2# B- -544# 724# 269 127911# 669# + 57 163 106 269 Sg -a 119692# 368# 7318# 1# B- -1785# 525# 269 128495# 395# + 55 162 107 269 Bh -a 121477# 374# 7309# 1# B- -3016# 396# 269 130411# 402# + 53 161 108 269 Hs -a 124493# 131# 7294# 0# B- -4807# 338# 269 133649# 141# + 51 160 109 269 Mt -a 129300# 312# 7274# 1# B- -5535# 313# 269 138809# 335# + 49 159 110 269 Ds -a 134834.671 31.403 7250.1551 0.1167 B- * 269 144750.965 33.712 +0 60 165 105 270 Db -a 122397# 575# 7314# 2# B- 966# 735# 270 131399# 617# + 58 164 106 270 Sg -a 121431# 458# 7314# 2# B- -2799# 547# 270 130362# 492# + 56 163 107 270 Bh -a 124230# 299# 7301# 1# B- -882# 388# 270 133366# 320# + 54 162 108 270 Hs -a 125112# 248# 7295# 1# B- -5597# 313# 270 134313# 266# + 52 161 109 270 Mt -a 130709# 191# 7271# 1# B- -3973# 195# 270 140322# 205# + 50 160 110 270 Ds -a 134681.584 39.275 7253.7634 0.1455 B- * 270 144586.620 42.163 +0 59 165 106 271 Sg -a 124617# 591# 7305# 2# B- -1242# 705# 271 133782# 634# + 57 164 107 271 Bh -a 125859# 384# 7298# 1# B- -1832# 473# 271 135115# 412# + 55 163 108 271 Hs -a 127691# 276# 7288# 1# B- -3409# 430# 271 137082# 296# + 53 162 109 271 Mt -a 131100# 330# 7273# 1# B- -4853# 344# 271 140741# 354# + 51 161 110 271 Ds -a 135952# 97# 7252# 0# B- * 271 145951# 104# +0 60 166 106 272 Sg -a 126520# 692# 7301# 3# B- -2267# 873# 272 135825# 743# + 58 165 107 272 Bh -a 128787# 532# 7290# 2# B- -217# 737# 272 138259# 571# + 56 164 108 272 Hs -a 129004# 510# 7286# 2# B- -4477# 704# 272 138492# 547# + 54 163 109 272 Mt -a 133481# 485# 7267# 2# B- -2601# 645# 272 143298# 521# + 52 162 110 272 Ds -a 136083# 424# 7255# 2# B- -6690# 484# 272 146091# 456# + 50 161 111 272 Rg -a 142773# 233# 7227# 1# B- * 272 153273# 251# +0 61 167 106 273 Sg x 129920# 400# 7292# 1# B- -763# 767# 273 139475# 429# + 59 166 107 273 Bh -a 130683# 655# 7286# 2# B- -1084# 754# 273 140294# 703# + 57 165 108 273 Hs -a 131767# 374# 7279# 1# B- -3015# 565# 273 141458# 401# + 55 164 109 273 Mt -a 134782# 424# 7265# 2# B- -3503# 447# 273 144695# 455# + 53 163 110 273 Ds -a 138285# 142# 7250# 1# B- -4600# 424# 273 148455# 152# + 51 162 111 273 Rg -a 142885# 400# 7230# 1# B- * 273 153393# 429# +0 60 167 107 274 Bh -a 133762# 578# 7278# 2# B- 356# 744# 274 143599# 620# + 58 166 108 274 Hs -a 133406# 469# 7276# 2# B- -3843# 602# 274 143217# 504# + 56 165 109 274 Mt -a 137249# 377# 7259# 1# B- -1948# 542# 274 147343# 404# + 54 164 110 274 Ds -a 139197# 389# 7249# 1# B- -5415# 442# 274 149434# 418# + 52 163 111 274 Rg -a 144612# 209# 7227# 1# B- * 274 155247# 225# +0 61 168 107 275 Bh x 135780# 600# 7273# 2# B- -712# 844# 275 145766# 644# + 59 167 108 275 Hs -a 136492# 593# 7268# 2# B- -2275# 709# 275 146530# 637# + 57 166 109 275 Mt -a 138767# 387# 7257# 1# B- -2899# 516# 275 148972# 416# + 55 165 110 275 Ds -a 141666# 340# 7243# 1# B- -3729# 561# 275 152085# 366# + 53 164 111 275 Rg -a 145395# 446# 7227# 2# B- * 275 156088# 479# +0 62 169 107 276 Bh x 138950# 600# 7265# 2# B- 765# 937# 276 149169# 644# + 60 168 108 276 Hs -a 138185# 720# 7265# 3# B- -3127# 895# 276 148348# 773# + 58 167 109 276 Mt -a 141312# 532# 7250# 2# B- -1227# 764# 276 151705# 571# + 56 166 110 276 Ds -a 142539# 548# 7243# 2# B- -4847# 834# 276 153022# 588# + 54 165 111 276 Rg -a 147386# 629# 7223# 2# B- -2974# 804# 276 158226# 675# + 52 164 112 276 Cn x 150360# 500# 7209# 2# B- * 276 161418# 537# +0 63 170 107 277 Bh x 141100# 600# 7260# 2# B- -275# 748# 277 151477# 644# + 61 169 108 277 Hs -a 141375# 447# 7256# 2# B- -1633# 799# 277 151772# 480# + 59 168 109 277 Mt -a 143008# 662# 7247# 2# B- -2084# 770# 277 153525# 711# + 57 167 110 277 Ds -a 145092# 392# 7237# 1# B- -3315# 611# 277 155763# 421# + 55 166 111 277 Rg -a 148407# 469# 7222# 2# B- -3925# 493# 277 159322# 504# + 53 165 112 277 Cn -a 152332# 153# 7205# 1# B- * 277 163535# 165# +0 64 171 107 278 Bh x 144370# 400# 7251# 1# B- 1150# 500# 278 154988# 429# + 62 170 108 278 Hs x 143220# 300# 7252# 1# B- -2547# 652# 278 153753# 322# + 60 169 109 278 Mt -a 145767# 579# 7240# 2# B- -484# 771# 278 156487# 621# + 58 168 110 278 Ds -a 146251# 510# 7236# 2# B- -4270# 641# 278 157007# 548# + 56 167 111 278 Rg -a 150521# 389# 7218# 1# B- -2321# 585# 278 161590# 417# + 54 166 112 278 Cn -a 152842# 438# 7206# 2# B- -6188# 491# 278 164083# 470# + 52 165 113 278 Nh -a 159030# 224# 7181# 1# B- * 278 170725# 240# +0 63 171 108 279 Hs x 146500# 600# 7243# 2# B- -1085# 900# 279 157274# 644# + 61 170 109 279 Mt -a 147585# 671# 7237# 2# B- -1439# 903# 279 158439# 720# + 59 169 110 279 Ds -a 149024# 605# 7229# 2# B- -2697# 737# 279 159984# 649# + 57 168 111 279 Rg -a 151721# 422# 7216# 2# B- -3299# 578# 279 162880# 453# + 55 167 112 279 Cn -a 155021# 395# 7202# 1# B- -4439# 718# 279 166422# 424# + 53 166 113 279 Nh x 159460# 600# 7183# 2# B- * 279 171187# 644# +0 64 172 108 280 Hs x 148420# 600# 7239# 2# B- -2090# 848# 280 159335# 644# + 62 171 109 280 Mt x 150510# 600# 7229# 2# B- 190# 958# 280 161579# 644# + 60 170 110 280 Ds -a 150320# 748# 7227# 3# B- -3566# 918# 280 161375# 803# + 58 169 111 280 Rg -a 153886# 532# 7212# 2# B- -1768# 789# 280 165204# 571# + 56 168 112 280 Cn -a 155654# 583# 7202# 2# B- -5585# 707# 280 167102# 626# + 54 167 113 280 Nh x 161240# 400# 7180# 1# B- * 280 173098# 429# +0 63 172 109 281 Mt x 152400# 600# 7225# 2# B- -873# 776# 281 163608# 644# + 61 171 110 281 Ds -a 153273# 493# 7220# 2# B- -2060# 918# 281 164545# 529# + 59 170 111 281 Rg -a 155333# 774# 7209# 3# B- -2614# 870# 281 166757# 831# + 57 169 112 281 Cn -a 157947# 397# 7197# 1# B- -3863# 498# 281 169563# 427# + 55 168 113 281 Nh x 161810# 300# 7181# 1# B- * 281 173710# 322# +0 64 173 109 282 Mt -a 155455# 447# 7218# 2# B- 665# 538# 282 166888# 480# + 62 172 110 282 Ds x 154790# 300# 7217# 1# B- -2952# 660# 282 166174# 322# + 60 171 111 282 Rg -a 157742# 588# 7204# 2# B- -1084# 804# 282 169343# 631# + 58 170 112 282 Cn -a 158826# 548# 7197# 2# B- -4903# 678# 282 170507# 588# + 56 169 113 282 Nh -a 163729# 400# 7177# 1# B- * 282 175770# 430# +0 63 173 110 283 Ds x 157830# 500# 7210# 2# B- -1550# 843# 283 169437# 537# + 61 172 111 283 Rg -a 159380# 678# 7201# 2# B- -1957# 916# 283 171101# 728# + 59 171 112 283 Cn -a 161337# 615# 7192# 2# B- -3226# 754# 283 173202# 660# + 57 170 113 283 Nh -a 164563# 437# 7177# 2# B- * 283 176666# 469# +0 64 174 110 284 Ds x 159460# 500# 7207# 2# B- -2510# 707# 284 171187# 537# + 62 173 111 284 Rg x 161970# 500# 7195# 2# B- -445# 912# 284 173882# 537# + 60 172 112 284 Cn -a 162415# 762# 7191# 3# B- -4176# 931# 284 174360# 819# + 58 171 113 284 Nh -a 166591# 533# 7173# 2# B- -2188# 845# 284 178843# 573# + 56 170 114 284 Fl -a 168779# 656# 7163# 2# B- * 284 181192# 704# +0 63 174 111 285 Rg x 163730# 600# 7192# 2# B- -1357# 785# 285 175771# 644# + 61 173 112 285 Cn -a 165086# 507# 7185# 2# B- -2682# 926# 285 177227# 544# + 59 172 113 285 Nh -a 167768# 775# 7172# 3# B- -3164# 874# 285 180106# 832# + 57 171 114 285 Fl -a 170932# 404# 7159# 1# B- * 285 183503# 433# +0 64 175 111 286 Rg -a 166510# 458# 7185# 2# B- 61# 836# 286 178756# 492# + 62 174 112 286 Cn x 166450# 700# 7183# 2# B- -3507# 915# 286 178691# 751# + 60 173 113 286 Nh -a 169957# 590# 7168# 2# B- -1649# 806# 286 182456# 634# + 58 172 114 286 Fl -a 171606# 549# 7159# 2# B- * 286 184226# 590# +0 63 175 112 287 Cn x 169370# 700# 7176# 2# B- -2085# 995# 287 181826# 751# + 61 174 113 287 Nh -a 171455# 707# 7166# 2# B- -2474# 939# 287 184064# 759# + 59 173 114 287 Fl -a 173929# 617# 7155# 2# B- -3819# 759# 287 186720# 663# + 57 172 115 287 Mc -a 177748# 443# 7139# 2# B- * 287 190820# 475# +0 64 176 112 288 Cn x 170930# 700# 7174# 2# B- -3039# 989# 288 183501# 751# + 62 175 113 288 Nh x 173970# 700# 7160# 2# B- -947# 1035# 288 186764# 751# + 60 174 114 288 Fl -a 174917# 763# 7154# 3# B- -4749# 932# 288 187781# 819# + 58 173 115 288 Mc -a 179666# 536# 7135# 2# B- * 288 192879# 575# +0 63 176 113 289 Nh x 175550# 500# 7158# 2# B- -1915# 715# 289 188461# 537# + 61 175 114 289 Fl -a 177465# 511# 7149# 2# B- -3217# 929# 289 190517# 548# + 59 174 115 289 Mc -a 180683# 776# 7135# 3# B- -3774# 925# 289 193971# 834# + 57 173 116 289 Lv -a 184457# 503# 7119# 2# B- * 289 198023# 540# +0 64 177 113 290 Nh -a 178315# 469# 7152# 2# B- -416# 843# 290 191429# 503# + 62 176 114 290 Fl -a 178731# 700# 7147# 2# B- -4061# 917# 290 191875# 752# + 60 175 115 290 Mc -a 182792# 592# 7131# 2# B- -2236# 809# 290 196235# 635# + 58 174 116 290 Lv -a 185028# 552# 7120# 2# B- * 290 198635# 593# +0 63 177 114 291 Fl x 181500# 700# 7141# 2# B- -2680# 1015# 291 194848# 751# + 61 176 115 291 Mc -a 184180# 735# 7129# 3# B- -3064# 964# 291 197725# 789# + 59 175 116 291 Lv -a 187244# 623# 7116# 2# B- -4409# 863# 291 201014# 669# + 57 174 117 291 Ts -a 191653# 597# 7098# 2# B- * 291 205748# 640# +0 62 177 115 292 Mc x 186600# 700# 7124# 2# B- -1533# 1035# 292 200323# 751# + 60 176 116 292 Lv -a 188133# 763# 7116# 3# B- -5488# 1014# 292 201969# 819# + 58 175 117 292 Ts -a 193621# 669# 7095# 2# B- * 292 207861# 718# +0 61 177 116 293 Lv -a 190568# 515# 7111# 2# B- -3860# 933# 293 204583# 553# + 59 176 117 293 Ts -a 194428# 778# 7095# 3# B- -4374# 1053# 293 208727# 835# + 57 175 118 293 Og -a 198802# 709# 7078# 2# B- * 293 213423# 761# +0 60 177 117 294 Ts -a 196397# 593# 7092# 2# B- -2923# 811# 294 210840# 637# + 58 176 118 294 Og -a 199320# 553# 7079# 2# B- * 294 213979# 594# +0 59 177 118 295 Og -a 201369# 655# 7076# 2# B- * 295 216178# 703# diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 508c76ac43..dd14e0d194 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -1,14 +1,12 @@ from numbers import Real from math import exp, erf, pi, sqrt from copy import deepcopy -import warnings import os import h5py import pickle import numpy as np from scipy.signal import find_peaks -import matplotlib.pyplot as plt import openmc.checkvalue as cv from ..exceptions import DataError @@ -196,9 +194,8 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, test_xs_ref[i] = np.interp(test_energy, energy, ce_xs[i]) if log: - print(" energy: {:.3e} to {:.3e} eV ({} points)".format( - energy[0], energy[-1], ne)) - print(" error tolerance: rtol={}, atol={}".format(rtol, atol)) + print(f" energy: {energy[0]:.3e} to {energy[-1]:.3e} eV ({ne} points)") + print(f" error tolerance: rtol={rtol}, atol={atol}") # transform xs (sigma) and energy (E) to f (sigma*E) and s (sqrt(E)) to be # compatible with the multipole representation @@ -232,8 +229,8 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, orders = list(range(lowest_order, highest_order + 1, 2)) if log: - print("Found {} peaks".format(n_peaks)) - print("Fitting orders from {} to {}".format(orders[0], orders[-1])) + print(f"Found {n_peaks} peaks") + print(f"Fitting orders from {orders[0]} to {orders[-1]}") # perform VF with increasing orders found_ideal = False @@ -241,7 +238,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, best_quality = best_ratio = -np.inf for i, order in enumerate(orders): if log: - print("Order={}({}/{})".format(order, i, len(orders))) + print(f"Order={order}({i}/{len(orders)})") # initial guessed poles poles_r = np.linspace(s[0], s[-1], order//2) poles = poles_r + poles_r*0.01j @@ -251,7 +248,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, # fitting iteration for i_vf in range(n_vf_iter): if log >= DETAILED_LOGGING: - print("VF iteration {}/{}".format(i_vf + 1, n_vf_iter)) + print(f"VF iteration {i_vf + 1}/{n_vf_iter}") # call vf poles, residues, cf, f_fit, rms = vf.vectfit(f, s, poles, weight) @@ -270,7 +267,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, # re-calculate residues if poles changed if n_real_poles > 0: if log >= DETAILED_LOGGING: - print(" # real poles: {}".format(n_real_poles)) + print(f" # real poles: {n_real_poles}") new_poles, residues, cf, f_fit, rms = \ vf.vectfit(f, s, new_poles, weight, skip_pole=True) @@ -298,10 +295,10 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, quality = -np.inf if log >= DETAILED_LOGGING: - print(" # poles: {}".format(new_poles.size)) - print(" Max relative error: {:.3f}%".format(maxre*100)) - print(" Satisfaction: {:.1f}%, {:.1f}%".format(ratio*100, ratio2*100)) - print(" Quality: {:.2f}".format(quality)) + print(f" # poles: {new_poles.size}") + print(f" Max relative error: {maxre * 100:.3f}%") + print(f" Satisfaction: {ratio * 100:.1f}%, {ratio2 * 100:.1f}%") + print(f" Quality: {quality:.2f}") if quality > best_quality: if log >= DETAILED_LOGGING: @@ -356,7 +353,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, mp_residues = np.concatenate((best_residues[:, real_idx], best_residues[:, conj_idx]*2), axis=1)/1j if log: - print("Final number of poles: {}".format(mp_poles.size)) + print(f"Final number of poles: {mp_poles.size}") if path_out: if not os.path.exists(path_out): @@ -364,6 +361,7 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, for i, mt in enumerate(mts): if not test_xs_ref[i].any(): continue + import matplotlib.pyplot as plt fig, ax1 = plt.subplots() lns1 = ax1.loglog(test_energy, test_xs_ref[i], 'g', label="ACE xs") lns2 = ax1.loglog(test_energy, best_test_xs[i], 'b', label="VF xs") @@ -379,14 +377,14 @@ def _vectfit_xs(energy, ce_xs, mts, rtol=1e-3, atol=1e-5, orders=None, ax2.set_ylabel('relative error', color='r') ax2.tick_params('y', colors='r') - plt.title("MT {} vector fitted with {} poles".format(mt, mp_poles.size)) + plt.title(f"MT {mt} vector fitted with {mp_poles.size} poles") fig.tight_layout() fig_file = os.path.join(path_out, "{:.0f}-{:.0f}_MT{}.png".format( energy[0], energy[-1], mt)) plt.savefig(fig_file) plt.close() if log: - print("Saved figure: {}".format(fig_file)) + print(f"Saved figure: {fig_file}") return (mp_poles, mp_residues) @@ -424,7 +422,7 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, # make 0K ACE data using njoy if log: - print("Running NJOY to get 0K point-wise data (error={})...".format(njoy_error)) + print(f"Running NJOY to get 0K point-wise data (error={njoy_error})...") nuc_ce = IncidentNeutron.from_njoy(endf_file, temperatures=[0.0], error=njoy_error, broadr=False, heatr=False, purr=False) @@ -478,9 +476,8 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, mts = [2, 27] if log: - print(" MTs: {}".format(mts)) - print(" Energy range: {:.3e} to {:.3e} eV ({} points)".format( - E_min, E_max, n_points)) + print(f" MTs: {mts}") + print(f" Energy range: {E_min:.3e} to {E_max:.3e} eV ({n_points} points)") # ====================================================================== # PERFORM VECTOR FITTING @@ -501,7 +498,7 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, # VF piece by piece for i_piece in range(vf_pieces): if log: - print("Vector fitting piece {}/{}...".format(i_piece + 1, vf_pieces)) + print(f"Vector fitting piece {i_piece + 1}/{vf_pieces}...") # start E of this piece e_bound = (sqrt(E_min) + piece_width*(i_piece-0.5))**2 if i_piece == 0 or sqrt(alpha*e_bound) < 4.0: @@ -535,12 +532,12 @@ def vectfit_nuclide(endf_file, njoy_error=5e-4, vf_pieces=None, if not os.path.exists(path_out): os.makedirs(path_out) if not mp_filename: - mp_filename = "{}_mp.pickle".format(nuc_ce.name) + mp_filename = f"{nuc_ce.name}_mp.pickle" mp_filename = os.path.join(path_out, mp_filename) with open(mp_filename, 'wb') as f: pickle.dump(mp_data, f) if log: - print("Dumped multipole data to file: {}".format(mp_filename)) + print(f"Dumped multipole data to file: {mp_filename}") return mp_data @@ -606,9 +603,8 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, if log: print("Windowing:") - print(" config: # windows={}, spacing={}, CF order={}".format( - n_win, spacing, n_cf)) - print(" error tolerance: rtol={}, atol={}".format(rtol, atol)) + print(f" config: # windows={n_win}, spacing={spacing}, CF order={n_cf}") + print(f" error tolerance: rtol={rtol}, atol={atol}") # sort poles (and residues) by the real component of the pole for ip in range(n_pieces): @@ -624,7 +620,7 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, win_data = [] for iw in range(n_win): if log >= DETAILED_LOGGING: - print("Processing window {}/{}...".format(iw + 1, n_win)) + print(f"Processing window {iw + 1}/{n_win}...") # inner window boundaries inbegin = sqrt(E_min) + spacing * iw @@ -659,7 +655,7 @@ def _windowing(mp_data, n_cf, rtol=1e-3, atol=1e-5, n_win=None, spacing=None, lp = rp = center_pole_ind while True: if log >= DETAILED_LOGGING: - print("Trying poles {} to {}".format(lp, rp)) + print(f"Trying poles {lp} to {rp}") # calculate the cross sections contributed by the windowed poles if rp > lp: @@ -748,12 +744,12 @@ class WindowedMultipole(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention Attributes ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention spacing : float The width of each window in sqrt(E)-space. For example, the frst window will end at (sqrt(E_min) + spacing)**2 and the second window at @@ -800,6 +796,11 @@ class WindowedMultipole(EqualityMixin): def name(self): return self._name + @name.setter + def name(self, name): + cv.check_type('name', name, str) + self._name = name + @property def fit_order(self): return self.curvefit.shape[1] - 1 @@ -824,39 +825,6 @@ class WindowedMultipole(EqualityMixin): def spacing(self): return self._spacing - @property - def sqrtAWR(self): - return self._sqrtAWR - - @property - def E_min(self): - return self._E_min - - @property - def E_max(self): - return self._E_max - - @property - def data(self): - return self._data - - @property - def windows(self): - return self._windows - - @property - def broaden_poly(self): - return self._broaden_poly - - @property - def curvefit(self): - return self._curvefit - - @name.setter - def name(self, name): - cv.check_type('name', name, str) - self._name = name - @spacing.setter def spacing(self, spacing): if spacing is not None: @@ -864,6 +832,10 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('spacing', spacing, 0.0, equality=False) self._spacing = spacing + @property + def sqrtAWR(self): + return self._sqrtAWR + @sqrtAWR.setter def sqrtAWR(self, sqrtAWR): if sqrtAWR is not None: @@ -871,6 +843,10 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('sqrtAWR', sqrtAWR, 0.0, equality=False) self._sqrtAWR = sqrtAWR + @property + def E_min(self): + return self._E_min + @E_min.setter def E_min(self, E_min): if E_min is not None: @@ -878,6 +854,10 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('E_min', E_min, 0.0, equality=True) self._E_min = E_min + @property + def E_max(self): + return self._E_max + @E_max.setter def E_max(self, E_max): if E_max is not None: @@ -885,6 +865,10 @@ class WindowedMultipole(EqualityMixin): cv.check_greater_than('E_max', E_max, 0.0, equality=False) self._E_max = E_max + @property + def data(self): + return self._data + @data.setter def data(self, data): if data is not None: @@ -900,6 +884,10 @@ class WindowedMultipole(EqualityMixin): raise TypeError('Multipole data arrays must be complex dtype') self._data = data + @property + def windows(self): + return self._windows + @windows.setter def windows(self, windows): if windows is not None: @@ -911,6 +899,10 @@ class WindowedMultipole(EqualityMixin): ' dtype') self._windows = windows + @property + def broaden_poly(self): + return self._broaden_poly + @broaden_poly.setter def broaden_poly(self, broaden_poly): if broaden_poly is not None: @@ -922,6 +914,10 @@ class WindowedMultipole(EqualityMixin): ' dtype') self._broaden_poly = broaden_poly + @property + def curvefit(self): + return self._curvefit + @curvefit.setter def curvefit(self, curvefit): if curvefit is not None: @@ -1109,7 +1105,7 @@ class WindowedMultipole(EqualityMixin): for n_w in np.unique(np.linspace(n_win_min, n_win_max, 20, dtype=int)): for n_cf in range(10, 1, -1): if log: - print("Testing N_win={} N_cf={}".format(n_w, n_cf)) + print(f"Testing N_win={n_w} N_cf={n_cf}") # update arguments dictionary kwargs.update(n_win=n_w, n_cf=n_cf) @@ -1154,8 +1150,8 @@ class WindowedMultipole(EqualityMixin): Returns ------- 3-tuple of Real - Total, absorption, and fission microscopic cross sections at the - given energy and temperature. + Scattering, absorption, and fission microscopic cross sections + at the given energy and temperature. """ @@ -1170,10 +1166,11 @@ class WindowedMultipole(EqualityMixin): sqrtE = sqrt(E) invE = 1.0 / E - # Locate us. The i_window calc omits a + 1 present in F90 because of - # the 1-based vs. 0-based indexing. Similarly startw needs to be - # decreased by 1. endw does not need to be decreased because - # range(startw, endw) does not include endw. + # Locate us. The i_window calc omits a + 1 present from the legacy + # Fortran version of OpenMC because of the 1-based vs. 0-based + # indexing. Similarly startw needs to be decreased by 1. endw does + # not need to be decreased because range(startw, endw) does not include + # endw. i_window = min(self.n_windows - 1, int(np.floor((sqrtE - sqrt(self.E_min)) / self.spacing))) startw = self.windows[i_window, 0] - 1 @@ -1248,8 +1245,8 @@ class WindowedMultipole(EqualityMixin): Returns ------- 3-tuple of Real or 3-tuple of numpy.ndarray - Total, absorption, and fission microscopic cross sections at the - given energy and temperature. + Scattering, absorption, and fission microscopic cross sections + at the given energy and temperature. """ @@ -1274,7 +1271,7 @@ class WindowedMultipole(EqualityMixin): # Open file and write version. with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.string_('data_wmp') + f.attrs['filetype'] = np.bytes_('data_wmp') f.attrs['version'] = np.array(WMP_VERSION) g = f.create_group(self.name) diff --git a/openmc/data/nbody.py b/openmc/data/nbody.py index 4db9934b95..ec1ac25c0f 100644 --- a/openmc/data/nbody.py +++ b/openmc/data/nbody.py @@ -43,18 +43,6 @@ class NBodyPhaseSpace(AngleEnergy): def total_mass(self): return self._total_mass - @property - def n_particles(self): - return self._n_particles - - @property - def atomic_weight_ratio(self): - return self._atomic_weight_ratio - - @property - def q_value(self): - return self._q_value - @total_mass.setter def total_mass(self, total_mass): name = 'N-body phase space total mass' @@ -62,6 +50,10 @@ class NBodyPhaseSpace(AngleEnergy): cv.check_greater_than(name, total_mass, 0.) self._total_mass = total_mass + @property + def n_particles(self): + return self._n_particles + @n_particles.setter def n_particles(self, n_particles): name = 'N-body phase space number of particles' @@ -69,6 +61,10 @@ class NBodyPhaseSpace(AngleEnergy): cv.check_greater_than(name, n_particles, 0) self._n_particles = n_particles + @property + def atomic_weight_ratio(self): + return self._atomic_weight_ratio + @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): name = 'N-body phase space atomic weight ratio' @@ -76,6 +72,10 @@ class NBodyPhaseSpace(AngleEnergy): cv.check_greater_than(name, atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio + @property + def q_value(self): + return self._q_value + @q_value.setter def q_value(self, q_value): name = 'N-body phase space Q value' @@ -91,7 +91,7 @@ class NBodyPhaseSpace(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('nbody') + group.attrs['type'] = np.bytes_('nbody') group.attrs['total_mass'] = self.total_mass group.attrs['n_particles'] = self.n_particles group.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 47ebfabf83..95a3424ea4 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from collections.abc import Mapping, MutableMapping from io import StringIO from math import log10 @@ -17,8 +16,7 @@ from .endf import ( Evaluation, SUM_RULES, get_head_record, get_tab1_record, get_evaluations) from .fission_energy import FissionEnergyRelease from .function import Tabulated1D, Sum, ResonancesWithBackground -from .grid import linearize, thin -from .njoy import make_ace +from .njoy import make_ace, make_pendf from .product import Product from .reaction import Reaction, _get_photon_products_ace, FISSION_MTS from . import resonance as res @@ -44,7 +42,7 @@ class IncidentNeutron(EqualityMixin): Parameters ---------- name : str - Name of the nuclide using the GND naming convention + Name of the nuclide using the GNDS naming convention atomic_number : int Number of protons in the target nucleus mass_number : int @@ -75,8 +73,8 @@ class IncidentNeutron(EqualityMixin): Metastable state of the target nucleus. A value of zero indicates ground state. name : str - Name of the nuclide using the GND naming convention - reactions : collections.OrderedDict + Name of the nuclide using the GNDS naming convention + reactions : dict Contains the cross sections, secondary angle and energy distributions, and other associated data for each reaction. The keys are the MT values and the values are Reaction objects. @@ -107,7 +105,7 @@ class IncidentNeutron(EqualityMixin): self.kTs = kTs self.energy = {} self._fission_energy = None - self.reactions = OrderedDict() + self.reactions = {} self._urr = {} self._resonances = None @@ -123,10 +121,10 @@ class IncidentNeutron(EqualityMixin): if len(mts) > 0: return self._get_redundant_reaction(mt, mts) else: - raise KeyError('No reaction with MT={}.'.format(mt)) + raise KeyError(f'No reaction with MT={mt}.') def __repr__(self): - return "".format(self.name) + return f"" def __iter__(self): return iter(self.reactions.values()) @@ -135,54 +133,14 @@ class IncidentNeutron(EqualityMixin): def name(self): return self._name - @property - def atomic_number(self): - return self._atomic_number - - @property - def mass_number(self): - return self._mass_number - - @property - def metastable(self): - return self._metastable - - @property - def atomic_weight_ratio(self): - return self._atomic_weight_ratio - - @property - def fission_energy(self): - return self._fission_energy - - @property - def reactions(self): - return self._reactions - - @property - def resonances(self): - return self._resonances - - @property - def resonance_covariance(self): - return self._resonance_covariance - - @property - def urr(self): - return self._urr - - @property - def temperatures(self): - return ["{}K".format(int(round(kT / K_BOLTZMANN))) for kT in self.kTs] - @name.setter def name(self, name): cv.check_type('name', name, str) self._name = name @property - def atomic_symbol(self): - return ATOMIC_SYMBOL[self.atomic_number] + def atomic_number(self): + return self._atomic_number @atomic_number.setter def atomic_number(self, atomic_number): @@ -190,46 +148,78 @@ class IncidentNeutron(EqualityMixin): cv.check_greater_than('atomic number', atomic_number, 0, True) self._atomic_number = atomic_number + @property + def mass_number(self): + return self._mass_number + @mass_number.setter def mass_number(self, mass_number): cv.check_type('mass number', mass_number, Integral) cv.check_greater_than('mass number', mass_number, 0, True) self._mass_number = mass_number + @property + def metastable(self): + return self._metastable + @metastable.setter def metastable(self, metastable): cv.check_type('metastable', metastable, Integral) cv.check_greater_than('metastable', metastable, 0, True) self._metastable = metastable + @property + def atomic_weight_ratio(self): + return self._atomic_weight_ratio + @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): cv.check_type('atomic weight ratio', atomic_weight_ratio, Real) cv.check_greater_than('atomic weight ratio', atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio + @property + def fission_energy(self): + return self._fission_energy + @fission_energy.setter def fission_energy(self, fission_energy): cv.check_type('fission energy release', fission_energy, FissionEnergyRelease) self._fission_energy = fission_energy + @property + def reactions(self): + return self._reactions + @reactions.setter def reactions(self, reactions): cv.check_type('reactions', reactions, Mapping) self._reactions = reactions + @property + def resonances(self): + return self._resonances + @resonances.setter def resonances(self, resonances): cv.check_type('resonances', resonances, res.Resonances) self._resonances = resonances + @property + def resonance_covariance(self): + return self._resonance_covariance + @resonance_covariance.setter def resonance_covariance(self, resonance_covariance): cv.check_type('resonance covariance', resonance_covariance, res_cov.ResonanceCovariances) self._resonance_covariance = resonance_covariance + @property + def urr(self): + return self._urr + @urr.setter def urr(self, urr): cv.check_type('probability table dictionary', urr, MutableMapping) @@ -238,6 +228,14 @@ class IncidentNeutron(EqualityMixin): cv.check_type('probability tables', value, ProbabilityTables) self._urr = urr + @property + def temperatures(self): + return [f"{int(round(kT / K_BOLTZMANN))}K" for kT in self.kTs] + + @property + def atomic_symbol(self): + return ATOMIC_SYMBOL[self.atomic_number] + def add_temperature_from_ace(self, ace_or_filename, metastable_scheme='nndc'): """Append data from an ACE file at a different temperature. @@ -262,7 +260,7 @@ class IncidentNeutron(EqualityMixin): # Check if temprature already exists strT = data.temperatures[0] if strT in self.temperatures: - warn('Cross sections at T={} already exist.'.format(strT)) + warn(f'Cross sections at T={strT} already exist.') return # Check that name matches @@ -287,7 +285,7 @@ class IncidentNeutron(EqualityMixin): if strT in data.urr: self.urr[strT] = data.urr[strT] - def add_elastic_0K_from_endf(self, filename, overwrite=False): + def add_elastic_0K_from_endf(self, filename, overwrite=False, **kwargs): """Append 0K elastic scattering cross section from an ENDF file. Parameters @@ -298,6 +296,8 @@ class IncidentNeutron(EqualityMixin): If existing 0 K data is present, this flag can be used to indicate that it should be overwritten. Otherwise, an exception will be thrown. + **kwargs + Keyword arguments passed to :func:`openmc.data.njoy.make_pendf` Raises ------ @@ -310,75 +310,22 @@ class IncidentNeutron(EqualityMixin): if '0K' in self.energy and not overwrite: raise ValueError('0 K data already exists for this nuclide.') - data = type(self).from_endf(filename) - if data.resonances is not None: - x = [] - y = [] - for rr in data.resonances: - if isinstance(rr, res.RMatrixLimited): - raise TypeError('R-Matrix Limited not supported.') - elif isinstance(rr, res.Unresolved): - continue + with tempfile.TemporaryDirectory() as tmpdir: + # Set arguments for make_pendf + pendf_path = os.path.join(tmpdir, 'pendf') + kwargs.setdefault('output_dir', tmpdir) + kwargs.setdefault('pendf', pendf_path) - # Get energies/widths for resonances - e_peak = rr.parameters['energy'].values - if isinstance(rr, res.MultiLevelBreitWigner): - gamma = rr.parameters['totalWidth'].values - elif isinstance(rr, res.ReichMoore): - df = rr.parameters - gamma = (df['neutronWidth'] + - df['captureWidth'] + - abs(df['fissionWidthA']) + - abs(df['fissionWidthB'])).values + # Run NJOY to create a pointwise ENDF file + make_pendf(filename, **kwargs) - # Determine peak energies and widths - e_min, e_max = rr.energy_min, rr.energy_max - in_range = (e_peak > e_min) & (e_peak < e_max) - e_peak = e_peak[in_range] - gamma = gamma[in_range] - - # Get midpoints between resonances (use min/max energy of - # resolved region as absolute lower/upper bound) - e_mid = np.concatenate( - ([e_min], (e_peak[1:] + e_peak[:-1])/2, [e_max])) - - # Add grid around each resonance that includes the peak +/- the - # width times each value in _RESONANCE_ENERGY_GRID. Values are - # constrained so that points around one resonance don't overlap - # with points around another. This algorithm is from Fudge - # (https://doi.org/10.1063/1.1945057). - energies = [] - for e, g, e_lower, e_upper in zip(e_peak, gamma, e_mid[:-1], - e_mid[1:]): - e_left = e - g*_RESONANCE_ENERGY_GRID - energies.append(e_left[e_left > e_lower][::-1]) - e_right = e + g*_RESONANCE_ENERGY_GRID[1:] - energies.append(e_right[e_right < e_upper]) - - # Concatenate all points - energies = np.concatenate(energies) - - # Create 1000 equal log-spaced energies over RRR, combine with - # resonance peaks and half-height energies - e_log = np.logspace(log10(e_min), log10(e_max), 1000) - energies = np.union1d(e_log, energies) - - # Linearize and thin cross section - xi, yi = linearize(energies, data[2].xs['0K']) - xi, yi = thin(xi, yi) - - # If there are multiple resolved resonance ranges (e.g. Pu239 in - # ENDF/B-VII.1), combine them - x = np.concatenate((x, xi)) - y = np.concatenate((y, yi)) - else: - energies = data[2].xs['0K'].x - x, y = linearize(energies, data[2].xs['0K']) - x, y = thin(x, y) - - # Set 0K energy grid and elastic scattering cross section - self.energy['0K'] = x - self[2].xs['0K'] = Tabulated1D(x, y) + # Add 0K elastic scattering cross section + pendf = Evaluation(pendf_path) + file_obj = StringIO(pendf.section[3, 2]) + get_head_record(file_obj) + params, xs = get_tab1_record(file_obj) + self.energy['0K'] = xs.x + self[2].xs['0K'] = xs def get_reaction_components(self, mt): """Determine what reactions make up redundant reaction. @@ -426,7 +373,7 @@ class IncidentNeutron(EqualityMixin): # Open file and write version with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.string_('data_neutron') + f.attrs['filetype'] = np.bytes_('data_neutron') f.attrs['version'] = np.array(HDF5_VERSION) # Write basic data @@ -462,7 +409,7 @@ class IncidentNeutron(EqualityMixin): if not (photon_rx or rx.mt in keep_mts): continue - rx_group = rxs_group.create_group('reaction_{:03}'.format(rx.mt)) + rx_group = rxs_group.create_group(f'reaction_{rx.mt:03}') rx.to_hdf5(rx_group) # Write total nu data if available @@ -594,7 +541,7 @@ class IncidentNeutron(EqualityMixin): zaid, xs = ace.name.split('.') if not xs.endswith('c'): raise TypeError( - "{} is not a continuous-energy neutron ACE table.".format(ace)) + f"{ace} is not a continuous-energy neutron ACE table.") name, element, Z, mass_number, metastable = \ get_metadata(int(zaid), metastable_scheme) @@ -733,9 +680,9 @@ class IncidentNeutron(EqualityMixin): # Determine name element = ATOMIC_SYMBOL[atomic_number] if metastable > 0: - name = '{}{}_m{}'.format(element, mass_number, metastable) + name = f'{element}{mass_number}_m{metastable}' else: - name = '{}{}'.format(element, mass_number) + name = f'{element}{mass_number}' # Instantiate incident neutron data data = cls(name, atomic_number, mass_number, metastable, @@ -868,16 +815,15 @@ class IncidentNeutron(EqualityMixin): heatr_evals = get_evaluations(kwargs["heatr"]) heatr_local_evals = get_evaluations(kwargs["heatr"] + "_local") - for ev, ev_local in zip(heatr_evals, heatr_local_evals): - temp = "{}K".format(round(ev.target["temperature"])) + for ev, ev_local, temp in zip(heatr_evals, heatr_local_evals, data.temperatures): # Get total KERMA (originally from ACE file) and energy grid kerma = data.reactions[301].xs[temp] E = kerma.x if f is not None: # Replace fission KERMA with (EFR + EB)*sigma_f - fission = data.reactions[18].xs[temp] + fission = data[18].xs[temp] kerma_fission = get_file3_xs(ev, 318, E) kerma.y = kerma.y - kerma_fission + ( f.fragments(E) + f.betas(E)) * fission(E) diff --git a/openmc/data/njoy.py b/openmc/data/njoy.py index 305edaf4f2..4c538bd6e6 100644 --- a/openmc/data/njoy.py +++ b/openmc/data/njoy.py @@ -18,21 +18,45 @@ _THERMAL_DATA = { 'c_Al27': ThermalTuple('al27', [13027], 1), 'c_Al_in_Al2O3': ThermalTuple('asap00', [13027], 1), 'c_Be': ThermalTuple('be', [4009], 1), + 'c_Be_distinct': ThermalTuple('besd', [4009], 1), 'c_Be_in_BeO': ThermalTuple('bebeo', [4009], 1), 'c_Be_in_Be2C': ThermalTuple('bebe2c', [4009], 1), + 'c_Be_in_BeF2': ThermalTuple('bebef2', [4009], 1), 'c_Be_in_FLiBe': ThermalTuple('beflib', [4009], 1), 'c_C6H6': ThermalTuple('benz', [1001, 6000, 6012], 2), + 'c_C_in_Be2C': ThermalTuple('cbe2c', [6000, 6012, 6013], 1), + 'c_C_in_C5O2H8': ThermalTuple('clucit', [6000, 6012, 6013], 1), + 'c_C_in_C8H8': ThermalTuple('cc8h8', [6000, 6012, 6013], 1), + 'c_C_in_CF2': ThermalTuple('ccf2', [6000, 6012, 6013], 1), 'c_C_in_SiC': ThermalTuple('csic', [6000, 6012, 6013], 1), + 'c_C_in_UC_100p': ThermalTuple('cuc100', [6000, 6012, 6013], 1), + 'c_C_in_UC_10p': ThermalTuple('cuc10', [6000, 6012, 6013], 1), + 'c_C_in_UC_5p': ThermalTuple('cuc5', [6000, 6012, 6013], 1), + 'c_C_in_UC': ThermalTuple('cinuc', [6000, 6012, 6013], 1), + 'c_C_in_UC_HALEU': ThermalTuple('cuchal', [6000, 6012, 6013], 1), + 'c_C_in_UC_HEU': ThermalTuple('cucheu', [6000, 6012, 6013], 1), + 'c_C_in_ZrC': ThermalTuple('czrc', [6000, 6012, 6013], 1), 'c_Ca_in_CaH2': ThermalTuple('cacah2', [20040, 20042, 20043, 20044, 20046, 20048], 1), + 'c_D_in_7LiD': ThermalTuple('dlid', [1002], 1), 'c_D_in_D2O': ThermalTuple('dd2o', [1002], 1), 'c_D_in_D2O_solid': ThermalTuple('dice', [1002], 1), + 'c_F_in_Be2': ThermalTuple('fbef2', [9019], 1), + 'c_F_in_CF2': ThermalTuple('fcf2', [9019], 1), 'c_F_in_FLiBe': ThermalTuple('fflibe', [9019], 1), + 'c_F_in_HF': ThermalTuple('f_hf', [9019], 1), + 'c_F_in_MgF2': ThermalTuple('fmgf2', [9019], 1), 'c_Fe56': ThermalTuple('fe56', [26056], 1), 'c_Graphite': ThermalTuple('graph', [6000, 6012, 6013], 1), 'c_Graphite_10p': ThermalTuple('grph10', [6000, 6012, 6013], 1), + 'c_Graphite_20p': ThermalTuple('grph20', [6000, 6012, 6013], 1), 'c_Graphite_30p': ThermalTuple('grph30', [6000, 6012, 6013], 1), + 'c_Graphite_distinct': ThermalTuple('grphsd', [6000, 6012, 6013], 1), + 'c_H_in_7LiH': ThermalTuple('hlih', [1001], 1), 'c_H_in_C5O2H8': ThermalTuple('lucite', [1001], 1), + 'c_H_in_C8H8': ThermalTuple('hc8h8', [1001], 1), 'c_H_in_CaH2': ThermalTuple('hcah2', [1001], 1), + 'c_H1_in_CaH2': ThermalTuple('h1cah2', [1001], 1), + 'c_H2_in_CaH2': ThermalTuple('h2cah2', [1001], 1), 'c_H_in_CH2': ThermalTuple('hch2', [1001], 1), 'c_H_in_CH4_liquid': ThermalTuple('lch4', [1001], 1), 'c_H_in_CH4_solid': ThermalTuple('sch4', [1001], 1), @@ -49,24 +73,67 @@ _THERMAL_DATA = { 'c_H_in_ZrH2': ThermalTuple('hzrh2', [1001], 1), 'c_H_in_ZrHx': ThermalTuple('hzrhx', [1001], 1), 'c_Li_in_FLiBe': ThermalTuple('liflib', [3006, 3007], 1), + 'c_Li_in_7LiD': ThermalTuple('lilid', [3007], 1), + 'c_Li_in_7LiH': ThermalTuple('lilih', [3007], 1), 'c_Mg24': ThermalTuple('mg24', [12024], 1), + 'c_Mg_in_MgF2': ThermalTuple('mgmgf2', [12024, 12025, 12026], 1), + 'c_Mg_in_MgO': ThermalTuple('mgmgo', [12024, 12025, 12026], 1), + 'c_N_in_UN_100p': ThermalTuple('nun100', [7014, 7015], 1), + 'c_N_in_UN_10p': ThermalTuple('nun10', [7014, 7015], 1), + 'c_N_in_UN_5p': ThermalTuple('nun5', [7014, 7015], 1), 'c_N_in_UN': ThermalTuple('n-un', [7014, 7015], 1), - 'c_O_in_Al2O3': ThermalTuple('osap00', [92238], 1), + 'c_N_in_UN_HALEU': ThermalTuple('nunhal', [7014, 7015], 1), + 'c_N_in_UN_HEU': ThermalTuple('nunheu', [7014, 7015], 1), + 'c_O_in_Al2O3': ThermalTuple('osap00', [8016, 8017, 8018], 1), 'c_O_in_BeO': ThermalTuple('obeo', [8016, 8017, 8018], 1), + 'c_O_in_C5O2H8': ThermalTuple('olucit', [8016, 8017, 8018], 1), 'c_O_in_D2O': ThermalTuple('od2o', [8016, 8017, 8018], 1), 'c_O_in_H2O_solid': ThermalTuple('oice', [8016, 8017, 8018], 1), + 'c_O_in_MgO': ThermalTuple('omgo', [8016, 8017, 8018], 1), + 'c_O_in_PuO2': ThermalTuple('opuo2', [8016, 8017, 8018], 1), + 'c_O_in_SiO2_alpha': ThermalTuple('osio2a', [8016, 8017, 8018], 1), + 'c_O_in_UO2_100p': ThermalTuple('ouo200', [8016, 8017, 8018], 1), + 'c_O_in_UO2_10p': ThermalTuple('ouo210', [8016, 8017, 8018], 1), + 'c_O_in_UO2_5p': ThermalTuple('ouo25', [8016, 8017, 8018], 1), 'c_O_in_UO2': ThermalTuple('ouo2', [8016, 8017, 8018], 1), + 'c_O_in_UO2_HALEU': ThermalTuple('ouo2hl', [8016, 8017, 8018], 1), + 'c_O_in_UO2_HEU': ThermalTuple('ouo2he', [8016, 8017, 8018], 1), 'c_ortho_D': ThermalTuple('orthod', [1002], 1), 'c_ortho_H': ThermalTuple('orthoh', [1001], 1), 'c_para_D': ThermalTuple('parad', [1002], 1), 'c_para_H': ThermalTuple('parah', [1001], 1), + 'c_Pu_in_PuO2': ThermalTuple('puo2', [94239, 94240, 94241, 94242, 94243], 1), 'c_Si28': ThermalTuple('si00', [14028], 1), 'c_Si_in_SiC': ThermalTuple('sisic', [14028, 14029, 14030], 1), + 'c_Si_in_SiO2_alpha': ThermalTuple('si_o2a', [14028, 14029, 14030], 1), 'c_SiO2_alpha': ThermalTuple('sio2-a', [8016, 8017, 8018, 14028, 14029, 14030], 3), 'c_SiO2_beta': ThermalTuple('sio2-b', [8016, 8017, 8018, 14028, 14029, 14030], 3), - 'c_U_in_UN': ThermalTuple('u-un', [92238], 1), - 'c_U_in_UO2': ThermalTuple('uuo2', [8016, 8017, 8018], 1), + 'c_U_metal_100p': ThermalTuple('u-100p', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_metal_10p': ThermalTuple('u-10p', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_metal_5p': ThermalTuple('u-5p', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_metal': ThermalTuple('umetal', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_metal_HALEU': ThermalTuple('uhaleu', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_metal_HEU': ThermalTuple('u-heu', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC_100p': ThermalTuple('uc-100', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC_10p': ThermalTuple('uc-10', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC_5p': ThermalTuple('uc-5', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC': ThermalTuple('uc-nat', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC_HALEU': ThermalTuple('uc-hal', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UC_HEU': ThermalTuple('uc-heu', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN_100p': ThermalTuple('un-100', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN_10p': ThermalTuple('un-10', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN_5p': ThermalTuple('un-5', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN': ThermalTuple('u-un', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN_HALEU': ThermalTuple('un-hal', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UN_HEU': ThermalTuple('un-heu', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2_100p': ThermalTuple('uo2100', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2_10p': ThermalTuple('uo2-10', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2_5p': ThermalTuple('uo2-5', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2': ThermalTuple('uuo2', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2_HALEU': ThermalTuple('uo2hal', [92233, 92234, 92235, 92236, 92238], 1), + 'c_U_in_UO2_HEU': ThermalTuple('uo2heu', [92233, 92234, 92235, 92236, 92238], 1), 'c_Y_in_YH2': ThermalTuple('yyh2', [39089], 1), + 'c_Zr_in_ZrC': ThermalTuple('zrzrc', [40000, 40090, 40091, 40092, 40094, 40096], 1), 'c_Zr_in_ZrH': ThermalTuple('zrzrh', [40000, 40090, 40091, 40092, 40094, 40096], 1), 'c_Zr_in_ZrH2': ThermalTuple('zrzrh2', [40000, 40090, 40091, 40092, 40094, 40096], 1), 'c_Zr_in_ZrHx': ThermalTuple('zrzrhx', [40000, 40090, 40091, 40092, 40094, 40096], 1), @@ -127,19 +194,19 @@ acer / %%%%%%%%%%%%%%%%%%%%%%%% Write out in ACE format %%%%%%%%%%%%%%%%%%%%%%%% 1 0 1 .{ext} / '{library}: {zsymam} at {temperature}'/ {mat} {temperature} -1 1/ +1 1 {ismooth}/ / """ _THERMAL_TEMPLATE_THERMR = """ thermr / %%%%%%%%%%%%%%%% Add thermal scattering data (free gas) %%%%%%%%%%%%%%% 0 {nthermr1_in} {nthermr1} -0 {mat} 12 {num_temp} 1 0 {iform} 1 221 1/ +0 {mat} {nbin} {num_temp} 1 0 {iform} 1 221 1/ {temps} {error} {energy_max} thermr / %%%%%%%%%%%%%%%% Add thermal scattering data (bound) %%%%%%%%%%%%%%%%%% {nthermal_endf} {nthermr2_in} {nthermr2} -{mat_thermal} {mat} 16 {num_temp} {inelastic} {elastic} {iform} {natom} 222 1/ +{mat_thermal} {mat} {nbin} {num_temp} {inelastic} {elastic} {iform} {natom} 222 1/ {temps} {error} {energy_max} """ @@ -188,7 +255,7 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, with tempfile.TemporaryDirectory() as tmpdir: # Copy evaluations to appropriates 'tapes' for tape_num, filename in tapein.items(): - tmpfilename = os.path.join(tmpdir, 'tape{}'.format(tape_num)) + tmpfilename = os.path.join(tmpdir, f'tape{tape_num}') shutil.copy(str(filename), tmpfilename) # Start up NJOY process @@ -216,12 +283,12 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, # Copy output files back to original directory for tape_num, filename in tapeout.items(): - tmpfilename = os.path.join(tmpdir, 'tape{}'.format(tape_num)) + tmpfilename = os.path.join(tmpdir, f'tape{tape_num}') if os.path.isfile(tmpfilename): shutil.move(tmpfilename, str(filename)) -def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): +def make_pendf(filename, pendf='pendf', **kwargs): """Generate pointwise ENDF file from an ENDF file Parameters @@ -230,10 +297,9 @@ def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): Path to ENDF file pendf : str, optional Path of pointwise ENDF file to write - error : float, optional - Fractional error tolerance for NJOY processing - stdout : bool - Whether to display NJOY standard output + **kwargs + Keyword arguments passed to :func:`openmc.data.njoy.make_ace`. All NJOY + module arguments other than pendf default to False. Raises ------ @@ -241,14 +307,15 @@ def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): If the NJOY process returns with a non-zero status """ - - make_ace(filename, pendf=pendf, error=error, broadr=False, - heatr=False, purr=False, acer=False, stdout=stdout) + for key in ('broadr', 'heatr', 'gaspr', 'purr', 'acer'): + kwargs.setdefault(key, False) + make_ace(filename, pendf=pendf, **kwargs) def make_ace(filename, temperatures=None, acer=True, xsdir=None, output_dir=None, pendf=False, error=0.001, broadr=True, - heatr=True, gaspr=True, purr=True, evaluation=None, **kwargs): + heatr=True, gaspr=True, purr=True, evaluation=None, + smoothing=True, **kwargs): """Generate incident neutron ACE file from an ENDF file File names can be passed to @@ -298,6 +365,8 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, evaluation : openmc.data.endf.Evaluation, optional If the ENDF file contains multiple material evaluations, this argument indicates which evaluation should be used. + smoothing : bool, optional + If the smoothing option (ACER card 6) is on (True) or off (False). **kwargs Keyword arguments passed to :func:`openmc.data.njoy.run` @@ -314,7 +383,7 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, else: output_dir = Path(output_dir) if not output_dir.is_dir(): - raise IOError("{} is not a directory".format(output_dir)) + raise IOError(f"{output_dir} is not a directory") ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material @@ -380,17 +449,18 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, # acer if acer: + ismooth = int(smoothing) nacer_in = nlast for i, temperature in enumerate(temperatures): # Extend input with an ACER run for each temperature nace = nacer_in + 1 + 2*i ndir = nace + 1 - ext = '{:02}'.format(i + 1) + ext = f'{i + 1:02}' commands += _TEMPLATE_ACER.format(**locals()) # Indicate tapes to save for each ACER run - tapeout[nace] = output_dir / "ace_{:.1f}".format(temperature) - tapeout[ndir] = output_dir / "xsdir_{:.1f}".format(temperature) + tapeout[nace] = output_dir / f"ace_{temperature:.1f}" + tapeout[ndir] = output_dir / f"xsdir_{temperature:.1f}" commands += 'stop\n' run(commands, tapein, tapeout, **kwargs) @@ -400,7 +470,7 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, with ace.open('w') as ace_file, xsdir.open('w') as xsdir_file: for temperature in temperatures: # Get contents of ACE file - text = (output_dir / "ace_{:.1f}".format(temperature)).read_text() + text = (output_dir / f"ace_{temperature:.1f}").read_text() # If the target is metastable, make sure that ZAID in the ACE # file reflects this by adding 400 @@ -413,19 +483,19 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, ace_file.write(text) # Concatenate into destination xsdir file - xsdir_in = output_dir / "xsdir_{:.1f}".format(temperature) + xsdir_in = output_dir / f"xsdir_{temperature:.1f}" xsdir_file.write(xsdir_in.read_text()) # Remove ACE/xsdir files for each temperature for temperature in temperatures: - (output_dir / "ace_{:.1f}".format(temperature)).unlink() - (output_dir / "xsdir_{:.1f}".format(temperature)).unlink() + (output_dir / f"ace_{temperature:.1f}").unlink() + (output_dir / f"xsdir_{temperature:.1f}").unlink() def make_ace_thermal(filename, filename_thermal, temperatures=None, - ace='ace', xsdir=None, output_dir=None, error=0.001, + ace=None, xsdir=None, output_dir=None, error=0.001, iwt=2, evaluation=None, evaluation_thermal=None, - table_name=None, zaids=None, nmix=None, **kwargs): + table_name=None, zaids=None, nmix=None, nbin=16, **kwargs): """Generate thermal scattering ACE file from ENDF files Parameters @@ -438,7 +508,7 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, Temperatures in Kelvin to produce data at. If omitted, data is produced at all temperatures given in the ENDF thermal scattering sublibrary. ace : str, optional - Path of ACE file to write + Path of ACE file to write. Default to ``"ace"``. xsdir : str, optional Path of xsdir file to write. Defaults to ``"xsdir"`` in the same directory as ``ace`` @@ -449,7 +519,7 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, error : float, optional Fractional error tolerance for NJOY processing iwt : int - `iwt` parameter used in NJOR/ACER card 9 + `iwt` parameter used in NJOY/ACER card 9 evaluation : openmc.data.endf.Evaluation, optional If the ENDF neutron sublibrary file contains multiple material evaluations, this argument indicates which evaluation to use. @@ -462,6 +532,8 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, ZAIDs that the thermal scattering data applies to nmix : int, optional Number of atom types in mixed moderator + nbin : int, optional + Number of equi-probable angles **kwargs Keyword arguments passed to :func:`openmc.data.njoy.run` @@ -476,7 +548,7 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, else: output_dir = Path(output_dir) if not output_dir.is_dir(): - raise IOError("{} is not a directory".format(output_dir)) + raise IOError(f"{output_dir} is not a directory") ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material @@ -577,27 +649,27 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, # Extend input with an ACER run for each temperature nace = nthermal_acer_in + 1 + 2*i ndir = nace + 1 - ext = '{:02}'.format(i + 1) + ext = f'{i + 1:02}' commands += _THERMAL_TEMPLATE_ACER.format(**locals()) # Indicate tapes to save for each ACER run - tapeout[nace] = output_dir / "ace_{:.1f}".format(temperature) - tapeout[ndir] = output_dir / "xsdir_{:.1f}".format(temperature) + tapeout[nace] = output_dir / f"ace_{temperature:.1f}" + tapeout[ndir] = output_dir / f"xsdir_{temperature:.1f}" commands += 'stop\n' run(commands, tapein, tapeout, **kwargs) - ace = output_dir / ace + ace = (output_dir / "ace") if ace is None else Path(ace) xsdir = (ace.parent / "xsdir") if xsdir is None else Path(xsdir) with ace.open('w') as ace_file, xsdir.open('w') as xsdir_file: # Concatenate ACE and xsdir files together for temperature in temperatures: - ace_in = output_dir / "ace_{:.1f}".format(temperature) + ace_in = output_dir / f"ace_{temperature:.1f}" ace_file.write(ace_in.read_text()) - xsdir_in = output_dir / "xsdir_{:.1f}".format(temperature) + xsdir_in = output_dir / f"xsdir_{temperature:.1f}" xsdir_file.write(xsdir_in.read_text()) # Remove ACE/xsdir files for each temperature for temperature in temperatures: - (output_dir / "ace_{:.1f}".format(temperature)).unlink() - (output_dir / "xsdir_{:.1f}".format(temperature)).unlink() + (output_dir / f"ace_{temperature:.1f}").unlink() + (output_dir / f"xsdir_{temperature:.1f}").unlink() diff --git a/openmc/data/photon.py b/openmc/data/photon.py index 48ecc3748c..25ded24cbe 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from collections.abc import Mapping, Callable from copy import deepcopy from io import StringIO @@ -13,7 +12,7 @@ from scipy.interpolate import CubicSpline import openmc.checkvalue as cv from openmc.mixin import EqualityMixin -from . import HDF5_VERSION +from . import HDF5_VERSION, HDF5_VERSION_MAJOR from .ace import Table, get_metadata, get_table from .data import ATOMIC_SYMBOL, EV_PER_MEV from .endf import Evaluation, get_head_record, get_tab1_record, get_list_record @@ -29,10 +28,10 @@ CM_PER_ANGSTROM = 1.0e-8 R0 = CM_PER_ANGSTROM * PLANCK_C / (2.0 * pi * FINE_STRUCTURE * MASS_ELECTRON_EV) # Electron subshell labels -_SUBSHELLS = [None, 'K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', +_SUBSHELLS = (None, 'K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'O1', 'O2', 'O3', 'O4', 'O5', 'O6', 'O7', 'O8', 'O9', 'P1', 'P2', 'P3', 'P4', - 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11', 'Q1', 'Q2', 'Q3'] + 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11', 'Q1', 'Q2', 'Q3') _REACTION_NAME = { 501: ('Total photon interaction', 'total'), @@ -125,7 +124,7 @@ class AtomicRelaxation(EqualityMixin): Dictionary indicating the number of electrons in a subshell when neutral (values) for given subshells (keys). The subshells should be given as strings, e.g., 'K', 'L1', 'L2', etc. - transitions : pandas.DataFrame + transitions : dict of str to pandas.DataFrame Dictionary indicating allowed transitions and their probabilities (values) for given subshells (keys). The subshells should be given as strings, e.g., 'K', 'L1', 'L2', etc. The transitions are represented as @@ -143,6 +142,8 @@ class AtomicRelaxation(EqualityMixin): Dictionary indicating the number of electrons in a subshell when neutral (values) for given subshells (keys). The subshells should be given as strings, e.g., 'K', 'L1', 'L2', etc. + subshells : list + List of subshells as strings, e.g. ``['K', 'L1', ...]`` transitions : pandas.DataFrame Dictionary indicating allowed transitions and their probabilities (values) for given subshells (keys). The subshells should be given as @@ -166,18 +167,6 @@ class AtomicRelaxation(EqualityMixin): def binding_energy(self): return self._binding_energy - @property - def num_electrons(self): - return self._num_electrons - - @property - def subshells(self): - return list(sorted(self.binding_energy.keys())) - - @property - def transitions(self): - return self._transitions - @binding_energy.setter def binding_energy(self, binding_energy): cv.check_type('binding energies', binding_energy, Mapping) @@ -187,6 +176,10 @@ class AtomicRelaxation(EqualityMixin): cv.check_greater_than('binding energy', energy, 0.0, True) self._binding_energy = binding_energy + @property + def num_electrons(self): + return self._num_electrons + @num_electrons.setter def num_electrons(self, num_electrons): cv.check_type('number of electrons', num_electrons, Mapping) @@ -196,6 +189,14 @@ class AtomicRelaxation(EqualityMixin): cv.check_greater_than('number of electrons', num, 0.0, True) self._num_electrons = num_electrons + @property + def subshells(self): + return list(sorted(self.binding_energy.keys())) + + @property + def transitions(self): + return self._transitions + @transitions.setter def transitions(self, transitions): cv.check_type('transitions', transitions, Mapping) @@ -362,8 +363,9 @@ class AtomicRelaxation(EqualityMixin): df = pd.DataFrame(sub_group['transitions'][()], columns=columns) # Replace float indexes back to subshell strings - df[columns[:2]] = df[columns[:2]].replace( - np.arange(float(len(_SUBSHELLS))), _SUBSHELLS) + with pd.option_context('future.no_silent_downcasting', True): + df[columns[:2]] = df[columns[:2]].replace( + np.arange(float(len(_SUBSHELLS))), _SUBSHELLS) transitions[shell] = df return cls(binding_energy, num_electrons, transitions) @@ -386,8 +388,9 @@ class AtomicRelaxation(EqualityMixin): # Write transition data with replacements if shell in self.transitions: - df = self.transitions[shell].replace( - _SUBSHELLS, range(len(_SUBSHELLS))) + with pd.option_context('future.no_silent_downcasting', True): + df = self.transitions[shell].replace( + _SUBSHELLS, range(len(_SUBSHELLS))) group.create_dataset('transitions', data=df.values.astype(float)) @@ -430,7 +433,7 @@ class IncidentPhoton(EqualityMixin): the projection of the electron momentum on the scattering vector, :math:`p_z` for each subshell). Note that subshell occupancies may not match the atomic relaxation data. - reactions : collections.OrderedDict + reactions : dict Contains the cross sections for each photon reaction. The keys are MT values and the values are instances of :class:`PhotonReaction`. @@ -439,7 +442,7 @@ class IncidentPhoton(EqualityMixin): def __init__(self, atomic_number): self.atomic_number = atomic_number self._atomic_relaxation = None - self.reactions = OrderedDict() + self.reactions = {} self.compton_profiles = {} self.bremsstrahlung = {} @@ -450,10 +453,10 @@ class IncidentPhoton(EqualityMixin): if mt in self.reactions: return self.reactions[mt] else: - raise KeyError('No reaction with MT={}.'.format(mt)) + raise KeyError(f'No reaction with MT={mt}.') def __repr__(self): - return "".format(self.name) + return f"" def __iter__(self): return iter(self.reactions.values()) @@ -462,26 +465,26 @@ class IncidentPhoton(EqualityMixin): def atomic_number(self): return self._atomic_number - @property - def atomic_relaxation(self): - return self._atomic_relaxation - - @property - def name(self): - return ATOMIC_SYMBOL[self.atomic_number] - @atomic_number.setter def atomic_number(self, atomic_number): cv.check_type('atomic number', atomic_number, Integral) cv.check_greater_than('atomic number', atomic_number, 0, True) self._atomic_number = atomic_number + @property + def atomic_relaxation(self): + return self._atomic_relaxation + @atomic_relaxation.setter def atomic_relaxation(self, atomic_relaxation): cv.check_type('atomic relaxation data', atomic_relaxation, AtomicRelaxation) self._atomic_relaxation = atomic_relaxation + @property + def name(self): + return ATOMIC_SYMBOL[self.atomic_number] + @classmethod def from_ace(cls, ace_or_filename): """Generate incident photon data from an ACE table @@ -507,18 +510,18 @@ class IncidentPhoton(EqualityMixin): # Get atomic number based on name of ACE table zaid, xs = ace.name.split('.') if not xs.endswith('p'): - raise TypeError("{} is not a photoatomic transport ACE table.".format(ace)) + raise TypeError(f"{ace} is not a photoatomic transport ACE table.") Z = get_metadata(int(zaid))[2] # Read each reaction data = cls(Z) - for mt in (502, 504, 515, 522, 525): + for mt in (502, 504, 517, 522, 525): data.reactions[mt] = PhotonReaction.from_ace(ace, mt) # Get heating cross sections [eV-barn] from factors [eV per collision] # by multiplying with total xs data.reactions[525].xs.y *= sum([data.reactions[mt].xs.y for mt in - (502, 504, 515, 522)]) + (502, 504, 517, 522)]) # Compton profiles n_shell = ace.nxs[5] @@ -637,7 +640,7 @@ class IncidentPhoton(EqualityMixin): with h5py.File(filename, 'r') as f: _COMPTON_PROFILES['pz'] = f['pz'][()] for i in range(1, 101): - group = f['{:03}'.format(i)] + group = f[f'{i:03}'] num_electrons = group['num_electrons'][()] binding_energy = group['binding_energy'][()]*EV_PER_MEV J = group['J'][()] @@ -712,7 +715,7 @@ class IncidentPhoton(EqualityMixin): # Check for necessary reactions for mt in (502, 504, 522): - assert mt in data, "Reaction {} not found".format(mt) + assert mt in data, f"Reaction {mt} not found" # Read atomic relaxation data.atomic_relaxation = AtomicRelaxation.from_hdf5(group['subshells']) @@ -763,7 +766,7 @@ class IncidentPhoton(EqualityMixin): """ with h5py.File(str(path), mode, libver=libver) as f: # Write filetype and version - f.attrs['filetype'] = np.string_('data_photon') + f.attrs['filetype'] = np.bytes_('data_photon') if 'version' not in f.attrs: f.attrs['version'] = np.array(HDF5_VERSION) @@ -835,7 +838,7 @@ class IncidentPhoton(EqualityMixin): filename = os.path.join(os.path.dirname(__file__), 'density_effect.h5') with h5py.File(filename, 'r') as f: for i in range(1, 101): - group = f['{:03}'.format(i)] + group = f[f'{i:03}'] _BREMSSTRAHLUNG[i] = { 'I': group.attrs['I'], 'num_electrons': group['num_electrons'][()], @@ -923,44 +926,43 @@ class PhotonReaction(EqualityMixin): def __repr__(self): if self.mt in _REACTION_NAME: - return "".format( - self.mt, _REACTION_NAME[self.mt][0]) + return f"" else: - return "".format(self.mt) + return f"" @property def anomalous_real(self): return self._anomalous_real - @property - def anomalous_imag(self): - return self._anomalous_imag - - @property - def scattering_factor(self): - return self._scattering_factor - - @property - def xs(self): - return self._xs - @anomalous_real.setter def anomalous_real(self, anomalous_real): cv.check_type('real part of anomalous scattering factor', anomalous_real, Callable) self._anomalous_real = anomalous_real + @property + def anomalous_imag(self): + return self._anomalous_imag + @anomalous_imag.setter def anomalous_imag(self, anomalous_imag): cv.check_type('imaginary part of anomalous scattering factor', anomalous_imag, Callable) self._anomalous_imag = anomalous_imag + @property + def scattering_factor(self): + return self._scattering_factor + @scattering_factor.setter def scattering_factor(self, scattering_factor): cv.check_type('scattering factor', scattering_factor, Callable) self._scattering_factor = scattering_factor + @property + def xs(self): + return self._xs + @xs.setter def xs(self, xs): cv.check_type('reaction cross section', xs, Callable) @@ -998,7 +1000,7 @@ class PhotonReaction(EqualityMixin): elif mt == 504: # Incoherent scattering idx = ace.jxs[1] + n - elif mt == 515: + elif mt == 517: # Pair production idx = ace.jxs[1] + 4*n elif mt == 522: @@ -1019,6 +1021,9 @@ class PhotonReaction(EqualityMixin): else: nonzero = (xs != 0.0) xs[nonzero] = np.exp(xs[nonzero]) + + # Replace zero elements to small non-zero to enable log-log + xs[~nonzero] = np.exp(-500.0) rx.xs = Tabulated1D(energy, xs, [n], [5]) # Get form factors for incoherent/coherent scattering diff --git a/openmc/data/product.py b/openmc/data/product.py index a6b2fd89e5..88c83b81fd 100644 --- a/openmc/data/product.py +++ b/openmc/data/product.py @@ -61,55 +61,55 @@ class Product(EqualityMixin): def applicability(self): return self._applicability - @property - def decay_rate(self): - return self._decay_rate - - @property - def distribution(self): - return self._distribution - - @property - def emission_mode(self): - return self._emission_mode - - @property - def particle(self): - return self._particle - - @property - def yield_(self): - return self._yield - @applicability.setter def applicability(self, applicability): cv.check_type('product distribution applicability', applicability, Iterable, Tabulated1D) self._applicability = applicability + @property + def decay_rate(self): + return self._decay_rate + @decay_rate.setter def decay_rate(self, decay_rate): cv.check_type('product decay rate', decay_rate, Real) cv.check_greater_than('product decay rate', decay_rate, 0.0, True) self._decay_rate = decay_rate + @property + def distribution(self): + return self._distribution + @distribution.setter def distribution(self, distribution): cv.check_type('product angle-energy distribution', distribution, Iterable, AngleEnergy) self._distribution = distribution + @property + def emission_mode(self): + return self._emission_mode + @emission_mode.setter def emission_mode(self, emission_mode): cv.check_value('product emission mode', emission_mode, ('prompt', 'delayed', 'total')) self._emission_mode = emission_mode + @property + def particle(self): + return self._particle + @particle.setter def particle(self, particle): cv.check_type('product particle type', particle, str) self._particle = particle + @property + def yield_(self): + return self._yield + @yield_.setter def yield_(self, yield_): cv.check_type('product yield', yield_, Function1D) @@ -124,8 +124,8 @@ class Product(EqualityMixin): HDF5 group to write to """ - group.attrs['particle'] = np.string_(self.particle) - group.attrs['emission_mode'] = np.string_(self.emission_mode) + group.attrs['particle'] = np.bytes_(self.particle) + group.attrs['emission_mode'] = np.bytes_(self.emission_mode) if self.decay_rate > 0.0: group.attrs['decay_rate'] = self.decay_rate @@ -135,7 +135,7 @@ class Product(EqualityMixin): # Write applicability/distribution group.attrs['n_distribution'] = len(self.distribution) for i, d in enumerate(self.distribution): - dgroup = group.create_group('distribution_{}'.format(i)) + dgroup = group.create_group(f'distribution_{i}') if self.applicability: self.applicability[i].to_hdf5(dgroup, 'applicability') d.to_hdf5(dgroup) @@ -170,7 +170,7 @@ class Product(EqualityMixin): distribution = [] applicability = [] for i in range(n_distribution): - dgroup = group['distribution_{}'.format(i)] + dgroup = group[f'distribution_{i}'] if 'applicability' in dgroup: applicability.append(Tabulated1D.from_hdf5( dgroup['applicability'])) diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index 5e4287f16e..65b59582cf 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -25,11 +25,11 @@ from .product import Product from .uncorrelated import UncorrelatedAngleEnergy -REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', - 5: '(n,misc)', 11: '(n,2nd)', 16: '(n,2n)', 17: '(n,3n)', - 18: '(n,fission)', 19: '(n,f)', 20: '(n,nf)', 21: '(n,2nf)', - 22: '(n,na)', 23: '(n,n3a)', 24: '(n,2na)', 25: '(n,3na)', - 27: '(n,absorption)', 28: '(n,np)', 29: '(n,n2a)', +REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 3: "(n,nonelastic)", + 4: '(n,level)', 5: '(n,misc)', 11: '(n,2nd)', 16: '(n,2n)', + 17: '(n,3n)', 18: '(n,fission)', 19: '(n,f)', 20: '(n,nf)', + 21: '(n,2nf)', 22: '(n,na)', 23: '(n,n3a)', 24: '(n,2na)', + 25: '(n,3na)', 27: '(n,absorption)', 28: '(n,np)', 29: '(n,n2a)', 30: '(n,2n2a)', 32: '(n,nd)', 33: '(n,nt)', 34: '(n,n3He)', 35: '(n,nd2a)', 36: '(n,nt2a)', 37: '(n,4n)', 38: '(n,3nf)', 41: '(n,2np)', 42: '(n,3np)', 44: '(n,n2p)', 45: '(n,npa)', @@ -56,13 +56,13 @@ REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', 301: 'heating', 444: 'damage-energy', 649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)', 849: '(n,ac)', 891: '(n,2nc)', 901: 'heating-local'} -REACTION_NAME.update({i: '(n,n{})'.format(i - 50) for i in range(51, 91)}) -REACTION_NAME.update({i: '(n,p{})'.format(i - 600) for i in range(600, 649)}) -REACTION_NAME.update({i: '(n,d{})'.format(i - 650) for i in range(650, 699)}) -REACTION_NAME.update({i: '(n,t{})'.format(i - 700) for i in range(700, 749)}) -REACTION_NAME.update({i: '(n,3He{})'.format(i - 750) for i in range(750, 799)}) -REACTION_NAME.update({i: '(n,a{})'.format(i - 800) for i in range(800, 849)}) -REACTION_NAME.update({i: '(n,2n{})'.format(i - 875) for i in range(875, 891)}) +REACTION_NAME.update({i: f'(n,n{i - 50})' for i in range(51, 91)}) +REACTION_NAME.update({i: f'(n,p{i - 600})' for i in range(600, 649)}) +REACTION_NAME.update({i: f'(n,d{i - 650})' for i in range(650, 699)}) +REACTION_NAME.update({i: f'(n,t{i - 700})' for i in range(700, 749)}) +REACTION_NAME.update({i: f'(n,3He{i - 750})' for i in range(750, 799)}) +REACTION_NAME.update({i: f'(n,a{i - 800})' for i in range(800, 849)}) +REACTION_NAME.update({i: f'(n,2n{i - 875})' for i in range(875, 891)}) REACTION_MT = {name: mt for mt, name in REACTION_NAME.items()} REACTION_MT['fission'] = 18 @@ -80,6 +80,14 @@ def _get_products(ev, mt): mt : int The MT value of the reaction to get products for + Raises + ------ + IOError + When the Kalbach-Mann systematics is used, but the product + is not defined in the 'center-of-mass' system. The breakup logic + is not implemented which can lead to this error being raised while + the definition of the product is correct. + Returns ------- products : list of openmc.data.Product @@ -101,7 +109,6 @@ def _get_products(ev, mt): za = int(params[0]) awr = params[1] - lip = params[2] law = params[3] if za == 0: @@ -112,7 +119,7 @@ def _get_products(ev, mt): p = Product('electron') else: Z, A = divmod(za, 1000) - p = Product('{}{}'.format(ATOMIC_SYMBOL[Z], A)) + p = Product(f'{ATOMIC_SYMBOL[Z]}{A}') p.yield_ = yield_ @@ -141,7 +148,26 @@ def _get_products(ev, mt): if lang == 1: p.distribution = [CorrelatedAngleEnergy.from_endf(file_obj)] elif lang == 2: - p.distribution = [KalbachMann.from_endf(file_obj)] + # Products need to be described in the center-of-mass system + product_center_of_mass = False + if reference_frame == 'center-of-mass': + product_center_of_mass = True + elif reference_frame == 'light-heavy': + product_center_of_mass = (awr <= 4.0) + # TODO: 'breakup' logic not implemented + + if product_center_of_mass is False: + raise IOError( + "Kalbach-Mann representation must be defined in the " + "'center-of-mass' system" + ) + + zat = ev.target["atomic_number"] * 1000 + ev.target["mass_number"] + projectile_mass = ev.projectile["mass"] + p.distribution = [KalbachMann.from_endf(file_obj, + za, + zat, + projectile_mass)] elif law == 2: # Discrete two-body scattering @@ -528,12 +554,12 @@ def _get_activation_products(ev, rx): Z, A = divmod(items[2], 1000) excited_state = items[3] - # Get GND name for product + # Get GNDS name for product symbol = ATOMIC_SYMBOL[Z] if excited_state > 0: - name = '{}{}_e{}'.format(symbol, A, excited_state) + name = f'{symbol}{A}_e{excited_state}' else: - name = '{}{}'.format(symbol, A) + name = f'{symbol}{A}' p = Product(name) if mf == 9: @@ -630,8 +656,7 @@ def _get_photon_products_ace(ace, rx): photon.yield_ = Tabulated1D(energy, yield_) else: - raise ValueError("MFTYPE must be 12, 13, 16. Got {0}".format( - mftype)) + raise ValueError(f"MFTYPE must be 12, 13, 16. Got {mftype}") # ================================================================== # Photon energy distribution @@ -820,60 +845,60 @@ class Reaction(EqualityMixin): def __repr__(self): if self.mt in REACTION_NAME: - return "".format(self.mt, REACTION_NAME[self.mt]) + return f"" else: - return "".format(self.mt) + return f"" @property def center_of_mass(self): return self._center_of_mass - @property - def redundant(self): - return self._redundant - - @property - def q_value(self): - return self._q_value - - @property - def products(self): - return self._products - - @property - def derived_products(self): - return self._derived_products - - @property - def xs(self): - return self._xs - @center_of_mass.setter def center_of_mass(self, center_of_mass): cv.check_type('center of mass', center_of_mass, (bool, np.bool_)) self._center_of_mass = center_of_mass + @property + def redundant(self): + return self._redundant + @redundant.setter def redundant(self, redundant): cv.check_type('redundant', redundant, (bool, np.bool_)) self._redundant = redundant + @property + def q_value(self): + return self._q_value + @q_value.setter def q_value(self, q_value): cv.check_type('Q value', q_value, Real) self._q_value = q_value + @property + def products(self): + return self._products + @products.setter def products(self, products): cv.check_type('reaction products', products, Iterable, Product) self._products = products + @property + def derived_products(self): + return self._derived_products + @derived_products.setter def derived_products(self, derived_products): cv.check_type('reaction derived products', derived_products, Iterable, Product) self._derived_products = derived_products + @property + def xs(self): + return self._xs + @xs.setter def xs(self, xs): cv.check_type('reaction cross section dictionary', xs, MutableMapping) @@ -894,9 +919,9 @@ class Reaction(EqualityMixin): group.attrs['mt'] = self.mt if self.mt in REACTION_NAME: - group.attrs['label'] = np.string_(REACTION_NAME[self.mt]) + group.attrs['label'] = np.bytes_(REACTION_NAME[self.mt]) else: - group.attrs['label'] = np.string_(self.mt) + group.attrs['label'] = np.bytes_(self.mt) group.attrs['Q_value'] = self.q_value group.attrs['center_of_mass'] = 1 if self.center_of_mass else 0 group.attrs['redundant'] = 1 if self.redundant else 0 @@ -907,7 +932,7 @@ class Reaction(EqualityMixin): threshold_idx = getattr(self.xs[T], '_threshold_idx', 0) dset.attrs['threshold_idx'] = threshold_idx for i, p in enumerate(self.products): - pgroup = group.create_group('product_{}'.format(i)) + pgroup = group.create_group(f'product_{i}') p.to_hdf5(pgroup) @classmethod @@ -959,7 +984,7 @@ class Reaction(EqualityMixin): # Read reaction products for i in range(n_product): - pgroup = group['product_{}'.format(i)] + pgroup = group[f'product_{i}'] rx.products.append(Product.from_hdf5(pgroup)) return rx diff --git a/openmc/data/reconstruct.pyx b/openmc/data/reconstruct.pyx deleted file mode 100644 index f63a155b15..0000000000 --- a/openmc/data/reconstruct.pyx +++ /dev/null @@ -1,522 +0,0 @@ -from libc.stdlib cimport malloc, calloc, free -from libc.math cimport cos, sin, sqrt, atan, M_PI - -cimport numpy as np -import numpy as np -from numpy.linalg import inv -cimport cython - - -cdef extern from "complex.h": - double cabs(double complex) - double complex conj(double complex) - double creal(complex double) - double cimag(complex double) - double complex cexp(double complex) - -# Physical constants are from CODATA 2014 -cdef double NEUTRON_MASS_ENERGY = 939.5654133e6 # eV/c^2 -cdef double HBAR_C = 197.3269788e5 # eV-b^0.5 - - -@cython.cdivision(True) -def wave_number(double A, double E): - r"""Neutron wave number in center-of-mass system. - - ENDF-102 defines the neutron wave number in the center-of-mass system in - Equation D.10 as - - .. math:: - k = \frac{2m_n}{\hbar} \frac{A}{A + 1} \sqrt{|E|} - - Parameters - ---------- - A : double - Ratio of target mass to neutron mass - E : double - Energy in eV - - Returns - ------- - double - Neutron wave number in b^-0.5 - - """ - return A/(A + 1)*sqrt(2*NEUTRON_MASS_ENERGY*abs(E))/HBAR_C - -@cython.cdivision(True) -cdef double _wave_number(double A, double E): - return A/(A + 1)*sqrt(2*NEUTRON_MASS_ENERGY*abs(E))/HBAR_C - - -@cython.cdivision(True) -cdef double phaseshift(int l, double rho): - """Calculate hardsphere phase shift as given in ENDF-102, Equation D.13 - - Parameters - ---------- - l : int - Angular momentum quantum number - rho : float - Product of the wave number and the channel radius - - Returns - ------- - double - Hardsphere phase shift - - """ - if l == 0: - return rho - elif l == 1: - return rho - atan(rho) - elif l == 2: - return rho - atan(3*rho/(3 - rho**2)) - elif l == 3: - return rho - atan((15*rho - rho**3)/(15 - 6*rho**2)) - elif l == 4: - return rho - atan((105*rho - 10*rho**3)/(105 - 45*rho**2 + rho**4)) - - -@cython.cdivision(True) -def penetration_shift(int l, double rho): - r"""Calculate shift and penetration factors as given in ENDF-102, Equations D.11 - and D.12. - - Parameters - ---------- - l : int - Angular momentum quantum number - rho : float - Product of the wave number and the channel radius - - Returns - ------- - double - Penetration factor for given :math:`l` - double - Shift factor for given :math:`l` - - """ - cdef double den - - if l == 0: - return rho, 0. - elif l == 1: - den = 1 + rho**2 - return rho**3/den, -1/den - elif l == 2: - den = 9 + 3*rho**2 + rho**4 - return rho**5/den, -(18 + 3*rho**2)/den - elif l == 3: - den = 225 + 45*rho**2 + 6*rho**4 + rho**6 - return rho**7/den, -(675 + 90*rho**2 + 6*rho**4)/den - elif l == 4: - den = 11025 + 1575*rho**2 + 135*rho**4 + 10*rho**6 + rho**8 - return rho**9/den, -(44100 + 4725*rho**2 + 270*rho**4 + 10*rho**6)/den - - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def reconstruct_mlbw(mlbw, double E): - """Evaluate cross section using MLBW data. - - Parameters - ---------- - mlbw : openmc.data.MultiLevelBreitWigner - Multi-level Breit-Wigner resonance parameters - E : double - Energy in eV at which to evaluate the cross section - - Returns - ------- - elastic : double - Elastic scattering cross section in barns - capture : double - Radiative capture cross section in barns - fission : double - Fission cross section in barns - - """ - cdef int i, nJ, ij, l, n_res, i_res - cdef double elastic, capture, fission - cdef double A, k, rho, rhohat, I - cdef double P, S, phi, cos2phi, sin2phi - cdef double Ex, Q, rhoc, rhochat, P_c, S_c - cdef double jmin, jmax, j, Dl - cdef double E_r, gt, gn, gg, gf, gx, P_r, S_r, P_rx - cdef double gnE, gtE, Eprime, x, f - cdef double *g - cdef double (*s)[2] - cdef double [:,:] params - - I = mlbw.target_spin - A = mlbw.atomic_weight_ratio - k = _wave_number(A, E) - - elastic = 0. - capture = 0. - fission = 0. - - for i, l in enumerate(mlbw._l_values): - params = mlbw._parameter_matrix[l] - - rho = k*mlbw.channel_radius[l](E) - rhohat = k*mlbw.scattering_radius[l](E) - P, S = penetration_shift(l, rho) - phi = phaseshift(l, rhohat) - cos2phi = cos(2*phi) - sin2phi = sin(2*phi) - - # Determine shift and penetration at modified energy - if mlbw._competitive[i]: - Ex = E + mlbw.q_value[l]*(A + 1)/A - rhoc = mlbw.channel_radius[l](Ex) - rhochat = mlbw.scattering_radius[l](Ex) - P_c, S_c = penetration_shift(l, rhoc) - if Ex < 0: - P_c = 0 - - # Determine range of total angular momentum values based on equation - # 41 in LA-UR-12-27079 - jmin = abs(abs(I - l) - 0.5) - jmax = I + l + 0.5 - nJ = int(jmax - jmin + 1) - - # Determine Dl factor using Equation 43 in LA-UR-12-27079 - Dl = 2*l + 1 - g = malloc(nJ*sizeof(double)) - for ij in range(nJ): - j = jmin + ij - g[ij] = (2*j + 1)/(4*I + 2) - Dl -= g[ij] - - s = calloc(2*nJ, sizeof(double)) - for i_res in range(params.shape[0]): - # Copy resonance parameters - E_r = params[i_res, 0] - j = params[i_res, 2] - ij = int(j - jmin) - gt = params[i_res, 3] - gn = params[i_res, 4] - gg = params[i_res, 5] - gf = params[i_res, 6] - gx = params[i_res, 7] - P_r = params[i_res, 8] - S_r = params[i_res, 9] - P_rx = params[i_res, 10] - - # Calculate neutron and total width at energy E - gnE = P*gn/P_r # ENDF-102, Equation D.7 - gtE = gnE + gg + gf - if gx > 0: - gtE += gx*P_c/P_rx - - Eprime = E_r + (S_r - S)/(2*P_r)*gn # ENDF-102, Equation D.9 - x = 2*(E - Eprime)/gtE # LA-UR-12-27079, Equation 26 - f = 2*gnE/(gtE*(1 + x*x)) # Common factor in Equation 40 - s[ij][0] += f # First sum in Equation 40 - s[ij][1] += f*x # Second sum in Equation 40 - capture += f*g[ij]*gg/gtE - if gf > 0: - fission += f*g[ij]*gf/gtE - - for ij in range(nJ): - # Add all but last term of LA-UR-12-27079, Equation 40 - elastic += g[ij]*((1 - cos2phi - s[ij][0])**2 + - (sin2phi + s[ij][1])**2) - - # Add final term with Dl from Equation 40 - elastic += 2*Dl*(1 - cos2phi) - - # Free memory - free(g) - free(s) - - capture *= 2*M_PI/(k*k) - fission *= 2*M_PI/(k*k) - elastic *= M_PI/(k*k) - - return (elastic, capture, fission) - - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def reconstruct_slbw(slbw, double E): - """Evaluate cross section using SLBW data. - - Parameters - ---------- - slbw : openmc.data.SingleLevelBreitWigner - Single-level Breit-Wigner resonance parameters - E : double - Energy in eV at which to evaluate the cross section - - Returns - ------- - elastic : double - Elastic scattering cross section in barns - capture : double - Radiative capture cross section in barns - fission : double - Fission cross section in barns - - """ - cdef int i, l, i_res - cdef double elastic, capture, fission - cdef double A, k, rho, rhohat, I - cdef double P, S, phi, cos2phi, sin2phi, sinphi2 - cdef double Ex, rhoc, rhochat, P_c, S_c - cdef double E_r, J, gt, gn, gg, gf, gx, P_r, S_r, P_rx - cdef double gnE, gtE, Eprime, f - cdef double x, theta, psi, chi - cdef double [:,:] params - - I = slbw.target_spin - A = slbw.atomic_weight_ratio - k = _wave_number(A, E) - - elastic = 0. - capture = 0. - fission = 0. - - for i, l in enumerate(slbw._l_values): - params = slbw._parameter_matrix[l] - - rho = k*slbw.channel_radius[l](E) - rhohat = k*slbw.scattering_radius[l](E) - P, S = penetration_shift(l, rho) - phi = phaseshift(l, rhohat) - cos2phi = cos(2*phi) - sin2phi = sin(2*phi) - sinphi2 = sin(phi)**2 - - # Add potential scattering -- first term in ENDF-102, Equation D.2 - elastic += 4*M_PI/(k*k)*(2*l + 1)*sinphi2 - - # Determine shift and penetration at modified energy - if slbw._competitive[i]: - Ex = E + slbw.q_value[l]*(A + 1)/A - rhoc = slbw.channel_radius[l](Ex) - rhochat = slbw.scattering_radius[l](Ex) - P_c, S_c = penetration_shift(l, rhoc) - if Ex < 0: - P_c = 0 - - for i_res in range(params.shape[0]): - # Copy resonance parameters - E_r = params[i_res, 0] - J = params[i_res, 2] - gt = params[i_res, 3] - gn = params[i_res, 4] - gg = params[i_res, 5] - gf = params[i_res, 6] - gx = params[i_res, 7] - P_r = params[i_res, 8] - S_r = params[i_res, 9] - P_rx = params[i_res, 10] - - # Calculate neutron and total width at energy E - gnE = P*gn/P_r # Equation D.7 - gtE = gnE + gg + gf - if gx > 0: - gtE += gx*P_c/P_rx - - Eprime = E_r + (S_r - S)/(2*P_r)*gn # Equation D.9 - gJ = (2*J + 1)/(4*I + 2) # Mentioned in section D.1.1.4 - - # Calculate common factor for elastic, capture, and fission - # cross sections - f = M_PI/(k*k)*gJ*gnE/((E - Eprime)**2 + gtE**2/4) - - # Add contribution to elastic per Equation D.2 - elastic += f*(gnE*cos2phi - 2*(gg + gf)*sinphi2 - + 2*(E - Eprime)*sin2phi) - - # Add contribution to capture per Equation D.3 - capture += f*gg - - # Add contribution to fission per Equation D.6 - if gf > 0: - fission += f*gf - - return (elastic, capture, fission) - - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def reconstruct_rm(rm, double E): - """Evaluate cross section using Reich-Moore data. - - Parameters - ---------- - rm : openmc.data.ReichMoore - Reich-Moore resonance parameters - E : double - Energy in eV at which to evaluate the cross section - - Returns - ------- - elastic : double - Elastic scattering cross section in barns - capture : double - Radiative capture cross section in barns - fission : double - Fission cross section in barns - - """ - cdef int i, l, m, n, i_res - cdef int i_s, num_s, i_J, num_J - cdef double elastic, capture, fission, total - cdef double A, k, rho, rhohat, I - cdef double P, S, phi - cdef double smin, smax, s, Jmin, Jmax, J, j - cdef double E_r, gn, gg, gfa, gfb, P_r - cdef double E_diff, abs_value, gJ - cdef double Kr, Ki, x - cdef double complex Ubar, U_, factor - cdef bint hasfission - cdef np.ndarray[double, ndim=2] one - cdef np.ndarray[double complex, ndim=2] K, Imat, U - cdef double [:,:] params - - # Get nuclear spin - I = rm.target_spin - - elastic = 0. - fission = 0. - total = 0. - A = rm.atomic_weight_ratio - k = _wave_number(A, E) - one = np.eye(3) - K = np.zeros((3,3), dtype=complex) - - for i, l in enumerate(rm._l_values): - # Check for l-dependent scattering radius - rho = k*rm.channel_radius[l](E) - rhohat = k*rm.scattering_radius[l](E) - - # Calculate shift and penetrability - P, S = penetration_shift(l, rho) - - # Calculate phase shift - phi = phaseshift(l, rhohat) - - # Calculate common factor on collision matrix terms (term outside curly - # braces in ENDF-102, Eq. D.27) - Ubar = cexp(-2j*phi) - - # The channel spin is the vector sum of the target spin, I, and the - # neutron spin, 1/2, so can take on values of |I - 1/2| < s < I + 1/2 - smin = abs(I - 0.5) - smax = I + 0.5 - num_s = int(smax - smin + 1) - - for i_s in range(num_s): - s = i_s + smin - - # Total angular momentum is the vector sum of l and s and can assume - # values between |l - s| < J < l + s - Jmin = abs(l - s) - Jmax = l + s - num_J = int(Jmax - Jmin + 1) - - for i_J in range(num_J): - J = i_J + Jmin - - # Initialize K matrix - for m in range(3): - for n in range(3): - K[m,n] = 0.0 - - hasfission = False - if (l, J) in rm._parameter_matrix: - params = rm._parameter_matrix[l, J] - - for i_res in range(params.shape[0]): - # Sometimes, the same (l, J) quantum numbers can occur - # for different values of the channel spin, s. In this - # case, the sign of the channel spin indicates which - # spin is to be used. If the spin is negative assume - # this resonance comes from the I - 1/2 channel and vice - # versa. - j = params[i_res, 2] - if l > 0: - if (j < 0 and s != smin) or (j > 0 and s != smax): - continue - - # Copy resonance parameters - E_r = params[i_res, 0] - gn = params[i_res, 3] - gg = params[i_res, 4] - gfa = params[i_res, 5] - gfb = params[i_res, 6] - P_r = params[i_res, 7] - - # Calculate neutron width at energy E - gn = sqrt(P*gn/P_r) - - # Calculate j/2 * inverse of denominator of K matrix terms - factor = 0.5j/(E_r - E - 0.5j*gg) - - # Upper triangular portion of K matrix -- see ENDF-102, - # Equation D.28 - K[0,0] = K[0,0] + gn*gn*factor - if gfa != 0.0 or gfb != 0.0: - # Negate fission widths if necessary - gfa = (-1 if gfa < 0 else 1)*sqrt(abs(gfa)) - gfb = (-1 if gfb < 0 else 1)*sqrt(abs(gfb)) - - K[0,1] = K[0,1] + gn*gfa*factor - K[0,2] = K[0,2] + gn*gfb*factor - K[1,1] = K[1,1] + gfa*gfa*factor - K[1,2] = K[1,2] + gfa*gfb*factor - K[2,2] = K[2,2] + gfb*gfb*factor - hasfission = True - - # Get collision matrix - gJ = (2*J + 1)/(4*I + 2) - if hasfission: - # Copy upper triangular portion of K to lower triangular - K[1,0] = K[0,1] - K[2,0] = K[0,2] - K[2,1] = K[1,2] - - Imat = inv(one - K) - U = Ubar*(2*Imat - one) # ENDF-102, Eq. D.27 - elastic += gJ*cabs(1 - U[0,0])**2 # ENDF-102, Eq. D.24 - total += 2*gJ*(1 - creal(U[0,0])) # ENDF-102, Eq. D.23 - - # Calculate fission from ENDF-102, Eq. D.26 - fission += 4*gJ*(cabs(Imat[1,0])**2 + cabs(Imat[2,0])**2) - else: - U_ = Ubar*(2/(1 - K[0,0]) - 1) - if abs(creal(K[0,0])) < 3e-4 and abs(phi) < 3e-4: - # If K and phi are both very small, the calculated cross - # sections can lose precision because the real part of U - # ends up very close to unity. To get around this, we - # use Euler's formula to express Ubar by real and - # imaginary parts, expand cos(2phi) = 1 - 2phi^2 + - # O(phi^4), and then simplify - Kr = creal(K[0,0]) - Ki = cimag(K[0,0]) - x = 2*(-Kr + (Kr*Kr + Ki*Ki)*(1 - phi*phi) + phi*phi - - sin(2*phi)*Ki)/((1 - Kr)*(1 - Kr) + Ki*Ki) - total += 2*gJ*x - elastic += gJ*(x*x + cimag(U_)**2) - else: - total += 2*gJ*(1 - creal(U_)) # ENDF-102, Eq. D.23 - elastic += gJ*cabs(1 - U_)**2 # ENDF-102, Eq. D.24 - - # Calculate capture as difference of other cross sections as per ENDF-102, - # Equation D.25 - capture = total - elastic - fission - - elastic *= M_PI/(k*k) - capture *= M_PI/(k*k) - fission *= M_PI/(k*k) - - return (elastic, capture, fission) diff --git a/openmc/data/resonance.py b/openmc/data/resonance.py index 7d27ea7cc3..31e230df58 100644 --- a/openmc/data/resonance.py +++ b/openmc/data/resonance.py @@ -46,6 +46,12 @@ class Resonances: def ranges(self): return self._ranges + @ranges.setter + def ranges(self, ranges): + cv.check_type('resonance ranges', ranges, MutableSequence) + self._ranges = cv.CheckedList(ResonanceRange, 'resonance ranges', + ranges) + @property def resolved(self): resolved_ranges = [r for r in self.ranges @@ -65,12 +71,6 @@ class Resonances: else: return None - @ranges.setter - def ranges(self, ranges): - cv.check_type('resonance ranges', ranges, MutableSequence) - self._ranges = cv.CheckedList(ResonanceRange, 'resonance ranges', - ranges) - @classmethod def from_endf(cls, ev): """Generate resonance data from an ENDF evaluation. @@ -93,9 +93,8 @@ class Resonances: n_isotope = items[4] # Number of isotopes ranges = [] - for iso in range(n_isotope): + for _ in range(n_isotope): items = get_cont_record(file_obj) - abundance = items[1] fission_widths = (items[3] == 1) # fission widths are given? n_ranges = items[4] # number of resonance energy ranges @@ -424,14 +423,12 @@ class MultiLevelBreitWigner(ResonanceRange): # Determine penetration and shift corresponding to resonance energy k = wave_number(A, E) rho = k*self.channel_radius[l](E) - rhohat = k*self.scattering_radius[l](E) p[i], s[i] = penetration_shift(l, rho) # Determine penetration at modified energy for competitive reaction if gx > 0: Ex = E + self.q_value[l]*(A + 1)/A rho = k*self.channel_radius[l](Ex) - rhohat = k*self.scattering_radius[l](Ex) px[i], sx[i] = penetration_shift(l, rho) else: px[i] = sx[i] = 0.0 @@ -680,7 +677,6 @@ class ReichMoore(ResonanceRange): # Determine penetration and shift corresponding to resonance energy k = wave_number(A, E) rho = k*self.channel_radius[l](E) - rhohat = k*self.scattering_radius[l](E) p[i], s[i] = penetration_shift(l, rho) df['p'] = p @@ -834,7 +830,7 @@ class RMatrixLimited(ResonanceRange): elif mt == 102: columns.append('captureWidth') else: - columns.append('width (MT={})'.format(mt)) + columns.append(f'width (MT={mt})') # Create Pandas dataframe with resonance parameters parameters = pd.DataFrame.from_records(records, columns=columns) @@ -900,7 +896,7 @@ class SpinGroup: self.parameters = parameters def __repr__(self): - return ''.format(self.spin, self.parity) + return f'' class Unresolved(ResonanceRange): diff --git a/openmc/data/resonance_covariance.py b/openmc/data/resonance_covariance.py index 9e80e52f2c..7096570449 100644 --- a/openmc/data/resonance_covariance.py +++ b/openmc/data/resonance_covariance.py @@ -93,10 +93,8 @@ class ResonanceCovariances(Resonances): n_isotope = items[4] # Number of isotopes ranges = [] - for iso in range(n_isotope): + for _ in range(n_isotope): items = endf.get_cont_record(file_obj) - abundance = items[1] - fission_widths = (items[3] == 1) # Flag for fission widths n_ranges = items[4] # Number of resonance energy ranges for j in range(n_ranges): @@ -241,14 +239,14 @@ class ResonanceCovarianceRange: samples = [] # Handling MLBW/SLBW sampling + rng = np.random.default_rng() if formalism == 'mlbw' or formalism == 'slbw': params = ['energy', 'neutronWidth', 'captureWidth', 'fissionWidth', 'competitiveWidth'] param_list = params[:mpar] mean_array = parameters[param_list].values mean = mean_array.flatten() - par_samples = np.random.multivariate_normal(mean, cov, - size=n_samples) + par_samples = rng.multivariate_normal(mean, cov, size=n_samples) spin = parameters['J'].values l_value = parameters['L'].values for sample in par_samples: @@ -279,8 +277,7 @@ class ResonanceCovarianceRange: param_list = params[:mpar] mean_array = parameters[param_list].values mean = mean_array.flatten() - par_samples = np.random.multivariate_normal(mean, cov, - size=n_samples) + par_samples = rng.multivariate_normal(mean, cov, size=n_samples) spin = parameters['J'].values l_value = parameters['L'].values for sample in par_samples: @@ -378,7 +375,6 @@ class MultiLevelBreitWignerCovariance(ResonanceCovarianceRange): # Other scatter radius parameters items = endf.get_cont_record(file_obj) - target_spin = items[0] lcomp = items[3] # Flag for compatibility 0, 1, 2 - 2 is compact form nls = items[4] # number of l-values @@ -387,8 +383,6 @@ class MultiLevelBreitWignerCovariance(ResonanceCovarianceRange): items = endf.get_cont_record(file_obj) # Number of short range type resonance covariances num_short_range = items[4] - # Number of long range type resonance covariances - num_long_range = items[5] # Read resonance widths, J values, etc records = [] @@ -421,7 +415,6 @@ class MultiLevelBreitWignerCovariance(ResonanceCovarianceRange): # compact correlations elif lcomp == 2: items, values = endf.get_list_record(file_obj) - mean = items num_res = items[5] energy = values[0::12] spin = values[1::12] @@ -613,20 +606,14 @@ class ReichMooreCovariance(ResonanceCovarianceRange): # Other scatter radius parameters items = endf.get_cont_record(file_obj) - target_spin = items[0] lcomp = items[3] # Flag for compatibility 0, 1, 2 - 2 is compact form - nls = items[4] # Number of l-values # Build covariance matrix for General Resolved Resonance Formats if lcomp == 1: items = endf.get_cont_record(file_obj) # Number of short range type resonance covariances num_short_range = items[4] - # Number of long range type resonance covariances - num_long_range = items[5] # Read resonance widths, J values, etc - channel_radius = {} - scattering_radius = {} records = [] for i in range(num_short_range): items, values = endf.get_list_record(file_obj) diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index 4e0a30b648..4ecc7c040e 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -5,7 +5,6 @@ from numbers import Real from io import StringIO import itertools import os -import re import tempfile from warnings import warn @@ -19,12 +18,12 @@ from . import HDF5_VERSION, HDF5_VERSION_MAJOR, endf from .data import K_BOLTZMANN, ATOMIC_SYMBOL, EV_PER_MEV, isotopes from .ace import Table, get_table, Library from .angle_energy import AngleEnergy -from .function import Tabulated1D, Function1D +from .function import Tabulated1D, Function1D, Sum from .njoy import make_ace_thermal from .thermal_angle_energy import (CoherentElasticAE, IncoherentElasticAE, IncoherentElasticAEDiscrete, IncoherentInelasticAEDiscrete, - IncoherentInelasticAE) + IncoherentInelasticAE, MixedElasticAE) _THERMAL_NAMES = { @@ -32,30 +31,54 @@ _THERMAL_NAMES = { 'c_Al_in_Al2O3': ('asap00', 'asap', 'al(al2o3)'), 'c_Be': ('be', 'be-metal', 'be-met', 'be00', 'be-metal', 'be metal', '4-be'), 'c_BeO': ('beo',), - 'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00', 'be(beo)'), - 'c_Be_in_Be2C': ('bebe2c',), + 'c_Be_distinct': ('besd', 'be+sd'), + 'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00', 'be(beo)', 'be_beo'), + 'c_Be_in_Be2C': ('bebe2c', 'be(be2c)'), + 'c_Be_in_BeF2': ('bebef2', 'be in bef2'), 'c_Be_in_FLiBe': ('beflib', 'be(flibe)'), 'c_C6H6': ('benz', 'c6h6', 'benzine'), - 'c_C_in_SiC': ('csic', 'c-sic', 'c(3c-sic)'), - 'c_Ca_in_CaH2': ('cah', 'cah00', 'cacah2', 'ca(cah2)'), + 'c_C_in_Be2C': ('cbe2c', 'c(be2c)'), + 'c_C_in_C5O2H8': ('clucit', 'c(lucite)'), + 'c_C_in_C8H8': ('cc8h8', 'c(polystyr'), + 'c_C_in_CF2': ('ccf2', 'c(teflon)'), + 'c_C_in_SiC': ('csic', 'c-sic', 'c(3c-sic)', 'c_sic'), + 'c_C_in_UC_100p': ('cuc100', 'cinuc_100p'), + 'c_C_in_UC_10p': ('cuc10', 'cinuc_10p'), + 'c_C_in_UC_5p': ('cuc5', 'cinuc_5p'), + 'c_C_in_UC': ('cinuc', 'cinuc_nat'), + 'c_C_in_UC_HALEU': ('cuchal', 'cinuc_haleu'), + 'c_C_in_UC_HEU': ('cucheu', 'cinuc_heu'), + 'c_C_in_ZrC': ('czrc', 'c(zrc)'), + 'c_Ca_in_CaH2': ('cah', 'cah00', 'cacah2', 'ca(cah2)', 'ca_cah2'), + 'c_D_in_7LiD': ('dlid', 'd(7lid)'), 'c_D_in_D2O': ('dd2o', 'd-d2o', 'hwtr', 'hw', 'dhw00', 'd(d2o)'), - 'c_D_in_D2O_ice': ('dice',), + 'c_D_in_D2O_solid': ('dice',), + 'c_F_in_Be2': ('fbef2', 'f in bef2'), + 'c_F_in_CF2': ('fcf2', 'f(teflon)'), 'c_F_in_FLiBe': ('fflibe', 'f(flibe)'), + 'c_F_in_HF': ('f_hf',), + 'c_F_in_MgF2': ('fmgf2', 'f in mgf2'), 'c_Fe56': ('fe', 'fe56', 'fe-56', '26-fe- 56'), 'c_Graphite': ('graph', 'grph', 'gr', 'gr00', 'graphite'), 'c_Graphite_10p': ('grph10', '10p graphit'), + 'c_Graphite_20p': ('grph20', '20 graphite'), 'c_Graphite_30p': ('grph30', '30p graphit'), + 'c_Graphite_distinct': ('grphsd', 'grph+sd'), + 'c_H_in_7LiH': ('hlih', 'h(7lih)'), 'c_H_in_C5O2H8': ('lucite', 'c5o2h8', 'h-luci', 'h(lucite)'), + 'c_H_in_C8H8': ('hc8h8', 'h(polystyr'), 'c_H_in_CaH2': ('hcah2', 'hca00', 'h(cah2)'), + 'c_H1_in_CaH2': ('h1cah2', 'h1_cah2'), + 'c_H2_in_CaH2': ('h2cah2', 'h2_cah2'), 'c_H_in_CH2': ('hch2', 'poly', 'pol', 'h-poly', 'pol00', 'h(ch2)'), 'c_H_in_CH4_liquid': ('lch4', 'lmeth', 'l-ch4'), 'c_H_in_CH4_solid': ('sch4', 'smeth', 's-ch4'), 'c_H_in_CH4_solid_phase_II': ('sch4p2',), 'c_H_in_H2O': ('hh2o', 'h-h2o', 'lwtr', 'lw', 'lw00', 'h(h2o)'), 'c_H_in_H2O_solid': ('hice', 'h-ice', 'ice00', 'h(ice-ih)', 'h(ice)'), - 'c_H_in_HF': ('hhf', 'h(hf)'), + 'c_H_in_HF': ('hhf', 'h(hf)', 'h_hf'), 'c_H_in_Mesitylene': ('mesi00', 'mesi', 'mesi-phii'), - 'c_H_in_ParaffinicOil': ('hparaf', 'h(paraffin'), + 'c_H_in_ParaffinicOil': ('hparaf', 'h(paraffin', 'h(paraffini'), 'c_H_in_Toluene': ('tol00', 'tol', 'tolue-phii'), 'c_H_in_UH3': ('huh3', 'h(uh3)'), 'c_H_in_YH2': ('hyh2', 'h-yh2', 'h(yh2)'), @@ -63,24 +86,67 @@ _THERMAL_NAMES = { 'c_H_in_ZrH2': ('hzrh2', 'h(zrh2)'), 'c_H_in_ZrHx': ('hzrhx', 'h(zrhx)'), 'c_Li_in_FLiBe': ('liflib', 'li(flibe)'), + 'c_Li_in_7LiD': ('lilid', '7li(7lid)'), + 'c_Li_in_7LiH': ('lilih', '7li(7lih)'), 'c_Mg24': ('mg', 'mg24', 'mg00', '24-mg'), - 'c_N_in_UN': ('n-un', 'n(un)', 'n(un) l'), + 'c_Mg_in_MgF2': ('mgmgf2', 'mg in mgf2'), + 'c_Mg_in_MgO': ('mgmgo', 'mg in mgo'), + 'c_N_in_UN_100p': ('nun100', 'n-un-100p'), + 'c_N_in_UN_10p': ('nun10', 'n-un-10p'), + 'c_N_in_UN_5p': ('nun5', 'n-un-5p'), + 'c_N_in_UN': ('n-un', 'n(un)', 'n(un) l', 'ninun'), + 'c_N_in_UN_HALEU': ('nunhal', 'n-un-haleu'), + 'c_N_in_UN_HEU': ('nunheu', 'n-un-heu'), 'c_O_in_Al2O3': ('osap00', 'osap', 'o(al2o3)'), - 'c_O_in_BeO': ('obeo', 'o-beo', 'o-be', 'o/be', 'obeo00', 'o(beo)'), + 'c_O_in_BeO': ('obeo', 'o-beo', 'o-be', 'o/be', 'obeo00', 'o(beo)', 'o_beo'), + 'c_O_in_C5O2H8': ('olucit', 'o(lucite)'), 'c_O_in_D2O': ('od2o', 'o-d2o', 'ohw00', 'o(d2o)'), 'c_O_in_H2O_solid': ('oice', 'o-ice', 'o(ice-ih)'), + 'c_O_in_MgO': ('omgo', 'o in mgo'), + 'c_O_in_PuO2': ('opuo2', 'o in puo2'), + 'c_O_in_SiO2_alpha': ('osio2a', 'o_sio2a'), + 'c_O_in_UO2_100p': ('ouo200', 'o-uo2-100p'), + 'c_O_in_UO2_10p': ('ouo210', 'oinuo2-10p'), + 'c_O_in_UO2_5p': ('ouo25', 'oinuo2-5p'), 'c_O_in_UO2': ('ouo2', 'o-uo2', 'o2-u', 'o2/u', 'ouo200', 'o(uo2)'), + 'c_O_in_UO2_HALEU': ('ouo2hl', 'ouo2-haleu'), + 'c_O_in_UO2_HEU': ('ouo2he', 'o_uo2-heu'), 'c_ortho_D': ('orthod', 'orthoD', 'dortho', 'od200', 'ortod', 'ortho-d'), 'c_ortho_H': ('orthoh', 'orthoH', 'hortho', 'oh200', 'ortoh', 'ortho-h'), 'c_para_D': ('parad', 'paraD', 'dpara', 'pd200', 'para-d'), 'c_para_H': ('parah', 'paraH', 'hpara', 'ph200', 'para-h'), + 'c_Pu_in_PuO2': ('puo2', 'pu in puo2'), 'c_Si28': ('si00', 'sili', 'si'), - 'c_Si_in_SiC': ('sisic', 'si-sic', 'si(3c-sic)'), + 'c_Si_in_SiC': ('sisic', 'si-sic', 'si(3c-sic)', 'si_sic'), + 'c_Si_in_SiO2_alpha': ('si_o2a', 'si_sio2a'), 'c_SiO2_alpha': ('sio2', 'sio2a', 'sio2alpha'), 'c_SiO2_beta': ('sio2b', 'sio2beta'), - 'c_U_in_UN': ('u-un', 'u(un)', 'u(un) l'), + 'c_U_metal_100p': ('u-100p',), + 'c_U_metal_10p': ('u-10p',), + 'c_U_metal_5p': ('u-5p',), + 'c_U_metal': ('umetal', 'u-metal'), + 'c_U_metal_HALEU': ('uhaleu', 'u-haleu'), + 'c_U_metal_HEU': ('u-heu',), + 'c_U_in_UC_100p': ('uc-100', 'uinuc_100p'), + 'c_U_in_UC_10p': ('uc-10', 'uinuc_10p'), + 'c_U_in_UC_5p': ('uc-5', 'uinuc_5p'), + 'c_U_in_UC': ('uc-nat', 'uinuc_nat'), + 'c_U_in_UC_HALEU': ('uc-hal', 'uinuc_haleu'), + 'c_U_in_UC_HEU': ('uc-heu', 'uinuc_heu'), + 'c_U_in_UN_100p': ('un-100', 'u-un-100p'), + 'c_U_in_UN_10p': ('un-10', 'u-un-10p'), + 'c_U_in_UN_5p': ('un-5', 'u-un-5p'), + 'c_U_in_UN': ('u-un', 'u(un)', 'u(un) l', 'uinun'), + 'c_U_in_UN_HALEU': ('un-hal', 'u-un-haleu'), + 'c_U_in_UN_HEU': ('un-heu', 'u-un-heu'), + 'c_U_in_UO2_100p': ('uo2100', 'uuo2-100p'), + 'c_U_in_UO2_10p': ('uo2-10', 'uuo2-10p'), + 'c_U_in_UO2_5p': ('uo2-5', 'uuo2-5p'), 'c_U_in_UO2': ('uuo2', 'u-uo2', 'u-o2', 'u/o2', 'uuo200', 'u(uo2)'), + 'c_U_in_UO2_HALEU': ('uo2hal', 'uuo2-haleu'), + 'c_U_in_UO2_HEU': ('uo2heu', 'u_uo2-heu'), 'c_Y_in_YH2': ('yyh2', 'y-yh2', 'y(yh2)'), + 'c_Zr_in_ZrC': ('zrzrc', 'zr(zrc)'), 'c_Zr_in_ZrH': ('zrzrh', 'zr-zrh', 'zr-h', 'zr/h', 'zr(zrh)'), 'c_Zr_in_ZrH2': ('zrzrh2', 'zr(zrh2)'), 'c_Zr_in_ZrHx': ('zrzrhx', 'zr(zrhx)'), @@ -90,7 +156,7 @@ _THERMAL_NAMES = { def _temperature_str(T): # round() normally returns an int when called with a single argument, but # numpy floats overload rounding to return another float - return "{}K".format(int(round(T))) + return f"{int(round(T))}K" def get_thermal_name(name): @@ -104,7 +170,7 @@ def get_thermal_name(name): Returns ------- str - GND-format thermal scattering name + GNDS-format thermal scattering name """ if name in _THERMAL_NAMES: @@ -193,15 +259,15 @@ class CoherentElastic(Function1D): def bragg_edges(self): return self._bragg_edges - @property - def factors(self): - return self._factors - @bragg_edges.setter def bragg_edges(self, bragg_edges): cv.check_type('Bragg edges', bragg_edges, Iterable, Real) self._bragg_edges = np.asarray(bragg_edges) + @property + def factors(self): + return self._factors + @factors.setter def factors(self, factors): cv.check_type('structure factor cumulative sums', factors, @@ -221,7 +287,7 @@ class CoherentElastic(Function1D): """ dataset = group.create_dataset(name, data=np.vstack( [self.bragg_edges, self.factors])) - dataset.attrs['type'] = np.string_(type(self).__name__) + dataset.attrs['type'] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -294,7 +360,7 @@ class IncoherentElastic(Function1D): """ data = np.array([self.bound_xs, self.debye_waller]) dataset = group.create_dataset(name, data=data) - dataset.attrs['type'] = np.string_(type(self).__name__) + dataset.attrs['type'] = np.bytes_(type(self).__name__) @classmethod def from_hdf5(cls, dataset): @@ -396,7 +462,7 @@ class ThermalScattering(EqualityMixin): Parameters ---------- name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O atomic_weight_ratio : float Atomic mass ratio of the target nuclide. kTs : Iterable of float @@ -415,7 +481,7 @@ class ThermalScattering(EqualityMixin): Inelastic scattering cross section derived in the incoherent approximation name : str - Name of the material using GND convention, e.g. c_H_in_H2O + Name of the material using GNDS convention, e.g. c_H_in_H2O temperatures : Iterable of str List of string representations the temperatures of the target nuclide in the data set. The temperatures are strings of the temperature, @@ -439,7 +505,7 @@ class ThermalScattering(EqualityMixin): def __repr__(self): if hasattr(self, 'name'): - return "".format(self.name) + return f"" else: return "" @@ -464,7 +530,7 @@ class ThermalScattering(EqualityMixin): """ # Open file and write version with h5py.File(str(path), mode, libver=libver) as f: - f.attrs['filetype'] = np.string_('data_thermal') + f.attrs['filetype'] = np.bytes_('data_thermal') f.attrs['version'] = np.array(HDF5_VERSION) # Write basic data @@ -491,7 +557,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. @@ -506,7 +572,7 @@ class ThermalScattering(EqualityMixin): # Check if temprature already exists strT = data.temperatures[0] if strT in self.temperatures: - warn('S(a,b) data at T={} already exists.'.format(strT)) + warn(f'S(a,b) data at T={strT} already exists.') return # Check that name matches @@ -596,7 +662,7 @@ class ThermalScattering(EqualityMixin): ACE table to read from. If given as a string, it is assumed to be the filename for the ACE file. name : str - GND-conforming name of the material, e.g. c_H_in_H2O. If none is + GNDS-conforming name of the material, e.g. c_H_in_H2O. If none is passed, the appropriate name is guessed based on the name of the ACE table. @@ -614,7 +680,7 @@ class ThermalScattering(EqualityMixin): # Get new name that is GND-consistent ace_name, xs = ace.name.split('.') if not xs.endswith('t'): - raise TypeError("{} is not a thermal scattering ACE table.".format(ace)) + raise TypeError(f"{ace} is not a thermal scattering ACE table.") if name is None: name = get_thermal_name(ace_name) @@ -670,12 +736,50 @@ class ThermalScattering(EqualityMixin): mu_i = [] for j in range(n_energy_out[i]): mu = ace.xss[idx + 4:idx + 4 + n_mu] + # The equiprobable angles produced by NJOY are not always + # sorted. This is problematic when the smearing algorithm + # is applied when sampling the angles. We sort the angles + # here, because they are equiprobable, so the order + # doesn't matter. + mu.sort() + + # Older versions of NJOY had a bug, and the discrete + # scattering angles could sometimes be less than -1 or + # greater than 1. We check for this here, and warn users. + if mu[0] < -1. or mu[-1] > 1.: + warn('S(a,b) scattering angle for incident energy index ' + f'{i} and exit energy index {j} outside of the ' + 'interval [-1, 1].') + p_mu = 1. / n_mu * np.ones(n_mu) mu_ij = Discrete(mu, p_mu) mu_ij.c = np.cumsum(p_mu) mu_i.append(mu_ij) idx += 3 + n_mu + # Check if the CDF for the outgoing energy distribution starts + # at 0. For NJOY and FRENDY evaluations, this is never the case, + # and can very rarely lead to negative energies when sampling + # the outgoing energy. From Eq. 7.6 of the ENDF manual, we can + # add an outgoing energy 0 eV that has a PDF of 0 (and of + # course, a CDF of 0 as well). + if eout_i.c[0] > 0.: + eout_i._x = np.insert(eout_i.x, 0, 0.) + eout_i._p = np.insert(eout_i.p, 0, 0.) + eout_i.c = np.insert(eout_i.c, 0, 0.) + + # For this added outgoing energy (of 0 eV) we add a set of + # isotropic discrete angles. + dmu = 2. / n_mu + mu = np.linspace(-1. + 0.5*dmu, 1. - 0.5*dmu, n_mu) + p_mu = 1. / n_mu * np.ones(n_mu) + mu_0 = Discrete(mu, p_mu) + mu_0.c = np.cumsum(p_mu) + mu_i.insert(0, mu_0) + # We don't worry about renormalizing the outgoing energy PDF/CDF + # after this manipulation, because it never seems to be + # normalized to begin with (at least with NJOY). + energy_out.append(eout_i) mu_out.append(mu_i) @@ -694,29 +798,53 @@ class ThermalScattering(EqualityMixin): # Incoherent/coherent elastic scattering cross section idx = ace.jxs[4] - n_mu = ace.nxs[6] + 1 if idx != 0: - n_energy = int(ace.xss[idx]) - energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV - P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy] - - if ace.nxs[5] == 4: + if ace.nxs[5] in (4, 5): # Coherent elastic - xs = CoherentElastic(energy, P*EV_PER_MEV) - distribution = CoherentElasticAE(xs) + n_energy = int(ace.xss[idx]) + energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV + P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy] + coherent_xs = CoherentElastic(energy, P*EV_PER_MEV) + coherent_dist = CoherentElasticAE(coherent_xs) # Coherent elastic shouldn't have angular distributions listed + n_mu = ace.nxs[6] + 1 assert n_mu == 0 - else: - # Incoherent elastic - xs = Tabulated1D(energy, P) + + if ace.nxs[5] in (3, 5): + # Incoherent elastic scattering -- first determine if both + # incoherent and coherent are present (mixed) + mixed = (ace.nxs[5] == 5) + + # Get cross section values + idx = ace.jxs[7] if mixed else ace.jxs[4] + n_energy = int(ace.xss[idx]) + energy = ace.xss[idx + 1: idx + 1 + n_energy]*EV_PER_MEV + values = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy] + + incoherent_xs = Tabulated1D(energy, values) # Angular distribution + n_mu = (ace.nxs[8] if mixed else ace.nxs[6]) + 1 assert n_mu > 0 - idx = ace.jxs[6] + idx = ace.jxs[9] if mixed else ace.jxs[6] mu_out = ace.xss[idx:idx + n_energy * n_mu] mu_out.shape = (n_energy, n_mu) - distribution = IncoherentElasticAEDiscrete(mu_out) + incoherent_dist = IncoherentElasticAEDiscrete(mu_out) + + if ace.nxs[5] == 3: + xs = incoherent_xs + distribution = incoherent_dist + elif ace.nxs[5] == 4: + xs = coherent_xs + distribution = coherent_dist + else: + # Create mixed cross section -- note that coherent must come + # first due to assumption on C++ side + xs = Sum([coherent_xs, incoherent_xs]) + + # Create mixed distribution + distribution = MixedElasticAE(coherent_dist, incoherent_dist) table.elastic = ThermalScatteringReaction({T: xs}, {T: distribution}) @@ -742,7 +870,7 @@ class ThermalScattering(EqualityMixin): @classmethod def from_njoy(cls, filename, filename_thermal, temperatures=None, evaluation=None, evaluation_thermal=None, - use_endf_data=True, **kwargs): + use_endf_data=True, divide_incoherent_elastic=False, **kwargs): """Generate thermal scattering data by running NJOY. Parameters @@ -765,8 +893,13 @@ class ThermalScattering(EqualityMixin): use_endf_data : bool If the material has incoherent elastic scattering, the ENDF data will be used rather than the ACE data. + divide_incoherent_elastic : bool + Divide incoherent elastic cross section by number of principal + atoms. This is not part of the ENDF-6 standard but it is how it is + processed by NJOY. **kwargs - Keyword arguments passed to :func:`openmc.data.njoy.make_ace_thermal` + Keyword arguments passed to + :func:`openmc.data.njoy.make_ace_thermal` Returns ------- @@ -791,7 +924,7 @@ class ThermalScattering(EqualityMixin): # Load ENDF data to replace incoherent elastic if use_endf_data: - data_endf = cls.from_endf(filename_thermal) + data_endf = cls.from_endf(filename_thermal, divide_incoherent_elastic) if data_endf.elastic is not None: # Get appropriate temperatures if temperatures is None: @@ -802,14 +935,14 @@ class ThermalScattering(EqualityMixin): # Replace ACE data with ENDF data rx, rx_endf = data.elastic, data_endf.elastic for t in temperatures: - if isinstance(rx_endf.xs[t], IncoherentElastic): + if isinstance(rx_endf.xs[t], (IncoherentElastic, Sum)): rx.xs[t] = rx_endf.xs[t] rx.distribution[t] = rx_endf.distribution[t] return data @classmethod - def from_endf(cls, ev_or_filename): + def from_endf(cls, ev_or_filename, divide_incoherent_elastic=False): """Generate thermal scattering data from an ENDF file Parameters @@ -817,6 +950,10 @@ class ThermalScattering(EqualityMixin): ev_or_filename : openmc.data.endf.Evaluation or str ENDF evaluation to read from. If given as a string, it is assumed to be the filename for the ENDF file. + divide_incoherent_elastic : bool + Divide incoherent elastic cross section by number of principal + atoms. This is not part of the ENDF-6 standard but it is how it is + processed by NJOY. Returns ------- @@ -829,43 +966,6 @@ class ThermalScattering(EqualityMixin): else: ev = endf.Evaluation(ev_or_filename) - # Read coherent/incoherent elastic data - elastic = None - if (7, 2) in ev.section: - xs = {} - distribution = {} - - file_obj = StringIO(ev.section[7, 2]) - lhtr = endf.get_head_record(file_obj)[2] - if lhtr == 1: - # coherent elastic - - # Get structure factor at first temperature - params, S = endf.get_tab1_record(file_obj) - strT = _temperature_str(params[0]) - n_temps = params[2] - bragg_edges = S.x - xs[strT] = CoherentElastic(bragg_edges, S.y) - distribution = {strT: CoherentElasticAE(xs[strT])} - - # Get structure factor for subsequent temperatures - for _ in range(n_temps): - params, S = endf.get_list_record(file_obj) - strT = _temperature_str(params[0]) - xs[strT] = CoherentElastic(bragg_edges, S) - distribution[strT] = CoherentElasticAE(xs[strT]) - - elif lhtr == 2: - # incoherent elastic - params, W = endf.get_tab1_record(file_obj) - bound_xs = params[0] - for T, debye_waller in zip(W.x, W.y): - strT = _temperature_str(T) - xs[strT] = IncoherentElastic(bound_xs, debye_waller) - distribution[strT] = IncoherentElasticAE(debye_waller) - - elastic = ThermalScatteringReaction(xs, distribution) - # Read incoherent inelastic data assert (7, 4) in ev.section, 'No MF=7, MT=4 found in thermal scattering' file_obj = StringIO(ev.section[7, 4]) @@ -880,6 +980,7 @@ class ThermalScattering(EqualityMixin): data['A0'] = awr = B[2] data['e_max'] = energy_max = B[3] data['M0'] = B[5] + free_xs = data['free_atom_xs'] / data['M0'] # Get information about non-principal atoms n_non_principal = params[5] @@ -924,6 +1025,74 @@ class ThermalScattering(EqualityMixin): _, Teff = endf.get_tab1_record(file_obj) data['effective_temperature'].append(Teff) + # Read coherent/incoherent elastic data + elastic = None + if (7, 2) in ev.section: + # Define helper functions to avoid duplication + def get_coherent_elastic(file_obj): + # Get structure factor at first temperature + params, S = endf.get_tab1_record(file_obj) + strT = _temperature_str(params[0]) + n_temps = params[2] + bragg_edges = S.x + xs = {strT: CoherentElastic(bragg_edges, S.y)} + distribution = {strT: CoherentElasticAE(xs[strT])} + + # Get structure factor for subsequent temperatures + for _ in range(n_temps): + params, S = endf.get_list_record(file_obj) + strT = _temperature_str(params[0]) + xs[strT] = CoherentElastic(bragg_edges, S) + distribution[strT] = CoherentElasticAE(xs[strT]) + return xs, distribution + + def get_incoherent_elastic(file_obj, natom): + params, W = endf.get_tab1_record(file_obj) + bound_xs = params[0]/natom + + # Check whether divide_incoherent_elastic was applied correctly + if abs(free_xs - bound_xs/(1 + 1/data['A0'])**2) > 0.5: + if divide_incoherent_elastic: + msg = ( + 'Thermal scattering evaluation follows ENDF-6 ' + 'definition of bound cross section but ' + 'divide_incoherent_elastic=True.' + ) + else: + msg = ( + 'Thermal scattering evaluation follows NJOY ' + 'definition of bound cross section but ' + 'divide_incoherent_elastic=False.' + ) + warn(msg) + + xs = {} + distribution = {} + for T, debye_waller in zip(W.x, W.y): + strT = _temperature_str(T) + xs[strT] = IncoherentElastic(bound_xs, debye_waller) + distribution[strT] = IncoherentElasticAE(debye_waller) + return xs, distribution + + file_obj = StringIO(ev.section[7, 2]) + lhtr = endf.get_head_record(file_obj)[2] + natom = data['M0'] if divide_incoherent_elastic else 1 + if lhtr == 1: + # coherent elastic + xs, distribution = get_coherent_elastic(file_obj) + elif lhtr == 2: + # incoherent elastic + xs, distribution = get_incoherent_elastic(file_obj, natom) + elif lhtr == 3: + # mixed coherent / incoherent elastic + xs_c, dist_c = get_coherent_elastic(file_obj) + xs_i, dist_i = get_incoherent_elastic(file_obj, natom) + assert sorted(xs_c) == sorted(xs_i) + xs = {T: Sum([xs_c[T], xs_i[T]]) for T in xs_c} + distribution = {T: MixedElasticAE(dist_c[T], dist_i[T]) for T in dist_c} + + elastic = ThermalScatteringReaction(xs, distribution) + name = ev.target['zsymam'].strip() instance = cls(name, awr, energy_max, kTs) if elastic is not None: diff --git a/openmc/data/thermal_angle_energy.py b/openmc/data/thermal_angle_energy.py index 05393e7bb7..4789ebcc6f 100644 --- a/openmc/data/thermal_angle_energy.py +++ b/openmc/data/thermal_angle_energy.py @@ -2,6 +2,7 @@ import numpy as np from .angle_energy import AngleEnergy from .correlated import CorrelatedAngleEnergy +import openmc.data class CoherentElasticAE(AngleEnergy): @@ -42,8 +43,28 @@ class CoherentElasticAE(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('coherent_elastic') - group['coherent_xs'] = group.parent['xs'] + group.attrs['type'] = np.bytes_('coherent_elastic') + self.coherent_xs.to_hdf5(group, 'coherent_xs') + + @classmethod + def from_hdf5(cls, group): + """Generate coherent elastic distribution from HDF5 data + + .. versionadded:: 0.13.1 + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.CoherentElasticAE + Coherent elastic distribution + + """ + coherent_xs = openmc.data.CoherentElastic.from_hdf5(group['coherent_xs']) + return cls(coherent_xs) class IncoherentElasticAE(AngleEnergy): @@ -83,7 +104,7 @@ class IncoherentElasticAE(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('incoherent_elastic') + group.attrs['type'] = np.bytes_('incoherent_elastic') group.create_dataset('debye_waller', data=self.debye_waller) @classmethod @@ -101,7 +122,7 @@ class IncoherentElasticAE(AngleEnergy): Incoherent elastic distribution """ - return cls(group['debye_waller']) + return cls(group['debye_waller'][()]) class IncoherentElasticAEDiscrete(AngleEnergy): @@ -125,7 +146,7 @@ class IncoherentElasticAEDiscrete(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('incoherent_elastic_discrete') + group.attrs['type'] = np.bytes_('incoherent_elastic_discrete') group.create_dataset('mu_out', data=self.mu_out) @classmethod @@ -182,7 +203,7 @@ class IncoherentInelasticAEDiscrete(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('incoherent_inelastic_discrete') + group.attrs['type'] = np.bytes_('incoherent_inelastic_discrete') group.create_dataset('energy_out', data=self.energy_out) group.create_dataset('mu_out', data=self.mu_out) group.create_dataset('skewed', data=self.skewed) @@ -204,9 +225,68 @@ class IncoherentInelasticAEDiscrete(AngleEnergy): """ energy_out = group['energy_out'][()] mu_out = group['mu_out'][()] - skewed = bool(group['skewed']) + skewed = bool(group['skewed'][()]) return cls(energy_out, mu_out, skewed) class IncoherentInelasticAE(CorrelatedAngleEnergy): _name = 'incoherent_inelastic' + + +class MixedElasticAE(AngleEnergy): + """Secondary distribution for mixed coherent/incoherent thermal elastic + + .. versionadded:: 0.13.1 + + Parameters + ---------- + coherent : AngleEnergy + Secondary distribution for coherent elastic scattering + incoherent : AngleEnergy + Secondary distribution for incoherent elastic scattering + + Attributes + ---------- + coherent : AngleEnergy + Secondary distribution for coherent elastic scattering + incoherent : AngleEnergy + Secondary distribution for incoherent elastic scattering + + """ + def __init__(self, coherent, incoherent): + self.coherent = coherent + self.incoherent = incoherent + + def to_hdf5(self, group): + """Write mixed elastic distribution to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + + """ + group.attrs['type'] = np.bytes_('mixed_elastic') + coherent_group = group.create_group('coherent') + self.coherent.to_hdf5(coherent_group) + incoherent_group = group.create_group('incoherent') + self.incoherent.to_hdf5(incoherent_group) + + @classmethod + def from_hdf5(cls, group): + """Generate mixed thermal elastic distribution from HDF5 data + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.MixedElasticAE + Mixed thermal elastic distribution + + """ + coherent = AngleEnergy.from_hdf5(group['coherent']) + incoherent = AngleEnergy.from_hdf5(group['incoherent']) + return cls(coherent, incoherent) diff --git a/openmc/data/uncorrelated.py b/openmc/data/uncorrelated.py index 0361c9b37a..141007b70a 100644 --- a/openmc/data/uncorrelated.py +++ b/openmc/data/uncorrelated.py @@ -38,16 +38,16 @@ class UncorrelatedAngleEnergy(AngleEnergy): def angle(self): return self._angle - @property - def energy(self): - return self._energy - @angle.setter def angle(self, angle): cv.check_type('uncorrelated angle distribution', angle, AngleDistribution) self._angle = angle + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('uncorrelated energy distribution', energy, @@ -63,7 +63,7 @@ class UncorrelatedAngleEnergy(AngleEnergy): HDF5 group to write to """ - group.attrs['type'] = np.string_('uncorrelated') + group.attrs['type'] = np.bytes_('uncorrelated') if self.angle is not None: angle_group = group.create_group('angle') self.angle.to_hdf5(angle_group) diff --git a/openmc/data/urr.py b/openmc/data/urr.py index 53961a50ac..f129c98f81 100644 --- a/openmc/data/urr.py +++ b/openmc/data/urr.py @@ -79,51 +79,51 @@ class ProbabilityTables(EqualityMixin): def absorption_flag(self): return self._absorption_flag - @property - def energy(self): - return self._energy - - @property - def inelastic_flag(self): - return self._inelastic_flag - - @property - def interpolation(self): - return self._interpolation - - @property - def multiply_smooth(self): - return self._multiply_smooth - - @property - def table(self): - return self._table - @absorption_flag.setter def absorption_flag(self, absorption_flag): cv.check_type('absorption flag', absorption_flag, Integral) self._absorption_flag = absorption_flag + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('probability table energies', energy, Iterable, Real) self._energy = energy + @property + def inelastic_flag(self): + return self._inelastic_flag + @inelastic_flag.setter def inelastic_flag(self, inelastic_flag): cv.check_type('inelastic flag', inelastic_flag, Integral) self._inelastic_flag = inelastic_flag + @property + def interpolation(self): + return self._interpolation + @interpolation.setter def interpolation(self, interpolation): cv.check_value('interpolation', interpolation, [2, 5]) self._interpolation = interpolation + @property + def multiply_smooth(self): + return self._multiply_smooth + @multiply_smooth.setter def multiply_smooth(self, multiply_smooth): cv.check_type('multiply by smooth', multiply_smooth, bool) self._multiply_smooth = multiply_smooth + @property + def table(self): + return self._table + @table.setter def table(self, table): cv.check_type('probability tables', table, np.ndarray) diff --git a/openmc/deplete/__init__.py b/openmc/deplete/__init__.py index b8c1cdffff..8a9509e900 100644 --- a/openmc/deplete/__init__.py +++ b/openmc/deplete/__init__.py @@ -7,12 +7,16 @@ A depletion front-end tool. from .nuclide import * from .chain import * -from .operator import * +from .openmc_operator import * +from .coupled_operator import * +from .independent_operator import * +from .microxs import * from .reaction_rates import * from .atom_number import * +from .stepresult import * from .results import * -from .results_list import * from .integrators import * +from .transfer_rates import * from . import abc from . import cram from . import helpers diff --git a/openmc/deplete/_matrix_funcs.py b/openmc/deplete/_matrix_funcs.py index a606d739f7..c7f7df76fb 100644 --- a/openmc/deplete/_matrix_funcs.py +++ b/openmc/deplete/_matrix_funcs.py @@ -77,4 +77,4 @@ def leqi_f4(chain, inputs, fission_yields=None): return (-dt ** 2 / (12 * dt_l * (dt + dt_l)) * f1 + (dt ** 2 + 2 * dt * dt_l + dt_l ** 2) / (12 * dt_l * (dt + dt_l)) * f2 - + (4 * dt * dt_l + 5 * dt_l ** 2) / (12 * dt_l * (dt + dt_l)) * f3) + + (4 * dt + 5 * dt_l) / (12 * (dt + dt_l)) * f3) diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 3c7a136876..c5a6219c7b 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -1,42 +1,99 @@ -"""function module. +"""abc module. -This module contains the Operator class, which is then passed to an integrator -to run a full depletion simulation. +This module contains Abstract Base Classes for implementing operator, +integrator, depletion system solver, and operator helper classes """ +from __future__ import annotations from abc import ABC, abstractmethod from collections import namedtuple, defaultdict from collections.abc import Iterable, Callable from copy import deepcopy from inspect import signature from numbers import Real, Integral -import os from pathlib import Path -import sys +from textwrap import dedent import time +from typing import Optional, Union, Sequence from warnings import warn -from numpy import nonzero, empty, asarray +import numpy as np from uncertainties import ufloat -from openmc.lib import MaterialFilter, Tally -from openmc.checkvalue import check_type, check_greater_than +from openmc.checkvalue import check_type, check_greater_than, PathLike from openmc.mpi import comm -from .results import Results -from .chain import Chain -from .results_list import ResultsList +from openmc.utility_funcs import change_directory +from openmc import Material +from .stepresult import StepResult +from .chain import _get_chain +from .results import Results, _SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, \ + _SECONDS_PER_DAY, _SECONDS_PER_JULIAN_YEAR from .pool import deplete +from .reaction_rates import ReactionRates +from .transfer_rates import TransferRates, ExternalSourceRates __all__ = [ - "OperatorResult", "TransportOperator", "ReactionRateHelper", - "NormalizationHelper", "FissionYieldHelper", "TalliedFissionYieldHelper", + "OperatorResult", "TransportOperator", + "ReactionRateHelper", "NormalizationHelper", "FissionYieldHelper", "Integrator", "SIIntegrator", "DepSystemSolver", "add_params"] -_SECONDS_PER_MINUTE = 60 -_SECONDS_PER_HOUR = 60*60 -_SECONDS_PER_DAY = 24*60*60 +def _normalize_timesteps( + timesteps: Sequence[float] | Sequence[tuple[float, str]], + source_rates: float | Sequence[float], + timestep_units: str = 's', + operator: TransportOperator | None = None, +): + if not isinstance(source_rates, Sequence): + # Ensure that rate is single value if that is the case + source_rates = [source_rates] * len(timesteps) + + if len(source_rates) != len(timesteps): + raise ValueError( + "Number of time steps ({}) != number of powers ({})".format( + len(timesteps), len(source_rates))) + + # Get list of times / units + if isinstance(timesteps[0], Sequence): + times, units = zip(*timesteps) + else: + times = timesteps + units = [timestep_units] * len(timesteps) + + # Determine number of seconds for each timestep + seconds = [] + for timestep, unit, rate in zip(times, units, source_rates): + # Make sure values passed make sense + check_type('timestep', timestep, Real) + check_greater_than('timestep', timestep, 0.0, False) + check_type('timestep units', unit, str) + check_type('source rate', rate, Real) + check_greater_than('source rate', rate, 0.0, True) + + if unit in ('s', 'sec'): + seconds.append(timestep) + elif unit in ('min', 'minute'): + seconds.append(timestep*_SECONDS_PER_MINUTE) + elif unit in ('h', 'hr', 'hour'): + seconds.append(timestep*_SECONDS_PER_HOUR) + elif unit in ('d', 'day'): + seconds.append(timestep*_SECONDS_PER_DAY) + elif unit in ('a', 'year'): + seconds.append(timestep*_SECONDS_PER_JULIAN_YEAR) + elif unit.lower() == 'mwd/kg': + watt_days_per_kg = 1e6*timestep + kilograms = 1e-3*operator.heavy_metal + if rate == 0.0: + raise ValueError("Cannot specify a timestep in [MWd/kg] when" + " the power is zero.") + days = watt_days_per_kg * kilograms / rate + seconds.append(days*_SECONDS_PER_DAY) + else: + raise ValueError(f"Invalid timestep unit '{unit}'") + + return (np.asarray(seconds), np.asarray(source_rates)) + OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) OperatorResult.__doc__ = """\ @@ -65,57 +122,42 @@ class TransportOperator(ABC): operator that takes a vector of material compositions and returns an eigenvalue and reaction rates. This abstract class sets the requirements for such a transport operator. Users should instantiate - :class:`openmc.deplete.Operator` rather than this class. + :class:`openmc.deplete.CoupledOperator` or + :class:`openmc.deplete.IndependentOperator` rather than this class. Parameters ---------- - chain_file : str - Path to the depletion chain XML file + chain_file : PathLike or Chain + Path to the depletion chain XML file or instance of openmc.deplete.Chain. fission_q : dict, optional Dictionary of nuclides and their fission Q values [eV]. If not given, values will be pulled from the ``chain_file``. - dilute_initial : float, optional - Initial atom density [atoms/cm^3] to add for nuclides that are zero - in initial condition to ensure they exist in the decay chain. - Only done for nuclides with reaction rates. - Defaults to 1.0e3. - prev_results : ResultsList, optional + prev_results : Results, optional Results from a previous depletion calculation. Attributes ---------- - dilute_initial : float - Initial atom density [atoms/cm^3] to add for nuclides that are zero - in initial condition to ensure they exist in the decay chain. - Only done for nuclides with reaction rates. - prev_res : ResultsList or None + output_dir : pathlib.Path + Path to output directory to save results. + prev_res : Results or None Results from a previous depletion calculation. ``None`` if no results are to be used. + chain : openmc.deplete.Chain + The depletion chain information necessary to form matrices and tallies. + """ - def __init__(self, chain_file, fission_q=None, dilute_initial=1.0e3, - prev_results=None): - self.dilute_initial = dilute_initial + def __init__(self, chain_file=None, fission_q=None, prev_results=None): self.output_dir = '.' # Read depletion chain - self.chain = Chain.from_xml(chain_file, fission_q) + self.chain = _get_chain(chain_file, fission_q) + if prev_results is None: self.prev_res = None else: - check_type("previous results", prev_results, ResultsList) + check_type("previous results", prev_results, Results) self.prev_res = prev_results - @property - def dilute_initial(self): - """Initial atom density for nuclides with zero initial concentration""" - return self._dilute_initial - - @dilute_initial.setter - def dilute_initial(self, value): - check_type("dilute_initial", value, Real) - check_greater_than("dilute_initial", value, 0.0, equality=True) - self._dilute_initial = value - @abstractmethod def __call__(self, vec, source_rate): """Runs a simulation. @@ -134,18 +176,6 @@ class TransportOperator(ABC): """ - def __enter__(self): - # Save current directory and move to specific output directory - self._orig_dir = os.getcwd() - self.output_dir.mkdir(exist_ok=True) - os.chdir(self.output_dir) - - return self.initial_condition() - - def __exit__(self, exc_type, exc_value, traceback): - self.finalize() - os.chdir(self._orig_dir) - @property def output_dir(self): return self._output_dir @@ -185,7 +215,7 @@ class TransportOperator(ABC): pass @abstractmethod - def write_bos_data(self, step): + def write_bos_data(self, step: int): """Document beginning of step data for a given step Called at the beginning of a depletion step and at @@ -210,9 +240,11 @@ class ReactionRateHelper(ABC): Parameters ---------- n_nucs : int - Number of burnable nuclides tracked by :class:`openmc.deplete.Operator` + Number of burnable nuclides tracked by + :class:`openmc.deplete.abc.TransportOperator` n_react : int - Number of reactions tracked by :class:`openmc.deplete.Operator` + Number of reactions tracked by + :class:`openmc.deplete.abc.TransportOperator` Attributes ---------- @@ -222,7 +254,7 @@ class ReactionRateHelper(ABC): def __init__(self, n_nucs, n_react): self._nuclides = None - self._results_cache = empty((n_nucs, n_react)) + self._results_cache = np.empty((n_nucs, n_react)) @abstractmethod def generate_tallies(self, materials, scores): @@ -239,7 +271,12 @@ class ReactionRateHelper(ABC): self._nuclides = nuclides @abstractmethod - def get_material_rates(self, mat_id, nuc_index, react_index): + def get_material_rates( + self, + mat_id: int, + nuc_index: Sequence[str], + react_index: Sequence[str] + ): """Return 2D array of [nuclide, reaction] reaction rates Parameters @@ -252,16 +289,15 @@ class ReactionRateHelper(ABC): Ordering of reactions """ - def divide_by_adens(self, number): - """Normalize reaction rates by number of nuclides + def divide_by_atoms(self, number: Sequence[float]): + """Normalize reaction rates by number of atoms Acts on the current material examined by :meth:`get_material_rates` Parameters ---------- number : iterable of float - Number density [atoms/b-cm] of each nuclide tracked in the - calculation. + Number of each nuclide in [atom] tracked in the calculation. Returns ------- @@ -270,7 +306,7 @@ class ReactionRateHelper(ABC): normalized by the number of nuclides """ - mask = nonzero(number) + mask = np.nonzero(number) results = self._results_cache for col in range(results.shape[1]): results[mask, col] /= number[mask] @@ -281,9 +317,9 @@ class NormalizationHelper(ABC): """Abstract class for obtaining normalization factor on tallies This helper class determines how reaction rates calculated by an instance of - :class:`openmc.deplete.Operator` should be normalized for the purpose of - constructing a burnup matrix. Based on the method chosen, the power or - source rate provided by the user, and reaction rates from a + :class:`openmc.deplete.abc.TransportOperator` should be normalized for the + purpose of constructing a burnup matrix. Based on the method chosen, the + power or source rate provided by the user, and reaction rates from a :class:`ReactionRateHelper`, this class will scale reaction rates to the correct values. @@ -291,7 +327,7 @@ class NormalizationHelper(ABC): ---------- nuclides : list of str All nuclides with desired reaction rates. Ordered to be - consistent with :class:`openmc.deplete.Operator` + consistent with :class:`openmc.deplete.abc.TransportOperator` """ @@ -302,12 +338,12 @@ class NormalizationHelper(ABC): """Reset state for normalization""" @abstractmethod - def prepare(self, chain_nucs, rate_index): + def prepare(self, chain_nucs: Sequence[str], rate_index: dict): """Perform work needed to obtain energy produced - This method is called prior to the transport simulations - in :meth:`openmc.deplete.Operator.initial_condition`. Only used for - energy-based normalization. + This method is called prior to calculating the reaction rates + in :meth:`openmc.deplete.abc.TransportOperator.initial_condition`. Only + used for energy-based normalization. Parameters ---------- @@ -341,7 +377,7 @@ class NormalizationHelper(ABC): self._nuclides = nuclides @abstractmethod - def factor(self, source_rate): + def factor(self, source_rate: float): """Return normalization factor Parameters @@ -418,7 +454,7 @@ class FissionYieldHelper(ABC): def unpack(): """Unpack tally data prior to compute fission yields. - Called after a :meth:`openmc.deplete.Operator.__call__` + Called after a :meth:`openmc.deplete.abc.TransportOperator.__call__` routine during the normalization of reaction rates. Not necessary for all subclasses to implement, unless tallies @@ -439,26 +475,27 @@ class FissionYieldHelper(ABC): mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the - :class:`openmc.deplete.Operator` that uses this helper + :class:`openmc.deplete.CoupledOperator` that uses this helper may only burn a subset of all materials when running in parallel mode. """ - def update_tally_nuclides(self, nuclides): + def update_tally_nuclides(self, nuclides: Sequence[str]) -> list: """Return nuclides with non-zero densities and yield data Parameters ---------- nuclides : iterable of str Nuclides with non-zero densities from the - :class:`openmc.deplete.Operator` + :class:`openmc.deplete.abc.TransportOperator` Returns ------- nuclides : list of str - Union of nuclides that the :class:`openmc.deplete.Operator` - says have non-zero densities at this stage and those that - have yield data. Sorted by nuclide name + Union of nuclides that the + :class:`openmc.deplete.abc.TransportOperator` says have non-zero + densities at this stage and those that have yield data. Sorted by + nuclide name """ return sorted(self._chain_set & set(nuclides)) @@ -472,7 +509,7 @@ class FissionYieldHelper(ABC): Parameters ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.abc.TransportOperator Operator with a depletion chain kwargs: optional Additional keyword arguments to be used in constuction @@ -480,99 +517,6 @@ class FissionYieldHelper(ABC): return cls(operator.chain.nuclides, **kwargs) -class TalliedFissionYieldHelper(FissionYieldHelper): - """Abstract class for computing fission yields with tallies - - Generates a basic fission rate tally in all burnable materials with - :meth:`generate_tallies`, and set nuclides to be tallied with - :meth:`update_tally_nuclides`. Subclasses will need to implement - :meth:`unpack` and :meth:`weighted_yields`. - - Parameters - ---------- - chain_nuclides : iterable of openmc.deplete.Nuclide - Nuclides tracked in the depletion chain. Not necessary - that all have yield data. - - Attributes - ---------- - constant_yields : dict of str to :class:`openmc.deplete.FissionYield` - Fission yields for all nuclides that only have one set of - fission yield data. Can be accessed as ``{parent: {product: yield}}`` - results : None or numpy.ndarray - Tally results shaped in a manner useful to this helper. - """ - - _upper_energy = 20.0e6 # upper energy for tallies - - def __init__(self, chain_nuclides): - super().__init__(chain_nuclides) - self._local_indexes = None - self._fission_rate_tally = None - self._tally_nucs = [] - self.results = None - - def generate_tallies(self, materials, mat_indexes): - """Construct the fission rate tally - - Parameters - ---------- - materials : iterable of :class:`openmc.lib.Material` - Materials to be used in :class:`openmc.lib.MaterialFilter` - mat_indexes : iterable of int - Indices of tallied materials that will have their fission - yields computed by this helper. Necessary as the - :class:`openmc.deplete.Operator` that uses this helper - may only burn a subset of all materials when running - in parallel mode. - """ - self._local_indexes = asarray(mat_indexes) - - # Tally group-wise fission reaction rates - self._fission_rate_tally = Tally() - self._fission_rate_tally.writable = False - self._fission_rate_tally.scores = ['fission'] - self._fission_rate_tally.filters = [MaterialFilter(materials)] - - def update_tally_nuclides(self, nuclides): - """Tally nuclides with non-zero density and multiple yields - - Must be run after :meth:`generate_tallies`. - - Parameters - ---------- - nuclides : iterable of str - Potential nuclides to be tallied, such as those with - non-zero density at this stage. - - Returns - ------- - nuclides : list of str - Union of input nuclides and those that have multiple sets - of yield data. Sorted by nuclide name - - Raises - ------ - AttributeError - If tallies not generated - """ - assert self._fission_rate_tally is not None, ( - "Run generate_tallies first") - overlap = set(self._chain_nuclides).intersection(set(nuclides)) - nuclides = sorted(overlap) - self._tally_nucs = [self._chain_nuclides[n] for n in nuclides] - self._fission_rate_tally.nuclides = nuclides - return nuclides - - @abstractmethod - def unpack(self): - """Unpack tallies after a transport run. - - Abstract because each subclass will need to arrange its - tally data. - """ - - def add_params(cls): cls.__doc__ += cls._params return cls @@ -583,10 +527,10 @@ class Integrator(ABC): r"""Abstract class for solving the time-integration for depletion """ - _params = r""" + _params = dedent(r""" Parameters ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.abc.TransportOperator Operator to perform transport simulations timesteps : iterable of float or iterable of tuple Array of timesteps. Note that values are not cumulative. The units are @@ -604,16 +548,17 @@ class Integrator(ABC): power_density : float or iterable of float, optional Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` - is not speficied. + is not specified. source_rates : float or iterable of float, optional - Source rate in [neutron/sec] for each interval in :attr:`timesteps` + Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for + each interval in :attr:`timesteps` .. versionadded:: 0.12.1 - timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} + timestep_units : {'s', 'min', 'h', 'd', 'a', 'MWd/kg'} Units for values specified in the `timesteps` argument. 's' means - seconds, 'min' means minutes, 'h' means hours, and 'MWd/kg' indicates - that the values are given in burnup (MW-d of energy deposited per - kilogram of initial heavy metal). + seconds, 'min' means minutes, 'h' means hours, 'a' means Julian years + and 'MWd/kg' indicates that the values are given in burnup (MW-d of + energy deposited per kilogram of initial heavy metal). solver : str or callable, optional If a string, must be the name of the solver responsible for solving the Bateman equations. Current options are: @@ -625,10 +570,21 @@ class Integrator(ABC): :attr:`solver`. .. versionadded:: 0.12 + continue_timesteps : bool, optional + Whether or not to treat the current solve as a continuation of a + previous simulation. Defaults to `False`. When `False`, the depletion + steps provided are appended to any previous steps. If `True`, the + timesteps provided to the `Integrator` must exacly match any that + exist in the `prev_results` passed to the `Operator`. The `power`, + `power_density`, or `source_rates` must match as well. The + method of specifying `power`, `power_density`, or + `source_rates` should be the same as the initial run. + + .. versionadded:: 0.15.1 Attributes ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.abc.TransportOperator Operator to perform transport simulations chain : openmc.deplete.Chain Depletion chain @@ -644,7 +600,7 @@ class Integrator(ABC): User-supplied functions are expected to have the following signature: ``solver(A, n0, t) -> n1`` where - * ``A`` is a :class:`scipy.sparse.csr_matrix` making up the + * ``A`` is a :class:`scipy.sparse.csc_matrix` making up the depletion matrix * ``n0`` is a 1-D :class:`numpy.ndarray` of initial compositions for a given material in atoms/cm3 @@ -652,12 +608,29 @@ class Integrator(ABC): * ``n1`` is a :class:`numpy.ndarray` of compositions at the next time step. Expected to be of the same shape as ``n0`` - .. versionadded:: 0.12 + transfer_rates : openmc.deplete.TransferRates + Transfer rates for the depletion system used to model continuous + removal/feed between materials. - """ + .. versionadded:: 0.14.0 + external_source_rates : openmc.deplete.ExternalSourceRates + External source rates for the depletion system. - def __init__(self, operator, timesteps, power=None, power_density=None, - source_rates=None, timestep_units='s', solver="cram48"): + .. versionadded:: 0.15.3 + + """) + + def __init__( + self, + operator: TransportOperator, + timesteps: Sequence[float] | Sequence[tuple[float, str]], + power: Optional[Union[float, Sequence[float]]] = None, + power_density: Optional[Union[float, Sequence[float]]] = None, + source_rates: Optional[Union[float, Sequence[float]]] = None, + timestep_units: str = 's', + solver: str = "cram48", + continue_timesteps: bool = False, + ): # Check number of stages previously used if operator.prev_res is not None: res = operator.prev_res[-1] @@ -668,6 +641,8 @@ class Integrator(ABC): "this uses {}".format( self.__class__.__name__, res.data.shape[0], self._num_stages)) + elif continue_timesteps: + raise ValueError("Continuation run requires passing prev_results.") self.operator = operator self.chain = operator.chain @@ -682,50 +657,43 @@ class Integrator(ABC): elif source_rates is None: raise ValueError("Either power, power_density, or source_rates must be set") - if not isinstance(source_rates, Iterable): - # Ensure that rate is single value if that is the case - source_rates = [source_rates] * len(timesteps) + # Normalize timesteps and source rates + seconds, source_rates = _normalize_timesteps( + timesteps, source_rates, timestep_units, operator) - if len(source_rates) != len(timesteps): - raise ValueError( - "Number of time steps ({}) != number of powers ({})".format( - len(timesteps), len(source_rates))) + if continue_timesteps: + # Get timesteps and source rates from previous results + prev_times = operator.prev_res.get_times(timestep_units) + prev_source_rates = operator.prev_res.get_source_rates() + prev_timesteps = np.diff(prev_times) - # Get list of times / units - if isinstance(timesteps[0], Iterable): - times, units = zip(*timesteps) - else: - times = timesteps - units = [timestep_units] * len(timesteps) + # Make sure parameters from the previous results are consistent with + # those passed to operator + num_prev = len(prev_timesteps) + if not np.array_equal(prev_timesteps, timesteps[:num_prev]): + raise ValueError( + "You are attempting to continue a run in which the previous timesteps " + "do not have the same initial timesteps as those provided to the " + "Integrator. Please make sure you are using the correct timesteps." + ) + if not np.array_equal(prev_source_rates, source_rates[:num_prev]): + raise ValueError( + "You are attempting to continue a run in which the previous results " + "do not have the same initial source rates, powers, or power densities " + "as those provided to the Integrator. Please make sure you are using " + "the correct powers, power densities, or source rates and previous " + "results file." + ) - # Determine number of seconds for each timestep - seconds = [] - for timestep, unit, rate in zip(times, units, source_rates): - # Make sure values passed make sense - check_type('timestep', timestep, Real) - check_greater_than('timestep', timestep, 0.0, False) - check_type('timestep units', unit, str) - check_type('source rate', rate, Real) - check_greater_than('source rate', rate, 0.0, True) + # Run with only the new time steps and source rates provided + seconds = seconds[num_prev:] + source_rates = source_rates[num_prev:] - if unit in ('s', 'sec'): - seconds.append(timestep) - elif unit in ('min', 'minute'): - seconds.append(timestep*_SECONDS_PER_MINUTE) - elif unit in ('h', 'hr', 'hour'): - seconds.append(timestep*_SECONDS_PER_HOUR) - elif unit in ('d', 'day'): - seconds.append(timestep*_SECONDS_PER_DAY) - elif unit.lower() == 'mwd/kg': - watt_days_per_kg = 1e6*timestep - kilograms = 1e-3*operator.heavy_metal - days = watt_days_per_kg * kilograms / rate - seconds.append(days*_SECONDS_PER_DAY) - else: - raise ValueError("Invalid timestep unit '{}'".format(unit)) + self.timesteps = np.asarray(seconds) + self.source_rates = np.asarray(source_rates) - self.timesteps = asarray(seconds) - self.source_rates = asarray(source_rates) + self.transfer_rates = None + self.external_source_rates = None if isinstance(solver, str): # Delay importing of cram module, which requires this file @@ -737,8 +705,7 @@ class Integrator(ABC): self._solver = CRAM16 else: raise ValueError( - "Solver {} not understood. Expected 'cram48' or " - "'cram16'".format(solver)) + f"Solver {solver} not understood. Expected 'cram48' or 'cram16'") else: self.solver = solver @@ -750,14 +717,13 @@ class Integrator(ABC): def solver(self, func): if not isinstance(func, Callable): raise TypeError( - "Solver must be callable, not {}".format(type(func))) + f"Solver must be callable, not {type(func)}") try: sig = signature(func) except ValueError: # Guard against callables that aren't introspectable, e.g. # fortran functions wrapped by F2PY - warn("Could not determine arguments to {}. Proceeding " - "anyways".format(func)) + warn(f"Could not determine arguments to {func}. Proceeding anyways") self._solver = func return @@ -769,25 +735,33 @@ class Integrator(ABC): for ix, param in enumerate(sig.parameters.values()): if param.kind in {param.KEYWORD_ONLY, param.VAR_KEYWORD}: raise ValueError( - "Keyword arguments like {} at position {} are not " - "allowed".format(ix, param)) + f"Keyword arguments like {ix} at position {param} are not allowed") self._solver = func - def _timed_deplete(self, concs, rates, dt, matrix_func=None): + def _timed_deplete(self, n, rates, dt, i=None, matrix_func=None): start = time.time() results = deplete( - self._solver, self.chain, concs, rates, dt, matrix_func) + self._solver, self.chain, n, rates, dt, i, matrix_func, + self.transfer_rates, self.external_source_rates) return time.time() - start, results @abstractmethod - def __call__(self, conc, rates, dt, source_rate, i): + def __call__( + self, + n: Sequence[np.ndarray], + rates: ReactionRates, + dt: float, + source_rate: float, + i: int + ): """Perform the integration across one time step Parameters ---------- - conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -801,7 +775,7 @@ class Integrator(ABC): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -833,7 +807,7 @@ class Integrator(ABC): self.operator.write_bos_data(step_index + self._i_res) return x, res - def _get_bos_data_from_restart(self, step_index, source_rate, bos_conc): + def _get_bos_data_from_restart(self, source_rate, bos_conc): """Get beginning of step concentrations, reaction rates from restart""" res = self.operator.prev_res[-1] # Depletion methods expect list of arrays @@ -841,17 +815,48 @@ class Integrator(ABC): rates = res.rates[0] k = ufloat(res.k[0, 0], res.k[0, 1]) - # Scale reaction rates by ratio of source rates - rates *= source_rate / res.source_rate[0] + if res.source_rate != 0.0: + # Scale reaction rates by ratio of source rates + rates *= source_rate / res.source_rate return bos_conc, OperatorResult(k, rates) - def _get_start_data(self): + def _get_start_data(self) -> tuple[float, int]: + """ + This function fetches the starting state of a depletion simulation in + terms of the simulation physical time at which to start and the index at + which the depletion simulation should start. When no previous results + exist, the time and index are both zero. When previous results do exist, + it returns the time corresponding to beginning the previous results last + timestep and the index as N-1 where N is the number of previous + StepResults found in the previous Results (as expected from 0-based + indexing). + + Note that the openmc.deplete.Results.time object is a list of float with + [t,t+dt] where t is the beginning of timestep time and t+dt is the end + of timestep time. If the previous results correspond to a simulation + that finished to completeion, it will contain a results in the form of + [t,t], but if a simulation doesn't finish all the given timesteps, it is + the t that is the desired start time, not t+dt. Thus, it is always safe + to take time[0]. + + Returns + ------- + start_time : float + Time at which depletion simulation should start in [s] + index : int + Index at which depletion simulation should start + """ if self.operator.prev_res is None: return 0.0, 0 - return (self.operator.prev_res[-1].time[-1], + return (self.operator.prev_res[-1].time[0], len(self.operator.prev_res) - 1) - def integrate(self, final_step=True): + def integrate( + self, + final_step: bool = True, + output: bool = True, + path: PathLike = 'depletion_results.h5' + ): """Perform the entire depletion process across all steps Parameters @@ -861,30 +866,41 @@ class Integrator(ABC): of the last timestep. .. versionadded:: 0.12.1 + output : bool, optional + Indicate whether to display information about progress + .. versionadded:: 0.13.1 + path : PathLike + Path to file to write. Defaults to 'depletion_results.h5'. + + .. versionadded:: 0.15.0 """ - with self.operator as conc: + with change_directory(self.operator.output_dir): + n = self.operator.initial_condition() t, self._i_res = self._get_start_data() for i, (dt, source_rate) in enumerate(self): + if output and comm.rank == 0: + print(f"[openmc.deplete] t={t} s, dt={dt} s, source={source_rate}") + # Solve transport equation (or obtain result from restart) if i > 0 or self.operator.prev_res is None: - conc, res = self._get_bos_data_from_operator(i, source_rate, conc) + n, res = self._get_bos_data_from_operator(i, source_rate, n) else: - conc, res = self._get_bos_data_from_restart(i, source_rate, conc) + n, res = self._get_bos_data_from_restart(source_rate, n) # Solve Bateman equations over time interval - proc_time, conc_list, res_list = self(conc, res.rates, dt, source_rate, i) + proc_time, n_list, res_list = self(n, res.rates, dt, source_rate, i) # Insert BOS concentration, transport results - conc_list.insert(0, conc) + n_list.insert(0, n) res_list.insert(0, res) # Remove actual EOS concentration for next step - conc = conc_list.pop() + n = n_list.pop() - Results.save(self.operator, conc_list, res_list, [t, t + dt], - source_rate, self._i_res + i, proc_time) + StepResult.save(self.operator, n_list, res_list, [t, t + dt], + source_rate, self._i_res + i, proc_time, path) t += dt @@ -892,11 +908,110 @@ class Integrator(ABC): # source rate is passed to the transport operator (which knows to # just return zero reaction rates without actually doing a transport # solve) - res_list = [self.operator(conc, source_rate if final_step else 0.0)] - Results.save(self.operator, [conc], res_list, [t, t], - source_rate, self._i_res + len(self), proc_time) + if output and final_step and comm.rank == 0: + print(f"[openmc.deplete] t={t} (final operator evaluation)") + res_list = [self.operator(n, source_rate if final_step else 0.0)] + StepResult.save(self.operator, [n], res_list, [t, t], + source_rate, self._i_res + len(self), proc_time, path) self.operator.write_bos_data(len(self) + self._i_res) + self.operator.finalize() + + def add_transfer_rate( + self, + material: str | int | Material, + components: Sequence[str], + transfer_rate: float, + transfer_rate_units: str = '1/s', + timesteps: Sequence[int] | None = None, + destination_material: str | int | Material | None = None + ): + """Add transfer rates to depletable material. + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + components : list of str + List of strings of elements and/or nuclides that share transfer rate. + A transfer rate for a nuclide cannot be added to a material + alongside a transfer rate for its element and vice versa. + transfer_rate : float + Rate at which elements are transferred. A positive or negative values + set removal of feed rates, respectively. + transfer_rate_units : {'1/s', '1/min', '1/h', '1/d', '1/a'} + Units for values specified in the transfer_rate argument. 's' means + seconds, 'min' means minutes, 'h' means hours, 'a' means Julian years. + timesteps : list of int, optional + List of timestep indices where to set external source rates. + Defaults to None, which means the external source rate is set for + all timesteps. + destination_material : openmc.Material or str or int, Optional + Destination material to where nuclides get fed. + + """ + if self.transfer_rates is None: + if hasattr(self.operator, 'model'): + materials = self.operator.model.materials + elif hasattr(self.operator, 'materials'): + materials = self.operator.materials + self.transfer_rates = TransferRates( + self.operator, materials, len(self.timesteps)) + + if self.external_source_rates is not None and destination_material: + raise ValueError('Currently is not possible to set a transfer rate ' + 'with destination matrial in combination with ' + 'external source rates.') + + self.transfer_rates.set_transfer_rate( + material, components, transfer_rate, transfer_rate_units, + timesteps, destination_material) + + def add_external_source_rate( + self, + material: str | int | Material, + composition: dict[str, float], + rate: float, + rate_units: str = 'g/s', + timesteps: Sequence[int] | None = None + ): + """Add external source rates to depletable material. + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + composition : dict of str to float + External source rate composition vector, where key can be an element + or a nuclide and value the corresponding weight percent. + rate : float + External source rate in units of mass per time. A positive or + negative value corresponds to a feed or removal rate, respectively. + units : {'g/s', 'g/min', 'g/h', 'g/d', 'g/a'} + Units for values specified in the `rate` argument. 's' for seconds, + 'min' for minutes, 'h' for hours, 'a' for Julian years. + timesteps : list of int, optional + List of timestep indices where to set external source rates. + Defaults to None, which means the external source rate is set for + all timesteps. + + """ + if self.external_source_rates is None: + if hasattr(self.operator, 'model'): + materials = self.operator.model.materials + elif hasattr(self.operator, 'materials'): + materials = self.operator.materials + self.external_source_rates = ExternalSourceRates( + self.operator, materials, len(self.timesteps)) + + if self.transfer_rates is not None and self.transfer_rates.index_transfer: + raise ValueError('Currently is not possible to set an external ' + 'source rate in combination with transfer rates ' + 'with destination matrial.') + + self.external_source_rates.set_external_source_rate( + material, composition, rate, rate_units, timesteps) + @add_params class SIIntegrator(Integrator): @@ -906,11 +1021,11 @@ class SIIntegrator(Integrator): the number of particles used in initial transport calculation """ - _params = r""" + _params = dedent(r""" Parameters ---------- - operator : openmc.deplete.TransportOperator - The operator object to simulate on. + operator : openmc.deplete.abc.TransportOperator + Operator to perform transport simulations timesteps : iterable of float or iterable of tuple Array of timesteps. Note that values are not cumulative. The units are specified by the `timestep_units` argument when `timesteps` is an @@ -927,9 +1042,10 @@ class SIIntegrator(Integrator): power_density : float or iterable of float, optional Power density of the reactor in [W/gHM]. It is multiplied by initial heavy metal inventory to get total power if ``power`` - is not speficied. + is not specified. source_rates : float or iterable of float, optional - Source rate in [neutron/sec] for each interval in :attr:`timesteps` + Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for + each interval in :attr:`timesteps` .. versionadded:: 0.12.1 timestep_units : {'s', 'min', 'h', 'd', 'MWd/kg'} @@ -951,10 +1067,22 @@ class SIIntegrator(Integrator): :attr:`solver`. .. versionadded:: 0.12 + continue_timesteps : bool, optional + Whether or not to treat the current solve as a continuation of a + previous simulation. Defaults to `False`. If `False`, all time + steps and source rates will be run in an append fashion and will run + after whatever time steps exist, if any. If `True`, the timesteps + provided to the `Integrator` must match exactly those that exist + in the `prev_results` passed to the `Opereator`. The `power`, + `power_density`, or `source_rates` must match as well. The + method of specifying `power`, `power_density`, or + `source_rates` should be the same as the initial run. + + .. versionadded:: 0.15.1 Attributes ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.abc.TransportOperator Operator to perform transport simulations chain : openmc.deplete.Chain Depletion chain @@ -971,7 +1099,7 @@ class SIIntegrator(Integrator): User-supplied functions are expected to have the following signature: ``solver(A, n0, t) -> n1`` where - * ``A`` is a :class:`scipy.sparse.csr_matrix` making up the + * ``A`` is a :class:`scipy.sparse.csc_matrix` making up the depletion matrix * ``n0`` is a 1-D :class:`numpy.ndarray` of initial compositions for a given material in atoms/cm3 @@ -981,64 +1109,93 @@ class SIIntegrator(Integrator): .. versionadded:: 0.12 - """ + """) - def __init__(self, operator, timesteps, power=None, power_density=None, - source_rates=None, timestep_units='s', n_steps=10, - solver="cram48"): + def __init__( + self, + operator: TransportOperator, + timesteps: Sequence[float], + power: Optional[Union[float, Sequence[float]]] = None, + power_density: Optional[Union[float, Sequence[float]]] = None, + source_rates: Optional[Sequence[float]] = None, + timestep_units: str = 's', + n_steps: int = 10, + solver: str = "cram48", + continue_timesteps: bool = False, + ): check_type("n_steps", n_steps, Integral) check_greater_than("n_steps", n_steps, 0) super().__init__( operator, timesteps, power, power_density, source_rates, - timestep_units=timestep_units, solver=solver) + timestep_units=timestep_units, solver=solver, continue_timesteps=continue_timesteps) self.n_steps = n_steps - def _get_bos_data_from_operator(self, step_index, step_power, bos_conc): + def _get_bos_data_from_operator(self, step_index, step_power, n_bos): reset_particles = False if step_index == 0 and hasattr(self.operator, "settings"): reset_particles = True self.operator.settings.particles *= self.n_steps inherited = super()._get_bos_data_from_operator( - step_index, step_power, bos_conc) + step_index, step_power, n_bos) if reset_particles: self.operator.settings.particles //= self.n_steps return inherited - def integrate(self): - """Perform the entire depletion process across all steps""" - with self.operator as conc: + def integrate( + self, + output: bool = True, + path: PathLike = "depletion_results.h5" + ): + """Perform the entire depletion process across all steps + + Parameters + ---------- + output : bool, optional + Indicate whether to display information about progress + path : PathLike + Path to file to write. Defaults to 'depletion_results.h5'. + + .. versionadded:: 0.15.0 + """ + with change_directory(self.operator.output_dir): + n = self.operator.initial_condition() t, self._i_res = self._get_start_data() for i, (dt, p) in enumerate(self): + if output: + print(f"[openmc.deplete] t={t} s, dt={dt} s, source={p}") + if i == 0: if self.operator.prev_res is None: - conc, res = self._get_bos_data_from_operator(i, p, conc) + n, res = self._get_bos_data_from_operator(i, p, n) else: - conc, res = self._get_bos_data_from_restart(i, p, conc) + n, res = self._get_bos_data_from_restart(p, n) else: # Pull rates, k from previous iteration w/o # re-running transport res = res_list[-1] # defined in previous i iteration - proc_time, conc_list, res_list = self(conc, res.rates, dt, p, i) + proc_time, n_list, res_list = self(n, res.rates, dt, p, i) # Insert BOS concentration, transport results - conc_list.insert(0, conc) + n_list.insert(0, n) res_list.insert(0, res) # Remove actual EOS concentration for next step - conc = conc_list.pop() + n = n_list.pop() - Results.save(self.operator, conc_list, res_list, [t, t + dt], - p, self._i_res + i, proc_time) + StepResult.save(self.operator, n_list, res_list, [t, t + dt], + p, self._i_res + i, proc_time, path) t += dt # No final simulation for SIE, use last iteration results - Results.save(self.operator, [conc], [res_list[-1]], [t, t], - p, self._i_res + len(self), proc_time) + StepResult.save(self.operator, [n], [res_list[-1]], [t, t], + p, self._i_res + len(self), proc_time, path) self.operator.write_bos_data(self._i_res + len(self)) + self.operator.finalize() + class DepSystemSolver(ABC): r"""Abstract class for solving depletion equations @@ -1059,8 +1216,8 @@ class DepSystemSolver(ABC): Parameters ---------- - A : scipy.sparse.csr_matrix - Sparse transmutation matrix ``A[j, i]`` desribing rates at + A : scipy.sparse.csc_matrix + Sparse transmutation matrix ``A[j, i]`` describing rates at which isotope ``i`` transmutes to isotope ``j`` n0 : numpy.ndarray Initial compositions, typically given in number of atoms in some diff --git a/openmc/deplete/atom_number.py b/openmc/deplete/atom_number.py index e4982f872e..b24c048cc9 100644 --- a/openmc/deplete/atom_number.py +++ b/openmc/deplete/atom_number.py @@ -2,10 +2,10 @@ An ndarray to store atom densities with string, integer, or slice indexing. """ -from collections import OrderedDict - import numpy as np +from openmc import Material + class AtomNumber: """Stores local material compositions (atoms of each nuclide). @@ -45,8 +45,8 @@ class AtomNumber: """ def __init__(self, local_mats, nuclides, volume, n_nuc_burn): - self.index_mat = OrderedDict((mat, i) for i, mat in enumerate(local_mats)) - self.index_nuc = OrderedDict((nuc, i) for i, nuc in enumerate(nuclides)) + self.index_mat = {mat: i for i, mat in enumerate(local_mats)} + self.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)} self.volume = np.ones(len(local_mats)) for mat, val in volume.items(): @@ -58,6 +58,12 @@ class AtomNumber: self.number = np.zeros((len(local_mats), len(nuclides))) + def _get_mat_index(self, mat): + """Helper method for getting material index""" + if isinstance(mat, Material): + mat = str(mat.id) + return self.index_mat[mat] if isinstance(mat, str) else mat + def __getitem__(self, pos): """Retrieves total atom number from AtomNumber. @@ -75,8 +81,7 @@ class AtomNumber: """ mat, nuc = pos - if isinstance(mat, str): - mat = self.index_mat[mat] + mat = self._get_mat_index(mat) if isinstance(nuc, str): nuc = self.index_nuc[nuc] @@ -96,8 +101,7 @@ class AtomNumber: """ mat, nuc = pos - if isinstance(mat, str): - mat = self.index_mat[mat] + mat = self._get_mat_index(mat) if isinstance(nuc, str): nuc = self.index_nuc[nuc] @@ -120,12 +124,29 @@ class AtomNumber: return [nuc for nuc, ind in self.index_nuc.items() if ind < self.n_nuc_burn] - def get_atom_density(self, mat, nuc): - """Accesses atom density instead of total number. + def get_mat_volume(self, mat): + """Return material volume Parameters ---------- - mat : str, int or slice + mat : str, int, openmc.Material, or slice + Material index. + + Returns + ------- + float + Material volume in [cm^3] + + """ + mat = self._get_mat_index(mat) + return self.volume[mat] + + def get_atom_density(self, mat, nuc): + """Return atom density of given material and nuclide + + Parameters + ---------- + mat : str, int, openmc.Material or slice Material index. nuc : str, int or slice Nuclide index. @@ -136,19 +157,43 @@ class AtomNumber: Density in [atom/cm^3] """ - if isinstance(mat, str): - mat = self.index_mat[mat] + mat = self._get_mat_index(mat) if isinstance(nuc, str): nuc = self.index_nuc[nuc] return self[mat, nuc] / self.volume[mat] + def get_atom_densities(self, mat, units='atom/b-cm'): + """Return atom densities for a given material + + Parameters + ---------- + mat : str, int, openmc.Material or slice + Material index. + units : {"atom/b-cm", "atom/cm3"}, optional + Units for the returned concentration. Default is ``"atom/b-cm"`` + + .. versionadded:: 0.13.1 + + Returns + ------- + dict + Dictionary mapping nuclides to atom densities + + """ + mat = self._get_mat_index(mat) + normalization = (1.0e-24 if units == 'atom/b-cm' else 1.0) / self.volume[mat] + return { + name: normalization * self[mat, nuc] + for name, nuc in self.index_nuc.items() + } + def set_atom_density(self, mat, nuc, val): """Sets atom density instead of total number. Parameters ---------- - mat : str, int or slice + mat : str, int, openmc.Material or slice Material index. nuc : str, int or slice Nuclide index. @@ -156,8 +201,7 @@ class AtomNumber: Array of densities to set in [atom/cm^3] """ - if isinstance(mat, str): - mat = self.index_mat[mat] + mat = self._get_mat_index(mat) if isinstance(nuc, str): nuc = self.index_nuc[nuc] @@ -168,7 +212,7 @@ class AtomNumber: Parameters ---------- - mat : str, int or slice + mat : str, int, openmc.Material or slice Material index. Returns @@ -177,9 +221,7 @@ class AtomNumber: The slice requested in [atom]. """ - if isinstance(mat, str): - mat = self.index_mat[mat] - + mat = self._get_mat_index(mat) return self[mat, :self.n_nuc_burn] def set_mat_slice(self, mat, val): @@ -187,15 +229,13 @@ class AtomNumber: Parameters ---------- - mat : str, int or slice + mat : str, int, openmc.Material, or slice Material index. val : numpy.ndarray The slice to set in [atom] """ - if isinstance(mat, str): - mat = self.index_mat[mat] - + mat = self._get_mat_index(mat) self[mat, :self.n_nuc_burn] = val def set_density(self, total_density): diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 0c1c34cf71..ac5c02aa50 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -7,123 +7,113 @@ loaded from an .xml file and all the nuclides are linked together. from io import StringIO from itertools import chain import math -import os import re -from collections import OrderedDict, defaultdict, namedtuple +from collections import defaultdict, namedtuple from collections.abc import Mapping, Iterable from numbers import Real, Integral +from pathlib import Path from warnings import warn +from typing import List -from openmc.checkvalue import check_type, check_greater_than -from openmc.data import gnd_name, zam, DataLibrary -from openmc.exceptions import DataError -from .nuclide import FissionYieldDistribution - -# Try to use lxml if it is available. It preserves the order of attributes and -# provides a pretty-printer by default. If not available, -# use OpenMC function to pretty print. -try: - import lxml.etree as ET - _have_lxml = True -except ImportError: - import xml.etree.ElementTree as ET - _have_lxml = False +import lxml.etree as ET import scipy.sparse as sp +from openmc.checkvalue import check_type, check_greater_than, PathLike +from openmc.data import gnds_name, zam +from openmc.exceptions import DataError +from .nuclide import FissionYieldDistribution, Nuclide +from .._xml import get_text import openmc.data -from openmc._xml import clean_indentation -from .nuclide import Nuclide, DecayTuple, ReactionTuple -# tuple of (possible MT values, (dA, dZ), secondaries) where dA is the change in -# the mass number and dZ is the change in the atomic number -ReactionInfo = namedtuple('ReactionInfo', ('mts', 'dadz', 'secondaries')) +# tuple of (possible MT values, secondaries) +ReactionInfo = namedtuple('ReactionInfo', ('mts', 'secondaries')) REACTIONS = { - '(n,2nd)': ReactionInfo({11}, (-3, -1), ('H2',)), - '(n,2n)': ReactionInfo(set(chain([16], range(875, 892))), (-1, 0), ()), - '(n,3n)': ReactionInfo({17}, (-2, 0), ()), - '(n,na)': ReactionInfo({22}, (-4, -2), ('He4',)), - '(n,n3a)': ReactionInfo({23}, (-12, -6), ('He4', 'He4', 'He4')), - '(n,2na)': ReactionInfo({24}, (-5, -2), ('He4',)), - '(n,3na)': ReactionInfo({25}, (-6, -2), ('He4',)), - '(n,np)': ReactionInfo({28}, (-1, -1), ('H1',)), - '(n,n2a)': ReactionInfo({29}, (-8, -4), ('He4', 'He4')), - '(n,2n2a)': ReactionInfo({30}, (-9, -4), ('He4', 'He4')), - '(n,nd)': ReactionInfo({32}, (-2, -1), ('H2',)), - '(n,nt)': ReactionInfo({33}, (-3, -1), ('H3',)), - '(n,n3He)': ReactionInfo({34}, (-3, -2), ('He3',)), - '(n,nd2a)': ReactionInfo({35}, (-10, -5), ('H2', 'He4', 'He4')), - '(n,nt2a)': ReactionInfo({36}, (-11, -5), ('H3', 'He4', 'He4')), - '(n,4n)': ReactionInfo({37}, (-3, 0), ()), - '(n,2np)': ReactionInfo({41}, (-2, -1), ('H1',)), - '(n,3np)': ReactionInfo({42}, (-3, -1), ('H1',)), - '(n,n2p)': ReactionInfo({44}, (-2, -2), ('H1', 'H1')), - '(n,npa)': ReactionInfo({45}, (-5, -3), ('H1', 'He4')), - '(n,gamma)': ReactionInfo({102}, (1, 0), ()), - '(n,p)': ReactionInfo(set(chain([103], range(600, 650))), (0, -1), ('H1',)), - '(n,d)': ReactionInfo(set(chain([104], range(650, 700))), (-1, -1), ('H2',)), - '(n,t)': ReactionInfo(set(chain([105], range(700, 750))), (-2, -1), ('H3',)), - '(n,3He)': ReactionInfo(set(chain([106], range(750, 800))), (-2, -2), ('He3',)), - '(n,a)': ReactionInfo(set(chain([107], range(800, 850))), (-3, -2), ('He4',)), - '(n,2a)': ReactionInfo({108}, (-7, -4), ('He4', 'He4')), - '(n,3a)': ReactionInfo({109}, (-11, -6), ('He4', 'He4', 'He4')), - '(n,2p)': ReactionInfo({111}, (-1, -2), ('H1', 'H1')), - '(n,pa)': ReactionInfo({112}, (-4, -3), ('H1', 'He4')), - '(n,t2a)': ReactionInfo({113}, (-10, -5), ('H3', 'He4', 'He4')), - '(n,d2a)': ReactionInfo({114}, (-9, -5), ('H2', 'He4', 'He4')), - '(n,pd)': ReactionInfo({115}, (-2, -2), ('H1', 'H2')), - '(n,pt)': ReactionInfo({116}, (-3, -2), ('H1', 'H3')), - '(n,da)': ReactionInfo({117}, (-5, -3), ('H2', 'He4')), - '(n,5n)': ReactionInfo({152}, (-4, 0), ()), - '(n,6n)': ReactionInfo({153}, (-5, 0), ()), - '(n,2nt)': ReactionInfo({154}, (-4, -1), ('H3',)), - '(n,ta)': ReactionInfo({155}, (-6, -3), ('H3', 'He4')), - '(n,4np)': ReactionInfo({156}, (-4, -1), ('H1',)), - '(n,3nd)': ReactionInfo({157}, (-4, -1), ('H2',)), - '(n,nda)': ReactionInfo({158}, (-6, -3), ('H2', 'He4')), - '(n,2npa)': ReactionInfo({159}, (-6, -3), ('H1', 'He4')), - '(n,7n)': ReactionInfo({160}, (-6, 0), ()), - '(n,8n)': ReactionInfo({161}, (-7, 0), ()), - '(n,5np)': ReactionInfo({162}, (-5, -1), ('H1',)), - '(n,6np)': ReactionInfo({163}, (-6, -1), ('H1',)), - '(n,7np)': ReactionInfo({164}, (-7, -1), ('H1',)), - '(n,4na)': ReactionInfo({165}, (-7, -2), ('He4',)), - '(n,5na)': ReactionInfo({166}, (-8, -2), ('He4',)), - '(n,6na)': ReactionInfo({167}, (-9, -2), ('He4',)), - '(n,7na)': ReactionInfo({168}, (-10, -2), ('He4',)), - '(n,4nd)': ReactionInfo({169}, (-5, -1), ('H2',)), - '(n,5nd)': ReactionInfo({170}, (-6, -1), ('H2',)), - '(n,6nd)': ReactionInfo({171}, (-7, -1), ('H2',)), - '(n,3nt)': ReactionInfo({172}, (-5, -1), ('H3',)), - '(n,4nt)': ReactionInfo({173}, (-6, -1), ('H3',)), - '(n,5nt)': ReactionInfo({174}, (-7, -1), ('H3',)), - '(n,6nt)': ReactionInfo({175}, (-8, -1), ('H3',)), - '(n,2n3He)': ReactionInfo({176}, (-4, -2), ('He3',)), - '(n,3n3He)': ReactionInfo({177}, (-5, -2), ('He3',)), - '(n,4n3He)': ReactionInfo({178}, (-6, -2), ('He3',)), - '(n,3n2p)': ReactionInfo({179}, (-4, -2), ('H1', 'H1')), - '(n,3n2a)': ReactionInfo({180}, (-10, -4), ('He4', 'He4')), - '(n,3npa)': ReactionInfo({181}, (-7, -3), ('H1', 'He4')), - '(n,dt)': ReactionInfo({182}, (-4, -2), ('H2', 'H3')), - '(n,npd)': ReactionInfo({183}, (-3, -2), ('H1', 'H2')), - '(n,npt)': ReactionInfo({184}, (-4, -2), ('H1', 'H3')), - '(n,ndt)': ReactionInfo({185}, (-5, -2), ('H2', 'H3')), - '(n,np3He)': ReactionInfo({186}, (-4, -3), ('H1', 'He3')), - '(n,nd3He)': ReactionInfo({187}, (-5, -3), ('H2', 'He3')), - '(n,nt3He)': ReactionInfo({188}, (-6, -3), ('H3', 'He3')), - '(n,nta)': ReactionInfo({189}, (-7, -3), ('H3', 'He4')), - '(n,2n2p)': ReactionInfo({190}, (-3, -2), ('H1', 'H1')), - '(n,p3He)': ReactionInfo({191}, (-4, -3), ('H1', 'He3')), - '(n,d3He)': ReactionInfo({192}, (-5, -3), ('H2', 'He3')), - '(n,3Hea)': ReactionInfo({193}, (-6, -4), ('He3', 'He4')), - '(n,4n2p)': ReactionInfo({194}, (-5, -2), ('H1', 'H1')), - '(n,4n2a)': ReactionInfo({195}, (-11, -4), ('He4', 'He4')), - '(n,4npa)': ReactionInfo({196}, (-8, -3), ('H1', 'He4')), - '(n,3p)': ReactionInfo({197}, (-2, -3), ('H1', 'H1', 'H1')), - '(n,n3p)': ReactionInfo({198}, (-3, -3), ('H1', 'H1', 'H1')), - '(n,3n2pa)': ReactionInfo({199}, (-8, -4), ('H1', 'H1', 'He4')), - '(n,5n2p)': ReactionInfo({200}, (-6, -2), ('H1', 'H1')), + '(n,2nd)': ReactionInfo({11}, ('H2',)), + '(n,2n)': ReactionInfo(set(chain([16], range(875, 892))), ()), + '(n,3n)': ReactionInfo({17}, ()), + '(n,na)': ReactionInfo({22}, ('He4',)), + '(n,n3a)': ReactionInfo({23}, ('He4', 'He4', 'He4')), + '(n,2na)': ReactionInfo({24}, ('He4',)), + '(n,3na)': ReactionInfo({25}, ('He4',)), + '(n,np)': ReactionInfo({28}, ('H1',)), + '(n,n2a)': ReactionInfo({29}, ('He4', 'He4')), + '(n,2n2a)': ReactionInfo({30}, ('He4', 'He4')), + '(n,nd)': ReactionInfo({32}, ('H2',)), + '(n,nt)': ReactionInfo({33}, ('H3',)), + '(n,n3He)': ReactionInfo({34}, ('He3',)), + '(n,nd2a)': ReactionInfo({35}, ('H2', 'He4', 'He4')), + '(n,nt2a)': ReactionInfo({36}, ('H3', 'He4', 'He4')), + '(n,4n)': ReactionInfo({37}, ()), + '(n,2np)': ReactionInfo({41}, ('H1',)), + '(n,3np)': ReactionInfo({42}, ('H1',)), + '(n,n2p)': ReactionInfo({44}, ('H1', 'H1')), + '(n,npa)': ReactionInfo({45}, ('H1', 'He4')), + '(n,gamma)': ReactionInfo({102}, ()), + '(n,p)': ReactionInfo(set(chain([103], range(600, 650))), ('H1',)), + '(n,d)': ReactionInfo(set(chain([104], range(650, 700))), ('H2',)), + '(n,t)': ReactionInfo(set(chain([105], range(700, 750))), ('H3',)), + '(n,3He)': ReactionInfo(set(chain([106], range(750, 800))), ('He3',)), + '(n,a)': ReactionInfo(set(chain([107], range(800, 850))), ('He4',)), + '(n,2a)': ReactionInfo({108}, ('He4', 'He4')), + '(n,3a)': ReactionInfo({109}, ('He4', 'He4', 'He4')), + '(n,2p)': ReactionInfo({111}, ('H1', 'H1')), + '(n,pa)': ReactionInfo({112}, ('H1', 'He4')), + '(n,t2a)': ReactionInfo({113}, ('H3', 'He4', 'He4')), + '(n,d2a)': ReactionInfo({114}, ('H2', 'He4', 'He4')), + '(n,pd)': ReactionInfo({115}, ('H1', 'H2')), + '(n,pt)': ReactionInfo({116}, ('H1', 'H3')), + '(n,da)': ReactionInfo({117}, ('H2', 'He4')), + '(n,5n)': ReactionInfo({152}, ()), + '(n,6n)': ReactionInfo({153}, ()), + '(n,2nt)': ReactionInfo({154}, ('H3',)), + '(n,ta)': ReactionInfo({155}, ('H3', 'He4')), + '(n,4np)': ReactionInfo({156}, ('H1',)), + '(n,3nd)': ReactionInfo({157}, ('H2',)), + '(n,nda)': ReactionInfo({158}, ('H2', 'He4')), + '(n,2npa)': ReactionInfo({159}, ('H1', 'He4')), + '(n,7n)': ReactionInfo({160}, ()), + '(n,8n)': ReactionInfo({161}, ()), + '(n,5np)': ReactionInfo({162}, ('H1',)), + '(n,6np)': ReactionInfo({163}, ('H1',)), + '(n,7np)': ReactionInfo({164}, ('H1',)), + '(n,4na)': ReactionInfo({165}, ('He4',)), + '(n,5na)': ReactionInfo({166}, ('He4',)), + '(n,6na)': ReactionInfo({167}, ('He4',)), + '(n,7na)': ReactionInfo({168}, ('He4',)), + '(n,4nd)': ReactionInfo({169}, ('H2',)), + '(n,5nd)': ReactionInfo({170}, ('H2',)), + '(n,6nd)': ReactionInfo({171}, ('H2',)), + '(n,3nt)': ReactionInfo({172}, ('H3',)), + '(n,4nt)': ReactionInfo({173}, ('H3',)), + '(n,5nt)': ReactionInfo({174}, ('H3',)), + '(n,6nt)': ReactionInfo({175}, ('H3',)), + '(n,2n3He)': ReactionInfo({176}, ('He3',)), + '(n,3n3He)': ReactionInfo({177}, ('He3',)), + '(n,4n3He)': ReactionInfo({178}, ('He3',)), + '(n,3n2p)': ReactionInfo({179}, ('H1', 'H1')), + '(n,3n2a)': ReactionInfo({180}, ('He4', 'He4')), + '(n,3npa)': ReactionInfo({181}, ('H1', 'He4')), + '(n,dt)': ReactionInfo({182}, ('H2', 'H3')), + '(n,npd)': ReactionInfo({183}, ('H1', 'H2')), + '(n,npt)': ReactionInfo({184}, ('H1', 'H3')), + '(n,ndt)': ReactionInfo({185}, ('H2', 'H3')), + '(n,np3He)': ReactionInfo({186}, ('H1', 'He3')), + '(n,nd3He)': ReactionInfo({187}, ('H2', 'He3')), + '(n,nt3He)': ReactionInfo({188}, ('H3', 'He3')), + '(n,nta)': ReactionInfo({189}, ('H3', 'He4')), + '(n,2n2p)': ReactionInfo({190}, ('H1', 'H1')), + '(n,p3He)': ReactionInfo({191}, ('H1', 'He3')), + '(n,d3He)': ReactionInfo({192}, ('H2', 'He3')), + '(n,3Hea)': ReactionInfo({193}, ('He3', 'He4')), + '(n,4n2p)': ReactionInfo({194}, ('H1', 'H1')), + '(n,4n2a)': ReactionInfo({195}, ('He4', 'He4')), + '(n,4npa)': ReactionInfo({196}, ('H1', 'He4')), + '(n,3p)': ReactionInfo({197}, ('H1', 'H1', 'H1')), + '(n,n3p)': ReactionInfo({198}, ('H1', 'H1', 'H1')), + '(n,3n2pa)': ReactionInfo({199}, ('H1', 'H1', 'He4')), + '(n,5n2p)': ReactionInfo({200}, ('H1', 'H1')), } __all__ = ["Chain", "REACTIONS"] @@ -135,14 +125,14 @@ def replace_missing(product, decay_data): Parameters ---------- product : str - Name of product in GND format, e.g. 'Y86_m1'. + Name of product in GNDS format, e.g. 'Y86_m1'. decay_data : dict Dictionary of decay data Returns ------- product : str - Replacement for missing product in GND format. + Replacement for missing product in GNDS format. """ # Determine atomic number, mass number, and metastable state @@ -155,7 +145,7 @@ def replace_missing(product, decay_data): # First check if ground state is available if state: - product = '{}{}'.format(symbol, A) + product = f'{symbol}{A}' # Find isotope with longest half-life half_life = 0.0 @@ -186,7 +176,7 @@ def replace_missing(product, decay_data): Z += 1 else: Z -= 1 - product = '{}{}'.format(openmc.data.ATOMIC_SYMBOL[Z], A) + product = f'{openmc.data.ATOMIC_SYMBOL[Z]}{A}' return product @@ -213,7 +203,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): # Check if metastable state has data (e.g., Am242m) Z, A, m = zam(actinide) if m == 0: - metastable = gnd_name(Z, A, 1) + metastable = gnds_name(Z, A, 1) if metastable in fpy_data: return metastable @@ -222,7 +212,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z += 1 A += 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -231,7 +221,7 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): while isotone in decay_data: Z -= 1 A -= 1 - isotone = gnd_name(Z, A, 0) + isotone = gnds_name(Z, A, 0) if isotone in fpy_data: return isotone @@ -239,24 +229,6 @@ def replace_missing_fpy(actinide, fpy_data, decay_data): return 'U235' -def _find_chain_file(cross_sections=None): - # First check deprecated OPENMC_DEPLETE_CHAIN environment variable - chain_file = os.environ.get("OPENMC_DEPLETE_CHAIN") - if chain_file is not None: - warn("Use of OPENMC_DEPLETE_CHAIN is deprecated in favor of adding " - "depletion_chain to OPENMC_CROSS_SECTIONS", FutureWarning) - return chain_file - - # Check for depletion chain in cross_sections.xml - data = DataLibrary.from_xml(cross_sections) - for lib in reversed(data.libraries): - if lib['type'] == 'depletion_chain': - return lib['path'] - - raise DataError("No depletion chain specified and could not find depletion " - f"chain in {cross_sections}") - - class Chain: """Full representation of a depletion chain. @@ -264,9 +236,9 @@ class Chain: requires a list of ENDF incident neutron, decay, and neutron fission product yield sublibrary files. The depletion chain used during a depletion simulation is indicated by either an argument to - :class:`openmc.deplete.Operator` or through the - ``depletion_chain`` item in the :envvar:`OPENMC_CROSS_SECTIONS` - environment variable. + :class:`openmc.deplete.CoupledOperator` or + :class:`openmc.deplete.IndependentOperator`, or through + openmc.config['chain_file']. Attributes ---------- @@ -274,8 +246,12 @@ class Chain: Nuclides present in the chain. reactions : list of str Reactions that are tracked in the depletion chain - nuclide_dict : OrderedDict of str to int + nuclide_dict : dict of str to int Maps a nuclide name to an index in nuclides. + stable_nuclides : list of openmc.deplete.Nuclide + List of stable nuclides available in the chain. + unstable_nuclides : list of openmc.deplete.Nuclide + List of unstable nuclides available in the chain. fission_yields : None or iterable of dict List of effective fission yields for materials. Each dictionary should be of the form ``{parent: {product: yield}}`` with @@ -288,9 +264,9 @@ class Chain: """ def __init__(self): - self.nuclides = [] + self.nuclides: List[Nuclide] = [] self.reactions = [] - self.nuclide_dict = OrderedDict() + self.nuclide_dict = {} self._fission_yields = None def __contains__(self, nuclide): @@ -304,7 +280,17 @@ class Chain: """Number of nuclides in chain.""" return len(self.nuclides) - def add_nuclide(self, nuclide): + @property + def stable_nuclides(self) -> List[Nuclide]: + """List of stable nuclides available in the chain""" + return [nuc for nuc in self.nuclides if nuc.half_life is None] + + @property + def unstable_nuclides(self) -> List[Nuclide]: + """List of unstable nuclides available in the chain""" + return [nuc for nuc in self.nuclides if nuc.half_life is not None] + + def add_nuclide(self, nuclide: Nuclide): """Add a nuclide to the depletion chain Parameters @@ -313,6 +299,7 @@ class Chain: Nuclide to add """ + _invalidate_chain_cache(self) self.nuclide_dict[nuclide.name] = len(self.nuclides) self.nuclides.append(nuclide) @@ -375,7 +362,7 @@ class Chain: reactions = {} for f in neutron_files: evaluation = openmc.data.endf.Evaluation(f) - name = evaluation.gnd_name + name = evaluation.gnds_name reactions[name] = {} for mf, mt, nc, mod in evaluation.reaction_list: if mf == 3: @@ -418,36 +405,45 @@ class Chain: if not data.nuclide['stable'] and data.half_life.nominal_value != 0.0: nuclide.half_life = data.half_life.nominal_value nuclide.decay_energy = data.decay_energy.nominal_value - sum_br = 0.0 - for i, mode in enumerate(data.modes): + branch_ratios = [] + branch_ids = [] + for mode in data.modes: type_ = ','.join(mode.modes) if mode.daughter in decay_data: target = mode.daughter else: print('missing {} {} {}'.format( - parent, ','.join(mode.modes), mode.daughter)) + parent, type_, mode.daughter)) target = replace_missing(mode.daughter, decay_data) - - # Write branching ratio, taking care to ensure sum is unity br = mode.branching_ratio.nominal_value - sum_br += br - if i == len(data.modes) - 1 and sum_br != 1.0: - br = 1.0 - sum(m.branching_ratio.nominal_value - for m in data.modes[:-1]) + branch_ratios.append(br) + branch_ids.append((type_, target)) - # Append decay mode + if not math.isclose(sum(branch_ratios), 1.0): + max_br = max(branch_ratios) + max_index = branch_ratios.index(max_br) + + # Adjust maximum branching ratio so they sum to unity + new_br = max_br - sum(branch_ratios) + 1.0 + branch_ratios[max_index] = new_br + assert math.isclose(sum(branch_ratios), 1.0) + + # Append decay modes + for br, (type_, target) in zip(branch_ratios, branch_ids): nuclide.add_decay_mode(type_, target, br) + nuclide.sources = data.sources + fissionable = False if parent in reactions: reactions_available = set(reactions[parent].keys()) for name in transmutation_reactions: - mts, changes, _ = REACTIONS[name] + mts = REACTIONS[name].mts + delta_A, delta_Z = openmc.data.DADZ[name] if mts & reactions_available: - delta_A, delta_Z = changes A = data.nuclide['mass_number'] + delta_A Z = data.nuclide['atomic_number'] + delta_Z - daughter = '{}{}'.format(openmc.data.ATOMIC_SYMBOL[Z], A) + daughter = f'{openmc.data.ATOMIC_SYMBOL[Z]}{A}' if daughter not in decay_data: daughter = replace_missing(daughter, decay_data) @@ -469,7 +465,6 @@ class Chain: nuclide.add_reaction('fission', None, q_value, 1.0) fissionable = True - if fissionable: if parent in fpy_data: fpy = fpy_data[parent] @@ -513,7 +508,7 @@ class Chain: if missing_daughter: print('The following decay modes have daughters with no decay data:') for mode in missing_daughter: - print(' {}'.format(mode)) + print(f' {mode}') print('') if missing_rx_product: @@ -525,7 +520,7 @@ class Chain: if missing_fpy: print('The following fissionable nuclides have no fission product yields:') for parent, replacement in missing_fpy: - print(' {}, replaced with {}'.format(parent, replacement)) + print(f' {parent}, replaced with {replacement}') print('') if missing_fp: @@ -559,11 +554,14 @@ class Chain: root = ET.parse(str(filename)) for i, nuclide_elem in enumerate(root.findall('nuclide')): - this_q = fission_q.get(nuclide_elem.get("name")) + this_q = fission_q.get(get_text(nuclide_elem, "name")) nuc = Nuclide.from_xml(nuclide_elem, root, this_q) chain.add_nuclide(nuc) + # Store path of XML file (used for handling cache invalidation) + chain._xml_path = str(Path(filename).resolve()) + return chain def export_to_xml(self, filename): @@ -581,11 +579,7 @@ class Chain: root_elem.append(nuclide.to_xml_element()) tree = ET.ElementTree(root_elem) - if _have_lxml: - tree.write(str(filename), encoding='utf-8', pretty_print=True) - else: - clean_indentation(root_elem) - tree.write(str(filename), encoding='utf-8') + tree.write(str(filename), encoding='utf-8', pretty_print=True) def get_default_fission_yields(self): """Return fission yields at lowest incident neutron energy @@ -624,16 +618,25 @@ class Chain: Returns ------- - scipy.sparse.csr_matrix + scipy.sparse.csc_matrix Sparse matrix representing depletion. See Also -------- :meth:`get_default_fission_yields` """ - matrix = defaultdict(float) reactions = set() + n = len(self) + + # we accumulate indices and value entries for everything and create the matrix + # in one step at the end to avoid expensive index checks scipy otherwise does. + rows, cols, vals = [], [], [] + def setval(i, j, val): + rows.append(i) + cols.append(j) + vals.append(val) + if fission_yields is None: fission_yields = self.get_default_fission_yields() @@ -642,18 +645,30 @@ class Chain: if nuc.half_life is not None: decay_constant = math.log(2) / nuc.half_life if decay_constant != 0.0: - matrix[i, i] -= decay_constant + setval(i, i, -decay_constant) # Gain from radioactive decay if nuc.n_decay_modes != 0: - for _, target, branching_ratio in nuc.decay_modes: - # Allow for total annihilation for debug purposes - if target is not None: - branch_val = branching_ratio * decay_constant + for decay_type, target, branching_ratio in nuc.decay_modes: + branch_val = branching_ratio * decay_constant - if branch_val != 0.0: + # Allow for total annihilation for debug purposes + if branch_val != 0.0: + if target is not None: k = self.nuclide_dict[target] - matrix[k, i] += branch_val + setval(k, i, branch_val) + + # Produce alphas and protons from decay + if 'alpha' in decay_type: + k = self.nuclide_dict.get('He4') + if k is not None: + count = decay_type.count('alpha') + setval(k, i, count * branch_val) + elif 'p' in decay_type: + k = self.nuclide_dict.get('H1') + if k is not None: + count = decay_type.count('p') + setval(k, i, count * branch_val) if nuc.name in rates.index_nuc: # Extract all reactions for this nuclide in this cell @@ -670,13 +685,13 @@ class Chain: if r_type not in reactions: reactions.add(r_type) if path_rate != 0.0: - matrix[i, i] -= path_rate + setval(i, i, -path_rate) # Gain term; allow for total annihilation for debug purposes if r_type != 'fission': if target is not None and path_rate != 0.0: k = self.nuclide_dict[target] - matrix[k, i] += path_rate * br + setval(k, i, path_rate * br) # Determine light nuclide production, e.g., (n,d) should # produce H2 @@ -684,23 +699,127 @@ class Chain: for light_nuc in light_nucs: k = self.nuclide_dict.get(light_nuc) if k is not None: - matrix[k, i] += path_rate * br + setval(k, i, path_rate * br) else: for product, y in fission_yields[nuc.name].items(): yield_val = y * path_rate if yield_val != 0.0: k = self.nuclide_dict[product] - matrix[k, i] += yield_val + setval(k, i, yield_val) # Clear set of reactions reactions.clear() - # Use DOK matrix as intermediate representation, then convert to CSR and return + # Return CSC representation instead of DOK + return sp.csc_matrix((vals, (rows, cols)), shape=(n, n)) + + def form_rr_term(self, tr_rates, current_timestep, mats): + """Function to form the transfer rate term matrices. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + tr_rates : openmc.deplete.TransferRates + Instance of openmc.deplete.TransferRates + current_timestep : int + Current timestep index + mats : string or two-tuple of strings + Two cases are possible: + + 1) Material ID as string: + Nuclide transfer only. In this case the transfer rate terms will be + subtracted from the respective depletion matrix + + 2) Two-tuple of material IDs as strings: + Nuclide transfer from one material into another. + The pair is assumed to be + ``(destination_material, source_material)``, where + ``destination_material`` and ``source_material`` are the nuclide + receiving and losing materials, respectively. + The transfer rate terms get placed in the final matrix with indexing + position corresponding to the ID of the materials set. + + Returns + ------- + scipy.sparse.csc_matrix + Sparse matrix representing transfer term. + + """ + # Use DOK as intermediate representation n = len(self) - matrix_dok = sp.dok_matrix((n, n)) - dict.update(matrix_dok, matrix) - return matrix_dok.tocsr() + matrix = sp.dok_matrix((n, n)) + + for i, nuc in enumerate(self.nuclides): + elm = re.split(r'\d+', nuc.name)[0] + # Build transfer terms (nuclide transfer only) + if isinstance(mats, str): + mat = mats + components = tr_rates.get_components(mat, current_timestep) + if not components: + break + if elm in components: + matrix[i, i] = sum( + tr_rates.get_external_rate(mat, elm, current_timestep)) + elif nuc.name in components: + matrix[i, i] = sum( + tr_rates.get_external_rate(mat, nuc.name, current_timestep)) + else: + matrix[i, i] = 0.0 + + # Build transfer terms (transfer from one material into another) + elif isinstance(mats, tuple): + dest_mat, mat = mats + components = tr_rates.get_components(mat, current_timestep, dest_mat) + if elm in components: + matrix[i, i] = tr_rates.get_external_rate( + mat, elm, current_timestep, dest_mat)[0] + elif nuc.name in components: + matrix[i, i] = tr_rates.get_external_rate( + mat, nuc.name, current_timestep, dest_mat)[0] + else: + matrix[i, i] = 0.0 + + # Return CSC instead of DOK + return matrix.tocsc() + + def form_ext_source_term(self, ext_source_rates, current_timestep, mat): + """Function to form the external source rate term vectors. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + ext_source_rates : openmc.deplete.ExternalSourceRates + Instance of openmc.deplete.ExternalSourceRates + current_timestep : int + Current timestep index + mat : string + Material id + + Returns + ------- + scipy.sparse.csc_matrix + Sparse vector representing external source term. + + """ + if not ext_source_rates.get_components(mat, current_timestep): + return + # Use DOK as intermediate representation + n = len(self) + vector = sp.dok_matrix((n, 1)) + + for i, nuc in enumerate(self.nuclides): + # Build source term vector + if nuc.name in ext_source_rates.get_components(mat, current_timestep): + vector[i] = sum(ext_source_rates.get_external_rate( + mat, nuc.name, current_timestep)) + else: + vector[i] = 0.0 + + # Return CSC instead of DOK + return vector.tocsc() def get_branch_ratios(self, reaction="(n,gamma)"): """Return a dictionary with reaction branching ratios @@ -779,7 +898,7 @@ class Chain: -------- :meth:`get_branch_ratios` """ - + _invalidate_chain_cache(self) # Store some useful information through the validation stage sums = {} @@ -832,8 +951,7 @@ class Chain: if len(indexes) == 0: if strict: raise AttributeError( - "Nuclide {} does not have {} reactions".format( - parent, reaction)) + f"Nuclide {parent} does not have {reaction} reactions") missing_reaction.add(parent) continue @@ -855,8 +973,7 @@ class Chain: if len(rxn_ix_map) == 0: raise IndexError( - "No {} reactions found in this {}".format( - reaction, self.__class__.__name__)) + f"No {reaction} reactions found in this {self.__class__.__name__}") if len(missing_parents) > 0: warn("The following nuclides were not found in {}: {}".format( @@ -867,14 +984,14 @@ class Chain: "{}".format(reaction, ", ".join(sorted(missing_reaction)))) if len(missing_products) > 0: - tail = ("{} -> {}".format(k, v) + tail = (f"{k} -> {v}" for k, v in sorted(missing_products.items())) warn("The following products were not found in the {} and " "parents were unmodified: \n{}".format( self.__class__.__name__, ", ".join(tail))) if len(bad_sums) > 0: - tail = ("{}: {:5.3f}".format(k, s) + tail = (f"{k}: {s:5.3f}" for k, s in sorted(bad_sums.items())) warn("The following parent nuclides were given {} branch ratios " "with a sum outside tolerance of 1 +/- {:5.3e}:\n{}".format( @@ -908,7 +1025,7 @@ class Chain: ground_target = grounds.get(parent_name) if ground_target is None: pz, pa, pm = zam(parent_name) - ground_target = gnd_name(pz, pa + 1, 0) + ground_target = gnds_name(pz, pa + 1, 0) new_ratios[ground_target] = ground_br parent.add_reaction(reaction, ground_target, rxn_Q, ground_br) @@ -920,6 +1037,7 @@ class Chain: @fission_yields.setter def fission_yields(self, yields): + _invalidate_chain_cache(self) if yields is not None: if isinstance(yields, Mapping): yields = [yields] @@ -1041,6 +1159,7 @@ class Chain: new_nuclide = Nuclide(previous.name) new_nuclide.half_life = previous.half_life new_nuclide.decay_energy = previous.decay_energy + new_nuclide.sources = previous.sources.copy() if hasattr(previous, '_fpy'): new_nuclide._fpy = previous._fpy @@ -1139,3 +1258,65 @@ class Chain: found.update(isotopes) return found + + +# A global cache for Chain objects +_CHAIN_CACHE = {} + + +def _get_chain( + chain_file: PathLike | Chain | None = None, + fission_q: dict | None = None +) -> Chain: + """Get a depletion chain from a file or the runtime configuration. + + Parameters + ---------- + chain_file : PathLike or Chain, optional + Path to depletion chain XML file, a Chain instance, or None to use + the file specified in ``openmc.config['chain_file']``. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not given, + values will be pulled from the ``chain_file``. + + Returns + ------- + Chain + Depletion chain instance. + """ + # If chain_file is already a Chain, return it directly + if isinstance(chain_file, Chain): + return chain_file + + # Resolve chain_file based on config if None + if chain_file is None: + chain_file = openmc.config.get('chain_file') + if 'chain_file' not in openmc.config: + raise DataError( + "No depletion chain specified and could not find depletion " + "chain in openmc.config['chain_file']" + ) + elif not isinstance(chain_file, PathLike): + raise TypeError("chain_file must be path-like, a Chain, or None") + + # Determine the key for the cache, which consists of the absolute path, the + # file modification time, the file size, and the fission Q values. + chain_path = Path(chain_file).resolve() + stat_result = chain_path.stat() + fq_tuple = tuple(sorted(fission_q.items())) if fission_q else () + key = (chain_path, stat_result.st_mtime, stat_result.st_size, fq_tuple) + + # Check the global cache. If not cached, load the chain from XML and store + global _CHAIN_CACHE + if key not in _CHAIN_CACHE: + _CHAIN_CACHE[key] = Chain.from_xml(chain_path, fission_q) + return _CHAIN_CACHE[key] + + +def _invalidate_chain_cache(chain): + """Invalidate the cache for a specific Chain (when it is modifed).""" + if hasattr(chain, '_xml_path'): + # Remove all entries with the same path as self._xml_path + for key in list(_CHAIN_CACHE.keys()): + if str(key[0]) == chain._xml_path: + del _CHAIN_CACHE[key] diff --git a/openmc/deplete/coupled_operator.py b/openmc/deplete/coupled_operator.py new file mode 100644 index 0000000000..34bb28b491 --- /dev/null +++ b/openmc/deplete/coupled_operator.py @@ -0,0 +1,561 @@ +"""Transport-coupled transport operator for depletion. + +This module implements a transport operator coupled to OpenMC's transport solver +so that it can be used by depletion integrators. The implementation makes use of +the Python bindings to OpenMC's C API so that reading tally results and updating +material number densities is all done in-memory instead of through the +filesystem. + +""" + +import copy +from warnings import warn + +import numpy as np +from uncertainties import ufloat + +import openmc +from openmc.checkvalue import check_value +from openmc.data import DataLibrary +from openmc.exceptions import DataError +import openmc.lib +from openmc.mpi import comm +from .abc import OperatorResult +from .openmc_operator import OpenMCOperator +from .pool import _distribute +from .results import Results +from .helpers import ( + DirectReactionRateHelper, ChainFissionHelper, ConstantFissionYieldHelper, + FissionYieldCutoffHelper, AveragedFissionYieldHelper, EnergyScoreHelper, + SourceRateHelper, FluxCollapseHelper) + + +__all__ = ["CoupledOperator", "Operator", "OperatorResult"] + + +def _find_cross_sections(model: str | None = None): + """Determine cross sections to use for depletion + + Parameters + ---------- + model : openmc.model.Model, optional + Reactor model + + """ + if model: + if model.materials and model.materials.cross_sections is not None: + # Prefer info from Model class if available + return model.materials.cross_sections + + # otherwise fallback to environment variable + cross_sections = openmc.config.get("cross_sections") + if cross_sections is None: + raise DataError( + "Cross sections were not specified in Model.materials and " + "openmc.config['cross_sections'] is not set." + ) + return cross_sections + + +def _get_nuclides_with_data(cross_sections): + """Loads cross_sections.xml file to find nuclides with neutron data + + Parameters + ---------- + cross_sections : str + Path to cross_sections.xml file + + Returns + ------- + nuclides : set of str + Set of nuclide names that have cross section data + + """ + nuclides = set() + data_lib = DataLibrary.from_xml(cross_sections) + for library in data_lib.libraries: + if library['type'] != 'neutron': + continue + for name in library['materials']: + if name not in nuclides: + nuclides.add(name) + + return nuclides + + +class CoupledOperator(OpenMCOperator): + """Transport-coupled transport operator. + + Instances of this class can be used to perform transport-coupled depletion + using OpenMC's transport solver. Normally, a user needn't call methods of + this class directly. Instead, an instance of this class is passed to an + integrator class, such as :class:`openmc.deplete.CECMIntegrator`. + + .. versionchanged:: 0.13.0 + The geometry and settings parameters have been replaced with a + model parameter that takes a :class:`~openmc.model.Model` object + + .. versionchanged:: 0.13.1 + Name changed from ``Operator`` to ``CoupledOperator`` + + Parameters + ---------- + model : openmc.model.Model + OpenMC model object + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Defaults to ``openmc.config['chain_file']``. + prev_results : Results, optional + Results from a previous depletion calculation. If this argument is + specified, the depletion calculation will start from the latest state + in the previous results. + diff_burnable_mats : bool, optional + Whether to differentiate burnable materials with multiple instances. + Volumes are divided equally from the original material volume. + normalization_mode : {"energy-deposition", "fission-q", "source-rate"} + Indicate how tally results should be normalized. ``"energy-deposition"`` + computes the total energy deposited in the system and uses the ratio of + the power to the energy produced as a normalization factor. + ``"fission-q"`` uses the fission Q values from the depletion chain to + compute the total energy deposited. ``"source-rate"`` normalizes + tallies based on the source rate (for fixed source calculations). + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not given, + values will be pulled from the ``chain_file``. Only applicable + if ``"normalization_mode" == "fission-q"`` + fission_yield_mode : {"constant", "cutoff", "average"} + Key indicating what fission product yield scheme to use. The + key determines what fission energy helper is used: + + * "constant": :class:`~openmc.deplete.helpers.ConstantFissionYieldHelper` + * "cutoff": :class:`~openmc.deplete.helpers.FissionYieldCutoffHelper` + * "average": :class:`~openmc.deplete.helpers.AveragedFissionYieldHelper` + + The documentation on these classes describe their methodology + and differences. Default: ``"constant"`` + fission_yield_opts : dict of str to option, optional + Optional arguments to pass to the helper determined by + ``fission_yield_mode``. Will be passed directly on to the + helper. Passing a value of None will use the defaults for + the associated helper. + reaction_rate_mode : {"direct", "flux"}, optional + Indicate how one-group reaction rates should be calculated. The "direct" + method tallies transmutation reaction rates directly. The "flux" method + tallies a multigroup flux spectrum and then collapses one-group reaction + rates after a transport solve (with an option to tally some reaction + rates directly). + + .. versionadded:: 0.12.1 + reaction_rate_opts : dict, optional + Keyword arguments that are passed to the reaction rate helper class. + When ``reaction_rate_mode`` is set to "flux", energy group boundaries + can be set using the "energies" key. See the + :class:`~openmc.deplete.helpers.FluxCollapseHelper` class for all + options. + + .. versionadded:: 0.12.1 + reduce_chain_level : int, optional + Depth of the search when reducing the depletion chain. The default + value of ``None`` implies no limit on the depth. + + .. versionadded:: 0.12 + diff_volume_method : str + Specifies how the volumes of the new materials should be found. Default + is to 'divide equally' which divides the original material volume + equally between the new materials, 'match cell' sets the volume of the + material to volume of the cell they fill. + + .. versionadded:: 0.14.0 + + Attributes + ---------- + model : openmc.model.Model + OpenMC model object + output_dir : pathlib.Path + Path to output directory to save results. + round_number : bool + Whether or not to round output to OpenMC to 8 digits. + Useful in testing, as OpenMC is incredibly sensitive to exact values. + number : openmc.deplete.AtomNumber + Total number of atoms in simulation. + nuclides_with_data : set of str + A set listing all unique nuclides available from cross_sections.xml. + chain : openmc.deplete.Chain + The depletion chain information necessary to form matrices and tallies. + reaction_rates : openmc.deplete.ReactionRates + Reaction rates from the last operator step. + burnable_mats : list of str + All burnable material IDs + heavy_metal : float + Initial heavy metal inventory [g] + local_mats : list of str + All burnable material IDs being managed by a single process + prev_res : Results or None + Results from a previous depletion calculation. ``None`` if no + results are to be used. + cleanup_when_done : bool + Whether to finalize and clear the shared library memory when the + depletion operation is complete. Defaults to clearing the library. + """ + _fission_helpers = { + "average": AveragedFissionYieldHelper, + "constant": ConstantFissionYieldHelper, + "cutoff": FissionYieldCutoffHelper, + } + + def __init__(self, model, chain_file=None, prev_results=None, + diff_burnable_mats=False, diff_volume_method="divide equally", + normalization_mode="fission-q", fission_q=None, + fission_yield_mode="constant", fission_yield_opts=None, + reaction_rate_mode="direct", reaction_rate_opts=None, + reduce_chain_level=None): + + # check for old call to constructor + if isinstance(model, openmc.Geometry): + msg = "As of version 0.13.0 openmc.deplete.CoupledOperator " \ + "requires an openmc.Model object rather than the " \ + "openmc.Geometry and openmc.Settings parameters. Please use " \ + "the geometry and settings objects passed here to create a " \ + " model with which to generate the transport Operator." + raise TypeError(msg) + + # Determine cross sections + cross_sections = _find_cross_sections(model) + + check_value('fission yield mode', fission_yield_mode, + self._fission_helpers.keys()) + check_value('normalization mode', normalization_mode, + ('energy-deposition', 'fission-q', 'source-rate')) + if normalization_mode != "fission-q": + if fission_q is not None: + warn("Fission Q dictionary will not be used") + fission_q = None + self.model = model + + # determine set of materials in the model + if not model.materials: + model.materials = openmc.Materials( + model.geometry.get_all_materials().values() + ) + + self.cleanup_when_done = True + + if reaction_rate_opts is None: + reaction_rate_opts = {} + if fission_yield_opts is None: + fission_yield_opts = {} + helper_kwargs = { + 'reaction_rate_mode': reaction_rate_mode, + 'normalization_mode': normalization_mode, + 'fission_yield_mode': fission_yield_mode, + 'reaction_rate_opts': reaction_rate_opts, + 'fission_yield_opts': fission_yield_opts + } + + # Records how many times the operator has been called + self._n_calls = 0 + + super().__init__( + materials=model.materials, + cross_sections=cross_sections, + chain_file=chain_file, + prev_results=prev_results, + diff_burnable_mats=diff_burnable_mats, + diff_volume_method=diff_volume_method, + fission_q=fission_q, + helper_kwargs=helper_kwargs, + reduce_chain_level=reduce_chain_level) + + def _differentiate_burnable_mats(self): + """Assign distribmats for each burnable material""" + + self.model.differentiate_depletable_mats( + diff_volume_method=self.diff_volume_method + ) + + def _load_previous_results(self): + """Load results from a previous depletion simulation""" + # Reload volumes into geometry + self.prev_res[-1].transfer_volumes(self.model) + + # Store previous results in operator + # Distribute reaction rates according to those tracked + # on this process + if comm.size != 1: + prev_results = self.prev_res + self.prev_res = Results(filename=None) + mat_indexes = _distribute(range(len(self.burnable_mats))) + for res_obj in prev_results: + new_res = res_obj.distribute(self.local_mats, mat_indexes) + self.prev_res.append(new_res) + + def _get_nuclides_with_data(self, cross_sections): + """Loads cross_sections.xml file to find nuclides with neutron data + + Parameters + ---------- + cross_sections : str + Path to cross_sections.xml file + + Returns + ------- + nuclides : set of str + Set of nuclide names that have cross secton data + + """ + return _get_nuclides_with_data(cross_sections) + + def _get_helper_classes(self, helper_kwargs): + """Create the ``_rate_helper``, ``_normalization_helper``, and + ``_yield_helper`` objects. + + Parameters + ---------- + helper_kwargs : dict + Keyword arguments for helper classes + + """ + reaction_rate_mode = helper_kwargs['reaction_rate_mode'] + normalization_mode = helper_kwargs['normalization_mode'] + fission_yield_mode = helper_kwargs['fission_yield_mode'] + reaction_rate_opts = helper_kwargs['reaction_rate_opts'] + fission_yield_opts = helper_kwargs['fission_yield_opts'] + + # Get classes to assist working with tallies + if reaction_rate_mode == "direct": + self._rate_helper = DirectReactionRateHelper( + self.reaction_rates.n_nuc, self.reaction_rates.n_react) + elif reaction_rate_mode == "flux": + # Ensure energy group boundaries were specified + if 'energies' not in reaction_rate_opts: + raise ValueError( + "Energy group boundaries must be specified in the " + "reaction_rate_opts argument when reaction_rate_mode is" + "set to 'flux'.") + + self._rate_helper = FluxCollapseHelper( + self.reaction_rates.n_nuc, + self.reaction_rates.n_react, + **reaction_rate_opts + ) + else: + raise ValueError("Invalid reaction rate mode.") + + if normalization_mode == "fission-q": + self._normalization_helper = ChainFissionHelper() + elif normalization_mode == "energy-deposition": + score = "heating" if self.model.settings.photon_transport else "heating-local" + self._normalization_helper = EnergyScoreHelper(score) + else: + self._normalization_helper = SourceRateHelper() + + # Select and create fission yield helper + fission_helper = self._fission_helpers[fission_yield_mode] + self._yield_helper = fission_helper.from_operator( + self, **fission_yield_opts) + + def initial_condition(self): + """Performs final setup and returns initial condition. + + Returns + ------- + list of numpy.ndarray + Total density for initial conditions. + + """ + + # Create XML files + if comm.rank == 0: + self.model.geometry.export_to_xml() + self.model.settings.export_to_xml() + if self.model.plots: + self.model.plots.export_to_xml() + if self.model.tallies: + self.model.tallies.export_to_xml() + self._generate_materials_xml() + + # Initialize OpenMC library + comm.barrier() + if not openmc.lib.is_initialized: + openmc.lib.init(intracomm=comm) + + # Generate tallies in memory + materials = [openmc.lib.materials[int(i)] for i in self.burnable_mats] + + return super().initial_condition(materials) + + def _generate_materials_xml(self): + """Creates materials.xml from self.number. + + Due to uncertainty with how MPI interacts with OpenMC API, this + constructs the XML manually. The long term goal is to do this + through direct memory writing. + + """ + # Sort nuclides according to order in AtomNumber object + nuclides = list(self.number.nuclides) + for mat in self.materials: + mat._nuclides.sort(key=lambda x: nuclides.index(x[0])) + + self.materials.export_to_xml(nuclides_to_ignore=self._decay_nucs) + + def __call__(self, vec, source_rate): + """Runs a simulation. + + Simulation will abort under the following circumstances: + + 1) No energy is computed using OpenMC tallies. + + Parameters + ---------- + vec : list of numpy.ndarray + Total atoms to be used in function. + source_rate : float + Power in [W] or source rate in [neutron/sec] + + Returns + ------- + openmc.deplete.OperatorResult + Eigenvalue and reaction rates resulting from transport operator + + """ + # Reset results in OpenMC + openmc.lib.reset() + + # The timers are reset only if the operator has been called before. + # This is because we call this method after loading cross sections, and + # no transport has taken place yet. As a result, we only reset the + # timers after the first step so as to correctly report the time spent + # reading cross sections in the first depletion step, and from there + # correctly report all particle tracking rates in multistep depletion + # solvers. + if self._n_calls > 0: + openmc.lib.reset_timers() + + self._update_materials_and_nuclides(vec) + + # If the source rate is zero, return zero reaction rates without running + # a transport solve + if source_rate == 0.0: + rates = self.reaction_rates.copy() + rates.fill(0.0) + return OperatorResult(ufloat(0.0, 0.0), rates) + + # Run OpenMC + openmc.lib.run() + + # Extract results + rates = self._calculate_reaction_rates(source_rate) + + # Get k and uncertainty + keff = ufloat(*openmc.lib.keff()) + + op_result = OperatorResult(keff, rates) + + self._n_calls += 1 + + return copy.deepcopy(op_result) + + def _update_materials(self): + """Updates material compositions in OpenMC on all processes.""" + + for rank in range(comm.size): + number_i = comm.bcast(self.number, root=rank) + + for mat in number_i.materials: + nuclides = [] + densities = [] + for nuc in number_i.nuclides: + if nuc in self.nuclides_with_data: + val = 1.0e-24 * number_i.get_atom_density(mat, nuc) + + # If nuclide is zero, do not add to the problem. + if val > 0.0: + if self.round_number: + val_magnitude = np.floor(np.log10(val)) + val_scaled = val / 10**val_magnitude + val_round = round(val_scaled, 8) + + val = val_round * 10**val_magnitude + + nuclides.append(nuc) + densities.append(val) + else: + # Only output warnings if values are significantly + # negative. CRAM does not guarantee positive + # values. + if val < -1.0e-21: + print(f'WARNING: nuclide {nuc} in material' + f'{mat} is negative (density = {val}' + + ' atom/b-cm)') + + number_i[mat, nuc] = 0.0 + + # Update densities on C API side + mat_internal = openmc.lib.materials[int(mat)] + mat_internal.set_densities(nuclides, densities) + + # TODO Update densities on the Python side, otherwise the + # summary.h5 file contains densities at the first time step + + @staticmethod + def write_bos_data(step): + """Write a state-point file with beginning of step data + + Parameters + ---------- + step : int + Current depletion step including restarts + + """ + openmc.lib.statepoint_write( + f"openmc_simulation_n{step}.h5", + write_source=False) + + def finalize(self): + """Finalize a depletion simulation and release resources.""" + if self.cleanup_when_done: + openmc.lib.finalize() + + # The next few class variables and methods should be removed after one + # release cycle or so. For now, we will provide compatibility to + # accessing CoupledOperator.settings and CoupledOperator.geometry. In + # the future these should stay on the Model class. + + var_warning_msg = "The CoupledOperator.{0} variable should be \ +accessed through CoupledOperator.model.{0}." + geometry_warning_msg = var_warning_msg.format("geometry") + settings_warning_msg = var_warning_msg.format("settings") + + @property + def settings(self): + warn(self.settings_warning_msg, FutureWarning) + return self.model.settings + + @settings.setter + def settings(self, new_settings): + warn(self.settings_warning_msg, FutureWarning) + self.model.settings = new_settings + + @property + def geometry(self): + warn(self.geometry_warning_msg, FutureWarning) + return self.model.geometry + + @geometry.setter + def geometry(self, new_geometry): + warn(self.geometry_warning_msg, FutureWarning) + self.model.geometry = new_geometry + + +# Retain deprecated name for the time being +def Operator(*args, **kwargs): + # warn of name change + warn( + "The Operator(...) class has been renamed and will " + "be removed in a future version of OpenMC. Use " + "CoupledOperator(...) instead.", + FutureWarning + ) + return CoupledOperator(*args, **kwargs) diff --git a/openmc/deplete/cram.py b/openmc/deplete/cram.py index f5f80dfdc4..53de83bb68 100644 --- a/openmc/deplete/cram.py +++ b/openmc/deplete/cram.py @@ -75,9 +75,9 @@ class IPFCramSolver(DepSystemSolver): Final compositions after ``dt`` """ - A = sp.csr_matrix(A * dt, dtype=np.float64) + A = dt * sp.csc_matrix(A, dtype=np.float64) y = n0.copy() - ident = sp.eye(A.shape[0]) + ident = sp.eye(A.shape[0], format='csc') for alpha, theta in zip(self.alpha, self.theta): y += 2*np.real(alpha*sla.spsolve(A - theta*ident, y)) return y * self.alpha0 diff --git a/openmc/deplete/d1s.py b/openmc/deplete/d1s.py new file mode 100644 index 0000000000..f51dea4160 --- /dev/null +++ b/openmc/deplete/d1s.py @@ -0,0 +1,250 @@ +"""D1S module + +This module contains functionality to support the direct 1-step (D1S) method for +shutdown dose rate calculations. + +""" + +from copy import deepcopy +from typing import Sequence +from math import log, prod + +import numpy as np + +import openmc +from openmc.data import half_life +from .abc import _normalize_timesteps +from .chain import Chain, _get_chain +from ..checkvalue import PathLike + + +def get_radionuclides(model: openmc.Model, chain_file: PathLike | Chain | None = None) -> list[str]: + """Determine all radionuclides that can be produced during D1S. + + Parameters + ---------- + model : openmc.Model + Model that should be used for determining what nuclides are present + chain_file : PathLike | Chain + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Used for inspecting decay data. Defaults to ``openmc.config['chain_file']`` + + Returns + ------- + List of nuclide names + + """ + + # Determine what nuclides appear in the model + model_nuclides = {nuc for mat in model._materials_by_id.values() + for nuc in mat.get_nuclides()} + + # Load chain file + chain = _get_chain(chain_file) + + radionuclides = set() + for nuclide in chain.nuclides: + # Restrict to set of nuclides present in model + if nuclide.name not in model_nuclides: + continue + + # Loop over reactions and add any targets that are unstable + for rx_tuple in nuclide.reactions: + target = rx_tuple.target + if target is None: + continue + target_nuclide = chain[target] + if target_nuclide.half_life is not None: + radionuclides.add(target_nuclide.name) + + return list(radionuclides) + + +def time_correction_factors( + nuclides: list[str], + timesteps: Sequence[float] | Sequence[tuple[float, str]], + source_rates: float | Sequence[float], + timestep_units: str = 's' +) -> dict[str, np.ndarray]: + """Calculate time correction factors for the D1S method. + + This function determines the time correction factor that should be applied + to photon tallies as part of the D1S method. + + Parameters + ---------- + nuclides : list of str + The name of the nuclide to find the time correction for, e.g., 'Ni65' + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing a sequence of (value, unit) tuples. + source_rates : float or iterable of float + Source rate in [neutron/sec] for each interval in `timesteps` + timestep_units : {'s', 'min', 'h', 'd', 'a'}, optional + Units for values specified in the `timesteps` argument. 's' means + seconds, 'min' means minutes, 'h' means hours, and 'a' means Julian + years. + + Returns + ------- + dict + Dictionary mapping nuclide to an array of time correction factors for + each time. + + """ + + # Determine normalized timesteps and source rates + timesteps, source_rates = _normalize_timesteps( + timesteps, source_rates, timestep_units) + + # Calculate decay rate for each nuclide + decay_rate = np.array([log(2.0) / half_life(x) for x in nuclides]) + + n_timesteps = len(timesteps) + 1 + n_nuclides = len(nuclides) + + # Create a 2D array for the time correction factors + h = np.zeros((n_timesteps, n_nuclides)) + + # Precompute all exponential terms with same shape as h + decay_dt = decay_rate[np.newaxis, :] * timesteps[:, np.newaxis] + g = np.exp(-decay_dt) + one_minus_g = -np.expm1(-decay_dt) + + # Apply recurrence relation step by step + for i in range(len(timesteps)): + # Eq. (4) in doi:10.1016/j.fusengdes.2019.111399 + h[i + 1] = source_rates[i] * one_minus_g[i] + h[i] * g[i] + + return {nuclides[i]: h[:, i] for i in range(n_nuclides)} + + +def apply_time_correction( + tally: openmc.Tally, + time_correction_factors: dict[str, np.ndarray], + index: int = -1, + sum_nuclides: bool = True +) -> openmc.Tally: + """Apply time correction factors to a tally. + + This function applies the time correction factors at the given index to a + tally that contains a :class:`~openmc.ParentNuclideFilter`. When + `sum_nuclides` is True, values over all parent nuclides will be summed, + leaving a single value for each filter combination. + + Parameters + ---------- + tally : openmc.Tally + Tally to apply the time correction factors to + time_correction_factors : dict + Time correction factors as returned by :func:`time_correction_factors` + index : int, optional + Index of the time of interest. If N timesteps are provided in + :func:`time_correction_factors`, there are N + 1 times to select from. + The default is -1 which corresponds to the final time. + sum_nuclides : bool + Whether to sum over the parent nuclides + + Returns + ------- + openmc.Tally + Derived tally with time correction factors applied + + """ + # Make sure the tally contains a ParentNuclideFilter + for i_filter, filter in enumerate(tally.filters): + if isinstance(filter, openmc.ParentNuclideFilter): + break + else: + raise ValueError('Tally must contain a ParentNuclideFilter') + + # Get list of radionuclides based on tally filter + radionuclides = [str(x) for x in tally.filters[i_filter].bins] + tcf = np.array([time_correction_factors[x][index] for x in radionuclides]) + + # Create copy of tally + new_tally = deepcopy(tally) + + # Determine number of bins in other filters + n_bins_before = prod([f.num_bins for f in tally.filters[:i_filter]]) + n_bins_after = prod([f.num_bins for f in tally.filters[i_filter + 1:]]) + + # Reshape sum and sum_sq, apply TCF, and sum along that axis + _, n_nuclides, n_scores = new_tally.shape + n_radionuclides = len(radionuclides) + shape = (n_bins_before, n_radionuclides, n_bins_after, n_nuclides, n_scores) + tally_sum = new_tally.sum.reshape(shape) + tally_sum_sq = new_tally.sum_sq.reshape(shape) + + # Apply TCF, broadcasting to the correct dimensions + tcf.shape = (1, -1, 1, 1, 1) + new_tally._sum = tally_sum * tcf + new_tally._sum_sq = tally_sum_sq * (tcf*tcf) + new_tally._mean = None + new_tally._std_dev = None + + shape = (-1, n_nuclides, n_scores) + + if sum_nuclides: + # Query the mean and standard deviation + mean = new_tally.mean + std_dev = new_tally.std_dev + + # Sum over parent nuclides (note that when combining different bins for + # parent nuclide, we can't work directly on sum_sq) + new_tally._mean = mean.sum(axis=1).reshape(shape) + new_tally._std_dev = np.linalg.norm(std_dev, axis=1).reshape(shape) + new_tally._derived = True + + # Remove ParentNuclideFilter + new_tally.filters.pop(i_filter) + else: + new_tally._sum.shape = shape + new_tally._sum_sq.shape = shape + + return new_tally + + +def prepare_tallies( + model: openmc.Model, + nuclides: list[str] | None = None, + chain_file: str | None = None +) -> list[str]: + """Prepare tallies for the D1S method. + + This function adds a :class:`~openmc.ParentNuclideFilter` to any tally that + has a particle filter with a single 'photon' bin. + + Parameters + ---------- + model : openmc.Model + Model to prepare tallies for + nuclides : list of str, optional + Nuclides to use for the parent nuclide filter. If None, radionuclides + are determined from :func:`get_radionuclides`. + chain_file : str, optional + Chain file to use for inspecting decay data. If None, defaults to + ``openmc.config['chain_file']`` + + Returns + ------- + list of str + List of parent nuclides being filtered on + + """ + if nuclides is None: + nuclides = get_radionuclides(model, chain_file) + filter = openmc.ParentNuclideFilter(nuclides) + + # Apply parent nuclide filter to any tally that has a particle filter with a + # single 'photon' bin + for tally in model.tallies: + for f in tally.filters: + if isinstance(f, openmc.ParticleFilter): + if list(f.bins) == ['photon']: + if not tally.contains_filter(openmc.ParentNuclideFilter): + tally.filters.append(filter) + break + return nuclides diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py index f223829233..f24b89868b 100644 --- a/openmc/deplete/helpers.py +++ b/openmc/deplete/helpers.py @@ -1,7 +1,8 @@ """ -Class for normalizing fission energy deposition +Classes for collecting and calculating quantities for reaction rate operators """ import bisect +from abc import abstractmethod from collections import defaultdict from copy import deepcopy from itertools import product @@ -14,17 +15,111 @@ from openmc.mpi import comm from openmc.checkvalue import check_type, check_greater_than from openmc.data import JOULE_PER_EV, REACTION_MT from openmc.lib import ( - Tally, MaterialFilter, EnergyFilter, EnergyFunctionFilter) + Tally, MaterialFilter, EnergyFilter, EnergyFunctionFilter, load_nuclide) import openmc.lib from .abc import ( - ReactionRateHelper, NormalizationHelper, FissionYieldHelper, - TalliedFissionYieldHelper) + ReactionRateHelper, NormalizationHelper, FissionYieldHelper) __all__ = ( "DirectReactionRateHelper", "ChainFissionHelper", "EnergyScoreHelper" - "SourceRateHelper", "ConstantFissionYieldHelper", "FissionYieldCutoffHelper", + "SourceRateHelper", "TalliedFissionYieldHelper", + "ConstantFissionYieldHelper", "FissionYieldCutoffHelper", "AveragedFissionYieldHelper", "FluxCollapseHelper") + +class TalliedFissionYieldHelper(FissionYieldHelper): + """Abstract class for computing fission yields with tallies + + Generates a basic fission rate tally in all burnable materials with + :meth:`generate_tallies`, and set nuclides to be tallied with + :meth:`update_tally_nuclides`. Subclasses will need to implement + :meth:`unpack` and :meth:`weighted_yields`. + + Parameters + ---------- + chain_nuclides : iterable of openmc.deplete.Nuclide + Nuclides tracked in the depletion chain. Not necessary + that all have yield data. + + Attributes + ---------- + constant_yields : dict of str to :class:`openmc.deplete.FissionYield` + Fission yields for all nuclides that only have one set of + fission yield data. Can be accessed as ``{parent: {product: yield}}`` + results : None or numpy.ndarray + Tally results shaped in a manner useful to this helper. + """ + + _upper_energy = 20.0e6 # upper energy for tallies + + def __init__(self, chain_nuclides): + super().__init__(chain_nuclides) + self._local_indexes = None + self._fission_rate_tally = None + self._tally_nucs = [] + self.results = None + + def generate_tallies(self, materials, mat_indexes): + """Construct the fission rate tally + + Parameters + ---------- + materials : iterable of :class:`openmc.lib.Material` + Materials to be used in :class:`openmc.lib.MaterialFilter` + mat_indexes : iterable of int + Indices of tallied materials that will have their fission + yields computed by this helper. Necessary as the + :class:`openmc.deplete.CoupledOperator` that uses this helper + may only burn a subset of all materials when running + in parallel mode. + """ + self._local_indexes = asarray(mat_indexes) + + # Tally group-wise fission reaction rates + self._fission_rate_tally = Tally() + self._fission_rate_tally.writable = False + self._fission_rate_tally.scores = ['fission'] + self._fission_rate_tally.filters = [MaterialFilter(materials)] + + def update_tally_nuclides(self, nuclides): + """Tally nuclides with non-zero density and multiple yields + + Must be run after :meth:`generate_tallies`. + + Parameters + ---------- + nuclides : iterable of str + Potential nuclides to be tallied, such as those with + non-zero density at this stage. + + Returns + ------- + nuclides : list of str + Union of input nuclides and those that have multiple sets + of yield data. Sorted by nuclide name + + Raises + ------ + AttributeError + If tallies not generated + """ + assert self._fission_rate_tally is not None, ( + "Run generate_tallies first") + overlap = set(self._chain_nuclides).intersection(set(nuclides)) + nuclides = sorted(overlap) + self._tally_nucs = [self._chain_nuclides[n] for n in nuclides] + self._fission_rate_tally.nuclides = nuclides + return nuclides + + @abstractmethod + def unpack(self): + """Unpack tallies after a transport run. + + Abstract because each subclass will need to arrange its + tally data. + """ + + # ------------------------------------- # Helpers for generating reaction rates # ------------------------------------- @@ -39,9 +134,11 @@ class DirectReactionRateHelper(ReactionRateHelper): Parameters ---------- n_nucs : int - Number of burnable nuclides tracked by :class:`openmc.deplete.Operator` + Number of burnable nuclides tracked by + :class:`openmc.deplete.CoupledOperator` n_react : int - Number of reactions tracked by :class:`openmc.deplete.Operator` + Number of reactions tracked by an instance of + :class:`openmc.deplete.CoupledOperator` Attributes ---------- @@ -63,34 +160,53 @@ class DirectReactionRateHelper(ReactionRateHelper): def generate_tallies(self, materials, scores): """Produce one-group reaction rate tally - Uses the :mod:`openmc.lib` to generate a tally - of relevant reactions across all burnable materials. + Uses the :mod:`openmc.lib` to generate a tally of relevant reactions + across all burnable materials. Parameters ---------- - materials : iterable of :class:`openmc.Material` - Burnable materials in the problem. Used to - construct a :class:`openmc.MaterialFilter` + materials : iterable of :class:`openmc.lib.Material` + Burnable materials in the problem. Used to construct a + :class:`openmc.lib.MaterialFilter` scores : iterable of str - Reaction identifiers, e.g. ``"(n, fission)"``, - ``"(n, gamma)"``, needed for the reaction rate tally. + Reaction identifiers, e.g. ``"(n, fission)"``, ``"(n, gamma)"``, + needed for the reaction rate tally. """ self._rate_tally = Tally() self._rate_tally.writable = False self._rate_tally.scores = scores self._rate_tally.filters = [MaterialFilter(materials)] + self._rate_tally.multiply_density = False + self._rate_tally_means_cache = None - def get_material_rates(self, mat_id, nuc_index, react_index): + @property + def rate_tally_means(self): + """The mean results of the tally of every material's reaction rates for this cycle + """ + # If the mean cache is empty, fill it once with this transport cycle's results + if self._rate_tally_means_cache is None: + self._rate_tally_means_cache = self._rate_tally.mean + return self._rate_tally_means_cache + + def reset_tally_means(self): + """Reset the cached mean rate tallies. + .. note:: + + This step must be performed after each transport cycle + """ + self._rate_tally_means_cache = None + + def get_material_rates(self, mat_index, nuc_index, rx_index): """Return an array of reaction rates for a material Parameters ---------- - mat_id : int - Unique ID for the requested material + mat_index : int + Index for the material nuc_index : iterable of int Index for each nuclide in :attr:`nuclides` in the desired reaction rate matrix - react_index : iterable of int + rx_index : iterable of int Index for each reaction scored in the tally Returns @@ -100,10 +216,9 @@ class DirectReactionRateHelper(ReactionRateHelper): reaction rates in this material """ self._results_cache.fill(0.0) - full_tally_res = self._rate_tally.mean[mat_id] - for i_tally, (i_nuc, i_react) in enumerate( - product(nuc_index, react_index)): - self._results_cache[i_nuc, i_react] = full_tally_res[i_tally] + full_tally_res = self.rate_tally_means[mat_index] + for i_tally, (i_nuc, i_rx) in enumerate(product(nuc_index, rx_index)): + self._results_cache[i_nuc, i_rx] = full_tally_res[i_tally] return self._results_cache @@ -123,9 +238,10 @@ class FluxCollapseHelper(ReactionRateHelper): Parameters ---------- n_nucs : int - Number of burnable nuclides tracked by :class:`openmc.deplete.Operator` + Number of burnable nuclides tracked by + :class:`openmc.deplete.CoupledOperator` n_react : int - Number of reactions tracked by :class:`openmc.deplete.Operator` + Number of reactions tracked by :class:`openmc.deplete.CoupledOperator` energies : iterable of float Energy group boundaries for flux spectrum in [eV] reactions : iterable of str @@ -152,6 +268,11 @@ class FluxCollapseHelper(ReactionRateHelper): if self._reactions_direct and self._nuclides_direct is None: self._rate_tally.nuclides = nuclides + # Make sure nuclide data is loaded + for nuclide in self.nuclides: + if nuclide not in openmc.lib.nuclides: + openmc.lib.load_nuclide(nuclide) + def generate_tallies(self, materials, scores): """Produce multigroup flux spectrum tally @@ -181,6 +302,7 @@ class FluxCollapseHelper(ReactionRateHelper): EnergyFilter(self._energies) ] self._flux_tally.scores = ['flux'] + self._flux_tally_means_cache = None # Create reaction rate tally if self._reactions_direct: @@ -188,9 +310,43 @@ class FluxCollapseHelper(ReactionRateHelper): self._rate_tally.writable = False self._rate_tally.scores = self._reactions_direct self._rate_tally.filters = [MaterialFilter(materials)] + self._rate_tally.multiply_density = False + self._rate_tally_means_cache = None if self._nuclides_direct is not None: + # check if any direct tally nuclides are requested that are not + # already loaded with the materials. Load separately if so. + mat_nuclides = {n for mat in materials for n in mat.nuclides} + extra_nuclides = set(self._nuclides_direct) - mat_nuclides + for nuc in extra_nuclides: + load_nuclide(nuc) self._rate_tally.nuclides = self._nuclides_direct + @property + def rate_tally_means(self): + """The mean results of the tally of every material's reaction rates for this cycle + """ + # If the mean cache is empty, fill it once with this transport cycle's results + if self._rate_tally_means_cache is None: + self._rate_tally_means_cache = self._rate_tally.mean + return self._rate_tally_means_cache + + @property + def flux_tally_means(self): + # If the mean cache is empty, fill it once for this transport cycle's results + if self._flux_tally_means_cache is None: + self._flux_tally_means_cache = self._flux_tally.mean + return self._flux_tally_means_cache + + def reset_tally_means(self): + """Reset the cached mean rate and flux tallies. + .. note:: + + This step must be performed after each transport cycle + """ + self._flux_tally_means_cache = None + if self._reactions_direct: + self._rate_tally_means_cache = None + def get_material_rates(self, mat_index, nuc_index, react_index): """Return an array of reaction rates for a material @@ -215,24 +371,18 @@ class FluxCollapseHelper(ReactionRateHelper): # Get flux for specified material shape = (len(self._materials), len(self._energies) - 1) - mean_value = self._flux_tally.mean.reshape(shape) + mean_value = self.flux_tally_means.reshape(shape) flux = mean_value[mat_index] # Get direct reaction rates if self._reactions_direct: nuclides_direct = self._rate_tally.nuclides shape = (len(nuclides_direct), len(self._reactions_direct)) - rx_rates = self._rate_tally.mean[mat_index].reshape(shape) + rx_rates = self.rate_tally_means[mat_index].reshape(shape) mat = self._materials[mat_index] - # Build nucname: density mapping to enable O(1) lookup in loop below - densities = dict(zip(mat.nuclides, mat.densities)) - for name, i_nuc in zip(self.nuclides, nuc_index): - # Determine density of nuclide - density = densities[name] - for mt, score, i_rx in zip(self._mts, self._scores, react_index): if score in self._reactions_direct and name in nuclides_direct: # Determine index in rx_rates @@ -247,8 +397,7 @@ class FluxCollapseHelper(ReactionRateHelper): rate_per_nuc = nuc.collapse_rate( mt, mat.temperature, self._energies, flux) - # Multiply by density to get absolute reaction rate - self._results_cache[i_nuc, i_rx] = rate_per_nuc * density + self._results_cache[i_nuc, i_rx] = rate_per_nuc return self._results_cache @@ -291,7 +440,7 @@ class ChainFissionHelper(EnergyNormalizationHelper): ---------- nuclides : list of str All nuclides with desired reaction rates. Ordered to be - consistent with :class:`openmc.deplete.Operator` + consistent with :class:`openmc.deplete.CoupledOperator` energy : float Total energy [J/s/source neutron] produced in a transport simulation. Updated in the material iteration with :meth:`update`. @@ -445,7 +594,6 @@ class ConstantFissionYieldHelper(FissionYieldHelper): self._constant_yields[name] = yield_data continue # Specific energy not found, use closest energy - distances = [abs(energy - ene) for ene in nuc.yield_energies] min_E = min(nuc.yield_energies, key=lambda e: abs(e - energy)) self._constant_yields[name] = nuc.yield_data[min_E] @@ -458,7 +606,7 @@ class ConstantFissionYieldHelper(FissionYieldHelper): Parameters ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.abc.TransportOperator operator with a depletion chain kwargs: Additional keyword arguments to be used in construction @@ -516,7 +664,6 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): Default: 0.0253 [eV] fast_energy : float, optional Energy of yield data corresponding to fast yields. - Default: 500 [kev] Attributes ---------- @@ -538,10 +685,10 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): Array of fission rate fractions with shape ``(n_mats, 2, n_nucs)``. ``results[:, 0]`` corresponds to the fraction of all fissions - that occured below ``cutoff``. The number + that occurred below ``cutoff``. The number of materials in the first axis corresponds to the number of materials burned by the - :class:`openmc.deplete.Operator` + :class:`openmc.deplete.CoupledOperator` """ def __init__(self, chain_nuclides, n_bmats, cutoff=112.0, @@ -598,7 +745,7 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): Parameters ---------- - operator : openmc.deplete.Operator + operator : openmc.deplete.CoupledOperator Operator with a chain and burnable materials kwargs: Additional keyword arguments to be used in construction @@ -624,7 +771,7 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the - :class:`openmc.deplete.Operator` that uses this helper + :class:`openmc.deplete.CoupledOperator` that uses this helper may only burn a subset of all materials when running in parallel mode. """ @@ -694,7 +841,7 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): class AveragedFissionYieldHelper(TalliedFissionYieldHelper): r"""Class that computes fission yields based on average fission energy - Computes average energy at which fission events occured with + Computes average energy at which fission events occurred with .. math:: @@ -749,7 +896,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the - :class:`openmc.deplete.Operator` that uses this helper + :class:`openmc.deplete.CoupledOperator` that uses this helper may only burn a subset of all materials when running in parallel mode. """ @@ -812,7 +959,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): Use the computed average energy of fission events to determine fission yields. If average energy is between two sets of yields, linearly - interpolate bewteen the two. + interpolate between the two. Otherwise take the closet set of yields. Parameters @@ -862,7 +1009,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): Parameters ---------- - operator : openmc.deplete.TransportOperator + operator : openmc.deplete.CoupledOperator Operator with a depletion chain kwargs : Additional keyword arguments to be used in construction diff --git a/openmc/deplete/independent_operator.py b/openmc/deplete/independent_operator.py new file mode 100644 index 0000000000..c192907cf2 --- /dev/null +++ b/openmc/deplete/independent_operator.py @@ -0,0 +1,451 @@ +"""Transport-independent transport operator for depletion. + +This module implements a transport operator that runs independently of any +transport solver by using user-provided multigroup fluxes and cross sections. + +""" + +from __future__ import annotations +from collections.abc import Iterable +import copy + +import numpy as np +from uncertainties import ufloat + +import openmc +from openmc.checkvalue import check_type +from openmc.mpi import comm +from .abc import ReactionRateHelper, OperatorResult +from .openmc_operator import OpenMCOperator +from .pool import _distribute +from .microxs import MicroXS +from .results import Results +from .helpers import ChainFissionHelper, ConstantFissionYieldHelper, SourceRateHelper + + +class IndependentOperator(OpenMCOperator): + """Transport-independent transport operator based on multigroup data. + + Instances of this class can be used to perform depletion using multigroup + cross sections and multigroup fluxes. Normally, a user needn't call methods + of this class directly. Instead, an instance of this class is passed to an + integrator class, such as :class:`openmc.deplete.CECMIntegrator`. + + Note that passing an empty :class:`~openmc.deplete.MicroXS` instance to the + ``micro_xs`` argument allows a decay-only calculation to be run. + + .. versionadded:: 0.13.1 + + .. versionchanged:: 0.14.0 + Arguments updated to include list of fluxes and microscopic cross + sections. + + Parameters + ---------- + materials : iterable of openmc.Material + Materials to deplete. + fluxes : list of numpy.ndarray + Flux in each group in [n-cm/src] for each domain + micros : list of MicroXS + Cross sections in [b] for each domain. If the + :class:`~openmc.deplete.MicroXS` object is empty, a decay-only + calculation will be run. + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Defaults to ``openmc.config['chain_file']``. + keff : 2-tuple of float, optional + keff eigenvalue and uncertainty from transport calculation. + prev_results : Results, optional + Results from a previous depletion calculation. + normalization_mode : {"fission-q", "source-rate"} + Indicate how reaction rates should be calculated. ``"fission-q"`` uses + the fission Q values from the depletion chain to compute the flux based + on the power. ``"source-rate"`` uses a the source rate (assumed to be + neutron flux) to calculate the reaction rates. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not given, + values will be pulled from the ``chain_file``. Only applicable if + ``"normalization_mode" == "fission-q"``. + reduce_chain_level : int, optional + Depth of the search when reducing the depletion chain. The default + value of ``None`` implies no limit on the depth. + fission_yield_opts : dict of str to option, optional + Optional arguments to pass to the + :class:`openmc.deplete.helpers.FissionYieldHelper` object. Will be + passed directly on to the helper. Passing a value of None will use the + defaults for the associated helper. + + Attributes + ---------- + materials : openmc.Materials + All materials present in the model + cross_sections : list of MicroXS + Object containing multigroup cross-sections in [b] for each material. + output_dir : pathlib.Path + Path to output directory to save results. + round_number : bool + Whether or not to round output to OpenMC to 8 digits. Useful in testing, + as OpenMC is incredibly sensitive to exact values. + number : openmc.deplete.AtomNumber + Total number of atoms in simulation. + nuclides_with_data : set of str + A set listing all unique nuclides available from cross_sections.xml. + chain : openmc.deplete.Chain + The depletion chain information necessary to form matrices and tallies. + reaction_rates : openmc.deplete.ReactionRates + Reaction rates from the last operator step. + burnable_mats : list of str + All burnable material IDs + heavy_metal : float + Initial heavy metal inventory [g] + local_mats : list of str + All burnable material IDs being managed by a single process + prev_res : Results or None + Results from a previous depletion calculation. ``None`` if no results + are to be used. + + """ + + def __init__(self, + materials, + fluxes, + micros, + chain_file=None, + keff=None, + normalization_mode='fission-q', + fission_q=None, + prev_results=None, + reduce_chain_level=None, + fission_yield_opts=None): + # Validate micro-xs parameters + check_type('materials', materials, Iterable, openmc.Material) + check_type('micros', micros, Iterable, MicroXS) + materials = openmc.Materials(materials) + + if not (len(fluxes) == len(micros) == len(materials)): + msg = (f'The length of fluxes ({len(fluxes)}) should be equal to ' + f'the length of micros ({len(micros)}) and the length of ' + f'materials ({len(materials)}).') + raise ValueError(msg) + + if keff is not None: + check_type('keff', keff, tuple, float) + keff = ufloat(*keff) + + self._keff = keff + + if fission_yield_opts is None: + fission_yield_opts = {} + helper_kwargs = {'normalization_mode': normalization_mode, + 'fission_yield_opts': fission_yield_opts} + + # Sort fluxes and micros in same order that materials get sorted + index_sort = np.argsort([mat.id for mat in materials]) + fluxes = [fluxes[i] for i in index_sort] + micros = [micros[i] for i in index_sort] + + self.fluxes = fluxes + super().__init__( + materials=materials, + cross_sections=micros, + chain_file=chain_file, + prev_results=prev_results, + fission_q=fission_q, + helper_kwargs=helper_kwargs, + reduce_chain_level=reduce_chain_level) + + @classmethod + def from_nuclides(cls, volume, nuclides, + flux, + micro_xs, + chain_file=None, + nuc_units='atom/b-cm', + keff=None, + normalization_mode='fission-q', + fission_q=None, + prev_results=None, + reduce_chain_level=None, + fission_yield_opts=None): + """ + Alternate constructor from a dictionary of nuclide concentrations + + volume : float + Volume of the material being depleted in [cm^3] + nuclides : dict of str to float + Dictionary with nuclide names as keys and nuclide concentrations as + values. + flux : numpy.ndarray + Flux in each group in [n-cm/src] + micro_xs : MicroXS + Cross sections in [b]. If the :class:`~openmc.deplete.MicroXS` + object is empty, a decay-only calculation will be run. + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or instance of + openmc.deplete.Chain. Defaults to ``openmc.config['chain_file']``. + nuc_units : {'atom/cm3', 'atom/b-cm'}, optional + Units for nuclide concentration. + keff : 2-tuple of float, optional + keff eigenvalue and uncertainty from transport calculation. + Default is None. + normalization_mode : {"fission-q", "source-rate"} + Indicate how reaction rates should be calculated. + ``"fission-q"`` uses the fission Q values from the depletion + chain to compute the flux based on the power. ``"source-rate"`` uses + the source rate (assumed to be neutron flux) to calculate the + reaction rates. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. If not + given, values will be pulled from the ``chain_file``. Only + applicable if ``"normalization_mode" == "fission-q"``. + prev_results : Results, optional + Results from a previous depletion calculation. + reduce_chain_level : int, optional + Depth of the search when reducing the depletion chain. The default + value of ``None`` implies no limit on the depth. + fission_yield_opts : dict of str to option, optional + Optional arguments to pass to the + :class:`openmc.deplete.helpers.FissionYieldHelper` class. Will be + passed directly on to the helper. Passing a value of None will use + the defaults for the associated helper. + + """ + check_type('nuclides', nuclides, dict, str) + materials = cls._consolidate_nuclides_to_material(nuclides, nuc_units, volume) + fluxes = [flux] + micros = [micro_xs] + return cls(materials, + fluxes, + micros, + chain_file, + keff=keff, + normalization_mode=normalization_mode, + fission_q=fission_q, + prev_results=prev_results, + reduce_chain_level=reduce_chain_level, + fission_yield_opts=fission_yield_opts) + + @staticmethod + def _consolidate_nuclides_to_material(nuclides, nuc_units, volume): + """Puts nuclide list into an openmc.Materials object. + + """ + mat = openmc.Material() + if nuc_units == 'atom/b-cm': + for nuc, conc in nuclides.items(): + mat.add_nuclide(nuc, conc) + elif nuc_units == 'atom/cm3': + for nuc, conc in nuclides.items(): + mat.add_nuclide(nuc, conc * 1e-24) # convert to at/b-cm + else: + raise ValueError(f"Unit '{nuc_units}' is invalid.") + + mat.volume = volume + mat.depletable = True + + return openmc.Materials([mat]) + + def _load_previous_results(self): + """Load results from a previous depletion simulation""" + # Reload volumes into geometry + model = openmc.Model(materials=self.materials) + self.prev_res[-1].transfer_volumes(model) + self.materials = model.materials + + # Store previous results in operator + # Distribute reaction rates according to those tracked + # on this process + if comm.size != 1: + prev_results = self.prev_res + self.prev_res = Results() + mat_indexes = _distribute(range(len(self.burnable_mats))) + for res_obj in prev_results: + new_res = res_obj.distribute(self.local_mats, mat_indexes) + self.prev_res.append(new_res) + + def _get_nuclides_with_data(self, cross_sections: list[MicroXS]) -> set[str]: + """Finds nuclides with cross section data + + Parameters + ---------- + cross_sections : iterable of :class`~openmc.deplete.MicroXS` + List of multigroup cross-section data. + + Returns + ------- + nuclides : set of str + Set of nuclide names that have cross section data + + """ + return set(cross_sections[0].nuclides) + + class _IndependentRateHelper(ReactionRateHelper): + """Class for generating reaction rates with multigroup fluxes and + multigroup cross sections. + + This class does not generate tallies and instead stores cross sections + for each nuclide and transmutation reaction relevant for a depletion + calculation. The reaction rate is calculated by multiplying the flux by + the cross sections. + + Parameters + ---------- + op : openmc.deplete.IndependentOperator + Reference to the object encapsulate _IndependentRateHelper. + We pass this so we don't have to duplicate the + :attr:`IndependentOperator.number` object. + + Attributes + ---------- + nuc_ind_map : dict of int to str + Dictionary mapping the nuclide index to nuclide name + rx_ind_map : dict of int to str + Dictionary mapping reaction index to reaction name + + """ + + def __init__(self, op: IndependentOperator): + rates = op.reaction_rates + super().__init__(rates.n_nuc, rates.n_react) + + self.nuc_ind_map = {ind: nuc for nuc, ind in rates.index_nuc.items()} + self.rx_ind_map = {ind: rxn for rxn, ind in rates.index_rx.items()} + self._op = op + + def generate_tallies(self, materials, scores): + """Unused in this case""" + pass + + def reset_tally_means(self): + """Unused in this case""" + pass + + def get_material_rates(self, mat_index, nuc_index, react_index): + """Return 2D array of [nuclide, reaction] reaction rates + + Parameters + ---------- + mat_index : int + Index for the material + nuc_index : list of str + Ordering of desired nuclides + react_index : list of str + Ordering of reactions + """ + self._results_cache.fill(0.0) + + # Get flux and microscopic cross sections from operator + flux = self._op.fluxes[mat_index] + xs = self._op.cross_sections[mat_index] + + for i_nuc in nuc_index: + nuc = self.nuc_ind_map[i_nuc] + for i_rx in react_index: + rx = self.rx_ind_map[i_rx] + + # Determine reaction rate by multiplying xs in [b] by flux + # in [n-cm/src] to give [(reactions/src)*b-cm/atom] + self._results_cache[i_nuc, i_rx] = (xs[nuc, rx] * flux).sum() + + return self._results_cache + + def _get_helper_classes(self, helper_kwargs): + """Get helper classes for calculating reaction rates and fission yields + + Parameters + ---------- + helper_kwargs : dict + Keyword arguments for helper classes + + """ + + normalization_mode = helper_kwargs['normalization_mode'] + fission_yield_opts = helper_kwargs['fission_yield_opts'] + + self._rate_helper = self._IndependentRateHelper(self) + if normalization_mode == "fission-q": + self._normalization_helper = ChainFissionHelper() + else: + self._normalization_helper = SourceRateHelper() + + # Select and create fission yield helper + fission_helper = ConstantFissionYieldHelper + self._yield_helper = fission_helper.from_operator( + self, **fission_yield_opts) + + def initial_condition(self): + """Performs final setup and returns initial condition. + + Returns + ------- + list of numpy.ndarray + Total density for initial conditions. + """ + + # Return number density vector + return super().initial_condition(self.materials) + + def __call__(self, vec, source_rate): + """Obtain the reaction rates + + Parameters + ---------- + vec : list of numpy.ndarray + Total atoms to be used in function. + source_rate : float + Power in [W] or flux in [neutron/cm^2-s] + + Returns + ------- + openmc.deplete.OperatorResult + Eigenvalue and reaction rates resulting from transport operator + + """ + + self._update_materials_and_nuclides(vec) + + # If the source rate is zero, return zero reaction rates + if source_rate == 0.0: + rates = self.reaction_rates.copy() + rates.fill(0.0) + return OperatorResult(ufloat(0.0, 0.0), rates) + + rates = self._calculate_reaction_rates(source_rate) + keff = self._keff + + op_result = OperatorResult(keff, rates) + return copy.deepcopy(op_result) + + def _update_materials(self): + """Updates material compositions in OpenMC on all processes.""" + + for rank in range(comm.size): + number_i = comm.bcast(self.number, root=rank) + + for mat in number_i.materials: + nuclides = [] + densities = [] + for nuc in number_i.nuclides: + if nuc in self.nuclides_with_data: + val = 1.0e-24 * number_i.get_atom_density(mat, nuc) + + # If nuclide is zero, do not add to the problem. + if val > 0.0: + if self.round_number: + val_magnitude = np.floor(np.log10(val)) + val_scaled = val / 10**val_magnitude + val_round = round(val_scaled, 8) + + val = val_round * 10**val_magnitude + + nuclides.append(nuc) + densities.append(val) + else: + # Only output warnings if values are significantly + # negative. CRAM does not guarantee positive + # values. + if val < -1.0e-21: + print(f'WARNING: nuclide {nuc} in material' + f'{mat} is negative (density = {val}' + + ' atom/b-cm)') + number_i[mat, nuc] = 0.0 diff --git a/openmc/deplete/integrators.py b/openmc/deplete/integrators.py index 49d1d62d75..7c543a6cb8 100644 --- a/openmc/deplete/integrators.py +++ b/openmc/deplete/integrators.py @@ -22,39 +22,39 @@ class PredictorIntegrator(Integrator): .. math:: \mathbf{n}_{i+1} = \exp\left(h\mathbf{A}(\mathbf{n}_i) \right) \mathbf{n}_i - """ _num_stages = 1 - def __call__(self, conc, rates, dt, source_rate, _i=None): + def __call__(self, n, rates, dt, source_rate, _i=None): """Perform the integration across one time step Parameters ---------- - conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float Time in [s] for the entire depletion interval source_rate : float Power in [W] or source rate in [neutron/sec] - _i : int or None - Iteration index. Not used + _i : int, optional + Current iteration count. Not used Returns ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at end of interval op_results : empty list - Kept for consistency with API. No intermediate calls to - operator with predictor + Kept for consistency with API. No intermediate calls to operator + with predictor """ - proc_time, conc_end = self._timed_deplete(conc, rates, dt) - return proc_time, [conc_end], [] + proc_time, n_end = self._timed_deplete(n, rates, dt, _i) + return proc_time, [n_end], [] @add_params @@ -77,13 +77,14 @@ class CECMIntegrator(Integrator): """ _num_stages = 2 - def __call__(self, conc, rates, dt, source_rate, _i=None): + def __call__(self, n, rates, dt, source_rate, _i=None): """Integrate using CE/CM Parameters ---------- - conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -97,21 +98,21 @@ class CECMIntegrator(Integrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult Eigenvalue and reaction rates from transport simulations """ - # deplete across first half of inteval - time0, x_middle = self._timed_deplete(conc, rates, dt / 2) - res_middle = self.operator(x_middle, source_rate) + # deplete across first half of interval + time0, n_middle = self._timed_deplete(n, rates, dt / 2, _i) + res_middle = self.operator(n_middle, source_rate) # deplete across entire interval with BOS concentrations, # MOS reaction rates - time1, x_end = self._timed_deplete(conc, res_middle.rates, dt) + time1, n_end = self._timed_deplete(n, res_middle.rates, dt, _i) - return time0 + time1, [x_middle, x_end], [res_middle] + return time0 + time1, [n_middle, n_end], [res_middle] @add_params @@ -124,29 +125,30 @@ class CF4Integrator(Integrator): .. math:: \begin{aligned} - \mathbf{A}_1 &= h\mathbf{A}(\mathbf{n}_0) \\ - \hat{\mathbf{n}}_1 &= \exp \left ( \frac{\mathbf{A}_1}{2} \right ) \\ + \mathbf{A}_1 &= h\mathbf{A}(\mathbf{n}_i) \\ + \hat{\mathbf{n}}_1 &= \exp \left ( \frac{\mathbf{A}_1}{2} \right ) \mathbf{n}_i \\ \mathbf{A}_2 &= h\mathbf{A}(\hat{\mathbf{n}}_1) \\ - \hat{\mathbf{n}}_2 &= \exp \left ( \frac{\mathbf{A}_2}{2} \right ) \\ + \hat{\mathbf{n}}_2 &= \exp \left ( \frac{\mathbf{A}_2}{2} \right ) \mathbf{n}_i \\ \mathbf{A}_3 &= h \mathbf{A}(\hat{\mathbf{n}}_2) \\ \hat{\mathbf{n}}_3 &= \exp \left ( -\frac{\mathbf{A}_1}{2} + \mathbf{A}_3 - \right ) \\ + \right ) \hat{\mathbf{n}}_1 \\ \mathbf{A}_4 &= h\mathbf{A}(\hat{\mathbf{n}}_3) \\ \mathbf{n}_{i+1} &= \exp \left ( \frac{\mathbf{A}_1}{4} + \frac{\mathbf{A}_2}{6} + \frac{\mathbf{A}_3}{6} - \frac{\mathbf{A}_4}{12} \right ) \exp \left ( -\frac{\mathbf{A}_1}{12} + \frac{\mathbf{A}_2}{6} + - \frac{\mathbf{A}_3}{6} - \frac{\mathbf{A}_4}{4} \right ) \mathbf{n}_i. + \frac{\mathbf{A}_3}{6} + \frac{\mathbf{A}_4}{4} \right ) \mathbf{n}_i. \end{aligned} """ _num_stages = 4 - def __call__(self, bos_conc, bos_rates, dt, source_rate, _i=None): + def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): """Perform the integration across one time step Parameters ---------- - bos_conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n_bos : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. bos_rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -160,7 +162,7 @@ class CF4Integrator(Integrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -168,30 +170,30 @@ class CF4Integrator(Integrator): simulations """ # Step 1: deplete with matrix 1/2*A(y0) - time1, conc_eos1 = self._timed_deplete( - bos_conc, bos_rates, dt, matrix_func=cf4_f1) - res1 = self.operator(conc_eos1, source_rate) + time1, n_eos1 = self._timed_deplete( + n_bos, bos_rates, dt, _i, matrix_func=cf4_f1) + res1 = self.operator(n_eos1, source_rate) # Step 2: deplete with matrix 1/2*A(y1) - time2, conc_eos2 = self._timed_deplete( - bos_conc, res1.rates, dt, matrix_func=cf4_f1) - res2 = self.operator(conc_eos2, source_rate) + time2, n_eos2 = self._timed_deplete( + n_bos, res1.rates, dt, _i, matrix_func=cf4_f1) + res2 = self.operator(n_eos2, source_rate) # Step 3: deplete with matrix -1/2*A(y0)+A(y2) list_rates = list(zip(bos_rates, res2.rates)) - time3, conc_eos3 = self._timed_deplete( - conc_eos1, list_rates, dt, matrix_func=cf4_f2) - res3 = self.operator(conc_eos3, source_rate) + time3, n_eos3 = self._timed_deplete( + n_eos1, list_rates, dt, _i, matrix_func=cf4_f2) + res3 = self.operator(n_eos3, source_rate) # Step 4: deplete with two matrix exponentials list_rates = list(zip(bos_rates, res1.rates, res2.rates, res3.rates)) - time4, conc_inter = self._timed_deplete( - bos_conc, list_rates, dt, matrix_func=cf4_f3) - time5, conc_eos5 = self._timed_deplete( - conc_inter, list_rates, dt, matrix_func=cf4_f4) + time4, n_inter = self._timed_deplete( + n_bos, list_rates, dt, _i, matrix_func=cf4_f3) + time5, n_eos5 = self._timed_deplete( + n_inter, list_rates, dt, _i, matrix_func=cf4_f4) return (time1 + time2 + time3 + time4 + time5, - [conc_eos1, conc_eos2, conc_eos3, conc_eos5], + [n_eos1, n_eos2, n_eos3, n_eos5], [res1, res2, res3]) @@ -207,7 +209,8 @@ class CELIIntegrator(Integrator): .. math:: \begin{aligned} - \mathbf{n}_{i+1}^p &= \exp \left ( h \mathbf{A}(\mathbf{n}_i ) \right ) \\ + \mathbf{n}_{i+1}^p &= \exp \left ( h \mathbf{A}(\mathbf{n}_i ) \right ) + \mathbf{n}_i \\ \mathbf{n}_{i+1} &= \exp \left( \frac{h}{12} \mathbf{A}(\mathbf{n}_i) + \frac{5h}{12} \mathbf{A}(\mathbf{n}_{i+1}^p) \right) \exp \left( \frac{5h}{12} \mathbf{A}(\mathbf{n}_i) + @@ -216,13 +219,14 @@ class CELIIntegrator(Integrator): """ _num_stages = 2 - def __call__(self, bos_conc, rates, dt, source_rate, _i=None): + def __call__(self, n_bos, rates, dt, source_rate, _i=None): """Perform the integration across one time step Parameters ---------- - bos_conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n_bos : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -236,7 +240,7 @@ class CELIIntegrator(Integrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -244,19 +248,19 @@ class CELIIntegrator(Integrator): simulation """ # deplete to end using BOS rates - proc_time, conc_ce = self._timed_deplete(bos_conc, rates, dt) - res_ce = self.operator(conc_ce, source_rate) + proc_time, n_ce = self._timed_deplete(n_bos, rates, dt, _i) + res_ce = self.operator(n_ce, source_rate) # deplete using two matrix exponentials list_rates = list(zip(rates, res_ce.rates)) - time_le1, conc_inter = self._timed_deplete( - bos_conc, list_rates, dt, matrix_func=celi_f1) + time_le1, n_inter = self._timed_deplete( + n_bos, list_rates, dt, _i, matrix_func=celi_f1) - time_le2, conc_end = self._timed_deplete( - conc_inter, list_rates, dt, matrix_func=celi_f2) + time_le2, n_end = self._timed_deplete( + n_inter, list_rates, dt, _i, matrix_func=celi_f2) - return proc_time + time_le1 + time_le1, [conc_ce, conc_end], [res_ce] + return proc_time + time_le1 + time_le1, [n_ce, n_end], [res_ce] @add_params @@ -268,12 +272,12 @@ class EPCRK4Integrator(Integrator): .. math:: \begin{aligned} - \mathbf{A}_1 &= h\mathbf{A}(\mathbf{n}_0) \\ - \hat{\mathbf{n}}_1 &= \exp \left ( \frac{\mathbf{A}_1}{2} \right ) \\ + \mathbf{A}_1 &= h\mathbf{A}(\mathbf{n}_i) \\ + \hat{\mathbf{n}}_1 &= \exp \left ( \frac{\mathbf{A}_1}{2} \right ) \mathbf{n}_i \\ \mathbf{A}_2 &= h\mathbf{A}(\hat{\mathbf{n}}_1) \\ - \hat{\mathbf{n}}_2 &= \exp \left ( \frac{\mathbf{A}_2}{2} \right ) \\ + \hat{\mathbf{n}}_2 &= \exp \left ( \frac{\mathbf{A}_2}{2} \right ) \mathbf{n}_i \\ \mathbf{A}_3 &= h \mathbf{A}(\hat{\mathbf{n}}_2) \\ - \hat{\mathbf{n}}_3 &= \exp \left ( \mathbf{A}_3 \right ) \\ + \hat{\mathbf{n}}_3 &= \exp \left ( \mathbf{A}_3 \right ) \mathbf{n}_i \\ \mathbf{A}_4 &= h\mathbf{A}(\hat{\mathbf{n}}_3) \\ \mathbf{n}_{i+1} &= \exp \left ( \frac{\mathbf{A}_1}{6} + \frac{\mathbf{A}_2}{3} + \frac{\mathbf{A}_3}{3} + \frac{\mathbf{A}_4}{6} \right ) \mathbf{n}_i. @@ -281,13 +285,14 @@ class EPCRK4Integrator(Integrator): """ _num_stages = 4 - def __call__(self, conc, rates, dt, source_rate, _i=None): + def __call__(self, n, rates, dt, source_rate, _i=None): """Perform the integration across one time step Parameters ---------- - conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] + n : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -301,7 +306,7 @@ class EPCRK4Integrator(Integrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -310,26 +315,22 @@ class EPCRK4Integrator(Integrator): """ # Step 1: deplete with matrix A(y0) / 2 - time1, conc1 = self._timed_deplete( - conc, rates, dt, matrix_func=rk4_f1) - res1 = self.operator(conc1, source_rate) + time1, n1 = self._timed_deplete(n, rates, dt, _i, matrix_func=rk4_f1) + res1 = self.operator(n1, source_rate) # Step 2: deplete with matrix A(y1) / 2 - time2, conc2 = self._timed_deplete( - conc, res1.rates, dt, matrix_func=rk4_f1) - res2 = self.operator(conc2, source_rate) + time2, n2 = self._timed_deplete(n, res1.rates, dt, _i, matrix_func=rk4_f1) + res2 = self.operator(n2, source_rate) # Step 3: deplete with matrix A(y2) - time3, conc3 = self._timed_deplete(conc, res2.rates, dt) - res3 = self.operator(conc3, source_rate) + time3, n3 = self._timed_deplete(n, res2.rates, dt, _i) + res3 = self.operator(n3, source_rate) # Step 4: deplete with matrix built from weighted rates list_rates = list(zip(rates, res1.rates, res2.rates, res3.rates)) - time4, conc4 = self._timed_deplete( - conc, list_rates, dt, matrix_func=rk4_f4) + time4, n4 = self._timed_deplete(n, list_rates, dt, _i, matrix_func=rk4_f4) - return (time1 + time2 + time3 + time4, [conc1, conc2, conc3, conc4], - [res1, res2, res3]) + return (time1 + time2 + time3 + time4, [n1, n2, n3, n4], [res1, res2, res3]) @add_params @@ -358,8 +359,7 @@ class LEQIIntegrator(Integrator): h_i)} \mathbf{A}_0 + \frac{h_{i-1}}{12 (h_{i-1} + h_i)} \mathbf{A}_1 \\ \mathbf{F}_4 &= \frac{-h_i^2}{12 h_{i-1} (h_{i-1} + h_i)} \mathbf{A}_{-1} + \frac{h_{i-1}^2 + 2 h_i h_{i-1} + h_i^2}{12 h_{i-1} (h_{i-1} + h_i)} - \mathbf{A}_0 + \frac{5 h_{i-1}^2 + 4 h_i h_{i-1}}{12 h_{i-1} - (h_{i-1} + h_i)} \mathbf{A}_1 \\ + \mathbf{A}_0 + \frac{5 h_{i-1} + 4 h_i}{12 (h_{i-1} + h_i)} \mathbf{A}_1 \\ \mathbf{n}_{i+1} &= \exp(h_i \mathbf{F}_4) \exp(h_i \mathbf{F}_3) \mathbf{n}_i \end{aligned} @@ -367,14 +367,15 @@ class LEQIIntegrator(Integrator): """ _num_stages = 2 - def __call__(self, bos_conc, bos_rates, dt, source_rate, i): + def __call__(self, n_bos, bos_rates, dt, source_rate, i): """Perform the integration across one time step Parameters ---------- - conc : numpy.ndarray - Initial concentrations for all nuclides in [atom] - rates : openmc.deplete.ReactionRates + n_bos : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. + bos_rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float Time in [s] for the entire depletion interval @@ -387,7 +388,7 @@ class LEQIIntegrator(Integrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -398,7 +399,7 @@ class LEQIIntegrator(Integrator): if self._i_res < 1: # need at least previous transport solution self._prev_rates = bos_rates return CELIIntegrator.__call__( - self, bos_conc, bos_rates, dt, source_rate, i) + self, n_bos, bos_rates, dt, source_rate, i) prev_res = self.operator.prev_res[-2] prev_dt = self.timesteps[i] - prev_res.time[0] self._prev_rates = prev_res.rates[0] @@ -406,32 +407,32 @@ class LEQIIntegrator(Integrator): prev_dt = self.timesteps[i - 1] # Remaining LE/QI - bos_res = self.operator(bos_conc, source_rate) + bos_res = self.operator(n_bos, source_rate) le_inputs = list(zip( self._prev_rates, bos_res.rates, repeat(prev_dt), repeat(dt))) - time1, conc_inter = self._timed_deplete( - bos_conc, le_inputs, dt, matrix_func=leqi_f1) - time2, conc_eos0 = self._timed_deplete( - conc_inter, le_inputs, dt, matrix_func=leqi_f2) + time1, n_inter = self._timed_deplete( + n_bos, le_inputs, dt, i, matrix_func=leqi_f1) + time2, n_eos0 = self._timed_deplete( + n_inter, le_inputs, dt, i, matrix_func=leqi_f2) - res_inter = self.operator(conc_eos0, source_rate) + res_inter = self.operator(n_eos0, source_rate) qi_inputs = list(zip( self._prev_rates, bos_res.rates, res_inter.rates, repeat(prev_dt), repeat(dt))) - time3, conc_inter = self._timed_deplete( - bos_conc, qi_inputs, dt, matrix_func=leqi_f3) - time4, conc_eos1 = self._timed_deplete( - conc_inter, qi_inputs, dt, matrix_func=leqi_f4) + time3, n_inter = self._timed_deplete( + n_bos, qi_inputs, dt, i, matrix_func=leqi_f3) + time4, n_eos1 = self._timed_deplete( + n_inter, qi_inputs, dt, i, matrix_func=leqi_f4) # store updated rates self._prev_rates = copy.deepcopy(bos_res.rates) return ( - time1 + time2 + time3 + time4, [conc_eos0, conc_eos1], + time1 + time2 + time3 + time4, [n_eos0, n_eos1], [bos_res, res_inter]) @@ -444,17 +445,18 @@ class SICELIIntegrator(SIIntegrator): `_. Detailed algorithm can be found in section 3.2 in `Colin Josey's thesis - `_. + `_. """ _num_stages = 2 - def __call__(self, bos_conc, bos_rates, dt, source_rate, _i=None): + def __call__(self, n_bos, bos_rates, dt, source_rate, _i=None): """Perform the integration across one time step Parameters ---------- - bos_conc : numpy.ndarray - Initial bos_concentrations for all nuclides in [atom] + n_bos : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. bos_rates : openmc.deplete.ReactionRates Reaction rates from operator dt : float @@ -468,19 +470,19 @@ class SICELIIntegrator(SIIntegrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - bos_conc_list : list of numpy.ndarray + n_bos_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with - the final bos_concentration as the last element + the final concentration as the last element op_results : list of openmc.deplete.OperatorResult Eigenvalue and reaction rates from intermediate transport simulations """ - proc_time, eos_conc = self._timed_deplete(bos_conc, bos_rates, dt) - inter_conc = copy.deepcopy(eos_conc) + proc_time, n_eos = self._timed_deplete(n_bos, bos_rates, dt, _i) + n_inter = copy.deepcopy(n_eos) # Begin iteration for j in range(self.n_steps + 1): - inter_res = self.operator(inter_conc, source_rate) + inter_res = self.operator(n_inter, source_rate) if j <= 1: res_bar = copy.deepcopy(inter_res) @@ -490,14 +492,14 @@ class SICELIIntegrator(SIIntegrator): res_bar = OperatorResult(k, rates) list_rates = list(zip(bos_rates, res_bar.rates)) - time1, inter_conc = self._timed_deplete( - bos_conc, list_rates, dt, matrix_func=celi_f1) - time2, inter_conc = self._timed_deplete( - inter_conc, list_rates, dt, matrix_func=celi_f2) + time1, n_inter = self._timed_deplete( + n_bos, list_rates, dt, _i, matrix_func=celi_f1) + time2, n_inter = self._timed_deplete( + n_inter, list_rates, dt, _i, matrix_func=celi_f2) proc_time += time1 + time2 # end iteration - return proc_time, [eos_conc, inter_conc], [res_bar] + return proc_time, [n_eos, n_inter], [res_bar] @add_params @@ -509,18 +511,18 @@ class SILEQIIntegrator(SIIntegrator): `_. Detailed algorithm can be found in Section 3.2 in `Colin Josey's thesis - `_. + `_. """ _num_stages = 2 - def __call__(self, bos_conc, bos_rates, dt, source_rate, i): + def __call__(self, n_bos, bos_rates, dt, source_rate, i): """Perform the integration across one time step Parameters ---------- - bos_conc : list of numpy.ndarray - Initial concentrations for all nuclides in [atom] for - all depletable materials + n_bos : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. bos_rates : list of openmc.deplete.ReactionRates Reaction rates from operator for all depletable materials dt : float @@ -534,7 +536,7 @@ class SILEQIIntegrator(SIIntegrator): ------- proc_time : float Time spent in CRAM routines for all materials in [s] - conc_list : list of numpy.ndarray + n_list : list of list of numpy.ndarray Concentrations at each of the intermediate points with the final concentration as the last element op_results : list of openmc.deplete.OperatorResult @@ -546,7 +548,7 @@ class SILEQIIntegrator(SIIntegrator): self._prev_rates = bos_rates # Perform CELI for initial steps return SICELIIntegrator.__call__( - self, bos_conc, bos_rates, dt, source_rate, i) + self, n_bos, bos_rates, dt, source_rate, i) prev_res = self.operator.prev_res[-2] prev_dt = self.timesteps[i] - prev_res.time[0] self._prev_rates = prev_res.rates[0] @@ -556,16 +558,16 @@ class SILEQIIntegrator(SIIntegrator): # Perform remaining LE/QI inputs = list(zip(self._prev_rates, bos_rates, repeat(prev_dt), repeat(dt))) - proc_time, inter_conc = self._timed_deplete( - bos_conc, inputs, dt, matrix_func=leqi_f1) - time1, eos_conc = self._timed_deplete( - inter_conc, inputs, dt, matrix_func=leqi_f2) + proc_time, n_inter = self._timed_deplete( + n_bos, inputs, dt, i, matrix_func=leqi_f1) + time1, n_eos = self._timed_deplete( + n_inter, inputs, dt, i, matrix_func=leqi_f2) proc_time += time1 - inter_conc = copy.deepcopy(eos_conc) + n_inter = copy.deepcopy(n_eos) for j in range(self.n_steps + 1): - inter_res = self.operator(inter_conc, source_rate) + inter_res = self.operator(n_inter, source_rate) if j <= 1: res_bar = copy.deepcopy(inter_res) @@ -576,13 +578,13 @@ class SILEQIIntegrator(SIIntegrator): inputs = list(zip(self._prev_rates, bos_rates, res_bar.rates, repeat(prev_dt), repeat(dt))) - time1, inter_conc = self._timed_deplete( - bos_conc, inputs, dt, matrix_func=leqi_f3) - time2, inter_conc = self._timed_deplete( - inter_conc, inputs, dt, matrix_func=leqi_f4) + time1, n_inter = self._timed_deplete( + n_bos, inputs, dt, i, matrix_func=leqi_f3) + time2, n_inter = self._timed_deplete( + n_inter, inputs, dt, i, matrix_func=leqi_f4) proc_time += time1 + time2 - return proc_time, [eos_conc, inter_conc], [res_bar] + return proc_time, [n_eos, n_inter], [res_bar] integrator_by_name = { diff --git a/openmc/deplete/microxs.py b/openmc/deplete/microxs.py new file mode 100644 index 0000000000..4ce199f0cc --- /dev/null +++ b/openmc/deplete/microxs.py @@ -0,0 +1,421 @@ +"""MicroXS module + +A class for storing microscopic cross section data that can be used with the +IndependentOperator class for depletion. +""" + +from __future__ import annotations +from collections.abc import Sequence +import shutil +from tempfile import TemporaryDirectory +from typing import Union, TypeAlias + +import pandas as pd +import numpy as np + +from openmc.checkvalue import check_type, check_value, check_iterable_type, PathLike +from openmc import StatePoint +from openmc.mgxs import GROUP_STRUCTURES +from openmc.data import REACTION_MT +import openmc +from .chain import Chain, REACTIONS, _get_chain +from .coupled_operator import _find_cross_sections, _get_nuclides_with_data +import openmc.lib +from openmc.mpi import comm + +_valid_rxns = list(REACTIONS) +_valid_rxns.append('fission') +_valid_rxns.append('damage-energy') + + +# TODO: Replace with type statement when support is Python 3.12+ +DomainTypes: TypeAlias = Union[ + Sequence[openmc.Material], + Sequence[openmc.Cell], + Sequence[openmc.Universe], + openmc.MeshBase, + openmc.Filter +] + + +def get_microxs_and_flux( + model: openmc.Model, + domains: DomainTypes, + nuclides: Sequence[str] | None = None, + reactions: Sequence[str] | None = None, + energies: Sequence[float] | str | None = None, + reaction_rate_mode: str = 'direct', + chain_file: PathLike | Chain | None = None, + path_statepoint: PathLike | None = None, + run_kwargs=None +) -> tuple[list[np.ndarray], list[MicroXS]]: + """Generate microscopic cross sections and fluxes for multiple domains. + + This function runs a neutron transport solve to obtain the flux and reaction + rates in the specified domains and computes multigroup microscopic cross + sections that can be used in depletion calculations with the + :class:`~openmc.deplete.IndependentOperator` class. + + .. versionadded:: 0.14.0 + + .. versionchanged:: 0.15.3 + Added `reaction_rate_mode` and `path_statepoint` arguments. + + Parameters + ---------- + model : openmc.Model + OpenMC model object. Must contain geometry, materials, and settings. + domains : list of openmc.Material or openmc.Cell or openmc.Universe, or openmc.MeshBase, or openmc.Filter + Domains in which to tally reaction rates, or a spatial tally filter. + nuclides : list of str + Nuclides to get cross sections for. If not specified, all burnable + nuclides from the depletion chain file are used. + reactions : list of str + Reactions to get cross sections for. If not specified, all neutron + reactions listed in the depletion chain file are used. + energies : iterable of float or str + Energy group boundaries in [eV] or the name of the group structure. + If left as None energies will default to [0.0, 100e6] + reaction_rate_mode : {"direct", "flux"}, optional + Indicate how reaction rates should be calculated. The "direct" method + tallies reaction rates directly. The "flux" method tallies a multigroup + flux spectrum and then collapses multigroup reaction rates after a + transport solve (with an option to tally some reaction rates directly). + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or an instance of + openmc.deplete.Chain. Used to determine cross sections for materials not + present in the inital composition. Defaults to + ``openmc.config['chain_file']``. + path_statepoint : path-like, optional + Path to write the statepoint file from the neutron transport solve to. + By default, The statepoint file is written to a temporary directory and + is not kept. + run_kwargs : dict, optional + Keyword arguments passed to :meth:`openmc.Model.run` + + Returns + ------- + list of numpy.ndarray + Flux in each group in [n-cm/src] for each domain + list of MicroXS + Cross section data in [b] for each domain + + See Also + -------- + openmc.deplete.IndependentOperator + + """ + check_value('reaction_rate_mode', reaction_rate_mode, {'direct', 'flux'}) + + # Save any original tallies on the model + original_tallies = model.tallies + + # Determine what reactions and nuclides are available in chain + chain = _get_chain(chain_file) + if reactions is None: + reactions = chain.reactions + if not nuclides: + cross_sections = _find_cross_sections(model) + nuclides_with_data = _get_nuclides_with_data(cross_sections) + nuclides = [nuc.name for nuc in chain.nuclides + if nuc.name in nuclides_with_data] + + # Set up the reaction rate and flux tallies + if energies is None: + energies = [0.0, 100.0e6] + if isinstance(energies, str): + energy_filter = openmc.EnergyFilter.from_group_structure(energies) + else: + energy_filter = openmc.EnergyFilter(energies) + + if isinstance(domains, openmc.Filter): + domain_filter = domains + elif isinstance(domains, openmc.MeshBase): + domain_filter = openmc.MeshFilter(domains) + elif isinstance(domains[0], openmc.Material): + domain_filter = openmc.MaterialFilter(domains) + elif isinstance(domains[0], openmc.Cell): + domain_filter = openmc.CellFilter(domains) + elif isinstance(domains[0], openmc.Universe): + domain_filter = openmc.UniverseFilter(domains) + else: + raise ValueError(f"Unsupported domain type: {type(domains[0])}") + + flux_tally = openmc.Tally(name='MicroXS flux') + flux_tally.filters = [domain_filter, energy_filter] + flux_tally.scores = ['flux'] + model.tallies = [flux_tally] + + if reaction_rate_mode == 'direct': + rr_tally = openmc.Tally(name='MicroXS RR') + rr_tally.filters = [domain_filter, energy_filter] + rr_tally.nuclides = nuclides + rr_tally.multiply_density = False + rr_tally.scores = reactions + model.tallies.append(rr_tally) + + if openmc.lib.is_initialized: + openmc.lib.finalize() + + if comm.rank == 0: + model.export_to_model_xml() + comm.barrier() + # Reinitialize with tallies + openmc.lib.init(intracomm=comm) + + # create temporary run + with TemporaryDirectory() as temp_dir: + if run_kwargs is None: + run_kwargs = {} + else: + run_kwargs = dict(run_kwargs) + run_kwargs.setdefault('cwd', temp_dir) + statepoint_path = model.run(**run_kwargs) + + if comm.rank == 0: + # Move the statepoint file if it is being saved to a specific path + if path_statepoint is not None: + shutil.move(statepoint_path, path_statepoint) + statepoint_path = path_statepoint + + with StatePoint(statepoint_path) as sp: + if reaction_rate_mode == 'direct': + rr_tally = sp.tallies[rr_tally.id] + rr_tally._read_results() + flux_tally = sp.tallies[flux_tally.id] + flux_tally._read_results() + + # Get flux values and make energy groups last dimension + flux_tally = comm.bcast(flux_tally) + flux = flux_tally.get_reshaped_data() # (domains, groups, 1, 1) + flux = np.moveaxis(flux, 1, -1) # (domains, 1, 1, groups) + + # Create list where each item corresponds to one domain + fluxes = list(flux.squeeze((1, 2))) + + if reaction_rate_mode == 'direct': + # Get reaction rates + rr_tally = comm.bcast(rr_tally) + reaction_rates = rr_tally.get_reshaped_data() # (domains, groups, nuclides, reactions) + + # Make energy groups last dimension + reaction_rates = np.moveaxis(reaction_rates, 1, -1) # (domains, nuclides, reactions, groups) + + # Divide RR by flux to get microscopic cross sections. The indexing + # ensures that only non-zero flux values are used, and broadcasting is + # applied to align the shapes of reaction_rates and flux for division. + xs = np.empty_like(reaction_rates) # (domains, nuclides, reactions, groups) + d, _, _, g = np.nonzero(flux) + xs[d, ..., g] = reaction_rates[d, ..., g] / flux[d, :, :, g] + + # Create lists where each item corresponds to one domain + micros = [MicroXS(xs_i, nuclides, reactions) for xs_i in xs] + else: + micros = [MicroXS.from_multigroup_flux( + energies=energies, + multigroup_flux=flux_i, + chain_file=chain_file, + nuclides=nuclides, + reactions=reactions + ) for flux_i in fluxes] + + # Reset tallies + model.tallies = original_tallies + + return fluxes, micros + + +class MicroXS: + """Microscopic cross section data for use in transport-independent depletion. + + .. versionadded:: 0.13.1 + + .. versionchanged:: 0.14.0 + Class was heavily refactored and no longer subclasses pandas.DataFrame. + + Parameters + ---------- + data : numpy.ndarray of floats + 3D array containing microscopic cross section values for each + nuclide, reaction, and energy group. Cross section values are assumed to + be in [b], and indexed by [nuclide, reaction, energy group] + nuclides : list of str + List of nuclide symbols for that have data for at least one + reaction. + reactions : list of str + List of reactions. All reactions must match those in + :data:`openmc.deplete.chain.REACTIONS` + + """ + def __init__(self, data: np.ndarray, nuclides: list[str], reactions: list[str]): + # Validate inputs + if len(data.shape) != 3: + raise ValueError('Data array must be 3D.') + if data.shape[:2] != (len(nuclides), len(reactions)): + raise ValueError( + f'Nuclides list of length {len(nuclides)} and ' + f'reactions array of length {len(reactions)} do not ' + f'match dimensions of data array of shape {data.shape}') + check_iterable_type('nuclides', nuclides, str) + check_iterable_type('reactions', reactions, str) + check_type('data', data, np.ndarray, expected_iter_type=float) + for reaction in reactions: + check_value('reactions', reaction, _valid_rxns) + + self.data = data + self.nuclides = nuclides + self.reactions = reactions + self._index_nuc = {nuc: i for i, nuc in enumerate(nuclides)} + self._index_rx = {rx: i for i, rx in enumerate(reactions)} + + @classmethod + def from_multigroup_flux( + cls, + energies: Sequence[float] | str, + multigroup_flux: Sequence[float], + chain_file: PathLike | None = None, + temperature: float = 293.6, + nuclides: Sequence[str] | None = None, + reactions: Sequence[str] | None = None, + **init_kwargs: dict, + ) -> MicroXS: + """Generated microscopic cross sections from a known flux. + + The size of the MicroXS matrix depends on the chain file and cross + sections available. MicroXS entry will be 0 if the nuclide cross section + is not found. + + It is recommended to make repeated calls to this method within a context + manager using the :class:`openmc.lib.TemporarySession` class to avoid + re-initializing OpenMC and loading cross sections each time. + + .. versionadded:: 0.15.0 + + Parameters + ---------- + energies : iterable of float or str + Energy group boundaries in [eV] or the name of the group structure + multigroup_flux : iterable of float + Energy-dependent multigroup flux values + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or an instance of + openmc.deplete.Chain. Defaults to ``openmc.config['chain_file']``. + temperature : int, optional + Temperature for cross section evaluation in [K]. + nuclides : list of str, optional + Nuclides to get cross sections for. If not specified, all burnable + nuclides from the depletion chain file are used. + reactions : list of str, optional + Reactions to get cross sections for. If not specified, all neutron + reactions listed in the depletion chain file are used. + **init_kwargs : dict + Keyword arguments passed to :func:`openmc.lib.init` + + Returns + ------- + MicroXS + """ + + check_type("temperature", temperature, (int, float)) + # if energy is string then use group structure of that name + if isinstance(energies, str): + energies = GROUP_STRUCTURES[energies] + else: + # if user inputs energies check they are ascending (low to high) as + # some depletion codes use high energy to low energy. + if not np.all(np.diff(energies) > 0): + raise ValueError('Energy group boundaries must be in ascending order') + + # check dimension consistency + if len(multigroup_flux) != len(energies) - 1: + raise ValueError('Length of flux array should be len(energies)-1') + + chain = _get_chain(chain_file) + cross_sections = _find_cross_sections(model=None) + nuclides_with_data = _get_nuclides_with_data(cross_sections) + + # If no nuclides were specified, default to all nuclides from the chain + if not nuclides: + nuclides = chain.nuclides + nuclides = [nuc.name for nuc in nuclides] + + # Get reaction MT values. If no reactions specified, default to the + # reactions available in the chain file + if reactions is None: + reactions = chain.reactions + mts = [REACTION_MT[name] for name in reactions] + + # Normalize multigroup flux + multigroup_flux = np.array(multigroup_flux) + multigroup_flux /= multigroup_flux.sum() + + # Create 3D array for microscopic cross sections + microxs_arr = np.zeros((len(nuclides), len(mts), 1)) + + # Compute microscopic cross sections within a temporary session + with openmc.lib.TemporarySession(**init_kwargs): + # For each nuclide and reaction, compute the flux-averaged xs + for nuc_index, nuc in enumerate(nuclides): + if nuc not in nuclides_with_data: + continue + lib_nuc = openmc.lib.load_nuclide(nuc) + for mt_index, mt in enumerate(mts): + microxs_arr[nuc_index, mt_index, 0] = lib_nuc.collapse_rate( + mt, temperature, energies, multigroup_flux + ) + + return cls(microxs_arr, nuclides, reactions) + + @classmethod + def from_csv(cls, csv_file, **kwargs): + """Load data from a comma-separated values (csv) file. + + Parameters + ---------- + csv_file : str + Relative path to csv-file containing microscopic cross section + data. Cross section values are assumed to be in [b] + **kwargs : dict + Keyword arguments to pass to :func:`pandas.read_csv()`. + + Returns + ------- + MicroXS + + """ + if 'float_precision' not in kwargs: + kwargs['float_precision'] = 'round_trip' + + df = pd.read_csv(csv_file, **kwargs) + df.set_index(['nuclides', 'reactions', 'groups'], inplace=True) + nuclides = list(df.index.unique(level='nuclides')) + reactions = list(df.index.unique(level='reactions')) + groups = list(df.index.unique(level='groups')) + shape = (len(nuclides), len(reactions), len(groups)) + data = df.values.reshape(shape) + return cls(data, nuclides, reactions) + + def __getitem__(self, index): + nuc, rx = index + i_nuc = self._index_nuc[nuc] + i_rx = self._index_rx[rx] + return self.data[i_nuc, i_rx] + + def to_csv(self, *args, **kwargs): + """Write data to a comma-separated values (csv) file + + Parameters + ---------- + *args + Positional arguments passed to :meth:`pandas.DataFrame.to_csv` + **kwargs + Keyword arguments passed to :meth:`pandas.DataFrame.to_csv` + + """ + groups = self.data.shape[2] + multi_index = pd.MultiIndex.from_product( + [self.nuclides, self.reactions, range(1, groups + 1)], + names=['nuclides', 'reactions', 'groups'] + ) + df = pd.DataFrame({'xs': self.data.flatten()}, index=multi_index) + df.to_csv(*args, **kwargs) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 992248246e..9588148347 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -1,4 +1,4 @@ -"""Nuclide module. +"""Nuclide module.xml.etree.Ele Contains the per-nuclide components of a depletion chain. """ @@ -8,14 +8,13 @@ from collections.abc import Mapping from collections import namedtuple, defaultdict from warnings import warn from numbers import Real -try: - import lxml.etree as ET -except ImportError: - import xml.etree.ElementTree as ET -from numpy import empty, searchsorted +import lxml.etree as ET +import numpy as np from openmc.checkvalue import check_type +from openmc.stats import Univariate +from .._xml import get_elem_list, get_text __all__ = [ "DecayTuple", "ReactionTuple", "Nuclide", "FissionYield", @@ -81,7 +80,7 @@ class Nuclide: Parameters ---------- name : str, optional - GND name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` + GNDS name of this nuclide, e.g. ``"He4"``, ``"Am242_m1"`` Attributes ---------- @@ -101,6 +100,9 @@ class Nuclide: reactions : list of openmc.deplete.ReactionTuple Reaction information. Each element of the list is a named tuple with attribute 'type', 'target', 'Q', and 'branching_ratio'. + sources : dict + Dictionary mapping particle type as string to energy distribution of + decay source represented as :class:`openmc.stats.Univariate` yield_data : FissionYieldDistribution or None Fission product yields at tabulated energies for this nuclide. Can be treated as a nested dictionary ``{energy: {product: yield}}`` @@ -120,9 +122,16 @@ class Nuclide: # Reaction paths self.reactions = [] + # Decay sources + self.sources = {} + # Neutron fission yields, if present self._yield_data = None + def __repr__(self): + n_modes, n_rx = self.n_decay_modes, self.n_reaction_paths + return f"" + @property def n_decay_modes(self): return len(self.decay_modes) @@ -201,9 +210,9 @@ class Nuclide: Parameters ---------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element to read nuclide data from - root : xml.etree.ElementTree.Element, optional + root : lxml.etree._Element, optional Root XML element for chain file (only used when fission product yields are borrowed from another parent) fission_q : None or float @@ -217,32 +226,39 @@ class Nuclide: """ nuc = cls() - nuc.name = element.get('name') + nuc.name = get_text(element, "name") # Check for half-life - if 'half_life' in element.attrib: - nuc.half_life = float(element.get('half_life')) - nuc.decay_energy = float(element.get('decay_energy', '0')) + half_life = get_text(element, "half_life") + if half_life is not None: + nuc.half_life = float(half_life) + nuc.decay_energy = float(get_text(element, "decay_energy", 0.0)) # Check for decay paths for decay_elem in element.iter('decay'): - d_type = decay_elem.get('type') - target = decay_elem.get('target') + d_type = get_text(decay_elem, "type") + target = get_text(decay_elem, "target") if target is not None and target.lower() == "nothing": target = None - branching_ratio = float(decay_elem.get('branching_ratio')) + branching_ratio = float(get_text(decay_elem, "branching_ratio")) nuc.decay_modes.append(DecayTuple(d_type, target, branching_ratio)) + # Check for sources + for src_elem in element.iter('source'): + particle = get_text(src_elem, "particle") + distribution = Univariate.from_xml_element(src_elem) + nuc.sources[particle] = distribution + # Check for reaction paths for reaction_elem in element.iter('reaction'): - r_type = reaction_elem.get('type') - Q = float(reaction_elem.get('Q', '0')) - branching_ratio = float(reaction_elem.get('branching_ratio', '1')) + r_type = get_text(reaction_elem, "type") + Q = float(get_text(reaction_elem, "Q", 0.0)) + branching_ratio = float(get_text(reaction_elem, "branching_ratio", 1.0)) # If the type is not fission, get target and Q value, otherwise # just set null values if r_type != 'fission': - target = reaction_elem.get('target') + target = get_text(reaction_elem, "target") if target is not None and target.lower() == "nothing": target = None else: @@ -257,11 +273,11 @@ class Nuclide: fpy_elem = element.find('neutron_fission_yields') if fpy_elem is not None: # Check for use of FPY from other nuclide - parent = fpy_elem.get('parent') + parent = get_text(fpy_elem, "parent") if parent is not None: assert root is not None fpy_elem = root.find( - './/nuclide[@name="{}"]/neutron_fission_yields'.format(parent) + f'.//nuclide[@name="{parent}"]/neutron_fission_yields' ) if fpy_elem is None: raise ValueError( @@ -280,7 +296,7 @@ class Nuclide: Returns ------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element to write nuclide data to """ @@ -298,6 +314,13 @@ class Nuclide: mode_elem.set('target', daughter) mode_elem.set('branching_ratio', str(br)) + # Write decay sources + if self.sources: + for particle, source in self.sources.items(): + src_elem = source.to_xml_element('source') + src_elem.set('particle', particle) + elem.append(src_elem) + elem.set('reactions', str(len(self.reactions))) for rx, daughter, Q, br in self.reactions: rx_elem = ET.SubElement(elem, 'reaction') @@ -392,7 +415,7 @@ class Nuclide: continue msg = msg_func( name=self.name, actual=sum_br, expected=1.0, tol=tolerance, - prop="{} reaction branch ratios".format(rxn_type)) + prop=f"{rxn_type} reaction branch ratios") if strict: raise ValueError(msg) elif quiet: @@ -409,7 +432,7 @@ class Nuclide: msg = msg_func( name=self.name, actual=sum_yield, expected=2.0, tol=tolerance, - prop="fission yields (E = {:7.4e} eV)".format(energy)) + prop=f"fission yields (E = {energy:7.4e} eV)") if strict: raise ValueError(msg) elif quiet: @@ -466,7 +489,7 @@ class FissionYieldDistribution(Mapping): shared_prod = set.union(*(set(x) for x in fission_yields.values())) ordered_prod = sorted(shared_prod) - yield_matrix = empty((len(energies), len(shared_prod))) + yield_matrix = np.empty((len(energies), len(shared_prod))) for g_index, energy in enumerate(energies): prod_map = fission_yields[energy] @@ -499,7 +522,7 @@ class FissionYieldDistribution(Mapping): Parameters ---------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element to pull fission yield data from Returns @@ -508,9 +531,9 @@ class FissionYieldDistribution(Mapping): """ all_yields = {} for yield_elem in element.iter("fission_yields"): - energy = float(yield_elem.get("energy")) - products = yield_elem.find("products").text.split() - yields = map(float, yield_elem.find("data").text.split()) + energy = float(get_text(yield_elem, "energy")) + products = get_elem_list(yield_elem, "products", str) or [] + yields = get_elem_list(yield_elem, "data", float) or [] # Get a map of products to their corresponding yield all_yields[energy] = dict(zip(products, yields)) @@ -521,7 +544,7 @@ class FissionYieldDistribution(Mapping): Parameters ---------- - root : xml.etree.ElementTree.Element + root : lxml.etree._Element Element to write distribution data to """ for energy, yield_obj in self.items(): @@ -556,7 +579,7 @@ class FissionYieldDistribution(Mapping): return None products = sorted(overlap) - indices = searchsorted(self.products, products) + indices = np.searchsorted(self.products, products) # coerce back to dictionary to pass back to __init__ new_yields = {} @@ -674,8 +697,12 @@ class FissionYield(Mapping): return self * scalar def __repr__(self): - return "<{} containing {} products and yields>".format( - self.__class__.__name__, len(self)) + return f"<{self.__class__.__name__} containing {len(self)} products and yields>" + + def __deepcopy__(self, memo): + result = FissionYield(self.products, self.yields.copy()) + memo[id(self)] = result + return result # Avoid greedy numpy operations like np.float64 * fission_yield # converting this to an array on the fly. Force __rmul__ and diff --git a/openmc/deplete/openmc_operator.py b/openmc/deplete/openmc_operator.py new file mode 100644 index 0000000000..2d7694093b --- /dev/null +++ b/openmc/deplete/openmc_operator.py @@ -0,0 +1,557 @@ +"""OpenMC transport operator + +This module implements functions shared by both OpenMC transport-coupled and +transport-independent transport operators. + +""" + +from abc import abstractmethod +from warnings import warn + +import numpy as np + +import openmc +from openmc.checkvalue import check_value, check_type, check_greater_than +from openmc.exceptions import DataError +from openmc.mpi import comm +from .abc import TransportOperator, OperatorResult +from .atom_number import AtomNumber +from .reaction_rates import ReactionRates +from .pool import _distribute + +__all__ = ["OpenMCOperator", "OperatorResult"] + + +class OpenMCOperator(TransportOperator): + """Abstract class holding OpenMC-specific functions for running + depletion calculations. + + Specific classes for running transport-coupled or transport-independent + depletion calculations are implemented as subclasses of OpenMCOperator. + + Parameters + ---------- + materials : openmc.Materials + List of all materials in the model + cross_sections : str or list of MicroXS + Path to continuous energy cross section library, or list of objects + containing cross sections. + chain_file : PathLike or Chain, optional + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Defaults to ``openmc.config['chain_file']``. + prev_results : Results, optional + Results from a previous depletion calculation. If this argument is + specified, the depletion calculation will start from the latest state + in the previous results. + diff_burnable_mats : bool, optional + Whether to differentiate burnable materials with multiple instances. + fission_q : dict, optional + Dictionary of nuclides and their fission Q values [eV]. + helper_kwargs : dict + Keyword arguments for helper classes + reduce_chain_level : int, optional + Depth of the search when reducing the depletion chain. The default + value of ``None`` implies no limit on the depth. + + diff_volume_method : str + Specifies how the volumes of the new materials should be found. Default + is to 'divide equally' which divides the original material volume + equally between the new materials, 'match cell' sets the volume of the + material to volume of the cell they fill. + + .. versionadded:: 0.14.0 + + Attributes + ---------- + materials : openmc.Materials + All materials present in the model + cross_sections : str or list of MicroXS + Path to continuous energy cross section library, or list of objects + containing cross sections. + output_dir : pathlib.Path + Path to output directory to save results. + round_number : bool + Whether or not to round output to OpenMC to 8 digits. + Useful in testing, as OpenMC is incredibly sensitive to exact values. + number : openmc.deplete.AtomNumber + Total number of atoms in simulation. + nuclides_with_data : set of str + A set listing all unique nuclides available from cross_sections.xml. + chain : openmc.deplete.Chain + The depletion chain information necessary to form matrices and tallies. + reaction_rates : openmc.deplete.ReactionRates + Reaction rates from the last operator step. + burnable_mats : list of str + All burnable material IDs + heavy_metal : float + Initial heavy metal inventory [g] + local_mats : list of str + All burnable material IDs being managed by a single process + prev_res : Results or None + Results from a previous depletion calculation. ``None`` if no + results are to be used. + + """ + + def __init__( + self, + materials=None, + cross_sections=None, + chain_file=None, + prev_results=None, + diff_burnable_mats=False, + diff_volume_method='divide equally', + fission_q=None, + helper_kwargs=None, + reduce_chain_level=None): + + # If chain file was not specified, try to get it from global config + if chain_file is None: + chain_file = openmc.config.get('chain_file') + if chain_file is None: + raise DataError( + "No depletion chain specified and could not find depletion " + "chain in openmc.config['chain_file']" + ) + + super().__init__(chain_file, fission_q, prev_results) + self.round_number = False + self.materials = materials + self.cross_sections = cross_sections + + check_value('diff volume method', diff_volume_method, + {'divide equally', 'match cell'}) + if reduce_chain_level: + check_type('reduce_chain_level', reduce_chain_level, int) + check_greater_than('reduce_chain_level', reduce_chain_level, 0) + self.diff_volume_method = diff_volume_method + + # Reduce the chain to only those nuclides present + if reduce_chain_level is not None: + init_nuclides = set() + for material in self.materials: + if not material.depletable: + continue + for name, _dens_percent, _dens_type in material.nuclides: + init_nuclides.add(name) + + self.chain = self.chain.reduce(init_nuclides, reduce_chain_level) + + if diff_burnable_mats: + self._differentiate_burnable_mats() + self.materials = self.model.materials + + # Determine which nuclides have cross section data + # This nuclides variables contains every nuclides + # for which there is an entry in the micro_xs parameter + self.nuclides_with_data = self._get_nuclides_with_data( + self.cross_sections) + + # Select nuclides with data that are also in the chain + self._burnable_nucs = [nuc.name for nuc in self.chain.nuclides + if nuc.name in self.nuclides_with_data] + + # Select nuclides without data that are also in the chain + self._decay_nucs = [nuc.name for nuc in self.chain.nuclides + if nuc.name not in self.nuclides_with_data] + + self.burnable_mats, volumes, all_nuclides = self._get_burnable_mats() + self.local_mats = _distribute(self.burnable_mats) + + self._mat_index_map = { + lm: self.burnable_mats.index(lm) for lm in self.local_mats} + + if self.prev_res is not None: + self._load_previous_results() + + # Extract number densities from the geometry / previous depletion run + self._extract_number(self.local_mats, + volumes, + all_nuclides, + self.prev_res) + + # Create reaction rates array + self.reaction_rates = ReactionRates( + self.local_mats, self._burnable_nucs, self.chain.reactions) + + self._get_helper_classes(helper_kwargs) + + def _differentiate_burnable_mats(self): + """Assign distribmats for each burnable material""" + pass + + def _get_burnable_mats(self) -> tuple[list[str], dict[str, float], list[str]]: + """Determine depletable materials, volumes, and nuclides + + Returns + ------- + burnable_mats : list of str + list of burnable material IDs + volume : dict of str to float + Volume of each material in [cm^3] + nuclides : list of str + Nuclides in order of how they'll appear in the simulation. + + """ + + burnable_mats = set() + model_nuclides = set() + volume = {} + + self.heavy_metal = 0.0 + + # Iterate once through the geometry to get dictionaries + for mat in self.materials: + for nuclide in mat.get_nuclides(): + if nuclide in self.nuclides_with_data or self._decay_nucs: + model_nuclides.add(nuclide) + else: + msg = (f"Nuclide {nuclide} in material {mat.id} is not " + "present in the depletion chain and has no cross " + "section data.") + warn(msg) + if mat.depletable: + burnable_mats.add(str(mat.id)) + if mat.volume is None: + if mat.name is None: + msg = ("Volume not specified for depletable material " + f"with ID={mat.id}.") + else: + msg = ("Volume not specified for depletable material " + f"with ID={mat.id} Name={mat.name}.") + raise RuntimeError(msg) + volume[str(mat.id)] = mat.volume + self.heavy_metal += mat.fissionable_mass + + # Make sure there are burnable materials + if not burnable_mats: + raise RuntimeError( + "No depletable materials were found in the model.") + + # Sort the sets + burnable_mats = sorted(burnable_mats, key=int) + model_nuclides = sorted(model_nuclides) + + # Construct a global nuclide dictionary, burned first + nuclides = list(self.chain.nuclide_dict) + for nuc in model_nuclides: + if nuc not in nuclides: + nuclides.append(nuc) + return burnable_mats, volume, nuclides + + def _load_previous_results(self): + """Load results from a previous depletion simulation""" + pass + + @abstractmethod + def _get_nuclides_with_data(self, cross_sections): + """Find nuclides with cross section data.""" + + def _extract_number(self, local_mats, volume, all_nuclides, prev_res=None): + """Construct AtomNumber using geometry + + Parameters + ---------- + local_mats : list of str + Material IDs to be managed by this process + volume : dict of str to float + Volumes for the above materials in [cm^3] + all_nuclides : list of str + Nuclides to be used in the simulation. + prev_res : Results, optional + Results from a previous depletion calculation + + """ + self.number = AtomNumber(local_mats, all_nuclides, volume, len(self.chain)) + + # Now extract and store the number densities + # From the geometry if no previous depletion results + if prev_res is None: + for mat in self.materials: + if str(mat.id) in local_mats: + self._set_number_from_mat(mat) + + # Else from previous depletion results + else: + for mat in self.materials: + if str(mat.id) in local_mats: + self._set_number_from_results(mat, prev_res) + + def _set_number_from_mat(self, mat): + """Extracts material and number densities from openmc.Material + + Parameters + ---------- + mat : openmc.Material + The material to read from + + """ + mat_id = str(mat.id) + + for nuclide, atom_per_bcm in mat.get_nuclide_atom_densities().items(): + atom_per_cc = atom_per_bcm * 1.0e24 + self.number.set_atom_density(mat_id, nuclide, atom_per_cc) + + def _set_number_from_results(self, mat, prev_res): + """Extracts material nuclides and number densities. + + If the nuclide concentration's evolution is tracked, the densities come + from depletion results. Else, densities are extracted from the geometry + in the summary. + + Parameters + ---------- + mat : openmc.Material + The material to read from + prev_res : Results + Results from a previous depletion calculation + + """ + mat_id = str(mat.id) + + # Get nuclide lists from geometry and depletion results + depl_nuc = prev_res[-1].index_nuc + geom_nuc_densities = mat.get_nuclide_atom_densities() + + # Merge lists of nuclides, with the same order for every calculation + geom_nuc_densities.update(depl_nuc) + + for nuclide, atom_per_bcm in geom_nuc_densities.items(): + if nuclide in depl_nuc: + concentration = prev_res.get_atoms(mat_id, nuclide)[1][-1] + volume = prev_res[-1].volume[mat_id] + atom_per_cc = concentration / volume + else: + atom_per_cc = atom_per_bcm * 1.0e24 + + self.number.set_atom_density(mat_id, nuclide, atom_per_cc) + + @abstractmethod + def _get_helper_classes(self, helper_kwargs): + """Create the ``_rate_helper``, ``_normalization_helper``, and + ``_yield_helper`` objects. + + Parameters + ---------- + helper_kwargs : dict + Keyword arguments for helper classes + + """ + + def initial_condition(self, materials): + """Performs final setup and returns initial condition. + + Parameters + ---------- + materials : list of openmc.lib.Material + list of materials + + Returns + ------- + list of numpy.ndarray + Total density for initial conditions. + + """ + + self._rate_helper.generate_tallies(materials, self.chain.reactions) + self._normalization_helper.prepare( + self.chain.nuclides, self.reaction_rates.index_nuc) + # Tell fission yield helper what materials this process is + # responsible for + self._yield_helper.generate_tallies( + materials, tuple(sorted(self._mat_index_map.values()))) + + # Return number density vector + return list(self.number.get_mat_slice(np.s_[:])) + + def _update_materials_and_nuclides(self, vec): + """Update the number density, material compositions, and nuclide + lists in helper objects + + Parameters + ---------- + vec : list of numpy.ndarray + Total atoms. + + """ + + # Update the number densities regardless of the source rate + self.number.set_density(vec) + self._update_materials() + + # Update tally nuclides data in preparation for transport solve + nuclides = self._get_reaction_nuclides() + self._rate_helper.nuclides = nuclides + self._normalization_helper.nuclides = nuclides + self._yield_helper.update_tally_nuclides(nuclides) + + @abstractmethod + def _update_materials(self): + """Updates material compositions in OpenMC on all processes.""" + + def write_bos_data(self, step): + """Document beginning of step data for a given step + + Called at the beginning of a depletion step and at + the final point in the simulation. + + Parameters + ---------- + step : int + Current depletion step including restarts + """ + # Since we aren't running a transport simulation, we simply pass + pass + + def _get_reaction_nuclides(self): + """Determine nuclides that should be tallied for reaction rates. + + This method returns a list of all nuclides that have cross section data + and are listed in the depletion chain. Technically, we should count + nuclides that may not appear in the depletion chain because we still + need to get the fission reaction rate for these nuclides in order to + normalize power, but that is left as a future exercise. + + Returns + ------- + list of str + Nuclides with reaction rates + + """ + nuc_set = set() + + # Create the set of all nuclides in the decay chain in materials marked + # for burning in which the number density is greater than zero. + for nuc in self.number.nuclides: + if nuc in self.nuclides_with_data: + nuc_set.add(nuc) + + # Communicate which nuclides have nonzeros to rank 0 + if comm.rank == 0: + for i in range(1, comm.size): + nuc_newset = comm.recv(source=i, tag=i) + nuc_set |= nuc_newset + else: + comm.send(nuc_set, dest=0, tag=comm.rank) + + if comm.rank == 0: + # Sort nuclides in the same order as self.number + nuc_list = [nuc for nuc in self.number.nuclides + if nuc in nuc_set] + else: + nuc_list = None + + # Store list of nuclides on each process + nuc_list = comm.bcast(nuc_list) + return [nuc for nuc in nuc_list if nuc in self.chain] + + def _calculate_reaction_rates(self, source_rate): + """Unpack tallies from OpenMC and return an operator result + + This method uses OpenMC's C API bindings to determine the k-effective + value and reaction rates from the simulation. The reaction rates are + normalized by a helper class depending on the method being used. + + Parameters + ---------- + source_rate : float + Power in [W] or source rate in [neutron/sec] + + Returns + ------- + rates : openmc.deplete.ReactionRates + Reaction rates for nuclides + + """ + rates = self.reaction_rates + rates.fill(0.0) + + # Extract reaction nuclides + rxn_nuclides = self._rate_helper.nuclides + + # Form fast map + nuc_ind = [rates.index_nuc[nuc] for nuc in rxn_nuclides] + rx_ind = [rates.index_rx[react] for react in self.chain.reactions] + + # Keep track of energy produced from all reactions in eV per source + # particle + self._normalization_helper.reset() + self._yield_helper.unpack() + + # Store fission yield dictionaries + fission_yields = [] + + # Create arrays to store fission Q values, reaction rates, and nuclide + # numbers, zeroed out in material iteration + number = np.empty(rates.n_nuc) + + fission_ind = rates.index_rx.get("fission") + + # Reset the cached material reaction rates tallies + self._rate_helper.reset_tally_means() + + # Extract results + for i, mat in enumerate(self.local_mats): + # Get tally index + mat_index = self._mat_index_map[mat] + + # Zero out reaction rates and nuclide numbers + number.fill(0.0) + + # Get new number densities + for nuc, i_nuc_results in zip(rxn_nuclides, nuc_ind): + number[i_nuc_results] = self.number[mat, nuc] + + # Get microscopic reaction rates in [(reactions/src)*b-cm/atom]. 2D + # array with shape (nuclides, reactions). + tally_rates = self._rate_helper.get_material_rates( + mat_index, nuc_ind, rx_ind) + + # Compute fission yields for this material + fission_yields.append(self._yield_helper.weighted_yields(i)) + + # Accumulate energy from fission + volume_b_cm = 1e24 * self.number.get_mat_volume(mat) + if fission_ind is not None: + atom_per_bcm = number / volume_b_cm + fission_rates = tally_rates[:, fission_ind] * atom_per_bcm + self._normalization_helper.update(fission_rates) + + # Divide by [b-cm] to get [(reactions/src)/atom] + rates[i] = tally_rates / volume_b_cm + + # Scale reaction rates to obtain units of [(reactions/sec)/atom] + rates *= self._normalization_helper.factor(source_rate) + + # Store new fission yields on the chain + self.chain.fission_yields = fission_yields + + return rates + + def get_results_info(self): + """Returns volume list, material lists, and nuc lists. + + Returns + ------- + volume : dict of str float + Volumes corresponding to materials in full_burn_dict + nuc_list : list of str + A list of all nuclide names. Used for sorting the simulation. + burn_list : list of int + A list of all material IDs to be burned. Used for sorting the simulation. + full_burn_list : list + List of all burnable material IDs + + """ + nuc_list = self.number.burnable_nuclides + burn_list = self.local_mats + + volume = {} + for i, mat in enumerate(burn_list): + volume[mat] = self.number.volume[i] + + # Combine volume dictionaries across processes + volume_list = comm.allgather(volume) + volume = {k: v for d in volume_list for k, v in d.items()} + + return volume, nuc_list, burn_list, self.burnable_mats diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py deleted file mode 100644 index b5e377bedd..0000000000 --- a/openmc/deplete/operator.py +++ /dev/null @@ -1,823 +0,0 @@ -"""OpenMC transport operator - -This module implements a transport operator for OpenMC so that it can be used by -depletion integrators. The implementation makes use of the Python bindings to -OpenMC's C API so that reading tally results and updating material number -densities is all done in-memory instead of through the filesystem. - -""" - -import copy -from collections import OrderedDict -import os -from warnings import warn - -import numpy as np -from uncertainties import ufloat - -import openmc -from openmc.checkvalue import check_value -from openmc.data import DataLibrary -from openmc.exceptions import DataError -import openmc.lib -from openmc.mpi import comm -from .abc import TransportOperator, OperatorResult -from .atom_number import AtomNumber -from .chain import _find_chain_file -from .reaction_rates import ReactionRates -from .results_list import ResultsList -from .helpers import ( - DirectReactionRateHelper, ChainFissionHelper, ConstantFissionYieldHelper, - FissionYieldCutoffHelper, AveragedFissionYieldHelper, EnergyScoreHelper, - SourceRateHelper, FluxCollapseHelper) - - -__all__ = ["Operator", "OperatorResult"] - - -def _distribute(items): - """Distribute items across MPI communicator - - Parameters - ---------- - items : list - List of items of distribute - - Returns - ------- - list - Items assigned to process that called - - """ - min_size, extra = divmod(len(items), comm.size) - j = 0 - for i in range(comm.size): - chunk_size = min_size + int(i < extra) - if comm.rank == i: - return items[j:j + chunk_size] - j += chunk_size - - -def _find_cross_sections(model): - """Determine cross sections to use for depletion""" - if model.materials and model.materials.cross_sections is not None: - # Prefer info from Model class if available - return model.materials.cross_sections - - # otherwise fallback to environment variable - cross_sections = os.environ.get("OPENMC_CROSS_SECTIONS") - if cross_sections is None: - raise DataError( - "Cross sections were not specified in Model.materials and " - "the OPENMC_CROSS_SECTIONS environment variable is not set." - ) - return cross_sections - - -class Operator(TransportOperator): - """OpenMC transport operator for depletion. - - Instances of this class can be used to perform depletion using OpenMC as the - transport operator. Normally, a user needn't call methods of this class - directly. Instead, an instance of this class is passed to an integrator - class, such as :class:`openmc.deplete.CECMIntegrator`. - - .. versionchanged:: 0.13.0 - The geometry and settings parameters have been replaced with a - model parameter that takes an openmc.Model object - - Parameters - ---------- - model : openmc.Model - OpenMC model object - chain_file : str, optional - Path to the depletion chain XML file. Defaults to the file - listed under ``depletion_chain`` in - :envvar:`OPENMC_CROSS_SECTIONS` environment variable. - prev_results : ResultsList, optional - Results from a previous depletion calculation. If this argument is - specified, the depletion calculation will start from the latest state - in the previous results. - diff_burnable_mats : bool, optional - Whether to differentiate burnable materials with multiple instances. - Volumes are divided equally from the original material volume. - Default: False. - normalization_mode : {"energy-deposition", "fission-q", "source-rate"} - Indicate how tally results should be normalized. ``"energy-deposition"`` - computes the total energy deposited in the system and uses the ratio of - the power to the energy produced as a normalization factor. - ``"fission-q"`` uses the fission Q values from the depletion chain to - compute the total energy deposited. ``"source-rate"`` normalizes - tallies based on the source rate (for fixed source calculations). - fission_q : dict, optional - Dictionary of nuclides and their fission Q values [eV]. If not given, - values will be pulled from the ``chain_file``. Only applicable - if ``"normalization_mode" == "fission-q"`` - dilute_initial : float, optional - Initial atom density [atoms/cm^3] to add for nuclides that are zero - in initial condition to ensure they exist in the decay chain. - Only done for nuclides with reaction rates. - Defaults to 1.0e3. - fission_yield_mode : {"constant", "cutoff", "average"} - Key indicating what fission product yield scheme to use. The - key determines what fission energy helper is used: - - * "constant": :class:`~openmc.deplete.helpers.ConstantFissionYieldHelper` - * "cutoff": :class:`~openmc.deplete.helpers.FissionYieldCutoffHelper` - * "average": :class:`~openmc.deplete.helpers.AveragedFissionYieldHelper` - - The documentation on these classes describe their methodology - and differences. Default: ``"constant"`` - fission_yield_opts : dict of str to option, optional - Optional arguments to pass to the helper determined by - ``fission_yield_mode``. Will be passed directly on to the - helper. Passing a value of None will use the defaults for - the associated helper. - reaction_rate_mode : {"direct", "flux"}, optional - Indicate how one-group reaction rates should be calculated. The "direct" - method tallies transmutation reaction rates directly. The "flux" method - tallies a multigroup flux spectrum and then collapses one-group reaction - rates after a transport solve (with an option to tally some reaction - rates directly). - - .. versionadded:: 0.12.1 - reaction_rate_opts : dict, optional - Keyword arguments that are passed to the reaction rate helper class. - When ``reaction_rate_mode`` is set to "flux", energy group boundaries - can be set using the "energies" key. See the - :class:`~openmc.deplete.helpers.FluxCollapseHelper` class for all - options. - - .. versionadded:: 0.12.1 - reduce_chain : bool, optional - If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the - depletion chain up to ``reduce_chain_level``. Default is False. - - .. versionadded:: 0.12 - reduce_chain_level : int, optional - Depth of the search when reducing the depletion chain. Only used - if ``reduce_chain`` evaluates to true. The default value of - ``None`` implies no limit on the depth. - - .. versionadded:: 0.12 - - Attributes - ---------- - model : openmc.Model - OpenMC model object - geometry : openmc.Geometry - OpenMC geometry object - settings : openmc.Settings - OpenMC settings object - dilute_initial : float - Initial atom density [atoms/cm^3] to add for nuclides that - are zero in initial condition to ensure they exist in the decay - chain. Only done for nuclides with reaction rates. - output_dir : pathlib.Path - Path to output directory to save results. - round_number : bool - Whether or not to round output to OpenMC to 8 digits. - Useful in testing, as OpenMC is incredibly sensitive to exact values. - number : openmc.deplete.AtomNumber - Total number of atoms in simulation. - nuclides_with_data : set of str - A set listing all unique nuclides available from cross_sections.xml. - chain : openmc.deplete.Chain - The depletion chain information necessary to form matrices and tallies. - reaction_rates : openmc.deplete.ReactionRates - Reaction rates from the last operator step. - burnable_mats : list of str - All burnable material IDs - heavy_metal : float - Initial heavy metal inventory [g] - local_mats : list of str - All burnable material IDs being managed by a single process - prev_res : ResultsList or None - Results from a previous depletion calculation. ``None`` if no - results are to be used. - diff_burnable_mats : bool - Whether to differentiate burnable materials with multiple instances - cleanup_when_done : bool - Whether to finalize and clear the shared library memory when the - depletion operation is complete. Defaults to clearing the library. - """ - _fission_helpers = { - "average": AveragedFissionYieldHelper, - "constant": ConstantFissionYieldHelper, - "cutoff": FissionYieldCutoffHelper, - } - - def __init__(self, model, chain_file=None, prev_results=None, - diff_burnable_mats=False, normalization_mode="fission-q", - fission_q=None, dilute_initial=1.0e3, - fission_yield_mode="constant", fission_yield_opts=None, - reaction_rate_mode="direct", reaction_rate_opts=None, - reduce_chain=False, reduce_chain_level=None): - # check for old call to constructor - if isinstance(model, openmc.Geometry): - msg = "As of version 0.13.0 openmc.deplete.Operator requires an " \ - "openmc.Model object rather than the openmc.Geometry and " \ - "openmc.Settings parameters. Please use the geometry and " \ - "settings objects passed here to create a model with which " \ - "to generate the depletion Operator." - raise TypeError(msg) - - # Determine cross sections / depletion chain - cross_sections = _find_cross_sections(model) - if chain_file is None: - chain_file = _find_chain_file(cross_sections) - - check_value('fission yield mode', fission_yield_mode, - self._fission_helpers.keys()) - check_value('normalization mode', normalization_mode, - ('energy-deposition', 'fission-q', 'source-rate')) - if normalization_mode != "fission-q": - if fission_q is not None: - warn("Fission Q dictionary will not be used") - fission_q = None - super().__init__(chain_file, fission_q, dilute_initial, prev_results) - self.round_number = False - self.model = model - self.settings = model.settings - self.geometry = model.geometry - - # determine set of materials in the model - if not model.materials: - model.materials = openmc.Materials( - model.geometry.get_all_materials().values() - ) - self.materials = model.materials - - self.diff_burnable_mats = diff_burnable_mats - self.cleanup_when_done = True - - # Reduce the chain before we create more materials - if reduce_chain: - all_isotopes = set() - for material in self.materials: - if not material.depletable: - continue - for name, _dens_percent, _dens_type in material.nuclides: - all_isotopes.add(name) - self.chain = self.chain.reduce(all_isotopes, reduce_chain_level) - - # Differentiate burnable materials with multiple instances - if self.diff_burnable_mats: - self._differentiate_burnable_mats() - - # Clear out OpenMC, create task lists, distribute - openmc.reset_auto_ids() - self.burnable_mats, volume, nuclides = self._get_burnable_mats() - self.local_mats = _distribute(self.burnable_mats) - - # Generate map from local materials => material index - self._mat_index_map = { - lm: self.burnable_mats.index(lm) for lm in self.local_mats} - - if self.prev_res is not None: - # Reload volumes into geometry - prev_results[-1].transfer_volumes(self.model) - - # Store previous results in operator - # Distribute reaction rates according to those tracked - # on this process - if comm.size == 1: - self.prev_res = prev_results - else: - self.prev_res = ResultsList() - mat_indexes = _distribute(range(len(self.burnable_mats))) - for res_obj in prev_results: - new_res = res_obj.distribute(self.local_mats, mat_indexes) - self.prev_res.append(new_res) - - # Determine which nuclides have incident neutron data - self.nuclides_with_data = self._get_nuclides_with_data(cross_sections) - - # Select nuclides with data that are also in the chain - self._burnable_nucs = [nuc.name for nuc in self.chain.nuclides - if nuc.name in self.nuclides_with_data] - - # Extract number densities from the geometry / previous depletion run - self._extract_number(self.local_mats, volume, nuclides, self.prev_res) - - # Create reaction rates array - self.reaction_rates = ReactionRates( - self.local_mats, self._burnable_nucs, self.chain.reactions) - - # Get classes to assist working with tallies - if reaction_rate_mode == "direct": - self._rate_helper = DirectReactionRateHelper( - self.reaction_rates.n_nuc, self.reaction_rates.n_react) - elif reaction_rate_mode == "flux": - if reaction_rate_opts is None: - reaction_rate_opts = {} - - # Ensure energy group boundaries were specified - if 'energies' not in reaction_rate_opts: - raise ValueError( - "Energy group boundaries must be specified in the " - "reaction_rate_opts argument when reaction_rate_mode is" - "set to 'flux'.") - - self._rate_helper = FluxCollapseHelper( - self.reaction_rates.n_nuc, - self.reaction_rates.n_react, - **reaction_rate_opts - ) - else: - raise ValueError("Invalid reaction rate mode.") - - if normalization_mode == "fission-q": - self._normalization_helper = ChainFissionHelper() - elif normalization_mode == "energy-deposition": - score = "heating" if self.settings.photon_transport else "heating-local" - self._normalization_helper = EnergyScoreHelper(score) - else: - self._normalization_helper = SourceRateHelper() - - # Select and create fission yield helper - fission_helper = self._fission_helpers[fission_yield_mode] - fission_yield_opts = ( - {} if fission_yield_opts is None else fission_yield_opts) - self._yield_helper = fission_helper.from_operator( - self, **fission_yield_opts) - - def __call__(self, vec, source_rate): - """Runs a simulation. - - Simulation will abort under the following circumstances: - - 1) No energy is computed using OpenMC tallies. - - Parameters - ---------- - vec : list of numpy.ndarray - Total atoms to be used in function. - source_rate : float - Power in [W] or source rate in [neutron/sec] - - Returns - ------- - openmc.deplete.OperatorResult - Eigenvalue and reaction rates resulting from transport operator - - """ - # Reset results in OpenMC - openmc.lib.reset() - - # Update the number densities regardless of the source rate - self.number.set_density(vec) - self._update_materials() - - # If the source rate is zero, return zero reaction rates without running - # a transport solve - if source_rate == 0.0: - rates = self.reaction_rates.copy() - rates.fill(0.0) - return OperatorResult(ufloat(0.0, 0.0), rates) - - # Prevent OpenMC from complaining about re-creating tallies - openmc.reset_auto_ids() - - # Update tally nuclides data in preparation for transport solve - nuclides = self._get_tally_nuclides() - self._rate_helper.nuclides = nuclides - self._normalization_helper.nuclides = nuclides - self._yield_helper.update_tally_nuclides(nuclides) - - # Run OpenMC - openmc.lib.run() - openmc.lib.reset_timers() - - # Extract results - op_result = self._unpack_tallies_and_normalize(source_rate) - - return copy.deepcopy(op_result) - - @staticmethod - def write_bos_data(step): - """Write a state-point file with beginning of step data - - Parameters - ---------- - step : int - Current depletion step including restarts - """ - openmc.lib.statepoint_write( - "openmc_simulation_n{}.h5".format(step), - write_source=False) - - def _differentiate_burnable_mats(self): - """Assign distribmats for each burnable material - - """ - - # Count the number of instances for each cell and material - self.geometry.determine_paths(instances_only=True) - - # Extract all burnable materials which have multiple instances - distribmats = set( - [mat for mat in self.materials - if mat.depletable and mat.num_instances > 1]) - - for mat in distribmats: - if mat.volume is None: - raise RuntimeError("Volume not specified for depletable " - "material with ID={}.".format(mat.id)) - mat.volume /= mat.num_instances - - if distribmats: - # Assign distribmats to cells - for cell in self.geometry.get_all_material_cells().values(): - if cell.fill in distribmats: - mat = cell.fill - cell.fill = [mat.clone() - for i in range(cell.num_instances)] - - def _get_burnable_mats(self): - """Determine depletable materials, volumes, and nuclides - - Returns - ------- - burnable_mats : list of str - List of burnable material IDs - volume : OrderedDict of str to float - Volume of each material in [cm^3] - nuclides : list of str - Nuclides in order of how they'll appear in the simulation. - - """ - - burnable_mats = set() - model_nuclides = set() - volume = OrderedDict() - - self.heavy_metal = 0.0 - - # Iterate once through the geometry to get dictionaries - for mat in self.materials: - for nuclide in mat.get_nuclides(): - model_nuclides.add(nuclide) - if mat.depletable: - burnable_mats.add(str(mat.id)) - if mat.volume is None: - raise RuntimeError("Volume not specified for depletable " - "material with ID={}.".format(mat.id)) - volume[str(mat.id)] = mat.volume - self.heavy_metal += mat.fissionable_mass - - # Make sure there are burnable materials - if not burnable_mats: - raise RuntimeError( - "No depletable materials were found in the model.") - - # Sort the sets - burnable_mats = sorted(burnable_mats, key=int) - model_nuclides = sorted(model_nuclides) - - # Construct a global nuclide dictionary, burned first - nuclides = list(self.chain.nuclide_dict) - for nuc in model_nuclides: - if nuc not in nuclides: - nuclides.append(nuc) - - return burnable_mats, volume, nuclides - - def _extract_number(self, local_mats, volume, nuclides, prev_res=None): - """Construct AtomNumber using geometry - - Parameters - ---------- - local_mats : list of str - Material IDs to be managed by this process - volume : OrderedDict of str to float - Volumes for the above materials in [cm^3] - nuclides : list of str - Nuclides to be used in the simulation. - prev_res : ResultsList, optional - Results from a previous depletion calculation - - """ - self.number = AtomNumber(local_mats, nuclides, volume, len(self.chain)) - - if self.dilute_initial != 0.0: - for nuc in self._burnable_nucs: - self.number.set_atom_density(np.s_[:], nuc, self.dilute_initial) - - # Now extract and store the number densities - # From the geometry if no previous depletion results - if prev_res is None: - for mat in self.materials: - if str(mat.id) in local_mats: - self._set_number_from_mat(mat) - - # Else from previous depletion results - else: - for mat in self.materials: - if str(mat.id) in local_mats: - self._set_number_from_results(mat, prev_res) - - def _set_number_from_mat(self, mat): - """Extracts material and number densities from openmc.Material - - Parameters - ---------- - mat : openmc.Material - The material to read from - - """ - mat_id = str(mat.id) - - for nuclide, density in mat.get_nuclide_atom_densities().values(): - number = density * 1.0e24 - self.number.set_atom_density(mat_id, nuclide, number) - - def _set_number_from_results(self, mat, prev_res): - """Extracts material nuclides and number densities. - - If the nuclide concentration's evolution is tracked, the densities come - from depletion results. Else, densities are extracted from the geometry - in the summary. - - Parameters - ---------- - mat : openmc.Material - The material to read from - prev_res : ResultsList - Results from a previous depletion calculation - - """ - mat_id = str(mat.id) - - # Get nuclide lists from geometry and depletion results - depl_nuc = prev_res[-1].nuc_to_ind - geom_nuc_densities = mat.get_nuclide_atom_densities() - - # Merge lists of nuclides, with the same order for every calculation - geom_nuc_densities.update(depl_nuc) - - for nuclide in geom_nuc_densities.keys(): - if nuclide in depl_nuc: - concentration = prev_res.get_atoms(mat_id, nuclide)[1][-1] - volume = prev_res[-1].volume[mat_id] - number = concentration / volume - else: - density = geom_nuc_densities[nuclide][1] - number = density * 1.0e24 - - self.number.set_atom_density(mat_id, nuclide, number) - - def initial_condition(self): - """Performs final setup and returns initial condition. - - Returns - ------- - list of numpy.ndarray - Total density for initial conditions. - """ - - # Create XML files - if comm.rank == 0: - self.geometry.export_to_xml() - self.settings.export_to_xml() - self._generate_materials_xml() - - # Initialize OpenMC library - comm.barrier() - if not openmc.lib.is_initialized: - openmc.lib.init(intracomm=comm) - - # Generate tallies in memory - materials = [openmc.lib.materials[int(i)] - for i in self.burnable_mats] - self._rate_helper.generate_tallies(materials, self.chain.reactions) - self._normalization_helper.prepare( - self.chain.nuclides, self.reaction_rates.index_nuc) - # Tell fission yield helper what materials this process is - # responsible for - self._yield_helper.generate_tallies( - materials, tuple(sorted(self._mat_index_map.values()))) - - # Return number density vector - return list(self.number.get_mat_slice(np.s_[:])) - - def finalize(self): - """Finalize a depletion simulation and release resources.""" - if self.cleanup_when_done: - openmc.lib.finalize() - - def _update_materials(self): - """Updates material compositions in OpenMC on all processes.""" - - for rank in range(comm.size): - number_i = comm.bcast(self.number, root=rank) - - for mat in number_i.materials: - nuclides = [] - densities = [] - for nuc in number_i.nuclides: - if nuc in self.nuclides_with_data: - val = 1.0e-24 * number_i.get_atom_density(mat, nuc) - - # If nuclide is zero, do not add to the problem. - if val > 0.0: - if self.round_number: - val_magnitude = np.floor(np.log10(val)) - val_scaled = val / 10**val_magnitude - val_round = round(val_scaled, 8) - - val = val_round * 10**val_magnitude - - nuclides.append(nuc) - densities.append(val) - else: - # Only output warnings if values are significantly - # negative. CRAM does not guarantee positive values. - if val < -1.0e-21: - print("WARNING: nuclide ", nuc, " in material ", mat, - " is negative (density = ", val, " at/barn-cm)") - number_i[mat, nuc] = 0.0 - - # Update densities on C API side - mat_internal = openmc.lib.materials[int(mat)] - mat_internal.set_densities(nuclides, densities) - - #TODO Update densities on the Python side, otherwise the - # summary.h5 file contains densities at the first time step - - def _generate_materials_xml(self): - """Creates materials.xml from self.number. - - Due to uncertainty with how MPI interacts with OpenMC API, this - constructs the XML manually. The long term goal is to do this - through direct memory writing. - - """ - # Sort nuclides according to order in AtomNumber object - nuclides = list(self.number.nuclides) - for mat in self.materials: - mat._nuclides.sort(key=lambda x: nuclides.index(x[0])) - - self.materials.export_to_xml() - - def _get_tally_nuclides(self): - """Determine nuclides that should be tallied for reaction rates. - - This method returns a list of all nuclides that have neutron data and - are listed in the depletion chain. Technically, we should tally nuclides - that may not appear in the depletion chain because we still need to get - the fission reaction rate for these nuclides in order to normalize - power, but that is left as a future exercise. - - Returns - ------- - list of str - Tally nuclides - - """ - nuc_set = set() - - # Create the set of all nuclides in the decay chain in materials marked - # for burning in which the number density is greater than zero. - for nuc in self.number.nuclides: - if nuc in self.nuclides_with_data: - if np.sum(self.number[:, nuc]) > 0.0: - nuc_set.add(nuc) - - # Communicate which nuclides have nonzeros to rank 0 - if comm.rank == 0: - for i in range(1, comm.size): - nuc_newset = comm.recv(source=i, tag=i) - nuc_set |= nuc_newset - else: - comm.send(nuc_set, dest=0, tag=comm.rank) - - if comm.rank == 0: - # Sort nuclides in the same order as self.number - nuc_list = [nuc for nuc in self.number.nuclides - if nuc in nuc_set] - else: - nuc_list = None - - # Store list of tally nuclides on each process - nuc_list = comm.bcast(nuc_list) - return [nuc for nuc in nuc_list if nuc in self.chain] - - def _unpack_tallies_and_normalize(self, source_rate): - """Unpack tallies from OpenMC and return an operator result - - This method uses OpenMC's C API bindings to determine the k-effective - value and reaction rates from the simulation. The reaction rates are - normalized by a helper class depending on the method being used. - - Parameters - ---------- - source_rate : float - Power in [W] or source rate in [neutron/sec] - - Returns - ------- - openmc.deplete.OperatorResult - Eigenvalue and reaction rates resulting from transport operator - - """ - rates = self.reaction_rates - rates.fill(0.0) - - # Get k and uncertainty - k_combined = ufloat(*openmc.lib.keff()) - - # Extract tally bins - nuclides = self._rate_helper.nuclides - - # Form fast map - nuc_ind = [rates.index_nuc[nuc] for nuc in nuclides] - react_ind = [rates.index_rx[react] for react in self.chain.reactions] - - # Keep track of energy produced from all reactions in eV per source - # particle - self._normalization_helper.reset() - self._yield_helper.unpack() - - # Store fission yield dictionaries - fission_yields = [] - - # Create arrays to store fission Q values, reaction rates, and nuclide - # numbers, zeroed out in material iteration - number = np.empty(rates.n_nuc) - - fission_ind = rates.index_rx.get("fission") - - # Extract results - for i, mat in enumerate(self.local_mats): - # Get tally index - mat_index = self._mat_index_map[mat] - - # Zero out reaction rates and nuclide numbers - number.fill(0.0) - - # Get new number densities - for nuc, i_nuc_results in zip(nuclides, nuc_ind): - number[i_nuc_results] = self.number[mat, nuc] - - tally_rates = self._rate_helper.get_material_rates( - mat_index, nuc_ind, react_ind) - - # Compute fission yields for this material - fission_yields.append(self._yield_helper.weighted_yields(i)) - - # Accumulate energy from fission - if fission_ind is not None: - self._normalization_helper.update(tally_rates[:, fission_ind]) - - # Divide by total number and store - rates[i] = self._rate_helper.divide_by_adens(number) - - # Scale reaction rates to obtain units of reactions/sec - rates *= self._normalization_helper.factor(source_rate) - - # Store new fission yields on the chain - self.chain.fission_yields = fission_yields - - return OperatorResult(k_combined, rates) - - def _get_nuclides_with_data(self, cross_sections): - """Loads cross_sections.xml file to find nuclides with neutron data""" - nuclides = set() - data_lib = DataLibrary.from_xml(cross_sections) - for library in data_lib.libraries: - if library['type'] != 'neutron': - continue - for name in library['materials']: - if name not in nuclides: - nuclides.add(name) - - return nuclides - - def get_results_info(self): - """Returns volume list, material lists, and nuc lists. - - Returns - ------- - volume : dict of str float - Volumes corresponding to materials in full_burn_dict - nuc_list : list of str - A list of all nuclide names. Used for sorting the simulation. - burn_list : list of int - A list of all material IDs to be burned. Used for sorting the simulation. - full_burn_list : list - List of all burnable material IDs - - """ - nuc_list = self.number.burnable_nuclides - burn_list = self.local_mats - - volume = {} - for i, mat in enumerate(burn_list): - volume[mat] = self.number.volume[i] - - # Combine volume dictionaries across processes - volume_list = comm.allgather(volume) - volume = {k: v for d in volume_list for k, v in d.items()} - - return volume, nuc_list, burn_list, self.burnable_mats diff --git a/openmc/deplete/pool.py b/openmc/deplete/pool.py index 8360ef602a..03b050af38 100644 --- a/openmc/deplete/pool.py +++ b/openmc/deplete/pool.py @@ -5,62 +5,197 @@ Provided to avoid some circular imports from itertools import repeat, starmap from multiprocessing import Pool +from scipy.sparse import bmat, hstack, vstack, csc_matrix +import numpy as np + +from openmc.mpi import comm # Configurable switch that enables / disables the use of # multiprocessing routines during depletion USE_MULTIPROCESSING = True +# Allow user to override the number of worker processes to use for depletion +# calculations +NUM_PROCESSES = None -def deplete(func, chain, x, rates, dt, matrix_func=None): +def _distribute(items): + """Distribute items across MPI communicator + + Parameters + ---------- + items : list + List of items of distribute + + Returns + ------- + list + Items assigned to process that called + + """ + min_size, extra = divmod(len(items), comm.size) + j = 0 + for i in range(comm.size): + chunk_size = min_size + int(i < extra) + if comm.rank == i: + return items[j:j + chunk_size] + j += chunk_size + +def deplete(func, chain, n, rates, dt, current_timestep=None, matrix_func=None, + transfer_rates=None, external_source_rates=None, *matrix_args): """Deplete materials using given reaction rates for a specified time Parameters ---------- func : callable - Function to use to get new compositions. Expected to have the - signature ``func(A, n0, t) -> n1`` + Function to use to get new compositions. Expected to have the signature + ``func(A, n0, t) -> n1`` chain : openmc.deplete.Chain Depletion chain - x : list of numpy.ndarray - Atom number vectors for each material + n : list of numpy.ndarray + List of atom number arrays for each material. Each array in the list + contains the number of [atom] of each nuclide. rates : openmc.deplete.ReactionRates Reaction rates (from transport operator) dt : float Time in [s] to deplete for + current_timestep : int + Current timestep index maxtrix_func : callable, optional - Function to form the depletion matrix after calling - ``matrix_func(chain, rates, fission_yields)``, where - ``fission_yields = {parent: {product: yield_frac}}`` - Expected to return the depletion matrix required by + Function to form the depletion matrix after calling ``matrix_func(chain, + rates, fission_yields)``, where ``fission_yields = {parent: {product: + yield_frac}}`` Expected to return the depletion matrix required by ``func`` + transfer_rates : openmc.deplete.TransferRates, Optional + Transfer rates for continuous removal/feed. + + .. versionadded:: 0.14.0 + external_source_rates : openmc.deplete.ExternalSourceRates, Optional + External source rates for continuous removal/feed. + + .. versionadded:: 0.15.3 + matrix_args: Any, optional + Additional arguments passed to matrix_func Returns ------- - x_result : list of numpy.ndarray - Updated atom number vectors for each material + n_result : list of numpy.ndarray + Updated list of atom number arrays for each material. Each array in the + list contains the number of [atom] of each nuclide. """ fission_yields = chain.fission_yields if len(fission_yields) == 1: fission_yields = repeat(fission_yields[0]) - elif len(fission_yields) != len(x): + elif len(fission_yields) != len(n): raise ValueError( "Number of material fission yield distributions {} is not " "equal to the number of compositions {}".format( - len(fission_yields), len(x))) + len(fission_yields), len(n))) if matrix_func is None: matrices = map(chain.form_matrix, rates, fission_yields) else: - matrices = map(matrix_func, repeat(chain), rates, fission_yields) + matrices = map(matrix_func, repeat(chain), rates, fission_yields, + *matrix_args) - inputs = zip(matrices, x, repeat(dt)) + if (transfer_rates is not None and + current_timestep in transfer_rates.external_timesteps): + # Calculate transfer rate terms as diagonal matrices + transfers = map(chain.form_rr_term, repeat(transfer_rates), + repeat(current_timestep), transfer_rates.local_mats) + + # Subtract transfer rate terms from Bateman matrices + matrices = [matrix - transfer for (matrix, transfer) in zip(matrices, + transfers)] + + if current_timestep in transfer_rates.index_transfer: + # Gather all on comm.rank 0 + matrices = comm.gather(matrices) + n = comm.gather(n) + + if comm.rank == 0: + # Expand lists + matrices = [elm for matrix in matrices for elm in matrix] + n = [n_elm for n_mat in n for n_elm in n_mat] + + # Calculate transfer rate terms as diagonal matrices + transfer_pair = {} + for mat_pair in transfer_rates.index_transfer[current_timestep]: + transfer_matrix = chain.form_rr_term(transfer_rates, + current_timestep, + mat_pair) + transfer_pair[mat_pair] = transfer_matrix + + # Combine all matrices together in a single matrix of matrices + # to be solved in one go + n_rows = n_cols = len(transfer_rates.burnable_mats) + rows = [] + for row in range(n_rows): + cols = [] + for col in range(n_cols): + mat_pair = (transfer_rates.burnable_mats[row], + transfer_rates.burnable_mats[col]) + if row == col: + # Fill the diagonals with the Bateman matrices + cols.append(matrices[row]) + elif mat_pair in transfer_rates.index_transfer[current_timestep]: + # Fill the off-diagonals with the transfer pair matrices + cols.append(transfer_pair[mat_pair]) + else: + cols.append(None) + + rows.append(cols) + matrix = bmat(rows) + + # Concatenate vectors of nuclides in one + n_multi = np.concatenate(n) + n_result = func(matrix, n_multi, dt) + + # Split back the nuclide vector result into the original form + n_result = np.split(n_result, np.cumsum([len(i) for i in n])[:-1]) + + else: + n_result = None + + # Braodcast result to other ranks + n_result = comm.bcast(n_result) + # Distribute results across MPI + n_result = _distribute(n_result) + + return n_result + + if (external_source_rates is not None and + current_timestep in external_source_rates.external_timesteps): + # Calculate external source term vectors + sources = map(chain.form_ext_source_term, repeat(external_source_rates), + repeat(current_timestep), external_source_rates.local_mats) + + # stack vector column at the end of the matrix + matrices = [ + hstack([matrix, source]) + for matrix, source in zip(matrices, sources) + ] + + # Add a last row of zeroes to the matrices and append 1 to the last row + # of the nuclide vectors + for i, matrix in enumerate(matrices): + if not np.equal(*matrix.shape): + matrices[i] = vstack([matrix, csc_matrix([0]*matrix.shape[1])]) + n[i] = np.append(n[i], 1.0) + + inputs = zip(matrices, n, repeat(dt)) if USE_MULTIPROCESSING: - with Pool() as pool: - x_result = list(pool.starmap(func, inputs)) + with Pool(NUM_PROCESSES) as pool: + n_result = list(pool.starmap(func, inputs)) else: - x_result = list(starmap(func, inputs)) + n_result = list(starmap(func, inputs)) - return x_result + # Remove extra value at the end of the nuclide vectors + if (external_source_rates is not None and + current_timestep in external_source_rates.external_timesteps): + external_source_rates.reformat_nuclide_vectors(n) + external_source_rates.reformat_nuclide_vectors(n_result) + + return n_result diff --git a/openmc/deplete/reaction_rates.py b/openmc/deplete/reaction_rates.py index 299f1133f6..714d9048b4 100644 --- a/openmc/deplete/reaction_rates.py +++ b/openmc/deplete/reaction_rates.py @@ -2,6 +2,7 @@ An ndarray to store reaction rates with string, integer, or slice indexing. """ + import numpy as np @@ -29,11 +30,11 @@ class ReactionRates(np.ndarray): Attributes ---------- - index_mat : OrderedDict of str to int + index_mat : dict of str to int A dictionary mapping material ID as string to index. - index_nuc : OrderedDict of str to int + index_nuc : dict of str to int A dictionary mapping nuclide name as string to index. - index_rx : OrderedDict of str to int + index_rx : dict of str to int A dictionary mapping reaction name as string to index. n_mat : int Number of materials. @@ -51,6 +52,10 @@ class ReactionRates(np.ndarray): # the __array_finalize__ method (discussed here: # https://docs.scipy.org/doc/numpy/user/basics.subclassing.html) + index_mat: dict[str, int] + index_nuc: dict[str, int] + index_rx: dict[str, int] + def __new__(cls, local_mats, nuclides, reactions, from_results=False): # Create appropriately-sized zeroed-out ndarray shape = (len(local_mats), len(nuclides), len(reactions)) diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index cda1864a1e..7427abd735 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -1,519 +1,643 @@ -"""The results module. - -Contains results generation and saving capabilities. -""" - -from collections import OrderedDict -import copy +import numbers +import bisect +import math +from collections.abc import Iterable +from warnings import warn import h5py import numpy as np -import openmc -from openmc.mpi import comm, MPI -from .reaction_rates import ReactionRates +from .stepresult import StepResult, VERSION_RESULTS +import openmc.checkvalue as cv +from openmc.data import atomic_mass, AVOGADRO +from openmc.data.library import DataLibrary +from openmc.material import Material, Materials +from openmc.exceptions import DataError +from openmc.checkvalue import PathLike -VERSION_RESULTS = (1, 1) +__all__ = ["Results", "ResultsList"] + +_SECONDS_PER_MINUTE = 60 +_SECONDS_PER_HOUR = 60*60 +_SECONDS_PER_DAY = 24*60*60 +_SECONDS_PER_JULIAN_YEAR = 365.25*24*60*60 # 365.25 due to the leap year -__all__ = ["Results"] +def _get_time_as(seconds: float, units: str) -> float: + """Converts the time in seconds to time in different units - -class Results: - """Output of a depletion run - - Attributes + Parameters ---------- - k : list of (float, float) - Eigenvalue and uncertainty for each substep. - time : list of float - Time at beginning, end of step, in seconds. - source_rate : float - Source rate during timestep in [W] or [neutron/sec] - n_mat : int - Number of mats. - n_nuc : int - Number of nuclides. - rates : list of ReactionRates - The reaction rates for each substep. - volume : OrderedDict of str to float - Dictionary mapping mat id to volume. - mat_to_ind : OrderedDict of str to int - A dictionary mapping mat ID as string to index. - nuc_to_ind : OrderedDict of str to int - A dictionary mapping nuclide name as string to index. - mat_to_hdf5_ind : OrderedDict of str to int - A dictionary mapping mat ID as string to global index. - n_hdf5_mats : int - Number of materials in entire geometry. - n_stages : int - Number of stages in simulation. - data : numpy.ndarray - Atom quantity, stored by stage, mat, then by nuclide. - proc_time: int - Average time spent depleting a material across all - materials and processes + seconds : float + The time to convert expressed in seconds + units : {"s", "min", "h", "d", "a"} + The units to convert time into. Available options are seconds ``"s"``, + minutes ``"min"``, hours ``"h"`` days ``"d"``, Julian years ``"a"`` """ - def __init__(self): - self.k = None - self.time = None - self.source_rate = None - self.rates = None - self.volume = None - self.proc_time = None + if units == "a": + return seconds / _SECONDS_PER_JULIAN_YEAR + if units == "d": + return seconds / _SECONDS_PER_DAY + elif units == "h": + return seconds / _SECONDS_PER_HOUR + elif units == "min": + return seconds / _SECONDS_PER_MINUTE + else: + return seconds - self.mat_to_ind = None - self.nuc_to_ind = None - self.mat_to_hdf5_ind = None - self.data = None +class Results(list): + """Results from a depletion simulation - def __getitem__(self, pos): - """Retrieves an item from results. + The :class:`Results` class acts as a list that stores the results from + each depletion step and provides extra methods for interrogating these + results. - Parameters - ---------- - pos : tuple - A three-length tuple containing a stage index, mat index and a nuc - index. All can be integers or slices. The second two can be - strings corresponding to their respective dictionary. + .. versionchanged:: 0.13.1 + Name changed from ``ResultsList`` to ``Results`` - Returns - ------- - float - The atoms for stage, mat, nuc + Parameters + ---------- + filename : str, optional + Path to depletion result file - """ - stage, mat, nuc = pos - if isinstance(mat, str): - mat = self.mat_to_ind[mat] - if isinstance(nuc, str): - nuc = self.nuc_to_ind[nuc] + """ + def __init__(self, filename='depletion_results.h5'): + data = [] + if filename is not None: + with h5py.File(str(filename), "r") as fh: + cv.check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0]) - return self.data[stage, mat, nuc] + # Get number of results stored + n = fh["number"][...].shape[0] - def __setitem__(self, pos, val): - """Sets an item from results. + for i in range(n): + data.append(StepResult.from_hdf5(fh, i)) + super().__init__(data) - Parameters - ---------- - pos : tuple - A three-length tuple containing a stage index, mat index and a nuc - index. All can be integers or slices. The second two can be - strings corresponding to their respective dictionary. - - val : float - The value to set data to. - - """ - stage, mat, nuc = pos - if isinstance(mat, str): - mat = self.mat_to_ind[mat] - if isinstance(nuc, str): - nuc = self.nuc_to_ind[nuc] - - self.data[stage, mat, nuc] = val - - @property - def n_mat(self): - return len(self.mat_to_ind) - - @property - def n_nuc(self): - return len(self.nuc_to_ind) - - @property - def n_hdf5_mats(self): - return len(self.mat_to_hdf5_ind) - - @property - def n_stages(self): - return self.data.shape[0] - - def allocate(self, volume, nuc_list, burn_list, full_burn_list, stages): - """Allocates memory of Results. - - Parameters - ---------- - volume : dict of str float - Volumes corresponding to materials in full_burn_dict - nuc_list : list of str - A list of all nuclide names. Used for sorting the simulation. - burn_list : list of int - A list of all mat IDs to be burned. Used for sorting the simulation. - full_burn_list : list of str - List of all burnable material IDs - stages : int - Number of stages in simulation. - - """ - self.volume = copy.deepcopy(volume) - self.nuc_to_ind = {nuc: i for i, nuc in enumerate(nuc_list)} - self.mat_to_ind = {mat: i for i, mat in enumerate(burn_list)} - self.mat_to_hdf5_ind = {mat: i for i, mat in enumerate(full_burn_list)} - - # Create storage array - self.data = np.zeros((stages, self.n_mat, self.n_nuc)) - - def distribute(self, local_materials, ranges): - """Create a new object containing data for distributed materials - - Parameters - ---------- - local_materials : iterable of str - Materials for this process - ranges : iterable of int - Slice-like object indicating indicies of ``local_materials`` - in the material dimension of :attr:`data` and each element - in :attr:`rates` - - Returns - ------- - Results - New results object - """ - new = Results() - new.volume = {lm: self.volume[lm] for lm in local_materials} - new.mat_to_ind = {mat: idx for (idx, mat) in enumerate(local_materials)} - - # Direct transfer - direct_attrs = ("time", "k", "source_rate", "nuc_to_ind", - "mat_to_hdf5_ind", "proc_time") - for attr in direct_attrs: - setattr(new, attr, getattr(self, attr)) - # Get applicable slice of data - new.data = self.data[:, ranges] - new.rates = [r[ranges] for r in self.rates] - return new - - def export_to_hdf5(self, filename, step): - """Export results to an HDF5 file + @classmethod + def from_hdf5(cls, filename: PathLike): + """Load in depletion results from a previous file Parameters ---------- filename : str - The filename to write to - step : int - What step is this? + Path to depletion result file + + Returns + ------- + Results + New instance of depletion results """ - # Write new file if first time step, else add to existing file - kwargs = {'mode': "w" if step == 0 else "a"} + warn( + "The ResultsList.from_hdf5(...) method is no longer necessary and will " + "be removed in a future version of OpenMC. Use Results(...) instead.", + FutureWarning + ) + return cls(filename) - if h5py.get_config().mpi and comm.size > 1: - # Write results in parallel - kwargs['driver'] = 'mpio' - kwargs['comm'] = comm - with h5py.File(filename, **kwargs) as handle: - self._to_hdf5(handle, step, parallel=True) + def get_activity( + self, + mat: Material | str, + units: str = "Bq/cm3", + by_nuclide: bool = False, + volume: float | None = None + ) -> tuple[np.ndarray, np.ndarray | list[dict]]: + """Get activity of material over time. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + mat : openmc.Material, str + Material object or material id to evaluate + units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'} + Specifies the type of activity to return, options include total + activity [Bq], specific [Bq/g, Bq/kg] or volumetric activity [Bq/cm3]. + by_nuclide : bool + Specifies if the activity should be returned for the material as a + whole or per nuclide. Default is False. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + Returns + ------- + times : numpy.ndarray + Array of times in [s] + activities : numpy.ndarray or List[dict] + Array of total activities if by_nuclide = False (default) + or list of dictionaries of activities by nuclide if + by_nuclide = True. + + """ + if isinstance(mat, Material): + mat_id = str(mat.id) + elif isinstance(mat, str): + mat_id = mat else: - # Gather results at root process - all_results = comm.gather(self) + raise TypeError('mat should be of type openmc.Material or str') - # Only root process writes results - if comm.rank == 0: - with h5py.File(filename, **kwargs) as handle: - for res in all_results: - res._to_hdf5(handle, step, parallel=False) - - def _write_hdf5_metadata(self, handle): - """Writes result metadata in HDF5 file - - Parameters - ---------- - handle : h5py.File or h5py.Group - An hdf5 file or group type to store this in. - - """ - # Create and save the 5 dictionaries: - # quantities - # self.mat_to_ind -> self.volume (TODO: support for changing volumes) - # self.nuc_to_ind - # reactions - # self.rates[0].nuc_to_ind (can be different from above, above is superset) - # self.rates[0].react_to_ind - # these are shared by every step of the simulation, and should be deduplicated. - - # Store concentration mat and nuclide dictionaries (along with volumes) - - handle.attrs['version'] = np.array(VERSION_RESULTS) - handle.attrs['filetype'] = np.string_('depletion results') - - mat_list = sorted(self.mat_to_hdf5_ind, key=int) - nuc_list = sorted(self.nuc_to_ind) - rxn_list = sorted(self.rates[0].index_rx) - - n_mats = self.n_hdf5_mats - n_nuc_number = len(nuc_list) - n_nuc_rxn = len(self.rates[0].index_nuc) - n_rxn = len(rxn_list) - n_stages = self.n_stages - - mat_group = handle.create_group("materials") - - for mat in mat_list: - mat_single_group = mat_group.create_group(mat) - mat_single_group.attrs["index"] = self.mat_to_hdf5_ind[mat] - mat_single_group.attrs["volume"] = self.volume[mat] - - nuc_group = handle.create_group("nuclides") - - for nuc in nuc_list: - nuc_single_group = nuc_group.create_group(nuc) - nuc_single_group.attrs["atom number index"] = self.nuc_to_ind[nuc] - if nuc in self.rates[0].index_nuc: - nuc_single_group.attrs["reaction rate index"] = self.rates[0].index_nuc[nuc] - - rxn_group = handle.create_group("reactions") - - for rxn in rxn_list: - rxn_single_group = rxn_group.create_group(rxn) - rxn_single_group.attrs["index"] = self.rates[0].index_rx[rxn] - - # Construct array storage - - handle.create_dataset("number", (1, n_stages, n_mats, n_nuc_number), - maxshape=(None, n_stages, n_mats, n_nuc_number), - chunks=(1, 1, n_mats, n_nuc_number), - dtype='float64') - - handle.create_dataset("reaction rates", (1, n_stages, n_mats, n_nuc_rxn, n_rxn), - maxshape=(None, n_stages, n_mats, n_nuc_rxn, n_rxn), - chunks=(1, 1, n_mats, n_nuc_rxn, n_rxn), - dtype='float64') - - handle.create_dataset("eigenvalues", (1, n_stages, 2), - maxshape=(None, n_stages, 2), dtype='float64') - - handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype='float64') - - handle.create_dataset("source_rate", (1, n_stages), maxshape=(None, n_stages), - dtype='float64') - - handle.create_dataset( - "depletion time", (1,), maxshape=(None,), - dtype="float64") - - def _to_hdf5(self, handle, index, parallel=False): - """Converts results object into an hdf5 object. - - Parameters - ---------- - handle : h5py.File or h5py.Group - An HDF5 file or group type to store this in. - index : int - What step is this? - parallel : bool - Being called with parallel HDF5? - - """ - if "/number" not in handle: - if parallel: - comm.barrier() - self._write_hdf5_metadata(handle) - - if parallel: - comm.barrier() - - # Grab handles - number_dset = handle["/number"] - rxn_dset = handle["/reaction rates"] - eigenvalues_dset = handle["/eigenvalues"] - time_dset = handle["/time"] - source_rate_dset = handle["/source_rate"] - proc_time_dset = handle["/depletion time"] - - # Get number of results stored - number_shape = list(number_dset.shape) - number_results = number_shape[0] - - new_shape = index + 1 - - if number_results < new_shape: - # Extend first dimension by 1 - number_shape[0] = new_shape - number_dset.resize(number_shape) - - rxn_shape = list(rxn_dset.shape) - rxn_shape[0] = new_shape - rxn_dset.resize(rxn_shape) - - eigenvalues_shape = list(eigenvalues_dset.shape) - eigenvalues_shape[0] = new_shape - eigenvalues_dset.resize(eigenvalues_shape) - - time_shape = list(time_dset.shape) - time_shape[0] = new_shape - time_dset.resize(time_shape) - - source_rate_shape = list(source_rate_dset.shape) - source_rate_shape[0] = new_shape - source_rate_dset.resize(source_rate_shape) - - proc_shape = list(proc_time_dset.shape) - proc_shape[0] = new_shape - proc_time_dset.resize(proc_shape) - - # If nothing to write, just return - if len(self.mat_to_ind) == 0: - return - - # Add data - # Note, for the last step, self.n_stages = 1, even if n_stages != 1. - n_stages = self.n_stages - inds = [self.mat_to_hdf5_ind[mat] for mat in self.mat_to_ind] - low = min(inds) - high = max(inds) - for i in range(n_stages): - number_dset[index, i, low:high+1] = self.data[i] - rxn_dset[index, i, low:high+1] = self.rates[i] - if comm.rank == 0: - eigenvalues_dset[index, i] = self.k[i] - if comm.rank == 0: - time_dset[index] = self.time - source_rate_dset[index] = self.source_rate - if self.proc_time is not None: - proc_time_dset[index] = ( - self.proc_time / (comm.size * self.n_hdf5_mats) - ) - - @classmethod - def from_hdf5(cls, handle, step): - """Loads results object from HDF5. - - Parameters - ---------- - handle : h5py.File or h5py.Group - An HDF5 file or group type to load from. - step : int - Index for depletion step - """ - results = cls() - - # Grab handles - number_dset = handle["/number"] - eigenvalues_dset = handle["/eigenvalues"] - time_dset = handle["/time"] - if "source_rate" in handle: - source_rate_dset = handle["/source_rate"] + times = np.empty_like(self, dtype=float) + if by_nuclide: + activities = [None] * len(self) else: - # Older versions used "power" instead of "source_rate" - source_rate_dset = handle["/power"] + activities = np.empty_like(self, dtype=float) - results.data = number_dset[step, :, :, :] - results.k = eigenvalues_dset[step, :] - results.time = time_dset[step, :] - results.source_rate = source_rate_dset[step, :] + # Evaluate activity for each depletion time + for i, result in enumerate(self): + times[i] = result.time[0] + activities[i] = result.get_material(mat_id).get_activity(units, by_nuclide, volume) - if "depletion time" in handle: - proc_time_dset = handle["/depletion time"] - if step < proc_time_dset.shape[0]: - results.proc_time = proc_time_dset[step] + return times, activities - if results.proc_time is None: - results.proc_time = np.array([np.nan]) - - # Reconstruct dictionaries - results.volume = OrderedDict() - results.mat_to_ind = OrderedDict() - results.nuc_to_ind = OrderedDict() - rxn_nuc_to_ind = OrderedDict() - rxn_to_ind = OrderedDict() - - for mat, mat_handle in handle["/materials"].items(): - vol = mat_handle.attrs["volume"] - ind = mat_handle.attrs["index"] - - results.volume[mat] = vol - results.mat_to_ind[mat] = ind - - for nuc, nuc_handle in handle["/nuclides"].items(): - ind_atom = nuc_handle.attrs["atom number index"] - results.nuc_to_ind[nuc] = ind_atom - - if "reaction rate index" in nuc_handle.attrs: - rxn_nuc_to_ind[nuc] = nuc_handle.attrs["reaction rate index"] - - for rxn, rxn_handle in handle["/reactions"].items(): - rxn_to_ind[rxn] = rxn_handle.attrs["index"] - - results.rates = [] - # Reconstruct reactions - for i in range(results.n_stages): - rate = ReactionRates(results.mat_to_ind, rxn_nuc_to_ind, rxn_to_ind, True) - - rate[:] = handle["/reaction rates"][step, i, :, :, :] - results.rates.append(rate) - - return results - - @staticmethod - def save(op, x, op_results, t, source_rate, step_ind, proc_time=None): - """Creates and writes depletion results to disk + def get_atoms( + self, + mat: Material | str, + nuc: str, + nuc_units: str = "atoms", + time_units: str = "s" + ) -> tuple[np.ndarray, np.ndarray]: + """Get number of nuclides over time from a single material Parameters ---------- - op : openmc.deplete.TransportOperator - The operator used to generate these results. - x : list of list of numpy.array - The prior x vectors. Indexed [i][cell] using the above equation. - op_results : list of openmc.deplete.OperatorResult - Results of applying transport operator - t : list of float - Time indices. - source_rate : float - Source rate during time step in [W] or [neutron/sec] - step_ind : int - Step index. - proc_time : float or None - Total process time spent depleting materials. This may - be process-dependent and will be reduced across MPI - processes. + mat : openmc.Material, str + Material object or material id to evaluate + nuc : str + Nuclide name to evaluate + nuc_units : {"atoms", "atom/b-cm", "atom/cm3"}, optional + Units for the returned concentration. Default is ``"atoms"`` + + .. versionadded:: 0.12 + time_units : {"s", "min", "h", "d", "a"}, optional + Units for the returned time array. Default is ``"s"`` to + return the value in seconds. Other options are minutes ``"min"``, + hours ``"h"``, days ``"d"``, and Julian years ``"a"``. + + .. versionadded:: 0.12 + + Returns + ------- + times : numpy.ndarray + Array of times in units of ``time_units`` + concentrations : numpy.ndarray + Concentration of specified nuclide in units of ``nuc_units`` """ - # Get indexing terms - vol_dict, nuc_list, burn_list, full_burn_list = op.get_results_info() + cv.check_value("time_units", time_units, {"s", "d", "min", "h", "a"}) + cv.check_value("nuc_units", nuc_units, + {"atoms", "atom/b-cm", "atom/cm3"}) - stages = len(x) - - # Create results - results = Results() - results.allocate(vol_dict, nuc_list, burn_list, full_burn_list, stages) - - n_mat = len(burn_list) - - for i in range(stages): - for mat_i in range(n_mat): - results[i, mat_i, :] = x[i][mat_i] - - results.k = [(r.k.nominal_value, r.k.std_dev) for r in op_results] - results.rates = [r.rates for r in op_results] - results.time = t - results.source_rate = source_rate - results.proc_time = proc_time - if results.proc_time is not None: - results.proc_time = comm.reduce(proc_time, op=MPI.SUM) - - results.export_to_hdf5("depletion_results.h5", step_ind) - - def transfer_volumes(self, model): - """Transfers volumes from depletion results to geometry - - Parameters - ---------- - model : OpenMC model to be used in a depletion restart - calculation - - """ - - if not model.materials: - materials = openmc.Materials( - model.geometry.get_all_materials().values() - ) + if isinstance(mat, Material): + mat_id = str(mat.id) + elif isinstance(mat, str): + mat_id = mat else: - materials = model.materials + raise TypeError('mat should be of type openmc.Material or str') + times = np.empty_like(self, dtype=float) + concentrations = np.empty_like(self, dtype=float) - for material in materials: - if material.depletable: - material.volume = self.volume[str(material.id)] \ No newline at end of file + # Evaluate value in each region + for i, result in enumerate(self): + times[i] = result.time[0] + concentrations[i] = result[0, mat_id, nuc] + + # Unit conversions + times = _get_time_as(times, time_units) + if nuc_units != "atoms": + # Divide by volume to get density + concentrations /= self[0].volume[mat_id] + if nuc_units == "atom/b-cm": + # 1 barn = 1e-24 cm^2 + concentrations *= 1e-24 + + return times, concentrations + + def get_decay_heat( + self, + mat: Material | str, + units: str = "W", + by_nuclide: bool = False, + volume: float | None = None + ) -> tuple[np.ndarray, np.ndarray | list[dict]]: + """Get decay heat of material over time. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + mat : openmc.Material, str + Material object or material id to evaluate. + units : {'W', 'W/g', 'W/kg', 'W/cm3'} + Specifies the units of decay heat to return. Options include total + heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3]. + by_nuclide : bool + Specifies if the decay heat should be returned for the material as a + whole or per nuclide. Default is False. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + Returns + ------- + times : numpy.ndarray + Array of times in [s] + decay_heat : numpy.ndarray or list[dict] + Array of total decay heat values if by_nuclide = False (default) + or list of dictionaries of decay heat values by nuclide if + by_nuclide = True. + """ + + if isinstance(mat, Material): + mat_id = str(mat.id) + elif isinstance(mat, str): + mat_id = mat + else: + raise TypeError('mat should be of type openmc.Material or str') + + times = np.empty_like(self, dtype=float) + if by_nuclide: + decay_heat = [None] * len(self) + else: + decay_heat = np.empty_like(self, dtype=float) + + # Evaluate decay heat for each depletion time + for i, result in enumerate(self): + times[i] = result.time[0] + decay_heat[i] = result.get_material(mat_id).get_decay_heat( + units, by_nuclide, volume) + + return times, decay_heat + + def get_mass(self, + mat: Material | str, + nuc: str, + mass_units: str = "g", + time_units: str = "s" + ) -> tuple[np.ndarray, np.ndarray]: + """Get mass of nuclides over time from a single material + + .. versionadded:: 0.14.0 + + Parameters + ---------- + mat : openmc.Material, str + Material object or material id to evaluate + nuc : str + Nuclide name to evaluate + mass_units : {"g", "g/cm3", "kg"}, optional + Units for the returned mass. + time_units : {"s", "min", "h", "d", "a"}, optional + Units for the returned time array. Default is ``"s"`` to + return the value in seconds. Other options are minutes ``"min"``, + hours ``"h"``, days ``"d"``, and Julian years ``"a"``. + + Returns + ------- + times : numpy.ndarray + Array of times in units of ``time_units`` + mass : numpy.ndarray + Mass of specified nuclide in units of ``mass_units`` + + """ + cv.check_value("mass_units", mass_units, {"g", "g/cm3", "kg"}) + + if isinstance(mat, Material): + mat_id = str(mat.id) + elif isinstance(mat, str): + mat_id = mat + else: + raise TypeError('mat should be of type openmc.Material or str') + + times, atoms = self.get_atoms(mat, nuc, time_units=time_units) + + mass = atoms * atomic_mass(nuc) / AVOGADRO + + # Unit conversions + if mass_units == "g/cm3": + # Divide by volume to get density + mass /= self[0].volume[mat_id] + elif mass_units == "kg": + mass /= 1e3 + + return times, mass + + def get_reaction_rate( + self, + mat: Material | str, + nuc: str, + rx: str + ) -> tuple[np.ndarray, np.ndarray]: + """Get reaction rate in a single material/nuclide over time + + Parameters + ---------- + mat : openmc.Material, str + Material object or material id to evaluate + nuc : str + Nuclide name to evaluate + rx : str + Reaction rate to evaluate + + Returns + ------- + times : numpy.ndarray + Array of times in [s] + rates : numpy.ndarray + Array of reaction rates + + """ + times = np.empty_like(self, dtype=float) + rates = np.empty_like(self, dtype=float) + + if isinstance(mat, Material): + mat_id = str(mat.id) + elif isinstance(mat, str): + mat_id = mat + else: + raise TypeError('mat should be of type openmc.Material or str') + + # Evaluate value in each region + for i, result in enumerate(self): + times[i] = result.time[0] + rates[i] = result.rates[0].get(mat_id, nuc, rx) * result[0, mat, nuc] + + return times, rates + + def get_keff(self, time_units: str = 's') -> tuple[np.ndarray, np.ndarray]: + """Evaluates the eigenvalue from a results list. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + time_units : {"s", "d", "min", "h", "a"}, optional + Desired units for the times array. Options are seconds ``"s"``, + minutes ``"min"``, hours ``"h"``, days ``"d"``, and Julian years + ``"a"``. + + Returns + ------- + times : numpy.ndarray + Array of times in specified units + eigenvalues : numpy.ndarray + k-eigenvalue at each time. Column 0 + contains the eigenvalue, while column + 1 contains the associated uncertainty + + """ + cv.check_value("time_units", time_units, {"s", "d", "min", "h", "a"}) + + times = np.empty_like(self, dtype=float) + eigenvalues = np.empty((len(self), 2), dtype=float) + + # Get time/eigenvalue at each point + for i, result in enumerate(self): + times[i] = result.time[0] + eigenvalues[i] = result.k[0] + + # Convert time units if necessary + times = _get_time_as(times, time_units) + return times, eigenvalues + + def get_eigenvalue(self, time_units: str = 's') -> tuple[np.ndarray, np.ndarray]: + warn("The get_eigenvalue(...) function has been renamed get_keff and " + "will be removed in a future version of OpenMC.", FutureWarning) + return self.get_keff(time_units) + + def get_depletion_time(self) -> np.ndarray: + """Return an array of the average time to deplete a material + + .. note:: + The return value will have one fewer values than several other + methods, such as :meth:`get_keff`, because no depletion is performed + at the final transport stage. + + Returns + ------- + times : numpy.ndarray + Vector of average time to deplete a single material + across all processes and materials. + + """ + times = np.empty(len(self) - 1) + # Need special logic because the predictor + # writes EOS values for step i as BOS values + # for step i+1 + # The first proc_time may be zero + if self[0].proc_time > 0.0: + items = self[:-1] + else: + items = self[1:] + for ix, res in enumerate(items): + times[ix] = res.proc_time + return times + + def get_times(self, time_units: str = "d") -> np.ndarray: + """Return the points in time that define the depletion schedule + + .. versionadded:: 0.12.1 + + Parameters + ---------- + time_units : {"s", "d", "min", "h", "a"}, optional + Return the vector in these units. Default is to + convert to days ``"d"``. Other options are seconds ``"s"``, minutes + ``"min"``, hours ``"h"``, days ``"d"``, and Julian years ``"a"``. + + Returns + ------- + numpy.ndarray + 1-D vector of time points + + """ + cv.check_value("time_units", time_units, {"s", "d", "min", "h", "a"}) + + times = np.fromiter( + (r.time[0] for r in self), + dtype=self[0].time.dtype, + count=len(self), + ) + + return _get_time_as(times, time_units) + + def get_source_rates(self) -> np.ndarray: + """ + .. versionadded:: 0.15.1 + + Returns + ------- + numpy.ndarray + 1-D vector of source rates at each point in the depletion simulation + with the units originally defined by the user. + + """ + # Results duplicate the final source rate at the final simulation time + source_rates = np.fromiter( + (r.source_rate for r in self), + dtype=self[0].source_rate.dtype, + count=len(self)-1, + ) + + return source_rates + + def get_step_where( + self, time, time_units: str = "d", atol: float = 1e-6, rtol: float = 1e-3 + ) -> int: + """Return the index closest to a given point in time + + In the event ``time`` lies exactly between two points, the + lower index will be returned. It is possible that the index + will be at most one past the point in time requested, but only + according to tolerances requested. + + Passing ``atol=math.inf`` and ``rtol=math.inf`` will return + the closest index to the requested point. + + .. versionadded:: 0.12.1 + + Parameters + ---------- + time : float + Desired point in time + time_units : {"s", "d", "min", "h", "a"}, optional + Units on ``time``. Default: days ``"d"``. Other options are seconds + ``"s"``, minutes ``"min"``, hours ``"h"`` and Julian years ``"a"``. + atol : float, optional + Absolute tolerance (in ``time_units``) if ``time`` is not + found. + rtol : float, optional + Relative tolerance if ``time`` is not found. + + Returns + ------- + int + + """ + cv.check_type("time", time, numbers.Real) + cv.check_type("atol", atol, numbers.Real) + cv.check_type("rtol", rtol, numbers.Real) + + times = self.get_times(time_units) + + if times[0] < time < times[-1]: + ix = bisect.bisect_left(times, time) + if ix == times.size: + ix -= 1 + # Bisection will place us either directly on the point + # or one-past the first value less than time + elif time - times[ix - 1] <= times[ix] - time: + ix -= 1 + elif times[0] >= time: + ix = 0 + elif time >= times[-1]: + ix = times.size - 1 + + if math.isclose(time, times[ix], rel_tol=rtol, abs_tol=atol): + return ix + + closest = min(times, key=lambda t: abs(time - t)) + raise ValueError( + f"A value of {time} {time_units} was not found given absolute and " + f"relative tolerances {atol} and {rtol}. Closest time is {closest} " + f"{time_units}." + ) + + def export_to_materials( + self, + burnup_index: int, + nuc_with_data: Iterable[str] | None = None, + path: PathLike = 'materials.xml' + ) -> Materials: + """Return openmc.Materials object based on results at a given step + + .. versionadded:: 0.12.1 + + Parameters + ---------- + burn_index : int + Index of burnup step to evaluate. See also: get_step_where for + obtaining burnup step indices from other data such as the time. + nuc_with_data : Iterable of str, optional + Nuclides to include in resulting materials. + This can be specified if not all nuclides appearing in + depletion results have associated neutron cross sections, and + as such cannot be used in subsequent transport calculations. + If not provided, nuclides from the cross_sections element of + materials.xml will be used. If that element is not present, + nuclides from openmc.config['cross_sections'] will be used. + path : PathLike + Path to materials XML file to read. Defaults to 'materials.xml'. + + .. versionadded:: 0.13.3 + + Returns + ------- + mat_file : Materials + A modified Materials instance containing depleted material data + and original isotopic compositions of non-depletable materials + """ + result = self[burnup_index] + + # Only materials found in the original materials.xml file will be + # updated. If for some reason you have modified OpenMC to produce + # new materials as depletion takes place, this method will not + # work as expected and leave out that material. + mat_file = Materials.from_xml(path) + + # Only nuclides with valid transport data will be written to + # the new materials XML file. The precedence of nuclides to select + # is first ones provided as a kwarg here, then ones specified + # in the materials.xml file if provided, then finally from + # openmc.config['cross_sections']. + if nuc_with_data: + cv.check_iterable_type('nuclide names', nuc_with_data, str) + available_cross_sections = nuc_with_data + else: + # select cross_sections.xml file to use + if mat_file.cross_sections: + this_library = DataLibrary.from_xml(path=mat_file.cross_sections) + else: + this_library = DataLibrary.from_xml() + + # Find neutron libraries we have access to + available_cross_sections = set() + for lib in this_library.libraries: + if lib['type'] == 'neutron': + available_cross_sections.update(lib['materials']) + if not available_cross_sections: + raise DataError('No neutron libraries found in cross_sections.xml') + + # Overwrite material definitions, if they can be found in the depletion + # results, and save them to the new depleted xml file. + for mat in mat_file: + mat_id = str(mat.id) + if mat_id in result.index_mat: + mat.volume = result.volume[mat_id] + + # Change density of all nuclides in material to atom/b-cm + atoms_per_barn_cm = mat.get_nuclide_atom_densities() + for nuc, value in atoms_per_barn_cm.items(): + mat.remove_nuclide(nuc) + mat.add_nuclide(nuc, value) + mat.set_density('sum') + + # For nuclides in chain that have cross sections, replace + # density in original material with new density from results + for nuc in result.index_nuc: + if nuc not in available_cross_sections: + continue + atoms = result[0, mat_id, nuc] + if atoms > 0.0: + atoms_per_barn_cm = 1e-24 * atoms / mat.volume + mat.remove_nuclide(nuc) # Replace if it's there + mat.add_nuclide(nuc, atoms_per_barn_cm) + + return mat_file + + +# Retain deprecated name for the time being +ResultsList = Results diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py deleted file mode 100644 index a9a0e52119..0000000000 --- a/openmc/deplete/results_list.py +++ /dev/null @@ -1,374 +0,0 @@ -import numbers -import bisect -import math - -import h5py -import numpy as np - -from .results import Results, VERSION_RESULTS -import openmc.checkvalue as cv -from openmc.data.library import DataLibrary -from openmc.material import Material, Materials -from openmc.exceptions import DataError, InvalidArgumentError - -__all__ = ["ResultsList"] - - -class ResultsList(list): - """A list of openmc.deplete.Results objects - - It is recommended to use :meth:`from_hdf5` over - direct creation. - """ - - @classmethod - def from_hdf5(cls, filename): - """Load in depletion results from a previous file - - Parameters - ---------- - filename : str - Path to depletion result file - - Returns - ------- - new : ResultsList - New instance of depletion results - """ - with h5py.File(str(filename), "r") as fh: - cv.check_filetype_version(fh, 'depletion results', VERSION_RESULTS[0]) - new = cls() - - # Get number of results stored - n = fh["number"][...].shape[0] - - for i in range(n): - new.append(Results.from_hdf5(fh, i)) - return new - - def get_atoms(self, mat, nuc, nuc_units="atoms", time_units="s"): - """Get number of nuclides over time from a single material - - .. note:: - Initial values for some isotopes that do not appear in - initial concentrations may be non-zero, depending on the - value of :class:`openmc.deplete.Operator` ``dilute_initial``. - The :class:`openmc.deplete.Operator` adds isotopes according - to this setting, which can be set to zero. - - Parameters - ---------- - mat : str - Material name to evaluate - nuc : str - Nuclide name to evaluate - nuc_units : {"atoms", "atom/b-cm", "atom/cm3"}, optional - Units for the returned concentration. Default is ``"atoms"`` - - .. versionadded:: 0.12 - time_units : {"s", "min", "h", "d"}, optional - Units for the returned time array. Default is ``"s"`` to - return the value in seconds. - - .. versionadded:: 0.12 - - Returns - ------- - times : numpy.ndarray - Array of times in units of ``time_units`` - concentrations : numpy.ndarray - Concentration of specified nuclide in units of ``nuc_units`` - - """ - cv.check_value("time_units", time_units, {"s", "d", "min", "h"}) - cv.check_value("nuc_units", nuc_units, - {"atoms", "atom/b-cm", "atom/cm3"}) - - times = np.empty_like(self, dtype=float) - concentrations = np.empty_like(self, dtype=float) - - # Evaluate value in each region - for i, result in enumerate(self): - times[i] = result.time[0] - concentrations[i] = result[0, mat, nuc] - - # Unit conversions - if time_units == "d": - times /= (60 * 60 * 24) - elif time_units == "h": - times /= (60 * 60) - elif time_units == "min": - times /= 60 - - if nuc_units != "atoms": - # Divide by volume to get density - concentrations /= self[0].volume[mat] - if nuc_units == "atom/b-cm": - # 1 barn = 1e-24 cm^2 - concentrations *= 1e-24 - - return times, concentrations - - def get_reaction_rate(self, mat, nuc, rx): - """Get reaction rate in a single material/nuclide over time - - .. note:: - - Initial values for some isotopes that do not appear in - initial concentrations may be non-zero, depending on the - value of :class:`openmc.deplete.Operator` ``dilute_initial`` - The :class:`openmc.deplete.Operator` adds isotopes according - to this setting, which can be set to zero. - - Parameters - ---------- - mat : str - Material name to evaluate - nuc : str - Nuclide name to evaluate - rx : str - Reaction rate to evaluate - - Returns - ------- - times : numpy.ndarray - Array of times in [s] - rates : numpy.ndarray - Array of reaction rates - - """ - times = np.empty_like(self, dtype=float) - rates = np.empty_like(self, dtype=float) - - # Evaluate value in each region - for i, result in enumerate(self): - times[i] = result.time[0] - rates[i] = result.rates[0].get(mat, nuc, rx) * result[0, mat, nuc] - - return times, rates - - def get_eigenvalue(self): - """Evaluates the eigenvalue from a results list. - - Returns - ------- - times : numpy.ndarray - Array of times in [s] - eigenvalues : numpy.ndarray - k-eigenvalue at each time. Column 0 - contains the eigenvalue, while column - 1 contains the associated uncertainty - - """ - times = np.empty_like(self, dtype=float) - eigenvalues = np.empty((len(self), 2), dtype=float) - - # Get time/eigenvalue at each point - for i, result in enumerate(self): - times[i] = result.time[0] - eigenvalues[i] = result.k[0] - - return times, eigenvalues - - def get_depletion_time(self): - """Return an array of the average time to deplete a material - - .. note:: - - Will have one fewer row than number of other methods, - like :meth:`get_eigenvalues`, because no depletion - is performed at the final transport stage - - Returns - ------- - times : numpy.ndarray - Vector of average time to deplete a single material - across all processes and materials. - - """ - times = np.empty(len(self) - 1) - # Need special logic because the predictor - # writes EOS values for step i as BOS values - # for step i+1 - # The first proc_time may be zero - if self[0].proc_time > 0.0: - items = self[:-1] - else: - items = self[1:] - for ix, res in enumerate(items): - times[ix] = res.proc_time - return times - - def get_times(self, time_units="d") -> np.ndarray: - """Return the points in time that define the depletion schedule - - - .. versionadded:: 0.12.1 - - Parameters - ---------- - time_units : {"s", "d", "h", "min"}, optional - Return the vector in these units. Default is to - convert to days - - Returns - ------- - numpy.ndarray - 1-D vector of time points - - """ - cv.check_type("time_units", time_units, str) - - times = np.fromiter( - (r.time[0] for r in self), - dtype=self[0].time.dtype, - count=len(self), - ) - - if time_units == "d": - times /= (60 * 60 * 24) - elif time_units == "h": - times /= (60 * 60) - elif time_units == "min": - times /= 60 - elif time_units != "s": - raise ValueError( - 'Unable to set "time_units" to {} since it is not ' - 'in ("s", "d", "min", "h")'.format(time_units) - ) - return times - - def get_step_where( - self, time, time_units="d", atol=1e-6, rtol=1e-3 - ) -> int: - """Return the index closest to a given point in time - - In the event ``time`` lies exactly between two points, the - lower index will be returned. It is possible that the index - will be at most one past the point in time requested, but only - according to tolerances requested. - - Passing ``atol=math.inf`` and ``rtol=math.inf`` will return - the closest index to the requested point. - - - .. versionadded:: 0.12.1 - - Parameters - ---------- - time : float - Desired point in time - time_units : {"s", "d", "min", "h"}, optional - Units on ``time``. Default: days - atol : float, optional - Absolute tolerance (in ``time_units``) if ``time`` is not - found. - rtol : float, optional - Relative tolerance if ``time`` is not found. - - Returns - ------- - int - - """ - cv.check_type("time", time, numbers.Real) - cv.check_type("atol", atol, numbers.Real) - cv.check_type("rtol", rtol, numbers.Real) - - times = self.get_times(time_units) - - if times[0] < time < times[-1]: - ix = bisect.bisect_left(times, time) - if ix == times.size: - ix -= 1 - # Bisection will place us either directly on the point - # or one-past the first value less than time - elif time - times[ix - 1] <= times[ix] - time: - ix -= 1 - elif times[0] >= time: - ix = 0 - elif time >= times[-1]: - ix = times.size - 1 - - if math.isclose(time, times[ix], rel_tol=rtol, abs_tol=atol): - return ix - - raise ValueError( - "A value of {} {} was not found given absolute and " - "relative tolerances {} and {}.".format( - time, time_units, atol, rtol) - ) - - def export_to_materials(self, burnup_index, nuc_with_data=None) -> Materials: - """Return openmc.Materials object based on results at a given step - - .. versionadded:: 0.12.1 - - Parameters - ---------- - burn_index : int - Index of burnup step to evaluate. See also: get_step_where for - obtaining burnup step indices from other data such as the time. - nuc_with_data : Iterable of str, optional - Nuclides to include in resulting materials. - This can be specified if not all nuclides appearing in - depletion results have associated neutron cross sections, and - as such cannot be used in subsequent transport calculations. - If not provided, nuclides from the cross_sections element of - materials.xml will be used. If that element is not present, - nuclides from OPENMC_CROSS_SECTIONS will be used. - - Returns - ------- - mat_file : Materials - A modified Materials instance containing depleted material data - and original isotopic compositions of non-depletable materials - """ - result = self[burnup_index] - - # Only materials found in the original materials.xml file will be - # updated. If for some reason you have modified OpenMC to produce - # new materials as depletion takes place, this method will not - # work as expected and leave out that material. - mat_file = Materials.from_xml("materials.xml") - - # Only nuclides with valid transport data will be written to - # the new materials XML file. The precedence of nuclides to select - # is first ones provided as a kwarg here, then ones specified - # in the materials.xml file if provided, then finally from - # the environment variable OPENMC_CROSS_SECTIONS. - if nuc_with_data: - cv.check_iterable_type('nuclide names', nuc_with_data, str) - available_cross_sections = nuc_with_data - else: - # select cross_sections.xml file to use - if mat_file.cross_sections: - this_library = DataLibrary.from_xml(path=mat_file.cross_sections) - else: - this_library = DataLibrary.from_xml() - - # Find neutron libraries we have access to - available_cross_sections = set() - for lib in this_library.libraries: - if lib['type'] == 'neutron': - available_cross_sections.update(lib['materials']) - if not available_cross_sections: - raise DataError('No neutron libraries found in cross_sections.xml') - - # Overwrite material definitions, if they can be found in the depletion - # results, and save them to the new depleted xml file. - for mat in mat_file: - mat_id = str(mat.id) - if mat_id in result.mat_to_ind: - mat.volume = result.volume[mat_id] - mat.set_density('sum') - for nuc in result.nuc_to_ind: - if nuc not in available_cross_sections: - continue - atoms = result[0, mat_id, nuc] - if atoms > 0.0: - atoms_per_barn_cm = 1e-24 * atoms / mat.volume - mat.remove_nuclide(nuc) # Replace if it's there - mat.add_nuclide(nuc, atoms_per_barn_cm) - - return mat_file diff --git a/openmc/deplete/stepresult.py b/openmc/deplete/stepresult.py new file mode 100644 index 0000000000..1a26cbe346 --- /dev/null +++ b/openmc/deplete/stepresult.py @@ -0,0 +1,591 @@ +"""The stepresult module. + +Contains capabilities for generating and saving results of a single depletion +timestep. +""" + +import copy +import warnings +from pathlib import Path + +import h5py +import numpy as np + +import openmc +from openmc.mpi import comm, MPI +from openmc.checkvalue import PathLike +from .reaction_rates import ReactionRates + +VERSION_RESULTS = (1, 1) + + +__all__ = ["StepResult"] + + +class StepResult: + """Result of a single depletion timestep + + .. versionchanged:: 0.13.1 + Name changed from ``Results`` to ``StepResult`` + + Attributes + ---------- + k : list of (float, float) + Eigenvalue and uncertainty for each substep. + time : list of float + Time at beginning, end of step, in seconds. + source_rate : float + Source rate during timestep in [W] or [neutron/sec] + n_mat : int + Number of mats. + n_nuc : int + Number of nuclides. + rates : list of ReactionRates + The reaction rates for each substep. + volume : dict of str to float + Dictionary mapping mat id to volume. + index_mat : dict of str to int + A dictionary mapping mat ID as string to index. + index_nuc : dict of str to int + A dictionary mapping nuclide name as string to index. + mat_to_hdf5_ind : dict of str to int + A dictionary mapping mat ID as string to global index. + n_hdf5_mats : int + Number of materials in entire geometry. + n_stages : int + Number of stages in simulation. + data : numpy.ndarray + Atom quantity, stored by stage, mat, then by nuclide. + proc_time : int + Average time spent depleting a material across all + materials and processes + + """ + def __init__(self): + self.k = None + self.time = None + self.source_rate = None + self.rates = None + self.volume = None + self.proc_time = None + + self.index_mat = None + self.index_nuc = None + self.mat_to_hdf5_ind = None + + self.data = None + + def __repr__(self): + t = self.time[0] + dt = self.time[1] - self.time[0] + return f"" + + def __getitem__(self, pos): + """Retrieves an item from results. + + Parameters + ---------- + pos : tuple + A three-length tuple containing a stage index, mat index and a nuc + index. All can be integers or slices. The second two can be + strings corresponding to their respective dictionary. + + Returns + ------- + float + The atoms for stage, mat, nuc + + """ + stage, mat, nuc = pos + if isinstance(mat, openmc.Material): + mat = str(mat.id) + if isinstance(mat, str): + mat = self.index_mat[mat] + if isinstance(nuc, str): + nuc = self.index_nuc[nuc] + + return self.data[stage, mat, nuc] + + def __setitem__(self, pos, val): + """Sets an item from results. + + Parameters + ---------- + pos : tuple + A three-length tuple containing a stage index, mat index and a nuc + index. All can be integers or slices. The second two can be + strings corresponding to their respective dictionary. + + val : float + The value to set data to. + + """ + stage, mat, nuc = pos + if isinstance(mat, str): + mat = self.index_mat[mat] + if isinstance(nuc, str): + nuc = self.index_nuc[nuc] + + self.data[stage, mat, nuc] = val + + @property + def n_mat(self): + return len(self.index_mat) + + @property + def n_nuc(self): + return len(self.index_nuc) + + @property + def n_hdf5_mats(self): + return len(self.mat_to_hdf5_ind) + + @property + def n_stages(self): + return self.data.shape[0] + + def allocate(self, volume, nuc_list, burn_list, full_burn_list, stages): + """Allocate memory for depletion step data + + Parameters + ---------- + volume : dict of str float + Volumes corresponding to materials in full_burn_dict + nuc_list : list of str + A list of all nuclide names. Used for sorting the simulation. + burn_list : list of int + A list of all mat IDs to be burned. Used for sorting the simulation. + full_burn_list : list of str + List of all burnable material IDs + stages : int + Number of stages in simulation. + + """ + self.volume = copy.deepcopy(volume) + self.index_nuc = {nuc: i for i, nuc in enumerate(nuc_list)} + self.index_mat = {mat: i for i, mat in enumerate(burn_list)} + self.mat_to_hdf5_ind = {mat: i for i, mat in enumerate(full_burn_list)} + + # Create storage array + self.data = np.zeros((stages, self.n_mat, self.n_nuc)) + + def distribute(self, local_materials, ranges): + """Create a new object containing data for distributed materials + + Parameters + ---------- + local_materials : iterable of str + Materials for this process + ranges : iterable of int + Slice-like object indicating indicies of ``local_materials`` + in the material dimension of :attr:`data` and each element + in :attr:`rates` + + Returns + ------- + StepResult + New results object + """ + new = StepResult() + new.volume = {lm: self.volume[lm] for lm in local_materials} + new.index_mat = {mat: idx for (idx, mat) in enumerate(local_materials)} + + # Direct transfer + direct_attrs = ("time", "k", "source_rate", "index_nuc", + "mat_to_hdf5_ind", "proc_time") + for attr in direct_attrs: + setattr(new, attr, getattr(self, attr)) + # Get applicable slice of data + new.data = self.data[:, ranges] + new.rates = [r[ranges] for r in self.rates] + return new + + def get_material(self, mat_id): + """Return material object for given depleted composition + + .. versionadded:: 0.13.2 + + Parameters + ---------- + mat_id : str + Material ID as a string + + Returns + ------- + openmc.Material + Equivalent material + + Raises + ------ + KeyError + If specified material ID is not found in the StepResult + + """ + with warnings.catch_warnings(): + warnings.simplefilter('ignore', openmc.IDWarning) + material = openmc.Material(material_id=int(mat_id)) + try: + vol = self.volume[mat_id] + except KeyError as e: + raise KeyError( + f'mat_id {mat_id} not found in StepResult. Available mat_id ' + f'values are {list(self.volume.keys())}' + ) from e + for nuc, _ in sorted(self.index_nuc.items(), key=lambda x: x[1]): + atoms = self[0, mat_id, nuc] + if atoms <= 0.0: + continue + atom_per_bcm = atoms / vol * 1e-24 + material.add_nuclide(nuc, atom_per_bcm) + material.volume = vol + return material + + def export_to_hdf5(self, filename, step): + """Export results to an HDF5 file + + Parameters + ---------- + filename : str + The filename to write to + step : int + What step is this? + + """ + # Write new file if first time step, else add to existing file + kwargs = {'mode': "w" if step == 0 else "a"} + + if h5py.get_config().mpi and comm.size > 1: + # Write results in parallel + kwargs['driver'] = 'mpio' + kwargs['comm'] = comm + with h5py.File(filename, **kwargs) as handle: + self._to_hdf5(handle, step, parallel=True) + else: + # Gather results at root process + all_results = comm.gather(self) + + # Only root process writes results + if comm.rank == 0: + with h5py.File(filename, **kwargs) as handle: + for res in all_results: + res._to_hdf5(handle, step, parallel=False) + + def _write_hdf5_metadata(self, handle): + """Writes result metadata in HDF5 file + + Parameters + ---------- + handle : h5py.File or h5py.Group + An hdf5 file or group type to store this in. + + """ + # Create and save the 5 dictionaries: + # quantities + # self.index_mat -> self.volume (TODO: support for changing volumes) + # self.index_nuc + # reactions + # self.rates[0].index_nuc (can be different from above, above is superset) + # self.rates[0].index_rx + # these are shared by every step of the simulation, and should be deduplicated. + + # Store concentration mat and nuclide dictionaries (along with volumes) + + handle.attrs['version'] = np.array(VERSION_RESULTS) + handle.attrs['filetype'] = np.bytes_('depletion results') + + mat_list = sorted(self.mat_to_hdf5_ind, key=int) + nuc_list = sorted(self.index_nuc) + rxn_list = sorted(self.rates[0].index_rx) + + n_mats = self.n_hdf5_mats + n_nuc_number = len(nuc_list) + n_nuc_rxn = len(self.rates[0].index_nuc) + n_rxn = len(rxn_list) + n_stages = self.n_stages + + mat_group = handle.create_group("materials") + + for mat in mat_list: + mat_single_group = mat_group.create_group(mat) + mat_single_group.attrs["index"] = self.mat_to_hdf5_ind[mat] + mat_single_group.attrs["volume"] = self.volume[mat] + + nuc_group = handle.create_group("nuclides") + + for nuc in nuc_list: + nuc_single_group = nuc_group.create_group(nuc) + nuc_single_group.attrs["atom number index"] = self.index_nuc[nuc] + if nuc in self.rates[0].index_nuc: + nuc_single_group.attrs["reaction rate index"] = self.rates[0].index_nuc[nuc] + + rxn_group = handle.create_group("reactions") + + for rxn in rxn_list: + rxn_single_group = rxn_group.create_group(rxn) + rxn_single_group.attrs["index"] = self.rates[0].index_rx[rxn] + + # Construct array storage + + handle.create_dataset("number", (1, n_stages, n_mats, n_nuc_number), + maxshape=(None, n_stages, n_mats, n_nuc_number), + chunks=True, + dtype='float64') + + if n_nuc_rxn > 0 and n_rxn > 0: + handle.create_dataset("reaction rates", (1, n_stages, n_mats, n_nuc_rxn, n_rxn), + maxshape=(None, n_stages, n_mats, n_nuc_rxn, n_rxn), + chunks=True, + dtype='float64') + + handle.create_dataset("eigenvalues", (1, n_stages, 2), + maxshape=(None, n_stages, 2), dtype='float64') + + handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype='float64') + + handle.create_dataset("source_rate", (1, n_stages), maxshape=(None, n_stages), + dtype='float64') + + handle.create_dataset( + "depletion time", (1,), maxshape=(None,), + dtype="float64") + + def _to_hdf5(self, handle, index, parallel=False): + """Converts results object into an hdf5 object. + + Parameters + ---------- + handle : h5py.File or h5py.Group + An HDF5 file or group type to store this in. + index : int + What step is this? + parallel : bool + Being called with parallel HDF5? + + """ + if "/number" not in handle: + if parallel: + comm.barrier() + self._write_hdf5_metadata(handle) + + if parallel: + comm.barrier() + + # Grab handles + number_dset = handle["/number"] + has_reactions = ("reaction rates" in handle) + if has_reactions: + rxn_dset = handle["/reaction rates"] + eigenvalues_dset = handle["/eigenvalues"] + time_dset = handle["/time"] + source_rate_dset = handle["/source_rate"] + proc_time_dset = handle["/depletion time"] + + # Get number of results stored + number_shape = list(number_dset.shape) + number_results = number_shape[0] + + new_shape = index + 1 + + if number_results < new_shape: + # Extend first dimension by 1 + number_shape[0] = new_shape + number_dset.resize(number_shape) + + if has_reactions: + rxn_shape = list(rxn_dset.shape) + rxn_shape[0] = new_shape + rxn_dset.resize(rxn_shape) + + eigenvalues_shape = list(eigenvalues_dset.shape) + eigenvalues_shape[0] = new_shape + eigenvalues_dset.resize(eigenvalues_shape) + + time_shape = list(time_dset.shape) + time_shape[0] = new_shape + time_dset.resize(time_shape) + + source_rate_shape = list(source_rate_dset.shape) + source_rate_shape[0] = new_shape + source_rate_dset.resize(source_rate_shape) + + proc_shape = list(proc_time_dset.shape) + proc_shape[0] = new_shape + proc_time_dset.resize(proc_shape) + + # If nothing to write, just return + if len(self.index_mat) == 0: + return + + # Add data + # Note, for the last step, self.n_stages = 1, even if n_stages != 1. + n_stages = self.n_stages + inds = [self.mat_to_hdf5_ind[mat] for mat in self.index_mat] + low = min(inds) + high = max(inds) + for i in range(n_stages): + number_dset[index, i, low:high+1] = self.data[i] + if has_reactions: + rxn_dset[index, i, low:high+1] = self.rates[i] + if comm.rank == 0: + eigenvalues_dset[index, i] = self.k[i] + if comm.rank == 0: + time_dset[index] = self.time + source_rate_dset[index] = self.source_rate + if self.proc_time is not None: + proc_time_dset[index] = ( + self.proc_time / (comm.size * self.n_hdf5_mats) + ) + + @classmethod + def from_hdf5(cls, handle, step): + """Loads results object from HDF5. + + Parameters + ---------- + handle : h5py.File or h5py.Group + An HDF5 file or group type to load from. + step : int + Index for depletion step + """ + results = cls() + + # Grab handles + number_dset = handle["/number"] + eigenvalues_dset = handle["/eigenvalues"] + time_dset = handle["/time"] + if "source_rate" in handle: + source_rate_dset = handle["/source_rate"] + else: + # Older versions used "power" instead of "source_rate" + source_rate_dset = handle["/power"] + + results.data = number_dset[step, :, :, :] + results.k = eigenvalues_dset[step, :] + results.time = time_dset[step, :] + results.source_rate = source_rate_dset[step, 0] + + if "depletion time" in handle: + proc_time_dset = handle["/depletion time"] + if step < proc_time_dset.shape[0]: + results.proc_time = proc_time_dset[step] + + if results.proc_time is None: + results.proc_time = np.array([np.nan]) + + # Reconstruct dictionaries + results.volume = {} + results.index_mat = {} + results.index_nuc = {} + rxn_nuc_to_ind = {} + rxn_to_ind = {} + + for mat, mat_handle in handle["/materials"].items(): + vol = mat_handle.attrs["volume"] + ind = mat_handle.attrs["index"] + + results.volume[mat] = vol + results.index_mat[mat] = ind + + for nuc, nuc_handle in handle["/nuclides"].items(): + ind_atom = nuc_handle.attrs["atom number index"] + results.index_nuc[nuc] = ind_atom + + if "reaction rate index" in nuc_handle.attrs: + rxn_nuc_to_ind[nuc] = nuc_handle.attrs["reaction rate index"] + + for rxn, rxn_handle in handle["/reactions"].items(): + rxn_to_ind[rxn] = rxn_handle.attrs["index"] + + results.rates = [] + # Reconstruct reactions + for i in range(results.n_stages): + rate = ReactionRates(results.index_mat, rxn_nuc_to_ind, rxn_to_ind, True) + + if "reaction rates" in handle: + rate[:] = handle["/reaction rates"][step, i, :, :, :] + results.rates.append(rate) + + return results + + @staticmethod + def save(op, x, op_results, t, source_rate, step_ind, proc_time=None, + path: PathLike = "depletion_results.h5"): + """Creates and writes depletion results to disk + + Parameters + ---------- + op : openmc.deplete.abc.TransportOperator + The operator used to generate these results. + x : list of list of numpy.array + The prior x vectors. Indexed [i][cell] using the above equation. + op_results : list of openmc.deplete.OperatorResult + Results of applying transport operator + t : list of float + Time indices. + source_rate : float + Source rate during time step in [W] or [neutron/sec] + step_ind : int + Step index. + proc_time : float or None + Total process time spent depleting materials. This may + be process-dependent and will be reduced across MPI + processes. + + path : PathLike + Path to file to write. Defaults to 'depletion_results.h5'. + + .. versionadded:: 0.14.0 + """ + # Get indexing terms + vol_dict, nuc_list, burn_list, full_burn_list = op.get_results_info() + + stages = len(x) + + # Create results + results = StepResult() + results.allocate(vol_dict, nuc_list, burn_list, full_burn_list, stages) + + n_mat = len(burn_list) + + for i in range(stages): + for mat_i in range(n_mat): + results[i, mat_i, :] = x[i][mat_i] + + ks = [] + for r in op_results: + if isinstance(r.k, type(None)): + ks += [(None, None)] + else: + ks += [(r.k.nominal_value, r.k.std_dev)] + results.k = ks + results.rates = [r.rates for r in op_results] + results.time = t + results.source_rate = source_rate + results.proc_time = proc_time + if results.proc_time is not None: + results.proc_time = comm.reduce(proc_time, op=MPI.SUM) + + if not Path(path).is_file(): + Path(path).parent.mkdir(parents=True, exist_ok=True) + results.export_to_hdf5(path, step_ind) + + def transfer_volumes(self, model): + """Transfers volumes from depletion results to geometry + + Parameters + ---------- + model : OpenMC model to be used in a depletion restart + calculation + + """ + + if not model.materials: + materials = openmc.Materials( + model.geometry.get_all_materials().values() + ) + else: + materials = model.materials + + for material in materials: + if material.depletable: + material.volume = self.volume[str(material.id)] diff --git a/openmc/deplete/transfer_rates.py b/openmc/deplete/transfer_rates.py new file mode 100644 index 0000000000..4f2b9aba5f --- /dev/null +++ b/openmc/deplete/transfer_rates.py @@ -0,0 +1,432 @@ +from collections import defaultdict +from numbers import Real +import re +from typing import Sequence + +import numpy as np + +from openmc.checkvalue import check_type, check_value +from openmc import Material +from openmc.data import ELEMENT_SYMBOL, isotopes, AVOGADRO, atomic_mass +from .results import _SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, \ + _SECONDS_PER_DAY, _SECONDS_PER_JULIAN_YEAR + + + +class ExternalRates: + """External rates class for defining addition terms of depletion equation. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + operator : openmc.TransportOperator + Depletion operator + materials : openmc.Materials + OpenMC materials. + number_of_timesteps : int + Total number of depletion timesteps + + Attributes + ---------- + burnable_mats : list of str + All burnable material IDs. + local_mats : list of str + All burnable material IDs being managed by a single process + number_of_timesteps : int + Total number of depletion timesteps + external_rates : dict of str to dict + Container of timesteps, external rates, components (elements and/or + nuclides) and optionally destination material + external_timesteps : list of int + Container of all timesteps indeces with an external rate defined. + """ + + def __init__(self, operator, materials, number_of_timesteps): + + self.materials = materials + self.burnable_mats = operator.burnable_mats + self.local_mats = operator.local_mats + self.number_of_timesteps = number_of_timesteps + + # initialize transfer rates container dict + self.external_rates = {mat: defaultdict(list) for mat in self.burnable_mats} + self.external_timesteps = [] + + def _get_material_id(self, val): + """Helper method for getting material id from Material obj or name. + + Parameters + ---------- + val : openmc.Material or str or int representing material name/id + + Returns + ------- + material_id : str + + """ + if isinstance(val, Material): + check_value('Depeletable Material', str(val.id), self.burnable_mats) + val = val.id + + elif isinstance(val, str): + if val.isnumeric(): + check_value('Material ID', str(val), self.burnable_mats) + else: + check_value('Material name', val, + [mat.name for mat in self.materials if mat.depletable]) + val = [mat.id for mat in self.materials if mat.name == val][0] + + elif isinstance(val, int): + check_value('Material ID', str(val), self.burnable_mats) + + return str(val) + + def get_external_rate( + self, + material: str | int | Material, + component: str, + timestep: int, + destination_material: str | int | Material | None = None + ): + """Return transfer rate for given material and element. + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + component : str + Element or nuclide to get transfer rate value + timestep : int + Current timestep index + destination_material : openmc.Material or str or int, Optional + Destination material to where nuclides get fed + + Returns + ------- + external_rate : list of floats + External rate values + + """ + material_id = self._get_material_id(material) + check_type('component', component, str) + if destination_material is not None: + dest_mat_id = self._get_material_id(destination_material) + return [i[1] for i in self.external_rates[material_id][component] + if timestep in i[0] and dest_mat_id == i[2]] + else: + return [i[1] for i in self.external_rates[material_id][component] + if timestep in i[0]] + + def get_components(self, material, timestep, destination_material=None): + """Extract removing elements and/or nuclides for a given material at a + given timestep + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + timestep : int + Current timestep index + destination_material : openmc.Material or str or int, Optional + Destination material to where nuclides get fed + + Returns + ------- + components : list + List of elements or nuclides with external rates set at a given + timestep + + """ + material_id = self._get_material_id(material) + if destination_material is not None: + dest_mat_id = self._get_material_id(destination_material) + else: + dest_mat_id = None + + all_components = [] + if material_id in self.external_rates: + mat_components = self.external_rates[material_id] + + for component in mat_components: + if dest_mat_id: + # check for both timestep and destination material ids + if np.isin(timestep, [val[0] for val in mat_components[component]]) and \ + np.isin(dest_mat_id, [val[2] for val in mat_components[component]]): + all_components.append(component) + else: + # check only for timesteps + if np.isin(timestep, [val[0] for val in mat_components[component]]): + all_components.append(component) + return all_components + + +class TransferRates(ExternalRates): + """Class for defining continuous removals and feeds. + + Molten Salt Reactors (MSRs) benefit from continuous reprocessing, + which removes fission products and feeds fresh fuel into the system. MSRs + inspired the development of this class. + + An instance of this class can be passed directly to an instance of one of + the :class:`openmc.deplete.Integrator` classes. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + operator : openmc.TransportOperator + Depletion operator + materials : openmc.Materials + OpenMC materials. + number_of_timesteps : int + Total number of depletion timesteps + + Attributes + ---------- + burnable_mats : list of str + All burnable material IDs. + local_mats : list of str + All burnable material IDs being managed by a single process + external_rates : dict of str to dict + Container of timesteps, transfer rates, components (elements and/or + nuclides) and destination material + external_timesteps : list of int + Container of all timesteps indeces with an external rate defined. + index_transfer : Set of pair of str + Pair of strings needed to build final matrix (destination_material, mat) + """ + + def __init__(self, operator, materials, number_of_timesteps): + super().__init__(operator, materials, number_of_timesteps) + self.index_transfer = defaultdict(list) + self.chain_nuclides = [nuc.name for nuc in operator.chain.nuclides] + + def set_transfer_rate(self, material, components, transfer_rate, + transfer_rate_units='1/s', timesteps=None, + destination_material=None): + """Set element and/or nuclide transfer rates in a depletable material. + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + components : list of str + List of strings of elements and/or nuclides that share transfer rate. + Cannot add transfer rates for nuclides to a material where a + transfer rate for its element is specified and vice versa. + transfer_rate : float + Rate at which elements and/or nuclides are transferred. A positive or + negative value corresponds to a removal or feed rate, respectively. + transfer_rate_units : {'1/s', '1/min', '1/h', '1/d', '1/a'} + Units for values specified in the transfer_rate argument. 's' for + seconds, 'min' for minutes, 'h' for hours, 'a' for Julian years. + timesteps : list of int, Optional + List of timestep indeces where to set transfer rates. + Default to None means the transfer rate is set for all timesteps. + destination_material : openmc.Material or str or int, Optional + Destination material to where nuclides get fed. + + """ + material_id = self._get_material_id(material) + check_type('transfer_rate', transfer_rate, Real) + check_type('components', components, list, expected_iter_type=str) + + if destination_material is not None: + destination_material_id = self._get_material_id(destination_material) + if len(self.burnable_mats) > 1: + check_value('destination_material', str(destination_material_id), + self.burnable_mats) + else: + raise ValueError('Transfer to material ' + f'{destination_material_id} is set, but there ' + 'is only one depletable material') + else: + destination_material_id = None + + if transfer_rate_units in ('1/s', '1/sec'): + unit_conv = 1 + elif transfer_rate_units in ('1/min', '1/minute'): + unit_conv = _SECONDS_PER_MINUTE + elif transfer_rate_units in ('1/h', '1/hr', '1/hour'): + unit_conv = _SECONDS_PER_HOUR + elif transfer_rate_units in ('1/d', '1/day'): + unit_conv = _SECONDS_PER_DAY + elif transfer_rate_units in ('1/a', '1/year'): + unit_conv = _SECONDS_PER_JULIAN_YEAR + else: + raise ValueError(f'Invalid transfer rate unit "{transfer_rate_units}"') + + if timesteps is not None: + for timestep in timesteps: + check_value('timestep', timestep, range(self.number_of_timesteps)) + timesteps = np.array(timesteps) + else: + timesteps = np.arange(self.number_of_timesteps) + + for component in components: + current_components = self.external_rates[material_id].keys() + split_component = re.split(r'\d+', component) + element = split_component[0] + if element not in ELEMENT_SYMBOL.values(): + raise ValueError(f'{component} is not a valid nuclide or ' + 'element.') + else: + if len(split_component) == 1: + element_nucs = [c for c in current_components + if re.match(component + r'\d', c)] + if len(element_nucs) > 0: + nuc_str = ", ".join(element_nucs) + raise ValueError('Cannot add transfer rate for element ' + f'{component} to material {material_id} ' + f'with transfer rate(s) for nuclide(s) ' + f'{nuc_str}.') + + else: + if element in current_components: + raise ValueError('Cannot add transfer rate for nuclide ' + f'{component} to material {material_id} ' + f'where element {element} already has ' + 'a transfer rate.') + + self.external_rates[material_id][component].append( + (timesteps, transfer_rate/unit_conv, destination_material_id)) + + if destination_material_id is not None: + for timestep in timesteps: + self.index_transfer[timestep].append( + (destination_material_id, material_id)) + + self.external_timesteps = np.unique(np.concatenate( + [self.external_timesteps, timesteps])) + + +class ExternalSourceRates(ExternalRates): + """Class for defining external source rates. + + An instance of this class can be passed directly to an instance of one of + the :class:`openmc.deplete.Integrator` classes. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + operator : openmc.TransportOperator + Depletion operator + materials : openmc.Materials + OpenMC materials. + number_of_timesteps : int + Total number of depletion timesteps + + Attributes + ---------- + burnable_mats : list of str + All burnable material IDs. + local_mats : list of str + All burnable material IDs being managed by a single process + external_timesteps : list of int + Container of all timesteps indeces with an external rate defined. + external_rates : dict of str to dict + Container of timesteps external source rates, and components + (elements and/or nuclides) + """ + + def reformat_nuclide_vectors(self, vectors): + """Remove last element of nuclide vector that was added for handling + external source rates by the depletion solver. + + Parameters + ---------- + vectors : list of array + List of nuclides vector to reformat + + """ + for mat_index, i in enumerate(self.local_mats): + if self.external_rates[i]: + vectors[mat_index] = vectors[mat_index][:-1] + + def set_external_source_rate( + self, + material: str | int | Material, + composition: dict[str, float], + rate: float, + rate_units: str = 'g/s', + timesteps: Sequence[int] | None = None + ): + """Set element and/or nuclide composition vector external source rates + to a depletable material. + + Parameters + ---------- + material : openmc.Material or str or int + Depletable material + composition : dict of str to float + External source rate composition vector, where key can be an element + or a nuclide and value the corresponding weight percent. + rate : float + External source rate in units of mass per time. A positive or + negative value corresponds to a feed or removal rate, respectively. + rate_units : {'g/s', 'g/min', 'g/h', 'g/d', 'g/a'} + Units for values specified in the `rate` argument. 's' for seconds, + 'min' for minutes, 'h' for hours, 'a' for Julian years. + timesteps : list of int, optional + List of timestep indices where to set external source rates. Default + to None, which means the external source rate is set for all + timesteps. + + """ + + material_id = self._get_material_id(material) + check_type('rate', rate, Real) + check_type('composition', composition, dict, str) + + if rate_units in ('g/s', 'g/sec'): + unit_conv = 1 + elif rate_units in ('g/min', 'g/minute'): + unit_conv = _SECONDS_PER_MINUTE + elif rate_units in ('g/h', 'g/hr', 'g/hour'): + unit_conv = _SECONDS_PER_HOUR + elif rate_units in ('g/d', 'g/day'): + unit_conv = _SECONDS_PER_DAY + elif rate_units in ('g/a', 'g/year'): + unit_conv = _SECONDS_PER_JULIAN_YEAR + else: + raise ValueError(f'Invalid external source rate unit "{rate_units}"') + + if timesteps is not None: + for timestep in timesteps: + check_value('timestep', timestep, range(self.number_of_timesteps)) + timesteps = np.asarray(timesteps) + else: + timesteps = np.arange(self.number_of_timesteps) + + components = composition.keys() + percents = composition.values() + norm_percents = [float(i) / sum(percents) for i in percents] + + atoms_per_nuc = {} + for component, percent in zip(components, norm_percents): + split_component = re.split(r'\d+', component) + element = split_component[0] + if element not in ELEMENT_SYMBOL.values(): + raise ValueError(f'{component} is not a valid nuclide or element.') + + if len(split_component) == 1: + if not isotopes(component): + raise ValueError(f'Cannot add element {component} ' + 'as it is not naturally abundant. ' + 'Specify a nuclide vector instead.') + for nuc, frac in isotopes(component): + atoms_per_nuc[nuc] = (rate / atomic_mass(nuc) * AVOGADRO * + frac * percent / unit_conv) + + else: + atoms_per_nuc[component] = (rate / atomic_mass(component) * + AVOGADRO * percent / unit_conv) + + for nuc, val in atoms_per_nuc.items(): + self.external_rates[material_id][nuc].append((timesteps, val, None)) + + self.external_timesteps = np.unique(np.concatenate( + [self.external_timesteps, timesteps] + )) diff --git a/openmc/element.py b/openmc/element.py index 1473bd63e2..f1d8f5f24d 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -1,10 +1,11 @@ -from collections import OrderedDict -import os import re -from xml.etree import ElementTree as ET +import warnings + +import lxml.etree as ET import openmc.checkvalue as cv -from openmc.data import NATURAL_ABUNDANCE, atomic_mass, \ +import openmc +from openmc.data import NATURAL_ABUNDANCE, atomic_mass, zam, \ isotopes as natural_isotopes @@ -40,10 +41,10 @@ class Element(str): cross_sections=None): """Expand natural element into its naturally-occurring isotopes. - An optional cross_sections argument or the :envvar:`OPENMC_CROSS_SECTIONS` - environment variable is used to specify a cross_sections.xml file. - If the cross_sections.xml file is found, the element is expanded only - into the isotopes/nuclides present in cross_sections.xml. If no + An optional cross_sections argument or the ``cross_sections`` + configuration value is used to specify a cross_sections.xml file. If the + cross_sections.xml file is found, the element is expanded only into the + isotopes/nuclides present in cross_sections.xml. If no cross_sections.xml file is found, the element is expanded based on its naturally occurring isotopes. @@ -54,12 +55,13 @@ class Element(str): percent_type : {'ao', 'wo'} 'ao' for atom percent and 'wo' for weight percent enrichment : float, optional - Enrichment of an enrichment_target nuclide in percent (ao or wo). - If enrichment_target is not supplied then it is enrichment for U235 - in weight percent. For example, input 4.95 for 4.95 weight percent + Enrichment of an enrichment_target nuclide in percent (ao or wo). If + enrichment_target is not supplied then it is enrichment for U235 in + weight percent. For example, input 4.95 for 4.95 weight percent enriched U. Default is None (natural composition). enrichment_target: str, optional - Single nuclide name to enrich from a natural composition (e.g., 'O16') + Single nuclide name to enrich from a natural composition (e.g., + 'O16') .. versionadded:: 0.12 enrichment_type: {'ao', 'wo'}, optional @@ -82,8 +84,8 @@ class Element(str): ValueError No data is available for any of natural isotopes of the element ValueError - If only some natural isotopes are available in the cross-section data - library and the element is not O, W, or Ta + If only some natural isotopes are available in the cross-section + data library and the element is not O, W, or Ta ValueError If a non-naturally-occurring isotope is requested ValueError @@ -101,8 +103,8 @@ class Element(str): `ORNL/CSD/TM-244 `_ is used to calculate the weight fractions of U234, U235, U236, and U238. Namely, the weight fraction of U234 and U236 are taken to be 0.89% and 0.46%, - respectively, of the U235 weight fraction. The remainder of the - isotopic weight is assigned to U238. + respectively, of the U235 weight fraction. The remainder of the isotopic + weight is assigned to U238. When the `enrichment` argument is specified with `enrichment_target`, a general enrichment procedure is used for elements composed of exactly @@ -122,13 +124,17 @@ class Element(str): # Get the nuclides present in nature natural_nuclides = {name for name, abundance in natural_isotopes(self)} - # Create dict to store the expanded nuclides and abundances - abundances = OrderedDict() + # Issue warning if no existing nuclides + if len(natural_nuclides) == 0: + warnings.warn(f"No naturally occurring isotopes found for {self}.") - # If cross_sections is None, get the cross sections from the - # OPENMC_CROSS_SECTIONS environment variable + # Create dict to store the expanded nuclides and abundances + abundances = {} + + # If cross_sections is None, get the cross sections from the global + # configuration if cross_sections is None: - cross_sections = os.environ.get('OPENMC_CROSS_SECTIONS') + cross_sections = openmc.config.get('cross_sections') # If a cross_sections library is present, check natural nuclides # against the nuclides in the library @@ -138,16 +144,15 @@ class Element(str): root = tree.getroot() for child in root.findall('library'): nuclide = child.attrib['materials'] - if re.match(r'{}\d+'.format(self), nuclide) and \ - '_m' not in nuclide: + if re.match(r'{}\d+'.format(self), nuclide): library_nuclides.add(nuclide) # Get a set of the mutual and absent nuclides. Convert to lists # and sort to avoid different ordering between Python 2 and 3. mutual_nuclides = natural_nuclides.intersection(library_nuclides) absent_nuclides = natural_nuclides.difference(mutual_nuclides) - mutual_nuclides = sorted(list(mutual_nuclides)) - absent_nuclides = sorted(list(absent_nuclides)) + mutual_nuclides = sorted(mutual_nuclides, key=zam) + absent_nuclides = sorted(absent_nuclides, key=zam) # If all naturally occurring isotopes are present in the library, # add them based on their abundance @@ -179,8 +184,10 @@ class Element(str): for nuclide in absent_nuclides: if nuclide in ['O17', 'O18'] and 'O16' in mutual_nuclides: abundances['O16'] += NATURAL_ABUNDANCE[nuclide] - elif nuclide == 'Ta180' and 'Ta181' in mutual_nuclides: - abundances['Ta181'] += NATURAL_ABUNDANCE[nuclide] + elif nuclide == 'Ta180_m1' and 'Ta180' in library_nuclides: + abundances['Ta180'] = NATURAL_ABUNDANCE[nuclide] + elif nuclide == 'Ta180_m1' and 'Ta181' in mutual_nuclides: + abundances['Ta181'] += NATURAL_ABUNDANCE[nuclide] elif nuclide == 'W180' and 'W182' in mutual_nuclides: abundances['W182'] += NATURAL_ABUNDANCE[nuclide] else: @@ -194,7 +201,7 @@ class Element(str): # If a cross_section library is not present, expand the element into # its natural nuclides else: - for nuclide in natural_nuclides: + for nuclide in sorted(natural_nuclides, key=zam): abundances[nuclide] = NATURAL_ABUNDANCE[nuclide] # Modify mole fractions if enrichment provided diff --git a/openmc/examples.py b/openmc/examples.py index 3b73d043ff..5578d513ea 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -3,24 +3,25 @@ from numbers import Integral import numpy as np import openmc -import openmc.model -def pwr_pin_cell(): + +def pwr_pin_cell() -> openmc.Model: """Create a PWR pin-cell model. This model is a single fuel pin with 2.4 w/o enriched UO2 corresponding to a beginning-of-cycle condition and borated water. The specifications are from - the `BEAVRS `_ benchmark. Note that the - number of particles/batches is initially set very low for testing purposes. + the `BEAVRS `_ benchmark. Note that + the number of particles/batches is initially set very low for testing + purposes. Returns ------- - model : openmc.model.Model + model : openmc.Model A PWR pin-cell model """ - model = openmc.model.Model() + model = openmc.Model() # Define materials. fuel = openmc.Material(name='UO2 (2.4%)') @@ -76,8 +77,11 @@ def pwr_pin_cell(): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( - [-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1], only_fissionable=True)) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-pitch/2, -pitch/2, -1], + [pitch/2, pitch/2, 1]), + constraints={'fissionable': True} + ) plot = openmc.Plot.from_geometry(model.geometry) plot.pixels = (300, 300) @@ -87,7 +91,7 @@ def pwr_pin_cell(): return model -def pwr_core(): +def pwr_core() -> openmc.Model: """Create a PWR full-core model. This model is the OECD/NEA Monte Carlo Performance benchmark which is a @@ -97,11 +101,11 @@ def pwr_core(): Returns ------- - model : openmc.model.Model + model : openmc.Model Full-core PWR model """ - model = openmc.model.Model() + model = openmc.Model() # Define materials. fuel = openmc.Material(1, name='UOX fuel') @@ -164,7 +168,8 @@ def pwr_core(): lower_rad_ref.add_nuclide('Cr52', 0.145407678031, 'wo') lower_rad_ref.add_s_alpha_beta('c_H_in_H2O') - upper_rad_ref = openmc.Material(7, name='Upper radial reflector / Top plate region') + upper_rad_ref = openmc.Material( + 7, name='Upper radial reflector / Top plate region') upper_rad_ref.set_density('g/cm3', 4.28) upper_rad_ref.add_nuclide('H1', 0.0086117, 'wo') upper_rad_ref.add_nuclide('O16', 0.0683369, 'wo') @@ -311,13 +316,15 @@ def pwr_core(): 11, 11, 11, 11, 11, 13, 13, 14, 14, 14]) # Define fuel lattices. - l100 = openmc.RectLattice(name='Fuel assembly (lower half)', lattice_id=100) + l100 = openmc.RectLattice( + name='Fuel assembly (lower half)', lattice_id=100) l100.lower_left = (-10.71, -10.71) l100.pitch = (1.26, 1.26) l100.universes = np.tile(fuel_cold, (17, 17)) l100.universes[tube_x, tube_y] = tube_cold - l101 = openmc.RectLattice(name='Fuel assembly (upper half)', lattice_id=101) + l101 = openmc.RectLattice( + name='Fuel assembly (upper half)', lattice_id=101) l101.lower_left = (-10.71, -10.71) l101.pitch = (1.26, 1.26) l101.universes = np.tile(fuel_hot, (17, 17)) @@ -403,10 +410,14 @@ def pwr_core(): c6 = openmc.Cell(cell_id=6, fill=top_fa, region=-s5 & +s36 & -s37) c7 = openmc.Cell(cell_id=7, fill=top_nozzle, region=-s5 & +s37 & -s38) c8 = openmc.Cell(cell_id=8, fill=upper_rad_ref, region=-s7 & +s38 & -s39) - c9 = openmc.Cell(cell_id=9, fill=bot_nozzle, region=+s6 & -s7 & +s32 & -s38) - c10 = openmc.Cell(cell_id=10, fill=rpv_steel, region=+s7 & -s8 & +s31 & -s39) - c11 = openmc.Cell(cell_id=11, fill=lower_rad_ref, region=+s5 & -s6 & +s32 & -s34) - c12 = openmc.Cell(cell_id=12, fill=upper_rad_ref, region=+s5 & -s6 & +s36 & -s38) + c9 = openmc.Cell(cell_id=9, fill=bot_nozzle, + region=+s6 & -s7 & +s32 & -s38) + c10 = openmc.Cell(cell_id=10, fill=rpv_steel, + region=+s7 & -s8 & +s31 & -s39) + c11 = openmc.Cell(cell_id=11, fill=lower_rad_ref, + region=+s5 & -s6 & +s32 & -s34) + c12 = openmc.Cell(cell_id=12, fill=upper_rad_ref, + region=+s5 & -s6 & +s36 & -s38) root.add_cells((c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12)) # Assign root universe to geometry @@ -415,7 +426,7 @@ def pwr_core(): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-160, -160, -183], [160, 160, 183])) plot = openmc.Plot() @@ -428,22 +439,22 @@ def pwr_core(): return model -def pwr_assembly(): +def pwr_assembly() -> openmc.Model: """Create a PWR assembly model. This model is a reflected 17x17 fuel assembly from the the `BEAVRS - `_ benchmark. The fuel is 2.4 w/o + `_ benchmark. The fuel is 2.4 w/o enriched UO2 corresponding to a beginning-of-cycle condition. Note that the number of particles/batches is initially set very low for testing purposes. Returns ------- - model : openmc.model.Model + model : openmc.Model A PWR assembly model """ - model = openmc.model.Model() + model = openmc.Model() # Define materials. fuel = openmc.Material(name='Fuel') @@ -487,10 +498,10 @@ def pwr_assembly(): fuel_pin_universe = openmc.Universe(name='Fuel Pin') fuel_cell = openmc.Cell(name='fuel', fill=fuel, region=-fuel_or) clad_cell = openmc.Cell(name='clad', fill=clad, region=+fuel_or & -clad_or) - hot_water_cell = openmc.Cell(name='hot water', fill=hot_water, region=+clad_or) + hot_water_cell = openmc.Cell( + name='hot water', fill=hot_water, region=+clad_or) fuel_pin_universe.add_cells([fuel_cell, clad_cell, hot_water_cell]) - # Create a control rod guide tube universe guide_tube_universe = openmc.Universe(name='Guide Tube') gt_inner_cell = openmc.Cell(name='guide tube inner water', fill=hot_water, @@ -527,8 +538,11 @@ def pwr_assembly(): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( - [-pitch/2, -pitch/2, -1], [pitch/2, pitch/2, 1], only_fissionable=True)) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-pitch/2, -pitch/2, -1], + [pitch/2, pitch/2, 1]), + constraints={'fissionable': True} + ) plot = openmc.Plot() plot.origin = (0.0, 0.0, 0) @@ -540,7 +554,7 @@ def pwr_assembly(): return model -def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5'): +def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5') -> openmc.Model: """Create a 1D slab model. Parameters @@ -557,7 +571,7 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5'): Returns ------- - model : openmc.model.Model + model : openmc.Model One-group, 1D slab model """ @@ -629,14 +643,486 @@ def slab_mg(num_regions=1, mat_names=None, mgxslib_name='2g.h5'): INF = 1000. bounds = [0., -INF, -INF, rads[0], INF, INF] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) - settings_file.source = openmc.source.Source(space=uniform_dist) + settings_file.source = openmc.IndependentSource(space=uniform_dist) settings_file.output = {'summary': False} - model = openmc.model.Model() + model = openmc.Model() model.geometry = geometry_file model.materials = materials_file model.settings = settings_file model.xs_data = macros return model + + +def random_ray_lattice() -> openmc.Model: + """Create a 2x2 PWR pincell asymmetrical lattic eexample. + + This model is a 2x2 reflective lattice of fuel pins with one of the lattice + locations having just moderator instead of a fuel pin. It uses 7 group + cross section data. + + Returns + ------- + model : openmc.Model + A PWR 2x2 lattice model + + """ + model = openmc.Model() + + ########################################################################### + # Create MGXS data for the problem + + # Instantiate the energy group data + group_edges = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + groups = openmc.mgxs.EnergyGroups(group_edges) + + # Instantiate the 7-group (C5G7) cross section data + uo2_xsdata = openmc.XSdata('UO2', groups) + uo2_xsdata.order = 0 + uo2_xsdata.set_total( + [0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, + 0.5644058]) + uo2_xsdata.set_absorption([8.0248e-03, 3.7174e-03, 2.6769e-02, 9.6236e-02, + 3.0020e-02, 1.1126e-01, 2.8278e-01]) + scatter_matrix = np.array( + [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.3244560, 0.0016314, 0.0000000, + 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.4509400, 0.0026792, + 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.4525650, + 0.0055664, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0001253, + 0.2714010, 0.0102550, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, + 0.0012968, 0.2658020, 0.0168090], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + uo2_xsdata.set_scatter_matrix(scatter_matrix) + uo2_xsdata.set_fission([7.21206e-03, 8.19301e-04, 6.45320e-03, + 1.85648e-02, 1.78084e-02, 8.30348e-02, + 2.16004e-01]) + uo2_xsdata.set_nu_fission([2.005998e-02, 2.027303e-03, 1.570599e-02, + 4.518301e-02, 4.334208e-02, 2.020901e-01, + 5.257105e-01]) + uo2_xsdata.set_chi([5.8791e-01, 4.1176e-01, 3.3906e-04, 1.1761e-07, 0.0000e+00, + 0.0000e+00, 0.0000e+00]) + + h2o_xsdata = openmc.XSdata('LWTR', groups) + h2o_xsdata.order = 0 + h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435, + 0.718, 1.2544497, 2.650379]) + h2o_xsdata.set_absorption([6.0105e-04, 1.5793e-05, 3.3716e-04, + 1.9406e-03, 5.7416e-03, 1.5001e-02, + 3.7239e-02]) + scatter_matrix = np.array( + [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000], + [0.0000000, 0.2823340, 0.1299400, 0.0006234, + 0.0000480, 0.0000074, 0.0000010], + [0.0000000, 0.0000000, 0.3452560, 0.2245700, + 0.0169990, 0.0026443, 0.0005034], + [0.0000000, 0.0000000, 0.0000000, 0.0910284, + 0.4155100, 0.0637320, 0.0121390], + [0.0000000, 0.0000000, 0.0000000, 0.0000714, + 0.1391380, 0.5118200, 0.0612290], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, + 0.0022157, 0.6999130, 0.5373200], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + h2o_xsdata.set_scatter_matrix(scatter_matrix) + + mg_cross_sections = openmc.MGXSLibrary(groups) + mg_cross_sections.add_xsdatas([uo2_xsdata, h2o_xsdata]) + mg_cross_sections.export_to_hdf5('mgxs.h5') + + ########################################################################### + # Create materials for the problem + + # Instantiate some Materials and register the appropriate macroscopic data + uo2 = openmc.Material(name='UO2 fuel') + uo2.set_density('macro', 1.0) + uo2.add_macroscopic('UO2') + + water = openmc.Material(name='Water') + water.set_density('macro', 1.0) + water.add_macroscopic('LWTR') + + # Instantiate a Materials collection and export to XML + materials = openmc.Materials([uo2, water]) + materials.cross_sections = "mgxs.h5" + + ########################################################################### + # Define problem geometry + + ######################################## + # Define an unbounded pincell universe + + pitch = 1.26 + + # Create a surface for the fuel outer radius + fuel_or = openmc.ZCylinder(r=0.54, name='Fuel OR') + inner_ring_a = openmc.ZCylinder(r=0.33, name='inner ring a') + inner_ring_b = openmc.ZCylinder(r=0.45, name='inner ring b') + outer_ring_a = openmc.ZCylinder(r=0.60, name='outer ring a') + outer_ring_b = openmc.ZCylinder(r=0.69, name='outer ring b') + + # Instantiate Cells + fuel_a = openmc.Cell(fill=uo2, region=-inner_ring_a, name='fuel inner a') + fuel_b = openmc.Cell(fill=uo2, region=+inner_ring_a & - + inner_ring_b, name='fuel inner b') + fuel_c = openmc.Cell(fill=uo2, region=+inner_ring_b & - + fuel_or, name='fuel inner c') + moderator_a = openmc.Cell( + fill=water, region=+fuel_or & -outer_ring_a, name='moderator inner a') + moderator_b = openmc.Cell( + fill=water, region=+outer_ring_a & -outer_ring_b, name='moderator outer b') + moderator_c = openmc.Cell( + fill=water, region=+outer_ring_b, name='moderator outer c') + + # Create pincell universe + pincell_base = openmc.Universe() + + # Register Cells with Universe + pincell_base.add_cells( + [fuel_a, fuel_b, fuel_c, moderator_a, moderator_b, moderator_c]) + + # Create planes for azimuthal sectors + azimuthal_planes = [] + for i in range(8): + angle = 2 * i * openmc.pi / 8 + normal_vector = (-openmc.sin(angle), openmc.cos(angle), 0) + azimuthal_planes.append(openmc.Plane( + a=normal_vector[0], b=normal_vector[1], c=normal_vector[2], d=0)) + + # Create a cell for each azimuthal sector + azimuthal_cells = [] + for i in range(8): + azimuthal_cell = openmc.Cell(name=f'azimuthal_cell_{i}') + azimuthal_cell.fill = pincell_base + azimuthal_cell.region = +azimuthal_planes[i] & -azimuthal_planes[(i+1) % 8] + azimuthal_cells.append(azimuthal_cell) + + # Create a geometry with the azimuthal universes + pincell = openmc.Universe(cells=azimuthal_cells, name='pincell') + + ######################################## + # Define a moderator lattice universe + + moderator_infinite = openmc.Cell(fill=water, name='moderator infinite') + mu = openmc.Universe() + mu.add_cells([moderator_infinite]) + + lattice = openmc.RectLattice() + lattice.lower_left = [-pitch/2.0, -pitch/2.0] + lattice.pitch = [pitch/10.0, pitch/10.0] + lattice.universes = np.full((10, 10), mu) + + mod_lattice_cell = openmc.Cell(fill=lattice) + + mod_lattice_uni = openmc.Universe() + + mod_lattice_uni.add_cells([mod_lattice_cell]) + + ######################################## + # Define 2x2 outer lattice + lattice2x2 = openmc.RectLattice() + lattice2x2.lower_left = (-pitch, -pitch) + lattice2x2.pitch = (pitch, pitch) + lattice2x2.universes = [ + [pincell, pincell], + [pincell, mod_lattice_uni] + ] + + ######################################## + # Define cell containing lattice and other stuff + box = openmc.model.RectangularPrism( + pitch*2, pitch*2, boundary_type='reflective') + + assembly = openmc.Cell(fill=lattice2x2, region=-box, name='assembly') + + # Create a geometry with the top-level cell + geometry = openmc.Geometry([assembly]) + + ########################################################################### + # Define problem settings + + # Instantiate a Settings object, set all runtime parameters, and export to XML + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.batches = 10 + settings.inactive = 5 + settings.particles = 100 + + # Create an initial uniform spatial source distribution over fissionable zones + lower_left = (-pitch, -pitch, -1) + upper_right = (pitch, pitch, 1) + uniform_dist = openmc.stats.Box(lower_left, upper_right) + rr_source = openmc.IndependentSource(space=uniform_dist) + + settings.random_ray['distance_active'] = 100.0 + settings.random_ray['distance_inactive'] = 20.0 + settings.random_ray['ray_source'] = rr_source + settings.random_ray['volume_normalized_flux_tallies'] = True + + ########################################################################### + # Define tallies + + # Create a mesh that will be used for tallying + mesh = openmc.RegularMesh() + mesh.dimension = (2, 2) + mesh.lower_left = (-pitch, -pitch) + mesh.upper_right = (pitch, pitch) + + # Create a mesh filter that can be used in a tally + mesh_filter = openmc.MeshFilter(mesh) + + # Create an energy group filter as well + group_edges = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + energy_filter = openmc.EnergyFilter(group_edges) + + # Now use the mesh filter in a tally and indicate what scores are desired + tally = openmc.Tally(name="Mesh tally") + tally.filters = [mesh_filter, energy_filter] + tally.scores = ['flux', 'fission', 'nu-fission'] + tally.estimator = 'analog' + + # Instantiate a Tallies collection and export to XML + tallies = openmc.Tallies([tally]) + + ########################################################################### + # Exporting to OpenMC model + ########################################################################### + + model.geometry = geometry + model.materials = materials + model.settings = settings + model.tallies = tallies + return model + + +def random_ray_three_region_cube() -> openmc.Model: + """Create a three region cube model. + + This is a simple monoenergetic problem of a cube with three concentric cubic + regions. The innermost region is near void (with Sigma_t around 10^-5) and + contains an external isotropic source term, the middle region is void (with + Sigma_t around 10^-4), and the outer region of the cube is an absorber + (with Sigma_t around 1). + + Returns + ------- + model : openmc.Model + A three region cube model + + """ + + model = openmc.Model() + + ########################################################################### + # Helper function creates a 3 region cube with different fills in each region + def fill_cube(N, n_1, n_2, fill_1, fill_2, fill_3): + cube = [[[0 for _ in range(N)] for _ in range(N)] for _ in range(N)] + for i in range(N): + for j in range(N): + for k in range(N): + if i < n_1 and j >= (N-n_1) and k < n_1: + cube[i][j][k] = fill_1 + elif i < n_2 and j >= (N-n_2) and k < n_2: + cube[i][j][k] = fill_2 + else: + cube[i][j][k] = fill_3 + return cube + + ########################################################################### + # Create multigroup data + + # Instantiate the energy group data + ebins = [1e-5, 20.0e6] + groups = openmc.mgxs.EnergyGroups(group_edges=ebins) + + void_sigma_a = 4.0e-6 + void_sigma_s = 3.0e-4 + void_mat_data = openmc.XSdata('void', groups) + void_mat_data.order = 0 + void_mat_data.set_total([void_sigma_a + void_sigma_s]) + void_mat_data.set_absorption([void_sigma_a]) + void_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[void_sigma_s]]]), 0, 3)) + + absorber_sigma_a = 0.75 + absorber_sigma_s = 0.25 + absorber_mat_data = openmc.XSdata('absorber', groups) + absorber_mat_data.order = 0 + absorber_mat_data.set_total([absorber_sigma_a + absorber_sigma_s]) + absorber_mat_data.set_absorption([absorber_sigma_a]) + absorber_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[absorber_sigma_s]]]), 0, 3)) + + multiplier = 0.1 + source_sigma_a = void_sigma_a * multiplier + source_sigma_s = void_sigma_s * multiplier + source_mat_data = openmc.XSdata('source', groups) + source_mat_data.order = 0 + source_mat_data.set_total([source_sigma_a + source_sigma_s]) + source_mat_data.set_absorption([source_sigma_a]) + source_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[source_sigma_s]]]), 0, 3)) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.add_xsdatas( + [source_mat_data, void_mat_data, absorber_mat_data]) + mg_cross_sections_file.export_to_hdf5() + + ########################################################################### + # Create materials for the problem + + # Instantiate some Macroscopic Data + source_data = openmc.Macroscopic('source') + void_data = openmc.Macroscopic('void') + absorber_data = openmc.Macroscopic('absorber') + + # Instantiate some Materials and register the appropriate Macroscopic objects + source_mat = openmc.Material(name='source') + source_mat.set_density('macro', 1.0) + source_mat.add_macroscopic(source_data) + + void_mat = openmc.Material(name='void') + void_mat.set_density('macro', 1.0) + void_mat.add_macroscopic(void_data) + + absorber_mat = openmc.Material(name='absorber') + absorber_mat.set_density('macro', 1.0) + absorber_mat.add_macroscopic(absorber_data) + + # Instantiate a Materials collection and export to XML + materials_file = openmc.Materials([source_mat, void_mat, absorber_mat]) + materials_file.cross_sections = "mgxs.h5" + + ########################################################################### + # Define problem geometry + + source_cell = openmc.Cell(fill=source_mat, name='infinite source region') + void_cell = openmc.Cell(fill=void_mat, name='infinite void region') + absorber_cell = openmc.Cell( + fill=absorber_mat, name='infinite absorber region') + + source_universe = openmc.Universe(name='source universe') + source_universe.add_cells([source_cell]) + + void_universe = openmc.Universe() + void_universe.add_cells([void_cell]) + + absorber_universe = openmc.Universe() + absorber_universe.add_cells([absorber_cell]) + + absorber_width = 30.0 + n_base = 6 + + # This variable can be increased above 1 to refine the FSR mesh resolution further + refinement_level = 2 + + n = n_base * refinement_level + pitch = absorber_width / n + + pattern = fill_cube(n, 1*refinement_level, 5*refinement_level, + source_universe, void_universe, absorber_universe) + + lattice = openmc.RectLattice() + lattice.lower_left = [0.0, 0.0, 0.0] + lattice.pitch = [pitch, pitch, pitch] + lattice.universes = pattern + + lattice_cell = openmc.Cell(fill=lattice) + + lattice_uni = openmc.Universe() + lattice_uni.add_cells([lattice_cell]) + + x_low = openmc.XPlane(x0=0.0, boundary_type='reflective') + x_high = openmc.XPlane(x0=absorber_width, boundary_type='vacuum') + + y_low = openmc.YPlane(y0=0.0, boundary_type='reflective') + y_high = openmc.YPlane(y0=absorber_width, boundary_type='vacuum') + + z_low = openmc.ZPlane(z0=0.0, boundary_type='reflective') + z_high = openmc.ZPlane(z0=absorber_width, boundary_type='vacuum') + + full_domain = openmc.Cell(fill=lattice_uni, region=+x_low & - + x_high & +y_low & -y_high & +z_low & -z_high, name='full domain') + + root = openmc.Universe(name='root universe') + root.add_cell(full_domain) + + # Create a geometry with the two cells and export to XML + geometry = openmc.Geometry(root) + + ########################################################################### + # Define problem settings + + # Instantiate a Settings object, set all runtime parameters, and export to XML + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.inactive = 5 + settings.batches = 10 + settings.particles = 90 + settings.run_mode = 'fixed source' + + # Create an initial uniform spatial source for ray integration + lower_left_ray = [0.0, 0.0, 0.0] + upper_right_ray = [absorber_width, absorber_width, absorber_width] + uniform_dist_ray = openmc.stats.Box( + lower_left_ray, upper_right_ray, only_fissionable=False) + rr_source = openmc.IndependentSource(space=uniform_dist_ray) + + settings.random_ray['distance_active'] = 500.0 + settings.random_ray['distance_inactive'] = 100.0 + settings.random_ray['ray_source'] = rr_source + settings.random_ray['volume_normalized_flux_tallies'] = True + + # Create the neutron source in the bottom right of the moderator + # Good - fast group appears largest (besides most thermal) + strengths = [1.0] + midpoints = [100.0] + energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) + + source = openmc.IndependentSource(energy=energy_distribution, constraints={ + 'domains': [source_universe]}, strength=3.14) + + settings.source = [source] + + ########################################################################### + # Define tallies + + estimator = 'tracklength' + + absorber_filter = openmc.MaterialFilter(absorber_mat) + absorber_tally = openmc.Tally(name="Absorber Tally") + absorber_tally.filters = [absorber_filter] + absorber_tally.scores = ['flux'] + absorber_tally.estimator = estimator + + void_filter = openmc.MaterialFilter(void_mat) + void_tally = openmc.Tally(name="Void Tally") + void_tally.filters = [void_filter] + void_tally.scores = ['flux'] + void_tally.estimator = estimator + + source_filter = openmc.MaterialFilter(source_mat) + source_tally = openmc.Tally(name="Source Tally") + source_tally.filters = [source_filter] + source_tally.scores = ['flux'] + source_tally.estimator = estimator + + # Instantiate a Tallies collection and export to XML + tallies = openmc.Tallies([source_tally, void_tally, absorber_tally]) + + ########################################################################### + # Assmble Model + + model.geometry = geometry + model.materials = materials_file + model.settings = settings + model.tallies = tallies + + return model diff --git a/openmc/executor.py b/openmc/executor.py index 63653f4d00..aacc48b3fa 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -1,5 +1,6 @@ from collections.abc import Iterable from numbers import Integral +import os import subprocess import openmc @@ -9,7 +10,7 @@ from .plots import _get_plot_image def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, plot=False, restart_file=None, threads=None, tracks=False, event_based=None, - openmc_exec='openmc', mpi_args=None): + openmc_exec='openmc', mpi_args=None, path_input=None): """Converts user-readable flags in to command-line arguments to be run with the OpenMC executable via subprocess. @@ -23,7 +24,7 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, Number of particles to simulate per generation. plot : bool, optional Run in plotting mode. Defaults to False. - restart_file : str, optional + restart_file : str or PathLike Path to restart file to use threads : int, optional Number of OpenMP threads. If OpenMC is compiled with OpenMP threading @@ -31,7 +32,9 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, to the number of hardware threads available (or a value set by the :envvar:`OMP_NUM_THREADS` environment variable). tracks : bool, optional - Write tracks for all particles. Defaults to False. + Enables the writing of particles tracks. The number of particle + tracks written to tracks.h5 is limited to 1000 unless + Settings.max_tracks is set. Defaults to False. event_based : None or bool, optional Turns on event-based parallelism if True. If None, the value in the Settings will be used. @@ -39,7 +42,10 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, Path to OpenMC executable. Defaults to 'openmc'. mpi_args : list of str, optional MPI execute command and any additional MPI arguments to pass, - e.g. ['mpiexec', '-n', '8']. + e.g., ['mpiexec', '-n', '8']. + path_input : str or PathLike + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. .. versionadded:: 0.13.0 @@ -68,8 +74,8 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, if event_based: args.append('-e') - if isinstance(restart_file, str): - args += ['-r', restart_file] + if isinstance(restart_file, (str, os.PathLike)): + args += ['-r', str(restart_file)] if tracks: args.append('-t') @@ -80,6 +86,9 @@ def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None, if mpi_args is not None: args = mpi_args + args + if path_input is not None: + args += [path_input] + return args @@ -116,7 +125,7 @@ def _run(args, output, cwd): raise RuntimeError(error_msg) -def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): +def plot_geometry(output=True, openmc_exec='openmc', cwd='.', path_input=None): """Run OpenMC in plotting mode Parameters @@ -127,6 +136,11 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): Path to OpenMC executable cwd : str, optional Path to working directory to run in + path_input : str + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. + + .. versionadded:: 0.13.3 Raises ------ @@ -134,10 +148,13 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): If the `openmc` executable returns a non-zero status """ - _run([openmc_exec, '-p'], output, cwd) + args = [openmc_exec, '-p'] + if path_input is not None: + args += [path_input] + _run(args, output, cwd) -def plot_inline(plots, openmc_exec='openmc', cwd='.'): +def plot_inline(plots, openmc_exec='openmc', cwd='.', path_input=None): """Display plots inline in a Jupyter notebook. .. versionchanged:: 0.13.0 @@ -153,6 +170,11 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): Path to OpenMC executable cwd : str, optional Path to working directory to run in + path_input : str + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. + + .. versionadded:: 0.13.3 Raises ------ @@ -166,18 +188,19 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.'): plots = [plots] # Create plots.xml - openmc.Plots(plots).export_to_xml() + openmc.Plots(plots).export_to_xml(cwd) # Run OpenMC in geometry plotting mode - plot_geometry(False, openmc_exec, cwd) + plot_geometry(False, openmc_exec, cwd, path_input) if plots is not None: - images = [_get_plot_image(p) for p in plots] + images = [_get_plot_image(p, cwd) for p in plots] display(*images) def calculate_volumes(threads=None, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None): + openmc_exec='openmc', mpi_args=None, + path_input=None): """Run stochastic volume calculations in OpenMC. This function runs OpenMC in stochastic volume calculation mode. To specify @@ -204,10 +227,14 @@ def calculate_volumes(threads=None, output=True, cwd='.', Path to OpenMC executable. Defaults to 'openmc'. mpi_args : list of str, optional MPI execute command and any additional MPI arguments to pass, - e.g. ['mpiexec', '-n', '8']. + e.g., ['mpiexec', '-n', '8']. cwd : str, optional Path to working directory to run in. Defaults to the current working directory. + path_input : str or PathLike + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. + Raises ------ @@ -221,14 +248,16 @@ def calculate_volumes(threads=None, output=True, cwd='.', """ args = _process_CLI_arguments(volume=True, threads=threads, - openmc_exec=openmc_exec, mpi_args=mpi_args) + openmc_exec=openmc_exec, mpi_args=mpi_args, + path_input=path_input) _run(args, output, cwd) def run(particles=None, threads=None, geometry_debug=False, restart_file=None, tracks=False, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, event_based=False): + openmc_exec='openmc', mpi_args=None, event_based=False, + path_input=None): """Run an OpenMC simulation. Parameters @@ -237,15 +266,17 @@ def run(particles=None, threads=None, geometry_debug=False, Number of particles to simulate per generation. threads : int, optional Number of OpenMP threads. If OpenMC is compiled with OpenMP threading - enabled, the default is implementation-dependent but is usually equal - to the number of hardware threads available (or a value set by the + enabled, the default is implementation-dependent but is usually equal to + the number of hardware threads available (or a value set by the :envvar:`OMP_NUM_THREADS` environment variable). geometry_debug : bool, optional Turn on geometry debugging during simulation. Defaults to False. - restart_file : str, optional + restart_file : str or PathLike Path to restart file to use tracks : bool, optional - Write tracks for all particles. Defaults to False. + Enables the writing of particles tracks. The number of particle tracks + written to tracks.h5 is limited to 1000 unless Settings.max_tracks is + set. Defaults to False. output : bool Capture OpenMC output from standard out cwd : str, optional @@ -254,13 +285,19 @@ def run(particles=None, threads=None, geometry_debug=False, openmc_exec : str, optional Path to OpenMC executable. Defaults to 'openmc'. mpi_args : list of str, optional - MPI execute command and any additional MPI arguments to pass, - e.g. ['mpiexec', '-n', '8']. + MPI execute command and any additional MPI arguments to pass, e.g., + ['mpiexec', '-n', '8']. event_based : bool, optional Turns on event-based parallelism, instead of default history-based .. versionadded:: 0.12 + path_input : str or PathLike + Path to a single XML file or a directory containing XML files for the + OpenMC executable to read. + + .. versionadded:: 0.13.3 + Raises ------ RuntimeError @@ -271,6 +308,7 @@ def run(particles=None, threads=None, geometry_debug=False, args = _process_CLI_arguments( volume=False, geometry_debug=geometry_debug, particles=particles, restart_file=restart_file, threads=threads, tracks=tracks, - event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args) + event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args, + path_input=path_input) _run(args, output, cwd) diff --git a/openmc/filter.py b/openmc/filter.py index df06d713fe..6a666d2a02 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -1,11 +1,12 @@ +from __future__ import annotations from abc import ABCMeta -from collections import OrderedDict -from collections.abc import Iterable +from collections.abc import Iterable, Sequence import hashlib from itertools import product from numbers import Real, Integral -from xml.etree import ElementTree as ET +import warnings +import lxml.etree as ET import numpy as np import pandas as pd @@ -16,15 +17,16 @@ from .material import Material from .mixin import IDManagerMixin from .surface import Surface from .universe import UniverseBase -from ._xml import get_text +from ._xml import get_elem_list, get_text _FILTER_TYPES = ( 'universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy', - 'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', - 'energyfunction', 'cellfrom', 'legendre', 'spatiallegendre', + 'energyout', 'mu', 'musurface', 'polar', 'azimuthal', 'distribcell', 'delayedgroup', + 'energyfunction', 'cellfrom', 'materialfrom', 'legendre', 'spatiallegendre', 'sphericalharmonics', 'zernike', 'zernikeradial', 'particle', 'cellinstance', - 'collision', 'time' + 'collision', 'time', 'parentnuclide', 'weight', 'meshborn', 'meshsurface', + 'meshmaterial', ) _CURRENT_NAMES = ( @@ -102,6 +104,8 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Unique identifier for the filter num_bins : Integral The number of filter bins + shape : tuple + The shape of the filter """ @@ -205,6 +209,10 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): def num_bins(self): return len(self.bins) + @property + def shape(self): + return (self.num_bins,) + def check_bins(self, bins): """Make sure given bins are valid for this filter. @@ -222,7 +230,7 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing filter data """ @@ -240,7 +248,7 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element **kwargs Keyword arguments (e.g., mesh information) @@ -251,15 +259,15 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Filter object """ - filter_type = elem.get('type') + filter_type = get_text(elem, "type") # If the filter type matches this class's short_name, then # there is no overridden from_xml_element method if filter_type == cls.short_name.lower(): # Get bins from element -- the default here works for any filters # that just store a list of bins that can be represented as integers - filter_id = int(elem.get('id')) - bins = [int(x) for x in get_text(elem, 'bins').split()] + filter_id = int(get_text(elem, "id")) + bins = get_elem_list(elem, "bins", int) or [] return cls(bins, filter_id=filter_id) # Search through all subclasses and find the one matching the HDF5 @@ -443,7 +451,7 @@ class UniverseFilter(WithIDFilter): Parameters ---------- bins : openmc.UniverseBase, int, or iterable thereof - The Universes to tally. Either openmc.UniverseBase objects or their + The Universes to tally. Either :class:`openmc.UniverseBase` objects or their Integral ID numbers can be used. filter_id : int Unique identifier for the filter @@ -467,7 +475,31 @@ class MaterialFilter(WithIDFilter): Parameters ---------- bins : openmc.Material, Integral, or iterable thereof - The Materials to tally. Either openmc.Material objects or their + The material(s) to tally. Either :class:`openmc.Material` objects or their + Integral ID numbers can be used. + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + bins : Iterable of Integral + openmc.Material IDs. + id : int + Unique identifier for the filter + num_bins : Integral + The number of filter bins + + """ + expected_type = Material + + +class MaterialFromFilter(WithIDFilter): + """Bins tally event locations based on the Material they occurred in. + + Parameters + ---------- + bins : openmc.Material, Integral, or iterable thereof + The material(s) to tally. Either :class:`openmc.Material` objects or their Integral ID numbers can be used. filter_id : int Unique identifier for the filter @@ -491,7 +523,7 @@ class CellFilter(WithIDFilter): Parameters ---------- bins : openmc.Cell, int, or iterable thereof - The cells to tally. Either openmc.Cell objects or their ID numbers can + The cells to tally. Either :class:`openmc.Cell` objects or their ID numbers can be used. filter_id : int Unique identifier for the filter @@ -533,7 +565,7 @@ class CellFromFilter(WithIDFilter): expected_type = Cell -class CellbornFilter(WithIDFilter): +class CellBornFilter(WithIDFilter): """Bins tally events based on which cell the particle was born in. Parameters @@ -557,6 +589,14 @@ class CellbornFilter(WithIDFilter): expected_type = Cell +# Temporary alias for CellbornFilter +def CellbornFilter(*args, **kwargs): + warnings.warn('The name of "CellbornFilter" has changed to ' + '"CellBornFilter". "CellbornFilter" will be ' + 'removed in the future.', FutureWarning) + return CellBornFilter(*args, **kwargs) + + class CellInstanceFilter(Filter): """Bins tally events based on which cell instance a particle is in. @@ -572,7 +612,7 @@ class CellInstanceFilter(Filter): bins : iterable of 2-tuples or numpy.ndarray The cell instances to tally, given as 2-tuples. For the first value in the tuple, either openmc.Cell objects or their integral ID numbers can - be used. + be used. The second value indicates the cell instance. filter_id : int Unique identifier for the filter @@ -645,7 +685,7 @@ class CellInstanceFilter(Filter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing filter data """ @@ -659,8 +699,8 @@ class CellInstanceFilter(Filter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = [int(x) for x in get_text(elem, 'bins').split()] + filter_id = int(get_text(elem, "id")) + bins = get_elem_list(elem, "bins", int) or [] cell_instances = list(zip(bins[::2], bins[1::2])) return cls(cell_instances, filter_id=filter_id) @@ -691,11 +731,11 @@ class SurfaceFilter(WithIDFilter): class ParticleFilter(Filter): - """Bins tally events based on the Particle type. + """Bins tally events based on the particle type. Parameters ---------- - bins : str, or iterable of str + bins : str, or sequence of str The particles to tally represented as strings ('neutron', 'photon', 'electron', 'positron'). filter_id : int @@ -703,7 +743,7 @@ class ParticleFilter(Filter): Attributes ---------- - bins : iterable of str + bins : sequence of str The particles to tally id : int Unique identifier for the filter @@ -723,8 +763,8 @@ class ParticleFilter(Filter): @Filter.bins.setter def bins(self, bins): + cv.check_type('bins', bins, Sequence, str) bins = np.atleast_1d(bins) - cv.check_iterable_type('filter bins', bins, str) for edge in bins: cv.check_value('filter bin', edge, _PARTICLES) self._bins = bins @@ -742,13 +782,40 @@ class ParticleFilter(Filter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = get_text(elem, 'bins').split() + filter_id = int(get_text(elem, "id")) + bins = get_elem_list(elem, "bins", str) or [] return cls(bins, filter_id=filter_id) +class ParentNuclideFilter(ParticleFilter): + """Bins tally events based on the parent nuclide + + Parameters + ---------- + bins : str, or iterable of str + Names of nuclides (e.g., 'Ni65') + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + bins : iterable of str + Names of nuclides + id : int + Unique identifier for the filter + num_bins : Integral + The number of filter bins + + """ + @Filter.bins.setter + def bins(self, bins): + bins = np.atleast_1d(bins) + cv.check_iterable_type('filter bins', bins, str) + self._bins = bins + + class MeshFilter(Filter): - """Bins tally event locations onto a regular, rectangular mesh. + """Bins tally event locations by mesh elements. Parameters ---------- @@ -764,8 +831,8 @@ class MeshFilter(Filter): id : int Unique identifier for the filter translation : Iterable of float - This array specifies a vector that is used to translate (shift) - the mesh for this filter + This array specifies a vector that is used to translate (shift) the mesh + for this filter bins : list of tuple A list of mesh indices for each filter bin, e.g. [(1, 1, 1), (2, 1, 1), ...] @@ -806,7 +873,6 @@ class MeshFilter(Filter): mesh_obj = kwargs['meshes'][mesh_id] filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - out = cls(mesh_obj, filter_id=filter_id) translation = group.get('translation') @@ -824,13 +890,17 @@ class MeshFilter(Filter): cv.check_type('filter mesh', mesh, openmc.MeshBase) self._mesh = mesh if isinstance(mesh, openmc.UnstructuredMesh): - if mesh.volumes is None: - self.bins = [] - else: + if mesh.has_statepoint_data: self.bins = list(range(len(mesh.volumes))) + else: + self.bins = [] else: self.bins = list(mesh.indices) + @property + def shape(self): + return self.mesh.dimension + @property def translation(self): return self._translation @@ -915,31 +985,242 @@ class MeshFilter(Filter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing filter data """ - element = super().to_xml_element() - element[0].text = str(self.mesh.id) + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + subelement = ET.SubElement(element, 'bins') + subelement.text = str(self.mesh.id) if self.translation is not None: element.set('translation', ' '.join(map(str, self.translation))) return element @classmethod - def from_xml_element(cls, elem, **kwargs): + def from_xml_element(cls, elem: ET.Element, **kwargs) -> MeshFilter: mesh_id = int(get_text(elem, 'bins')) mesh_obj = kwargs['meshes'][mesh_id] - filter_id = int(elem.get('id')) + filter_id = int(get_text(elem, "id")) out = cls(mesh_obj, filter_id=filter_id) - translation = elem.get('translation') + translation = get_elem_list(elem, "translation", float) or [] if translation: - out.translation = [float(x) for x in translation.split()] + out.translation = translation return out -class MeshSurfaceFilter(MeshFilter): - """Filter events by surface crossings on a regular, rectangular mesh. +class MeshBornFilter(MeshFilter): + """Filter events by the mesh cell a particle originated from. + + Parameters + ---------- + mesh : openmc.MeshBase + The mesh object that events will be tallied onto + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + mesh : openmc.MeshBase + The mesh object that events will be tallied onto + id : int + Unique identifier for the filter + translation : Iterable of float + This array specifies a vector that is used to translate (shift) + the mesh for this filter + bins : list of tuple + A list of mesh indices for each filter bin, e.g. [(1, 1, 1), (2, 1, 1), + ...] + num_bins : Integral + The number of filter bins + + """ + + +class MeshMaterialFilter(MeshFilter): + """Filter events by combinations of mesh elements and materials. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + mesh : openmc.MeshBase + The mesh object that events will be tallied onto + bins : iterable of 2-tuples or numpy.ndarray + Combinations of (mesh element, material) to tally, given as 2-tuples. + The first value in the tuple represents the index of the mesh element, + and the second value indicates the material (either a + :class:`openmc.Material` instance of the ID). + filter_id : int + Unique identifier for the filter + + """ + def __init__(self, mesh: openmc.MeshBase, bins, filter_id=None): + self.mesh = mesh + self.bins = bins + self.id = filter_id + self._translation = None + + @classmethod + def from_volumes(cls, mesh: openmc.MeshBase, volumes: openmc.MeshMaterialVolumes): + """Construct a MeshMaterialFilter from a MeshMaterialVolumes object. + + Parameters + ---------- + mesh : openmc.MeshBase + The mesh object that events will be tallied onto + volumes : openmc.MeshMaterialVolumes + The mesh material volumes to use for the filter + + Returns + ------- + MeshMaterialFilter + A new MeshMaterialFilter instance + + """ + # Get flat arrays of material IDs and element indices + mat_ids = volumes._materials[volumes._materials > -1] + elems, _ = np.where(volumes._materials > -1) + + # Stack them into a 2D array of (element, material) pairs + bins = np.column_stack((elems, mat_ids)) + return cls(mesh, bins) + + def __hash__(self): + data = (type(self).__name__, self.mesh.id, tuple(self.bins.ravel())) + return hash(data) + + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tID', self.id) + string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) + string += '{: <16}=\n{}\n'.format('\tBins', self.bins) + string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation) + return string + + @property + def shape(self): + return (self.num_bins,) + + @property + def mesh(self): + return self._mesh + + @mesh.setter + def mesh(self, mesh): + cv.check_type('filter mesh', mesh, openmc.MeshBase) + self._mesh = mesh + + @Filter.bins.setter + def bins(self, bins): + pairs = np.empty((len(bins), 2), dtype=int) + for i, (elem, mat) in enumerate(bins): + cv.check_type('element', elem, Integral) + cv.check_type('material', mat, (Integral, openmc.Material)) + pairs[i, 0] = elem + pairs[i, 1] = mat if isinstance(mat, Integral) else mat.id + self._bins = pairs + + def to_xml_element(self): + """Return XML element representing the filter. + + Returns + ------- + element : lxml.etree._Element + XML element containing filter data + + """ + element = ET.Element('filter') + element.set('id', str(self.id)) + element.set('type', self.short_name.lower()) + element.set('mesh', str(self.mesh.id)) + + if self.translation is not None: + element.set('translation', ' '.join(map(str, self.translation))) + + subelement = ET.SubElement(element, 'bins') + subelement.text = ' '.join(str(i) for i in self.bins.ravel()) + + return element + + @classmethod + def from_xml_element(cls, elem: ET.Element, **kwargs) -> MeshMaterialFilter: + filter_id = int(get_text(elem, "id")) + mesh_id = int(get_text(elem, "mesh")) + mesh_obj = kwargs['meshes'][mesh_id] + bins = get_elem_list(elem, "bins", int) or [] + bins = list(zip(bins[::2], bins[1::2])) + out = cls(mesh_obj, bins, filter_id=filter_id) + + translation = get_elem_list(elem, "translation", float) or [] + if translation: + out.translation = translation + return out + + @classmethod + def from_hdf5(cls, group, **kwargs): + if group['type'][()].decode() != cls.short_name.lower(): + raise ValueError("Expected HDF5 data for filter type '" + + cls.short_name.lower() + "' but got '" + + group['type'][()].decode() + " instead") + + if 'meshes' not in kwargs: + raise ValueError(cls.__name__ + " requires a 'meshes' keyword " + "argument.") + + mesh_id = group['mesh'][()] + mesh_obj = kwargs['meshes'][mesh_id] + bins = group['bins'][()] + filter_id = int(group.name.split('/')[-1].lstrip('filter ')) + out = cls(mesh_obj, bins, filter_id=filter_id) + + translation = group.get('translation') + if translation: + out.translation = translation[()] + + return out + + def get_pandas_dataframe(self, data_size, stride, **kwargs): + """Builds a Pandas DataFrame for the Filter's bins. + + This method constructs a Pandas DataFrame object for the filter with + columns annotated by filter bin information. This is a helper method for + :meth:`Tally.get_pandas_dataframe`. + + Parameters + ---------- + data_size : int + The total number of bins in the tally corresponding to this filter + stride : int + Stride in memory for the filter + + Returns + ------- + pandas.DataFrame + A Pandas DataFrame with a multi-index column for the cell instance. + The number of rows in the DataFrame is the same as the total number + of bins in the corresponding tally, with the filter bin appropriately + tiled to map to the corresponding tally bins. + + See also + -------- + Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe() + + """ + # Repeat and tile bins as necessary to account for other filters. + bins = np.repeat(self.bins, stride, axis=0) + tile_factor = data_size // len(bins) + bins = np.tile(bins, (tile_factor, 1)) + + columns = pd.MultiIndex.from_product([[self.short_name.lower()], + ['element', 'material']]) + return pd.DataFrame(bins, columns=columns) + + +class MeshSurfaceFilter(MeshFilter): + """Filter events by surface crossings on a mesh. Parameters ---------- @@ -950,8 +1231,6 @@ class MeshSurfaceFilter(MeshFilter): Attributes ---------- - bins : Integral - The mesh ID mesh : openmc.MeshBase The mesh object that events will be tallied onto translation : Iterable of float @@ -960,14 +1239,15 @@ class MeshSurfaceFilter(MeshFilter): id : int Unique identifier for the filter bins : list of tuple - A list of mesh indices / surfaces for each filter bin, e.g. [(1, 1, 'x-min out'), (1, 1, 'x-min in'), ...] - num_bins : Integral The number of filter bins """ + @property + def shape(self): + return (self.num_bins,) @MeshFilter.mesh.setter def mesh(self, mesh): @@ -1141,7 +1421,7 @@ class RealFilter(Filter): cv.check_type('filter value', v1, Real) # Make sure that each tuple has values that are increasing - if v1 < v0: + if v1 <= v0: raise ValueError(f'Values {v0} and {v1} appear to be out of ' 'order') @@ -1204,8 +1484,8 @@ class RealFilter(Filter): def get_bin_index(self, filter_bin): i = np.where(self.bins[:, 1] == filter_bin[1])[0] if len(i) == 0: - msg = (f'Unable to get the bin index for Filter since ' - '"{filter_bin}" is not one of the bins') + msg = ('Unable to get the bin index for Filter since ' + f'"{filter_bin}" is not one of the bins') raise ValueError(msg) else: return i[0] @@ -1265,7 +1545,7 @@ class RealFilter(Filter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing filter data """ @@ -1275,8 +1555,8 @@ class RealFilter(Filter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - bins = [float(x) for x in get_text(elem, 'bins').split()] + filter_id = int(get_text(elem, "id")) + bins = get_elem_list(elem, "bins", float) or [] return cls(bins, filter_id=filter_id) @@ -1287,7 +1567,7 @@ class EnergyFilter(RealFilter): ---------- values : Iterable of Real A list of values for which each successive pair constitutes a range of - energies in [eV] for a single bin + energies in [eV] for a single bin. Entries must be positive and ascending. filter_id : int Unique identifier for the filter @@ -1307,6 +1587,10 @@ class EnergyFilter(RealFilter): """ units = 'eV' + def __init__(self, values, filter_id=None): + cv.check_length('values', values, 2) + super().__init__(values, filter_id) + def get_bin_index(self, filter_bin): # Use lower energy bound to find index for RealFilters deltas = np.abs(self.bins[:, 1] - filter_bin[1]) / filter_bin[1] @@ -1324,6 +1608,67 @@ class EnergyFilter(RealFilter): cv.check_greater_than('filter value', v0, 0., equality=True) cv.check_greater_than('filter value', v1, 0., equality=True) + def get_tabular(self, values, **kwargs): + """Create a tabulated distribution based on tally results with an energy filter + + This method provides an easy way to create a distribution in energy + (e.g., a source spectrum) based on tally results that were obtained from + using an :class:`~openmc.EnergyFilter`. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + values : iterable of float + Array of numeric values, typically from a tally result + **kwargs + Keyword arguments passed to :class:`openmc.stats.Tabular` + + Returns + ------- + openmc.stats.Tabular + Tabular distribution with histogram interpolation + """ + + probabilities = np.array(values, dtype=float) + probabilities /= probabilities.sum() + + # Determine probability per eV, adding extra 0 at the end since it is a histogram + probability_per_ev = probabilities / np.diff(self.values) + probability_per_ev = np.append(probability_per_ev, 0.0) + + kwargs.setdefault('interpolation', 'histogram') + return openmc.stats.Tabular(self.values, probability_per_ev, **kwargs) + + @property + def lethargy_bin_width(self): + """Calculates the base 10 log width of energy bins which is useful when + plotting the normalized flux. + + Returns + ------- + numpy.array + Array of bin widths + """ + return np.log10(self.bins[:, 1]/self.bins[:, 0]) + + @classmethod + def from_group_structure(cls, group_structure): + """Construct an EnergyFilter instance from a standard group structure. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + group_structure : str + Name of the group structure. Must be a valid key of + openmc.mgxs.GROUP_STRUCTURES dictionary. + + """ + + cv.check_value('group_structure', group_structure, openmc.mgxs.GROUP_STRUCTURES.keys()) + return cls(openmc.mgxs.GROUP_STRUCTURES[group_structure.upper()]) + class EnergyoutFilter(EnergyFilter): """Bins tally events based on outgoing particle energy. @@ -1496,6 +1841,11 @@ class DistribcellFilter(Filter): def paths(self): return self._paths + @paths.setter + def paths(self, paths): + cv.check_iterable_type('paths', paths, str) + self._paths = paths + @Filter.bins.setter def bins(self, bins): # Format the bins as a 1D numpy array. @@ -1514,11 +1864,6 @@ class DistribcellFilter(Filter): self._bins = bins - @paths.setter - def paths(self, paths): - cv.check_iterable_type('paths', paths, str) - self._paths = paths - def can_merge(self, other): # Distribcell filters cannot have more than one bin return False @@ -1598,7 +1943,7 @@ class DistribcellFilter(Filter): level_key = f'level {i_level + 1}' # Create a dictionary for this level for Pandas Multi-index - level_dict = OrderedDict() + level_dict = {} # Use the first distribcell path to determine if level # is a universe/cell or lattice level @@ -1667,7 +2012,7 @@ class DistribcellFilter(Filter): # Concatenate with DataFrame of distribcell instance IDs if level_df is not None: level_df = level_df.dropna(axis=1, how='all') - level_df = level_df.astype(np.int) + level_df = level_df.astype(int) df = pd.concat([level_df, df], axis=1) return df @@ -1715,6 +2060,44 @@ class MuFilter(RealFilter): cv.check_less_than('filter value', x, 1., equality=True) +class MuSurfaceFilter(MuFilter): + """Bins tally events based on the angle of surface crossing. + + This filter bins events based on the cosine of the angle between the + direction of the particle and the normal to the surface at the point it + crosses. Only used in conjunction with a SurfaceFilter and current score. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + values : int or Iterable of Real + A grid of surface crossing angles which the events will be divided into. + Values represent the cosine of the angle between the direction of the + particle and the normal to the surface at the point it crosses. If an + iterable is given, the values will be used explicitly as grid points. If + a single int is given, the range [-1, 1] will be divided equally into + that number of bins. + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + values : numpy.ndarray + An array of values for which each successive pair constitutes a range of + surface crossing angle cosines for a single bin. + id : int + Unique identifier for the filter + bins : numpy.ndarray + An array of shape (N, 2) where each row is a pair of cosines of surface + crossing angle for a single filter + num_bins : Integral + The number of filter bins + + """ + # Note: inherits implementation from MuFilter + + class PolarFilter(RealFilter): """Bins tally events based on the incident particle's direction. @@ -1847,6 +2230,9 @@ class EnergyFunctionFilter(Filter): A grid of energy values in [eV] y : iterable of Real A grid of interpolant values in [eV] + interpolation : str + Interpolation scheme: {'histogram', 'linear-linear', 'linear-log', + 'log-linear', 'log-log', 'quadratic', 'cubic'} filter_id : int Unique identifier for the filter @@ -1856,6 +2242,9 @@ class EnergyFunctionFilter(Filter): A grid of energy values in [eV] y : iterable of Real A grid of interpolant values in [eV] + interpolation : str + Interpolation scheme: {'histogram', 'linear-linear', 'linear-log', + 'log-linear', 'log-log', 'quadratic', 'cubic'} id : int Unique identifier for the filter num_bins : Integral @@ -1863,14 +2252,25 @@ class EnergyFunctionFilter(Filter): """ - def __init__(self, energy, y, filter_id=None): + # keys selected to match those in function.py where possible + # skip 6 b/c ENDF-6 reserves this value for + # "special one-dimensional interpolation law" + INTERPOLATION_SCHEMES = {1: 'histogram', 2: 'linear-linear', + 3: 'linear-log', 4: 'log-linear', + 5: 'log-log', 7: 'quadratic', + 8: 'cubic'} + + def __init__(self, energy, y, interpolation='linear-linear', filter_id=None): self.energy = energy self.y = y self.id = filter_id + self.interpolation = interpolation def __eq__(self, other): if type(self) is not type(other): return False + elif not self.interpolation == other.interpolation: + return False elif not all(self.energy == other.energy): return False else: @@ -1904,12 +2304,14 @@ class EnergyFunctionFilter(Filter): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tEnergy', self.energy) string += '{: <16}=\t{}\n'.format('\tInterpolant', self.y) + string += '{: <16}=\t{}\n'.format('\tInterpolation', self.interpolation) return hash(string) def __repr__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tEnergy', self.energy) string += '{: <16}=\t{}\n'.format('\tInterpolant', self.y) + string += '{: <16}=\t{}\n'.format('\tInterpolation', self.interpolation) string += '{: <16}=\t{}\n'.format('\tID', self.id) return string @@ -1921,10 +2323,16 @@ class EnergyFunctionFilter(Filter): + group['type'][()].decode() + " instead") energy = group['energy'][()] - y = group['y'][()] + y_grp = group['y'] + y = y_grp[()] filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - return cls(energy, y, filter_id=filter_id) + out = cls(energy, y, filter_id=filter_id) + if 'interpolation' in y_grp.attrs: + out.interpolation = \ + cls.INTERPOLATION_SCHEMES[y_grp.attrs['interpolation'][()]] + + return out @classmethod def from_tabulated1d(cls, tab1d): @@ -1946,27 +2354,16 @@ class EnergyFunctionFilter(Filter): if tab1d.n_regions > 1: raise ValueError('Only Tabulated1Ds with a single interpolation ' 'region are supported') - if tab1d.interpolation[0] != 2: - raise ValueError('Only linear-linear Tabulated1Ds are supported') - - return cls(tab1d.x, tab1d.y) + interpolation_val = tab1d.interpolation[0] + if interpolation_val not in cls.INTERPOLATION_SCHEMES.keys(): + raise ValueError('Only histogram, linear-linear, linear-log, log-linear, and ' + 'log-log Tabulated1Ds are supported') + return cls(tab1d.x, tab1d.y, cls.INTERPOLATION_SCHEMES[interpolation_val]) @property def energy(self): return self._energy - @property - def y(self): - return self._y - - @property - def bins(self): - raise AttributeError('EnergyFunctionFilters have no bins.') - - @property - def num_bins(self): - return 1 - @energy.setter def energy(self, energy): # Format the bins as a 1D numpy array. @@ -1979,6 +2376,10 @@ class EnergyFunctionFilter(Filter): self._energy = energy + @property + def y(self): + return self._y + @y.setter def y(self, y): # Format the bins as a 1D numpy array. @@ -1989,16 +2390,41 @@ class EnergyFunctionFilter(Filter): self._y = y + @property + def interpolation(self): + return self._interpolation + + @interpolation.setter + def interpolation(self, val): + cv.check_type('interpolation', val, str) + cv.check_value('interpolation', val, self.INTERPOLATION_SCHEMES.values()) + + if val == 'quadratic' and len(self.energy) < 3: + raise ValueError('Quadratic interpolation requires 3 or more values.') + + if val == 'cubic' and len(self.energy) < 4: + raise ValueError('Cubic interpolation requires 3 or more values.') + + self._interpolation = val + + @property + def bins(self): + raise AttributeError('EnergyFunctionFilters have no bins.') + @bins.setter def bins(self, bins): raise RuntimeError('EnergyFunctionFilters have no bins.') + @property + def num_bins(self): + return 1 + def to_xml_element(self): """Return XML Element representing the Filter. Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing filter data """ @@ -2012,14 +2438,21 @@ class EnergyFunctionFilter(Filter): subelement = ET.SubElement(element, 'y') subelement.text = ' '.join(str(y) for y in self.y) + subelement = ET.SubElement(element, 'interpolation') + subelement.text = self.interpolation + return element @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - energy = [float(x) for x in get_text(elem, 'energy').split()] - y = [float(x) for x in get_text(elem, 'y').split()] - return cls(energy, y, filter_id=filter_id) + filter_id = int(get_text(elem, "id")) + energy = get_elem_list(elem, "energy", float) or [] + y = get_elem_list(elem, "y", float) or [] + out = cls(energy, y, filter_id=filter_id) + interpolation = get_text(elem, "interpolation") + if interpolation is not None: + out.interpolation = interpolation + return out def can_merge(self, other): return False @@ -2079,3 +2512,26 @@ class EnergyFunctionFilter(Filter): {self.short_name.lower(): filter_bins})]) return df + + +class WeightFilter(RealFilter): + """Bins tally events based on the incoming particle weight. + + Parameters + ---------- + Values : Iterable of float + A list or iterable of the weight boundaries, as float values. + filter_id : int + Unique identifier for the filter + + Attributes + ---------- + id : int + Unique identifier for the filter + bins : numpy.ndarray + An array of integer values representing the weights by which to filter + num_bins : int + The number of filter bins + values : numpy.ndarray + Array of weight boundaries + """ diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 1c07f58b71..b79c8fc79e 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -1,8 +1,10 @@ from numbers import Integral, Real -from xml.etree import ElementTree as ET + +import lxml.etree as ET import openmc.checkvalue as cv -from . import Filter +from .filter import Filter +from ._xml import get_text class ExpansionFilter(Filter): @@ -33,7 +35,7 @@ class ExpansionFilter(Filter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Legendre filter data """ @@ -48,10 +50,38 @@ class ExpansionFilter(Filter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) + filter_id = int(get_text(elem, "id")) + order = int(get_text(elem, "order")) return cls(order, filter_id=filter_id) + def merge(self, other): + """Merge this filter with another. + + This overrides the behavior of the parent Filter class, since its + merging technique is to take the union of the set of bins of each + filter. That technique does not apply to expansion filters, since the + argument should be the maximum filter order rather than the list of all + bins. + + Parameters + ---------- + other : openmc.Filter + Filter to merge with + + Returns + ------- + merged_filter : openmc.Filter + Filter resulting from the merge + + """ + + if not self.can_merge(other): + msg = f'Unable to merge "{type(self)}" with "{type(other)}"' + raise ValueError(msg) + + # Create a new filter with these bins and a new auto-generated ID + return type(self)(max(self.order, other.order)) + class LegendreFilter(ExpansionFilter): r"""Score Legendre expansion moments up to specified order. @@ -218,7 +248,7 @@ class SpatialLegendreFilter(ExpansionFilter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Legendre filter data """ @@ -234,11 +264,11 @@ class SpatialLegendreFilter(ExpansionFilter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) - axis = elem.find('axis').text - minimum = float(elem.find('min').text) - maximum = float(elem.find('max').text) + filter_id = int(get_text(elem, "id")) + order = int(get_text(elem, "order")) + axis = get_text(elem, "axis") + minimum = float(get_text(elem, "min")) + maximum = float(get_text(elem, "max")) return cls(order, axis, minimum, maximum, filter_id=filter_id) @@ -323,7 +353,7 @@ class SphericalHarmonicsFilter(ExpansionFilter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing spherical harmonics filter data """ @@ -333,10 +363,10 @@ class SphericalHarmonicsFilter(ExpansionFilter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) + filter_id = int(get_text(elem, "id")) + order = int(get_text(elem, "order")) filter = cls(order, filter_id=filter_id) - filter.cosine = elem.get('cosine') + filter.cosine = get_text(elem, "cosine") return filter @@ -473,7 +503,7 @@ class ZernikeFilter(ExpansionFilter): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Zernike filter data """ @@ -489,11 +519,11 @@ class ZernikeFilter(ExpansionFilter): @classmethod def from_xml_element(cls, elem, **kwargs): - filter_id = int(elem.get('id')) - order = int(elem.find('order').text) - x = float(elem.find('x').text) - y = float(elem.find('y').text) - r = float(elem.find('r').text) + filter_id = int(get_text(elem, "id")) + order = int(get_text(elem, "order")) + x = float(get_text(elem, "x")) + y = float(get_text(elem, "y")) + r = float(get_text(elem, "r")) return cls(order, x, y, r, filter_id=filter_id) diff --git a/openmc/geometry.py b/openmc/geometry.py index 58a33c1b12..8496fb23ad 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -1,12 +1,16 @@ -from collections import OrderedDict, defaultdict -from collections.abc import Iterable +from __future__ import annotations +import os +from collections import defaultdict from copy import deepcopy +from collections.abc import Iterable from pathlib import Path -from xml.etree import ElementTree as ET +import warnings +import lxml.etree as ET import openmc import openmc._xml as xml -from .checkvalue import check_type +from .plots import add_plot_params +from .checkvalue import check_type, check_less_than, check_greater_than, PathLike class Geometry: @@ -22,15 +26,27 @@ class Geometry: ---------- root_universe : openmc.UniverseBase Root universe which contains all others - bounding_box : 2-tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box of the universe. + merge_surfaces : bool + Whether to remove redundant surfaces when the geometry is exported. + surface_precision : int + Number of decimal places to round to for comparing the coefficients of + surfaces for considering them topologically equivalent. """ - def __init__(self, root=None): + def __init__( + self, + root: openmc.UniverseBase | Iterable[openmc.Cell] | None = None, + merge_surfaces: bool = False, + surface_precision: int = 10 + ): self._root_universe = None self._offsets = {} + self.merge_surfaces = merge_surfaces + self.surface_precision = surface_precision if root is not None: if isinstance(root, openmc.UniverseBase): self.root_universe = root @@ -41,18 +57,38 @@ class Geometry: self._root_universe = univ @property - def root_universe(self): + def root_universe(self) -> openmc.UniverseBase: return self._root_universe - @property - def bounding_box(self): - return self.root_universe.bounding_box - @root_universe.setter def root_universe(self, root_universe): check_type('root universe', root_universe, openmc.UniverseBase) self._root_universe = root_universe + @property + def bounding_box(self) -> openmc.BoundingBox: + return self.root_universe.bounding_box + + @property + def merge_surfaces(self) -> bool: + return self._merge_surfaces + + @merge_surfaces.setter + def merge_surfaces(self, merge_surfaces): + check_type('merge surfaces', merge_surfaces, bool) + self._merge_surfaces = merge_surfaces + + @property + def surface_precision(self) -> int: + return self._surface_precision + + @surface_precision.setter + def surface_precision(self, surface_precision): + check_type('surface precision', surface_precision, int) + check_less_than('surface_precision', surface_precision, 16) + check_greater_than('surface_precision', surface_precision, 0) + self._surface_precision = surface_precision + def add_volume_information(self, volume_calc): """Add volume information from a stochastic volume calculation. @@ -75,6 +111,38 @@ class Geometry: if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) + def to_xml_element(self, remove_surfs=False) -> ET.Element: + """Creates a 'geometry' element to be written to an XML file. + + Parameters + ---------- + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting + + """ + # Find and remove redundant surfaces from the geometry + if remove_surfs: + warnings.warn("remove_surfs kwarg will be deprecated soon, please " + "set the Geometry.merge_surfaces attribute instead.") + self.merge_surfaces = True + + if self.merge_surfaces: + self.remove_redundant_surfaces() + + # Create XML representation + element = ET.Element("geometry") + self.root_universe.create_xml_subelement(element) + + # Sort the elements in the file + element[:] = sorted(element, key=lambda x: ( + x.tag, int(x.get('id')))) + + # Clean the indentation in the file to be user-readable + xml.clean_indentation(element) + + return element + def export_to_xml(self, path='geometry.xml', remove_surfs=False): """Export geometry to an XML file. @@ -89,20 +157,7 @@ class Geometry: .. versionadded:: 0.12 """ - # Find and remove redundant surfaces from the geometry - if remove_surfs: - self.remove_redundant_surfaces() - - # Create XML representation - root_element = ET.Element("geometry") - self.root_universe.create_xml_subelement(root_element, memo=set()) - - # Sort the elements in the file - root_element[:] = sorted(root_element, key=lambda x: ( - x.tag, int(x.get('id')))) - - # Clean the indentation in the file to be user-readable - xml.clean_indentation(root_element) + root_element = self.to_xml_element(remove_surfs) # Check if path is a directory p = Path(path) @@ -110,18 +165,17 @@ class Geometry: p /= 'geometry.xml' # Write the XML Tree to the geometry.xml file - xml.reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml(cls, path='geometry.xml', materials=None): - """Generate geometry from XML file + def from_xml_element(cls, elem, materials=None) -> Geometry: + """Generate geometry from an XML element Parameters ---------- - path : str, optional - Path to geometry XML file + elem : lxml.etree._Element + XML element materials : openmc.Materials or None Materials used to assign to cells. If None, an attempt is made to generate it from the materials.xml file. @@ -132,6 +186,11 @@ class Geometry: Geometry object """ + mats = dict() + if materials is not None: + mats.update({str(m.id): m for m in materials}) + mats['void'] = None + # Helper function for keeping a cache of Universe instances universes = {} def get_universe(univ_id): @@ -140,13 +199,10 @@ class Geometry: universes[univ_id] = univ return universes[univ_id] - tree = ET.parse(path) - root = tree.getroot() - # Get surfaces surfaces = {} periodic = {} - for surface in root.findall('surface'): + for surface in elem.findall('surface'): s = openmc.Surface.from_xml_element(surface) surfaces[s.id] = s @@ -160,24 +216,24 @@ class Geometry: surfaces[s1].periodic_surface = surfaces[s2] # Add any DAGMC universes - for elem in root.findall('dagmc_universe'): - dag_univ = openmc.DAGMCUniverse.from_xml_element(elem) + for e in elem.findall('dagmc_universe'): + dag_univ = openmc.DAGMCUniverse.from_xml_element(e, mats) universes[dag_univ.id] = dag_univ # Dictionary that maps each universe to a list of cells/lattices that - # contain it (needed to determine which universe is the root) + # contain it (needed to determine which universe is the elem) child_of = defaultdict(list) - for elem in root.findall('lattice'): - lat = openmc.RectLattice.from_xml_element(elem, get_universe) + for e in elem.findall('lattice'): + lat = openmc.RectLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: child_of[lat.outer].append(lat) for u in lat.universes.ravel(): child_of[u].append(lat) - for elem in root.findall('hex_lattice'): - lat = openmc.HexLattice.from_xml_element(elem, get_universe) + for e in elem.findall('hex_lattice'): + lat = openmc.HexLattice.from_xml_element(e, get_universe) universes[lat.id] = lat if lat.outer is not None: child_of[lat.outer].append(lat) @@ -191,15 +247,8 @@ class Geometry: for u in ring: child_of[u].append(lat) - # Create dictionary to easily look up materials - if materials is None: - filename = Path(path).parent / 'materials.xml' - materials = openmc.Materials.from_xml(str(filename)) - mats = {str(m.id): m for m in materials} - mats['void'] = None - - for elem in root.findall('cell'): - c = openmc.Cell.from_xml_element(elem, surfaces, mats, get_universe) + for e in elem.findall('cell'): + c = openmc.Cell.from_xml_element(e, surfaces, mats, get_universe) if c.fill_type in ('universe', 'lattice'): child_of[c.fill].append(c) @@ -211,7 +260,43 @@ class Geometry: else: raise ValueError('Error determining root universe.') - def find(self, point): + @classmethod + def from_xml( + cls, + path: PathLike = 'geometry.xml', + materials: PathLike | 'openmc.Materials' | None = 'materials.xml' + ) -> Geometry: + """Generate geometry from XML file + + Parameters + ---------- + path : PathLike, optional + Path to geometry XML file + materials : openmc.Materials or PathLike + Materials used to assign to cells. If PathLike, an attempt is made + to generate materials from the provided xml file. + + Returns + ------- + openmc.Geometry + Geometry object + + """ + + # Using str and os.PathLike here to avoid error when using just the imported PathLike + # TypeError: Subscripted generics cannot be used with class and instance checks + check_type('materials', materials, (str, os.PathLike, openmc.Materials)) + + if isinstance(materials, (str, os.PathLike)): + materials = openmc.Materials.from_xml(materials) + + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) + root = tree.getroot() + + return cls.from_xml_element(root, materials) + + def find(self, point) -> list: """Find cells/universes/lattices which contain a given point Parameters @@ -228,7 +313,7 @@ class Geometry: """ return self.root_universe.find(point) - def get_instances(self, paths): + def get_instances(self, paths) -> int | list[int]: """Return the instance number(s) for a cell/material in a geometry path. The instance numbers are used as indices into distributed @@ -275,61 +360,75 @@ class Geometry: return indices if return_list else indices[0] - def get_all_cells(self): + def get_all_cells(self) -> dict[int, openmc.Cell]: """Return all cells in the geometry. Returns ------- - collections.OrderedDict + dict Dictionary mapping cell IDs to :class:`openmc.Cell` instances """ if self.root_universe is not None: - return self.root_universe.get_all_cells(memo=set()) + return self.root_universe.get_all_cells() else: - return OrderedDict() + return {} - def get_all_universes(self): + def get_all_universes(self) -> dict[int, openmc.Universe]: """Return all universes in the geometry. Returns ------- - collections.OrderedDict + dict Dictionary mapping universe IDs to :class:`openmc.Universe` instances """ - universes = OrderedDict() + universes = {} universes[self.root_universe.id] = self.root_universe universes.update(self.root_universe.get_all_universes()) return universes - def get_all_materials(self): + def get_all_nuclides(self) -> list[str]: + """Return all nuclides within the geometry. + + Returns + ------- + list + Sorted list of all nuclides in materials appearing in the geometry + + """ + all_nuclides = set() + for material in self.get_all_materials().values(): + all_nuclides |= set(material.get_nuclides()) + return sorted(all_nuclides) + + def get_all_materials(self) -> dict[int, openmc.Material]: """Return all materials within the geometry. Returns ------- - collections.OrderedDict + dict Dictionary mapping material IDs to :class:`openmc.Material` instances """ if self.root_universe is not None: - return self.root_universe.get_all_materials(memo=set()) + return self.root_universe.get_all_materials() else: - return OrderedDict() + return {} - def get_all_material_cells(self): + def get_all_material_cells(self) -> dict[int, openmc.Cell]: """Return all cells filled by a material Returns ------- - collections.OrderedDict + dict Dictionary mapping cell IDs to :class:`openmc.Cell` instances that are filled with materials or distributed materials. """ - material_cells = OrderedDict() + material_cells = {} for cell in self.get_all_cells().values(): if cell.fill_type in ('material', 'distribmat'): @@ -338,7 +437,7 @@ class Geometry: return material_cells - def get_all_material_universes(self): + def get_all_material_universes(self) -> dict[int, openmc.Universe]: """Return all universes having at least one material-filled cell. This method can be used to find universes that have at least one cell @@ -346,12 +445,12 @@ class Geometry: Returns ------- - collections.OrderedDict + dict Dictionary mapping universe IDs to :class:`openmc.Universe` instances with at least one material-filled cell """ - material_universes = OrderedDict() + material_universes = {} for universe in self.get_all_universes().values(): for cell in universe.cells.values(): @@ -361,16 +460,16 @@ class Geometry: return material_universes - def get_all_lattices(self): + def get_all_lattices(self) -> dict[int, openmc.Lattice]: """Return all lattices defined Returns ------- - collections.OrderedDict + dict Dictionary mapping lattice IDs to :class:`openmc.Lattice` instances """ - lattices = OrderedDict() + lattices = {} for cell in self.get_all_cells().values(): if cell.fill_type == 'lattice': @@ -379,46 +478,24 @@ class Geometry: return lattices - def get_all_surfaces(self): + def get_all_surfaces(self) -> dict[int, openmc.Surface]: """ Return all surfaces used in the geometry Returns ------- - collections.OrderedDict + dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances """ - surfaces = OrderedDict() + surfaces = {} for cell in self.get_all_cells().values(): if cell.region is not None: surfaces = cell.region.get_surfaces(surfaces) return surfaces - def get_redundant_surfaces(self): - """Return all of the topologically redundant surface IDs - - .. versionadded:: 0.12 - - Returns - ------- - dict - Dictionary whose keys are the ID of a redundant surface and whose - values are the topologically equivalent :class:`openmc.Surface` - that should replace it. - - """ - tally = defaultdict(list) - for surf in self.get_all_surfaces().values(): - coeffs = tuple(surf._coefficients[k] for k in surf._coeff_keys) - key = (surf._type,) + coeffs - tally[key].append(surf) - return {replace.id: keep - for keep, *redundant in tally.values() - for replace in redundant} - - def _get_domains_by_name(self, name, case_sensitive, matching, domain_type): + def _get_domains_by_name(self, name, case_sensitive, matching, domain_type) -> list: if not case_sensitive: name = name.lower() @@ -435,7 +512,9 @@ class Geometry: domains.sort(key=lambda x: x.id) return domains - def get_materials_by_name(self, name, case_sensitive=False, matching=False): + def get_materials_by_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Material]: """Return a list of materials with matching names. Parameters @@ -456,7 +535,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'material') - def get_cells_by_name(self, name, case_sensitive=False, matching=False): + def get_cells_by_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Cell]: """Return a list of cells with matching names. Parameters @@ -477,7 +558,34 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'cell') - def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False): + def get_surfaces_by_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Surface]: + """Return a list of surfaces with matching names. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + name : str + The name to search match + case_sensitive : bool + Whether to distinguish upper and lower case letters in each + surface's name (default is False) + matching : bool + Whether the names must match completely (default is False) + + Returns + ------- + list of openmc.Surface + Surfaces matching the queried name + + """ + return self._get_domains_by_name(name, case_sensitive, matching, 'surface') + + def get_cells_by_fill_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Cell]: """Return a list of cells with fills with matching names. Parameters @@ -522,7 +630,9 @@ class Geometry: return sorted(cells, key=lambda x: x.id) - def get_universes_by_name(self, name, case_sensitive=False, matching=False): + def get_universes_by_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Universe]: """Return a list of universes with matching names. Parameters @@ -543,7 +653,9 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'universe') - def get_lattices_by_name(self, name, case_sensitive=False, matching=False): + def get_lattices_by_name( + self, name, case_sensitive=False, matching=False + ) -> list[openmc.Lattice]: """Return a list of lattices with matching names. Parameters @@ -564,17 +676,43 @@ class Geometry: """ return self._get_domains_by_name(name, case_sensitive, matching, 'lattice') - def remove_redundant_surfaces(self): - """Remove redundant surfaces from the geometry""" + def remove_redundant_surfaces(self) -> dict[int, openmc.Surface]: + """Remove and return all of the redundant surfaces. + Uses surface_precision attribute of Geometry instance for rounding and + comparing surface coefficients. + + .. versionadded:: 0.12 + + Returns + ------- + redundant_surfaces + Dictionary whose keys are the ID of a redundant surface and whose + values are the topologically equivalent :class:`openmc.Surface` + that should replace it. + + """ # Get redundant surfaces - redundant_surfaces = self.get_redundant_surfaces() + redundancies = defaultdict(list) + for surf in self.get_all_surfaces().values(): + coeffs = tuple(round(surf._coefficients[k], + self.surface_precision) + for k in surf._coeff_keys) + key = (surf._type, surf._boundary_type) + coeffs + redundancies[key].append(surf) - # Iterate through all cells contained in the geometry - for cell in self.get_all_cells().values(): - # Recursively remove redundant surfaces from regions - if cell.region: - cell.region.remove_redundant_surfaces(redundant_surfaces) + redundant_surfaces = {replace.id: keep + for keep, *redundant in redundancies.values() + for replace in redundant} + + if redundant_surfaces: + # Iterate through all cells contained in the geometry + for cell in self.get_all_cells().values(): + # Recursively remove redundant surfaces from regions + if cell.region: + cell.region.remove_redundant_surfaces(redundant_surfaces) + + return redundant_surfaces def determine_paths(self, instances_only=False): """Determine paths through CSG tree for cells and materials. @@ -601,10 +739,45 @@ class Geometry: # Recursively traverse the CSG tree to count all cell instances self.root_universe._determine_paths(instances_only=instances_only) - def clone(self): + def clone(self) -> Geometry: """Create a copy of this geometry with new unique IDs for all of its enclosed materials, surfaces, cells, universes and lattices.""" clone = deepcopy(self) clone.root_universe = self.root_universe.clone() return clone + + @add_plot_params + def plot(self, *args, **kwargs): + """Display a slice plot of the geometry. + + .. versionadded:: 0.14.0 + """ + model = openmc.Model() + model.geometry = self + model.materials = self.get_all_materials().values() + + # collect all the material names from the geometry + all_material_names = {m.name for m in model.materials if m.name is not None} + + # makes a placeholder material for each material name if it isn't + # already present on the model. These materials are otherwise missing + # from the geometry and are needed for plotting. + for universe in model.geometry.get_all_universes().values(): + if not isinstance(universe, openmc.DAGMCUniverse): + continue + for name in universe.material_names: + # if this name is already present in the model, skip it + # (this can happen if the same material name is used in multiple + # universes) + if name in all_material_names: + continue + # if the material name is not present on the model, + # create a placeholder material with the same name + # and add it to the model + mat_dag = openmc.Material(name=name) + mat_dag.add_nuclide('H1', 1.0) + model.materials.append(mat_dag) + all_material_names.add(name) + + return model.plot(*args, **kwargs) diff --git a/openmc/lattice.py b/openmc/lattice.py index 1c311fbb1c..f0e8b40b0d 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -1,17 +1,16 @@ from abc import ABC -from collections import OrderedDict from collections.abc import Iterable from copy import deepcopy from math import sqrt, floor from numbers import Real import types -from xml.etree import ElementTree as ET +import lxml.etree as ET import numpy as np import openmc import openmc.checkvalue as cv -from ._xml import get_text +from ._xml import get_elem_list, get_text from .mixin import IDManagerMixin @@ -34,16 +33,16 @@ class Lattice(IDManagerMixin, ABC): Name of the lattice pitch : Iterable of float Pitch of the lattice in each direction in cm - outer : openmc.Universe + outer : openmc.UniverseBase A universe to fill all space outside the lattice - universes : Iterable of Iterable of openmc.Universe + universes : Iterable of Iterable of openmc.UniverseBase A two-or three-dimensional list/array of universes filling each element of the lattice """ next_id = 1 - used_ids = openmc.Universe.used_ids + used_ids = openmc.UniverseBase.used_ids def __init__(self, lattice_id=None, name=''): # Initialize Lattice class attributes @@ -57,18 +56,6 @@ class Lattice(IDManagerMixin, ABC): def name(self): return self._name - @property - def pitch(self): - return self._pitch - - @property - def outer(self): - return self._outer - - @property - def universes(self): - return self._universes - @name.setter def name(self, name): if name is not None: @@ -77,11 +64,23 @@ class Lattice(IDManagerMixin, ABC): else: self._name = '' + @property + def pitch(self): + return self._pitch + + @property + def outer(self): + return self._outer + @outer.setter def outer(self, outer): - cv.check_type('outer universe', outer, openmc.Universe) + cv.check_type('outer universe', outer, openmc.UniverseBase) self._outer = outer + @property + def universes(self): + return self._universes + @staticmethod def from_hdf5(group, universes): """Create lattice from HDF5 group @@ -92,7 +91,7 @@ class Lattice(IDManagerMixin, ABC): Group in HDF5 file universes : dict Dictionary mapping universe IDs to instances of - :class:`openmc.Universe`. + :class:`openmc.UniverseBase`. Returns ------- @@ -113,22 +112,22 @@ class Lattice(IDManagerMixin, ABC): Returns ------- - universes : collections.OrderedDict + universes : dict Dictionary whose keys are universe IDs and values are - :class:`openmc.Universe` instances + :class:`openmc.UniverseBase` instances """ - univs = OrderedDict() + univs = {} for k in range(len(self._universes)): for j in range(len(self._universes[k])): - if isinstance(self._universes[k][j], openmc.Universe): + if isinstance(self._universes[k][j], openmc.UniverseBase): u = self._universes[k][j] univs[u._id] = u else: for i in range(len(self._universes[k][j])): u = self._universes[k][j][i] - assert isinstance(u, openmc.Universe) + assert isinstance(u, openmc.UniverseBase) univs[u._id] = u if self.outer is not None: @@ -164,18 +163,18 @@ class Lattice(IDManagerMixin, ABC): Returns ------- - cells : collections.OrderedDict + cells : dict Dictionary whose keys are cell IDs and values are :class:`Cell` instances """ - cells = OrderedDict() + cells = {} - if memo and self in memo: + if memo is None: + memo = set() + elif self in memo: return cells - - if memo is not None: - memo.add(self) + memo.add(self) unique_universes = self.get_unique_universes() @@ -189,13 +188,16 @@ class Lattice(IDManagerMixin, ABC): Returns ------- - materials : collections.OrderedDict + materials : dict Dictionary whose keys are material IDs and values are :class:`Material` instances """ - materials = OrderedDict() + if memo is None: + memo = set() + + materials = {} # Append all Cells in each Cell in the Universe to the dictionary cells = self.get_all_cells(memo) @@ -204,20 +206,25 @@ class Lattice(IDManagerMixin, ABC): return materials - def get_all_universes(self): + def get_all_universes(self, memo=None): """Return all universes that are contained within the lattice Returns ------- - universes : collections.OrderedDict + universes : dict Dictionary whose keys are universe IDs and values are :class:`Universe` instances """ - # Initialize a dictionary of all Universes contained by the Lattice # in each nested Universe level - all_universes = OrderedDict() + all_universes = {} + + if memo is None: + memo = set() + elif self in memo: + return all_universes + memo.add(self) # Get all unique Universes contained in each of the lattice cells unique_universes = self.get_unique_universes() @@ -227,7 +234,7 @@ class Lattice(IDManagerMixin, ABC): # Append all Universes containing each cell to the dictionary for universe in unique_universes.values(): - all_universes.update(universe.get_all_universes()) + all_universes.update(universe.get_all_universes(memo)) return all_universes @@ -246,7 +253,7 @@ class Lattice(IDManagerMixin, ABC): Returns ------- - openmc.Universe + openmc.UniverseBase Universe with given indices """ @@ -370,9 +377,9 @@ class RectLattice(Lattice): pitch : Iterable of float Pitch of the lattice in the x, y, and (if applicable) z directions in cm. - outer : openmc.Universe + outer : openmc.UniverseBase A universe to fill all space outside the lattice - universes : Iterable of Iterable of openmc.Universe + universes : Iterable of Iterable of openmc.UniverseBase A two- or three-dimensional list/array of universes filling each element of the lattice. The first dimension corresponds to the z-direction (if applicable), the second dimension corresponds to the y-direction, and @@ -460,6 +467,12 @@ class RectLattice(Lattice): def lower_left(self): return self._lower_left + @lower_left.setter + def lower_left(self, lower_left): + cv.check_type('lattice lower left corner', lower_left, Iterable, Real) + cv.check_length('lattice lower left corner', lower_left, 2, 3) + self._lower_left = lower_left + @property def ndim(self): if self.pitch is not None: @@ -472,12 +485,6 @@ class RectLattice(Lattice): def shape(self): return self._universes.shape[::-1] - @lower_left.setter - def lower_left(self, lower_left): - cv.check_type('lattice lower left corner', lower_left, Iterable, Real) - cv.check_length('lattice lower left corner', lower_left, 2, 3) - self._lower_left = lower_left - @Lattice.pitch.setter def pitch(self, pitch): cv.check_type('lattice pitch', pitch, Iterable, Real) @@ -633,11 +640,11 @@ class RectLattice(Lattice): cv.check_value('strategy', strategy, ('degenerate', 'lns')) cv.check_type('universes_to_ignore', universes_to_ignore, Iterable, - openmc.Universe) + openmc.UniverseBase) cv.check_type('materials_to_clone', materials_to_clone, Iterable, openmc.Material) cv.check_type('lattice_neighbors', lattice_neighbors, Iterable, - openmc.Universe) + openmc.UniverseBase) cv.check_value('number of lattice_neighbors', len(lattice_neighbors), (0, 8)) cv.check_type('key', key, types.FunctionType) @@ -833,7 +840,7 @@ class RectLattice(Lattice): Parameters ---------- - xml_element : xml.etree.ElementTree.Element + xml_element : lxml.etree._Element XML element to be added to memo : set or None @@ -847,10 +854,15 @@ class RectLattice(Lattice): """ # If the element already contains the Lattice subelement, then return - if memo and self in memo: + if memo is None: + memo = set() + elif self in memo: return - if memo is not None: - memo.add(self) + memo.add(self) + + # Make sure universes have been assigned + if self.universes is None: + raise ValueError(f"Lattice {self.id} does not have universes assigned.") lattice_subelement = ET.Element("lattice") lattice_subelement.set("id", str(self._id)) @@ -872,11 +884,15 @@ class RectLattice(Lattice): dimension = ET.SubElement(lattice_subelement, "dimension") dimension.text = ' '.join(map(str, self.shape)) + # Make sure lower_left has been specified + if self.lower_left is None: + raise ValueError(f"Lattice {self.id} does not have lower_left specified.") + # Export Lattice lower left lower_left = ET.SubElement(lattice_subelement, "lower_left") lower_left.text = ' '.join(map(str, self._lower_left)) - # Export the Lattice nested Universe IDs - column major for Fortran + # Export the Lattice nested Universe IDs universe_ids = '\n' # 3D Lattices @@ -928,7 +944,7 @@ class RectLattice(Lattice): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element `` element get_universe : function Function returning universe (defined in @@ -943,18 +959,17 @@ class RectLattice(Lattice): lat_id = int(get_text(elem, 'id')) name = get_text(elem, 'name') lat = cls(lat_id, name) - lat.lower_left = [float(i) - for i in get_text(elem, 'lower_left').split()] - lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + lat.lower_left = get_elem_list(elem, "lower_left", float) + lat.pitch = get_elem_list(elem, "pitch", float) outer = get_text(elem, 'outer') if outer is not None: lat.outer = get_universe(int(outer)) # Get array of universes - dimension = get_text(elem, 'dimension').split() + dimension = get_elem_list(elem, 'dimension', int) shape = np.array(dimension, dtype=int)[::-1] - uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + universes = get_elem_list(elem, 'universes', int) + uarray = np.array([get_universe(u) for u in universes]) uarray.shape = shape lat.universes = uarray return lat @@ -969,7 +984,7 @@ class RectLattice(Lattice): Group in HDF5 file universes : dict Dictionary mapping universe IDs to instances of - :class:`openmc.Universe`. + :class:`openmc.UniverseBase`. Returns ------- @@ -995,7 +1010,7 @@ class RectLattice(Lattice): lattice.outer = universes[outer] # Build array of Universe pointers for the Lattice - uarray = np.empty(universe_ids.shape, dtype=openmc.Universe) + uarray = np.empty(universe_ids.shape, dtype=openmc.UniverseBase) for z in range(universe_ids.shape[0]): for y in range(universe_ids.shape[1]): @@ -1050,9 +1065,9 @@ class HexLattice(Lattice): Pitch of the lattice in cm. The first item in the iterable specifies the pitch in the radial direction and, if the lattice is 3D, the second item in the iterable specifies the pitch in the axial direction. - outer : openmc.Universe + outer : openmc.UniverseBase A universe to fill all space outside the lattice - universes : Nested Iterable of openmc.Universe + universes : Nested Iterable of openmc.UniverseBase A two- or three-dimensional list/array of universes filling each element of the lattice. Each sub-list corresponds to one ring of universes and should be ordered from outermost ring to innermost ring. The universes @@ -1124,6 +1139,11 @@ class HexLattice(Lattice): def orientation(self): return self._orientation + @orientation.setter + def orientation(self, orientation): + cv.check_value('orientation', orientation.lower(), ('x', 'y')) + self._orientation = orientation.lower() + @property def num_axial(self): return self._num_axial @@ -1132,6 +1152,12 @@ class HexLattice(Lattice): def center(self): return self._center + @center.setter + def center(self, center): + cv.check_type('lattice center', center, Iterable, Real) + cv.check_length('lattice center', center, 2, 3) + self._center = center + @property def indices(self): if self.num_axial is None: @@ -1169,18 +1195,7 @@ class HexLattice(Lattice): @property def ndim(self): - return 2 if isinstance(self.universes[0][0], openmc.Universe) else 3 - - @center.setter - def center(self, center): - cv.check_type('lattice center', center, Iterable, Real) - cv.check_length('lattice center', center, 2, 3) - self._center = center - - @orientation.setter - def orientation(self, orientation): - cv.check_value('orientation', orientation.lower(), ('x', 'y')) - self._orientation = orientation.lower() + return 2 if isinstance(self.universes[0][0], openmc.UniverseBase) else 3 @Lattice.pitch.setter def pitch(self, pitch): @@ -1192,7 +1207,7 @@ class HexLattice(Lattice): @Lattice.universes.setter def universes(self, universes): - cv.check_iterable_type('lattice universes', universes, openmc.Universe, + cv.check_iterable_type('lattice universes', universes, openmc.UniverseBase, min_depth=2, max_depth=3) self._universes = universes @@ -1414,10 +1429,11 @@ class HexLattice(Lattice): def create_xml_subelement(self, xml_element, memo=None): # If this subelement has already been written, return - if memo and self in memo: + if memo is None: + memo = set() + elif self in memo: return - if memo is not None: - memo.add(self) + memo.add(self) lattice_subelement = ET.Element("hex_lattice") lattice_subelement.set("id", str(self._id)) @@ -1448,6 +1464,8 @@ class HexLattice(Lattice): center.text = ' '.join(map(str, self._center)) # Export the Lattice nested Universe IDs. + if self.universes is None: + raise ValueError(f"Lattice {self.id} does not have universes assigned.") # 3D Lattices if self._num_axial is not None: @@ -1496,7 +1514,7 @@ class HexLattice(Lattice): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element `` element get_universe : function Function returning universe (defined in @@ -1511,8 +1529,8 @@ class HexLattice(Lattice): lat_id = int(get_text(elem, 'id')) name = get_text(elem, 'name') lat = cls(lat_id, name) - lat.center = [float(i) for i in get_text(elem, 'center').split()] - lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + lat.center = get_elem_list(elem, "center", float) + lat.pitch = get_elem_list(elem, "pitch", float) lat.orientation = get_text(elem, 'orientation', 'y') outer = get_text(elem, 'outer') if outer is not None: @@ -1529,8 +1547,8 @@ class HexLattice(Lattice): univs = [deepcopy(univs) for i in range(n_axial)] # Get flat array of universes - uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + universes = get_elem_list(elem, "universes", int) + uarray = np.array([get_universe(u) for u in universes]) # Fill nested lists j = 0 @@ -1559,6 +1577,7 @@ class HexLattice(Lattice): alpha -= 1 if not lat.is_valid_index((x, alpha, z)): # Reached the bottom + j += 1 break j += 1 else: @@ -1578,6 +1597,7 @@ class HexLattice(Lattice): # Check if we've reached the bottom if y == -n_rings: + j += 1 break while not lat.is_valid_index((alpha, y, z)): @@ -1846,7 +1866,7 @@ class HexLattice(Lattice): largest_index = 6*(num_rings - 1) n_digits_index = len(str(largest_index)) n_digits_ring = len(str(num_rings - 1)) - str_form = '({{:{}}},{{:{}}})'.format(n_digits_ring, n_digits_index) + str_form = f'({{:{n_digits_ring}}},{{:{n_digits_index}}})' pad = ' '*(n_digits_index + n_digits_ring + 3) # Initialize the list for each row. @@ -1951,7 +1971,7 @@ class HexLattice(Lattice): largest_index = 6*(num_rings - 1) n_digits_index = len(str(largest_index)) n_digits_ring = len(str(num_rings - 1)) - str_form = '({{:{}}},{{:{}}})'.format(n_digits_ring, n_digits_index) + str_form = f'({{:{n_digits_ring}}},{{:{n_digits_index}}})' pad = ' '*(n_digits_index + n_digits_ring + 3) # Initialize the list for each row. @@ -2046,12 +2066,12 @@ class HexLattice(Lattice): Group in HDF5 file universes : dict Dictionary mapping universe IDs to instances of - :class:`openmc.Universe`. + :class:`openmc.UniverseBase`. Returns ------- - openmc.RectLattice - Rectangular lattice + openmc.HexLattice + Hexagonal lattice """ n_rings = group['n_rings'][()] diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index c14b0d9c28..d2a794eb15 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -13,11 +13,10 @@ functions or objects in :mod:`openmc.lib`, for example: """ from ctypes import CDLL, c_bool, c_int +import importlib.resources import os import sys -import pkg_resources - # Determine shared-library suffix if sys.platform == 'darwin': @@ -27,9 +26,8 @@ else: if os.environ.get('READTHEDOCS', None) != 'True': # Open shared library - _filename = pkg_resources.resource_filename( - __name__, 'libopenmc.{}'.format(_suffix)) - _dll = CDLL(_filename) + _filename = importlib.resources.files(__name__) / f'libopenmc.{_suffix}' + _dll = CDLL(str(_filename)) # TODO: Remove str() when Python 3.12+ else: # For documentation builds, we don't actually have the shared library # available. Instead, we create a mock object so that when the modules @@ -48,6 +46,10 @@ def _coord_levels(): def _libmesh_enabled(): return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value +def _uwuw_enabled(): + return c_bool.in_dll(_dll, "UWUW_ENABLED").value + + from .error import * from .core import * from .nuclide import * @@ -59,6 +61,8 @@ from .tally import * from .settings import settings from .math import * from .plot import * +from .weight_windows import * +from .dagmc import * # Flag to denote whether or not openmc.lib.init has been called # TODO: Establish and use a flag in the C++ code to represent the status of the diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index b53a6be7a7..dfd09d2f9c 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -11,6 +11,8 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler from .material import Material +from ..bounding_box import BoundingBox + __all__ = ['Cell', 'cells'] @@ -32,6 +34,10 @@ _dll.openmc_cell_get_temperature.argtypes = [ c_int32, POINTER(c_int32), POINTER(c_double)] _dll.openmc_cell_get_temperature.restype = c_int _dll.openmc_cell_get_temperature.errcheck = _error_handler +_dll.openmc_cell_get_density.argtypes = [ + c_int32, POINTER(c_int32), POINTER(c_double)] +_dll.openmc_cell_get_density.restype = c_int +_dll.openmc_cell_get_density.errcheck = _error_handler _dll.openmc_cell_get_name.argtypes = [c_int32, POINTER(c_char_p)] _dll.openmc_cell_get_name.restype = c_int _dll.openmc_cell_get_name.errcheck = _error_handler @@ -56,6 +62,10 @@ _dll.openmc_cell_set_temperature.argtypes = [ c_int32, c_double, POINTER(c_int32), c_bool] _dll.openmc_cell_set_temperature.restype = c_int _dll.openmc_cell_set_temperature.errcheck = _error_handler +_dll.openmc_cell_set_density.argtypes = [ + c_int32, c_double, POINTER(c_int32), c_bool] +_dll.openmc_cell_set_density.restype = c_int +_dll.openmc_cell_set_density.errcheck = _error_handler _dll.openmc_cell_set_translation.argtypes = [c_int32, POINTER(c_double)] _dll.openmc_cell_set_translation.restype = c_int _dll.openmc_cell_set_translation.errcheck = _error_handler @@ -101,8 +111,8 @@ class Cell(_FortranObjectWithID): Name of the cell num_instances : int Number of unique cell instances - bounding_box : 2-tuple of numpy.ndarray - Lower-left and upper-right coordinates of bounding box + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the cell translation : Iterable of float 3-D coordinates of the translation vector rotation : Iterable of float @@ -234,6 +244,44 @@ class Cell(_FortranObjectWithID): _dll.openmc_cell_set_temperature(self._index, T, instance, set_contained) + def get_density(self, instance: int | None = None): + """Get the density of a cell in [g/cm3] + + Parameters + ---------- + instance : int or None + Which instance of the cell + + """ + + if instance is not None: + instance = c_int32(instance) + + rho = c_double() + _dll.openmc_cell_get_density(self._index, instance, rho) + return rho.value + + def set_density(self, rho: float, instance: int | None = None, + set_contained: bool = False): + """Set the density of a cell + + Parameters + ---------- + rho : float + Density of the cell in [g/cm3] + instance : int or None + Which instance of the cell + set_contained : bool + If cell is not filled by a material, whether to set the density + of all filled cells + + """ + + if instance is not None: + instance = c_int32(instance) + + _dll.openmc_cell_set_density(self._index, rho, instance, set_contained) + @property def translation(self): translation = np.zeros(3) @@ -266,7 +314,7 @@ class Cell(_FortranObjectWithID): return rotation_data[9:] else: raise ValueError( - 'Invalid size of rotation matrix: {}'.format(rot_size)) + f'Invalid size of rotation matrix: {rot_size}') @rotation.setter def rotation(self, rotation_data): @@ -289,7 +337,7 @@ class Cell(_FortranObjectWithID): llc[llc == -inf] = -np.inf urc[urc == -inf] = -np.inf - return llc, urc + return BoundingBox(llc, urc) class _CellMapping(Mapping): diff --git a/openmc/lib/core.py b/openmc/lib/core.py index de5f4adf21..9f8db69d57 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -1,15 +1,21 @@ from contextlib import contextmanager from ctypes import (c_bool, c_int, c_int32, c_int64, c_double, c_char_p, - c_char, POINTER, Structure, c_void_p, create_string_buffer) + c_char, POINTER, Structure, c_void_p, create_string_buffer, + c_uint64, c_size_t) import sys import os +from pathlib import Path +from random import getrandbits +from tempfile import TemporaryDirectory import numpy as np from numpy.ctypeslib import as_array from . import _dll from .error import _error_handler +from openmc.checkvalue import PathLike import openmc.lib +import openmc class _SourceSite(Structure): @@ -21,6 +27,7 @@ class _SourceSite(Structure): ('delayed_group', c_int), ('surf_id', c_int), ('particle', c_int), + ('parent_nuclide', c_int), ('parent_id', c_int64), ('progeny_id', c_int64)] @@ -91,11 +98,18 @@ _dll.openmc_simulation_finalize.errcheck = _error_handler _dll.openmc_statepoint_write.argtypes = [c_char_p, POINTER(c_bool)] _dll.openmc_statepoint_write.restype = c_int _dll.openmc_statepoint_write.errcheck = _error_handler +_dll.openmc_statepoint_load.argtypes = [c_char_p] +_dll.openmc_statepoint_load.restype = c_int +_dll.openmc_statepoint_load.errcheck = _error_handler +_dll.openmc_statepoint_write.restype = c_int +_dll.openmc_statepoint_write.errcheck = _error_handler _dll.openmc_global_bounding_box.argtypes = [POINTER(c_double), POINTER(c_double)] _dll.openmc_global_bounding_box.restype = c_int _dll.openmc_global_bounding_box.errcheck = _error_handler - +_dll.openmc_sample_external_source.argtypes = [c_size_t, POINTER(c_uint64), POINTER(_SourceSite)] +_dll.openmc_sample_external_source.restype = c_int +_dll.openmc_sample_external_source.errcheck = _error_handler def global_bounding_box(): """Calculate a global bounding box for the model""" @@ -165,6 +179,54 @@ def export_properties(filename=None, output=True): _dll.openmc_properties_export(filename) +def export_weight_windows(filename="weight_windows.h5", output=True): + """Export weight windows. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + filename : PathLike or None + Filename to export weight windows to + output : bool, optional + Whether or not to show output. + + See Also + -------- + openmc.lib.import_weight_windows + + """ + if filename is not None: + filename = c_char_p(str(filename).encode()) + + with quiet_dll(output): + _dll.openmc_weight_windows_export(filename) + + +def import_weight_windows(filename='weight_windows.h5', output=True): + """Import weight windows. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + filename : PathLike or None + Filename to import weight windows from + output : bool, optional + Whether or not to show output. + + See Also + -------- + openmc.lib.export_weight_windows + + """ + if filename is not None: + filename = c_char_p(str(filename).encode()) + + with quiet_dll(output): + _dll.openmc_weight_windows_import(filename) + + def finalize(): """Finalize simulation and free memory""" _dll.openmc_finalize() @@ -415,6 +477,47 @@ def run(output=True): _dll.openmc_run() +def sample_external_source( + n_samples: int = 1000, + prn_seed: int | None = None +) -> openmc.ParticleList: + """Sample external source and return source particles. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + n_samples : int + Number of samples + prn_seed : int + Pseudorandom number generator (PRNG) seed; if None, one will be + generated randomly. + + Returns + ------- + openmc.ParticleList + List of sampled source particles + + """ + if n_samples <= 0: + raise ValueError("Number of samples must be positive") + if prn_seed is None: + prn_seed = getrandbits(63) + + # Call into C API to sample source + sites_array = (_SourceSite * n_samples)() + _dll.openmc_sample_external_source(c_size_t(n_samples), c_uint64(prn_seed), sites_array) + + # Convert to list of SourceParticle and return + return openmc.ParticleList([openmc.SourceParticle( + r=site.r, u=site.u, E=site.E, time=site.time, wgt=site.wgt, + delayed_group=site.delayed_group, surf_id=site.surf_id, + particle=openmc.ParticleType(site.particle) + ) + for site in sites_array + ]) + + def simulation_init(): """Initialize simulation""" _dll.openmc_simulation_init() @@ -475,6 +578,19 @@ def statepoint_write(filename=None, write_source=True): _dll.openmc_statepoint_write(filename, c_bool(write_source)) +def statepoint_load(filename: PathLike): + """Load a statepoint file. + + Parameters + ---------- + filename : path-like + Path to the statepoint to load. + + """ + filename = c_char_p(str(filename).encode()) + _dll.openmc_statepoint_load(filename) + + @contextmanager def run_in_memory(**kwargs): """Provides context manager for calling OpenMC shared library functions. @@ -503,6 +619,73 @@ def run_in_memory(**kwargs): finalize() +class TemporarySession: + """Context manager for running via openmc.lib in a temporary directory. + + This class is useful for accessing functionality from openmc.lib without + polluting your current working directory with OpenMC files. It is used + internally as a persistent session to avoid loading cross sections multiple + times. + + Parameters + ---------- + model : openmc.Model, optional + OpenMC model to use for the session. If None, a minimal working model is + created. + **init_kwargs + Keyword arguments to pass to :func:`openmc.lib.init`. + + Attributes + ---------- + model : openmc.Model + The OpenMC model used for the session. + + """ + def __init__(self, model=None, **init_kwargs): + self.init_kwargs = init_kwargs + if model is None: + surf = openmc.Sphere(boundary_type="vacuum") + cell = openmc.Cell(region=-surf) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings = openmc.Settings( + particles=1, batches=1, output={'summary': False}) + self.model = model + + def __enter__(self): + """Initialize the OpenMC library in a temporary directory.""" + # If already initialized, the context manager is a no-op + self.already_initialized = openmc.lib.is_initialized + if self.already_initialized: + return self + + # Store original working directory + self.orig_dir = Path.cwd() + + # Set up temporary directory + self.tmp_dir = TemporaryDirectory() + working_dir = Path(self.tmp_dir.name) + working_dir.mkdir(parents=True, exist_ok=True) + os.chdir(working_dir) + + # Export model and initialize OpenMC + self.model.export_to_model_xml() + openmc.lib.init(**self.init_kwargs) + + return self + + def __exit__(self, exc_type, exc_value, traceback): + """Finalize the OpenMC library and clean up temporary directory.""" + if self.already_initialized: + return + + try: + finalize() + finally: + os.chdir(self.orig_dir) + self.tmp_dir.cleanup() + + class _DLLGlobal: """Data descriptor that exposes global variables from libopenmc.""" def __init__(self, ctype, name): @@ -518,7 +701,7 @@ class _DLLGlobal: class _FortranObject: def __repr__(self): - return "{}[{}]".format(type(self).__name__, self._index) + return f"<{type(self).__name__}(index={self._index})>" class _FortranObjectWithID(_FortranObject): @@ -529,6 +712,9 @@ class _FortranObjectWithID(_FortranObject): # OutOfBoundsError will be raised here by virtue of referencing self.id self.id + def __repr__(self): + return f"<{type(self).__name__}(id={self.id})>" + @contextmanager def quiet_dll(output=True): diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py new file mode 100644 index 0000000000..18ec81a4be --- /dev/null +++ b/openmc/lib/dagmc.py @@ -0,0 +1,43 @@ +from ctypes import c_int, c_int32, POINTER, c_size_t + +import numpy as np + +from . import _dll +from .error import _error_handler + + +__all__ = [ + 'dagmc_universe_cell_ids' +] + +# DAGMC functions +_dll.openmc_dagmc_universe_get_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] +_dll.openmc_dagmc_universe_get_cell_ids.restype = c_int +_dll.openmc_dagmc_universe_get_cell_ids.errcheck = _error_handler +_dll.openmc_dagmc_universe_get_num_cells.argtypes = [c_int32, POINTER(c_size_t)] +_dll.openmc_dagmc_universe_get_num_cells.restype = c_int +_dll.openmc_dagmc_universe_get_num_cells.errcheck = _error_handler + + +def dagmc_universe_cell_ids(universe_id: int) -> np.ndarray: + """Return an array of cell IDs for a DAGMC universe. + + Parameters + ---------- + dagmc_id : int + ID of the DAGMC universe to get cell IDs from. + + Returns + ------- + numpy.ndarray + DAGMC cell IDs for the universe. + + """ + n = c_size_t() + _dll.openmc_dagmc_universe_get_num_cells(universe_id, n) + cell_ids = np.empty(n.value, dtype=np.int32) + + _dll.openmc_dagmc_universe_get_cell_ids( + universe_id, cell_ids.ctypes.data_as(POINTER(c_int32)), n + ) + return cell_ids diff --git a/openmc/lib/error.py b/openmc/lib/error.py index 89e7b6e390..dbe08e1ef8 100644 --- a/openmc/lib/error.py +++ b/openmc/lib/error.py @@ -37,5 +37,5 @@ def _error_handler(err, func, args): warn(msg) elif err < 0: if not msg: - msg = "Unknown error encountered (code {}).".format(err) + msg = f"Unknown error encountered (code {err})." raise exc.OpenMCError(msg) diff --git a/openmc/lib/filter.py b/openmc/lib/filter.py index 5d2231cc2e..55b681d89a 100644 --- a/openmc/lib/filter.py +++ b/openmc/lib/filter.py @@ -7,6 +7,8 @@ import numpy as np from numpy.ctypeslib import as_array from openmc.exceptions import AllocationError, InvalidIDError +from openmc.data.function import INTERPOLATION_SCHEME +from openmc import ParticleType from . import _dll from .core import _FortranObjectWithID from .error import _error_handler @@ -16,11 +18,13 @@ from .mesh import _get_mesh __all__ = [ 'Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', - 'CellInstanceFilter', 'CollisionFilter', 'DistribcellFilter', 'DelayedGroupFilter', - 'EnergyFilter', 'EnergyoutFilter', 'EnergyFunctionFilter', 'LegendreFilter', - 'MaterialFilter', 'MeshFilter', 'MeshSurfaceFilter', 'MuFilter', 'ParticleFilter', - 'PolarFilter', 'SphericalHarmonicsFilter', 'SpatialLegendreFilter', 'SurfaceFilter', - 'UniverseFilter', 'ZernikeFilter', 'ZernikeRadialFilter', 'filters' + 'CellInstanceFilter', 'CollisionFilter', 'DistribcellFilter', 'DelayedGroupFilter', + 'EnergyFilter', 'EnergyoutFilter', 'EnergyFunctionFilter', 'LegendreFilter', + 'MaterialFilter', 'MaterialFromFilter', 'MeshFilter', 'MeshBornFilter', + 'MeshMaterialFilter', 'MeshSurfaceFilter', 'MuFilter', 'MuSurfaceFilter', + 'ParentNuclideFilter', 'ParticleFilter', 'PolarFilter', 'SphericalHarmonicsFilter', + 'SpatialLegendreFilter', 'SurfaceFilter', 'TimeFilter', 'UniverseFilter', + 'WeightFilter', 'ZernikeFilter', 'ZernikeRadialFilter', 'filters' ] # Tally functions @@ -47,9 +51,18 @@ _dll.openmc_energyfunc_filter_get_y.resttpe = c_int _dll.openmc_energyfunc_filter_get_y.errcheck = _error_handler _dll.openmc_energyfunc_filter_get_y.argtypes = [ c_int32, POINTER(c_size_t), POINTER(POINTER(c_double))] +_dll.openmc_energyfunc_filter_get_interpolation.resttpe = c_int +_dll.openmc_energyfunc_filter_get_interpolation.errcheck = _error_handler +_dll.openmc_energyfunc_filter_get_interpolation.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_energyfunc_filter_set_interpolation.resttpe = c_int +_dll.openmc_energyfunc_filter_set_interpolation.errcheck = _error_handler +_dll.openmc_energyfunc_filter_set_interpolation.argtypes = [c_int32, c_char_p] _dll.openmc_filter_get_id.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_filter_get_id.restype = c_int _dll.openmc_filter_get_id.errcheck = _error_handler +_dll.openmc_filter_get_num_bins.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_filter_get_num_bins.restype = c_int +_dll.openmc_filter_get_num_bins.errchck = _error_handler _dll.openmc_filter_get_type.argtypes = [c_int32, c_char_p] _dll.openmc_filter_get_type.restype = c_int _dll.openmc_filter_get_type.errcheck = _error_handler @@ -78,18 +91,36 @@ _dll.openmc_mesh_filter_get_mesh.errcheck = _error_handler _dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler -_dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] -_dll.openmc_meshsurface_filter_get_mesh.restype = c_int -_dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler -_dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] -_dll.openmc_meshsurface_filter_set_mesh.restype = c_int -_dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler _dll.openmc_mesh_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] _dll.openmc_mesh_filter_get_translation.restype = c_int _dll.openmc_mesh_filter_get_translation.errcheck = _error_handler _dll.openmc_mesh_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] _dll.openmc_mesh_filter_set_translation.restype = c_int _dll.openmc_mesh_filter_set_translation.errcheck = _error_handler +_dll.openmc_meshborn_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_meshborn_filter_get_mesh.restype = c_int +_dll.openmc_meshborn_filter_get_mesh.errcheck = _error_handler +_dll.openmc_meshborn_filter_set_mesh.argtypes = [c_int32, c_int32] +_dll.openmc_meshborn_filter_set_mesh.restype = c_int +_dll.openmc_meshborn_filter_set_mesh.errcheck = _error_handler +_dll.openmc_meshborn_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshborn_filter_get_translation.restype = c_int +_dll.openmc_meshborn_filter_get_translation.errcheck = _error_handler +_dll.openmc_meshborn_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshborn_filter_set_translation.restype = c_int +_dll.openmc_meshborn_filter_set_translation.errcheck = _error_handler +_dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_meshsurface_filter_get_mesh.restype = c_int +_dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler +_dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] +_dll.openmc_meshsurface_filter_set_mesh.restype = c_int +_dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler +_dll.openmc_meshsurface_filter_get_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshsurface_filter_get_translation.restype = c_int +_dll.openmc_meshsurface_filter_get_translation.errcheck = _error_handler +_dll.openmc_meshsurface_filter_set_translation.argtypes = [c_int32, POINTER(c_double*3)] +_dll.openmc_meshsurface_filter_set_translation.restype = c_int +_dll.openmc_meshsurface_filter_set_translation.errcheck = _error_handler _dll.openmc_new_filter.argtypes = [c_char_p, POINTER(c_int32)] _dll.openmc_new_filter.restype = c_int _dll.openmc_new_filter.errcheck = _error_handler @@ -113,7 +144,6 @@ _dll.openmc_zernike_filter_set_order.restype = c_int _dll.openmc_zernike_filter_set_order.errcheck = _error_handler _dll.tally_filters_size.restype = c_size_t - class Filter(_FortranObjectWithID): __instances = WeakValueDictionary() @@ -156,6 +186,12 @@ class Filter(_FortranObjectWithID): def id(self, filter_id): _dll.openmc_filter_set_id(self._index, filter_id) + @property + def n_bins(self): + n = c_int() + _dll.openmc_filter_get_num_bins(self._index, n) + return n.value + class EnergyFilter(Filter): filter_type = 'energy' @@ -247,6 +283,8 @@ class EnergyFunctionFilter(Filter): Independent variable for the interpolation y : numpy.ndarray Dependent variable for the interpolation + interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log', 'quadratic', 'cubic'} + Interpolation scheme """ energy_array = np.asarray(energy) y_array = np.asarray(y) @@ -264,6 +302,17 @@ class EnergyFunctionFilter(Filter): def y(self): return self._get_attr(_dll.openmc_energyfunc_filter_get_y) + @property + def interpolation(self) -> str: + interp = c_int() + _dll.openmc_energyfunc_filter_get_interpolation(self._index, interp) + return INTERPOLATION_SCHEME[interp.value] + + @interpolation.setter + def interpolation(self, interp: str): + interp_ptr = c_char_p(interp.encode()) + _dll.openmc_energyfunc_filter_set_interpolation(self._index, interp_ptr) + def _get_attr(self, cfunc): array_p = POINTER(c_double)() n = c_size_t() @@ -313,7 +362,39 @@ class MaterialFilter(Filter): _dll.openmc_material_filter_set_bins(self._index, n, bins) +class MaterialFromFilter(Filter): + filter_type = 'materialfrom' + + class MeshFilter(Filter): + """Mesh filter stored internally. + + This class exposes a Mesh filter that is stored internally in the OpenMC + library. To obtain a view of a Mesh filter with a given ID, use the + :data:`openmc.lib.filters` mapping. + + Parameters + ---------- + mesh : openmc.lib.Mesh + Mesh to use for the filter + uid : int or None + Unique ID of the Mesh filter + new : bool + When `index` is None, this argument controls whether a new object is + created or a view of an existing object is returned. + index : int + Index in the `filters` array. + + Attributes + ---------- + filter_type : str + Type of filter + mesh : openmc.lib.Mesh + Mesh used for the filter + translation : Iterable of float + 3-D coordinates of the translation vector + + """ filter_type = 'mesh' def __init__(self, mesh=None, uid=None, new=True, index=None): @@ -342,7 +423,96 @@ class MeshFilter(Filter): _dll.openmc_mesh_filter_set_translation(self._index, (c_double*3)(*translation)) +class MeshBornFilter(Filter): + """MeshBorn filter stored internally. + + This class exposes a MeshBorn filter that is stored internally in the OpenMC + library. To obtain a view of a MeshBorn filter with a given ID, use the + :data:`openmc.lib.filters` mapping. + + Parameters + ---------- + mesh : openmc.lib.Mesh + Mesh to use for the filter + uid : int or None + Unique ID of the MeshBorn filter + new : bool + When `index` is None, this argument controls whether a new object is + created or a view of an existing object is returned. + index : int + Index in the `filters` array. + + Attributes + ---------- + filter_type : str + Type of filter + mesh : openmc.lib.Mesh + Mesh used for the filter + translation : Iterable of float + 3-D coordinates of the translation vector + + """ + filter_type = 'meshborn' + + def __init__(self, mesh=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if mesh is not None: + self.mesh = mesh + + @property + def mesh(self): + index_mesh = c_int32() + _dll.openmc_meshborn_filter_get_mesh(self._index, index_mesh) + return _get_mesh(index_mesh.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_meshborn_filter_set_mesh(self._index, mesh._index) + + @property + def translation(self): + translation = (c_double*3)() + _dll.openmc_meshborn_filter_get_translation(self._index, translation) + return tuple(translation) + + @translation.setter + def translation(self, translation): + _dll.openmc_meshborn_filter_set_translation(self._index, (c_double*3)(*translation)) + + +class MeshMaterialFilter(Filter): + filter_type = 'meshmaterial' + + class MeshSurfaceFilter(Filter): + """MeshSurface filter stored internally. + + This class exposes a MeshSurface filter that is stored internally in the + OpenMC library. To obtain a view of a MeshSurface filter with a given ID, + use the :data:`openmc.lib.filters` mapping. + + Parameters + ---------- + mesh : openmc.lib.Mesh + Mesh to use for the filter + uid : int or None + Unique ID of the MeshSurface filter + new : bool + When `index` is None, this argument controls whether a new object is + created or a view of an existing object is returned. + index : int + Index in the `filters` array. + + Attributes + ---------- + filter_type : str + Type of filter + mesh : openmc.lib.Mesh + Mesh used for the filter + translation : Iterable of float + 3-D coordinates of the translation vector + + """ filter_type = 'meshsurface' def __init__(self, mesh=None, uid=None, new=True, index=None): @@ -363,21 +533,36 @@ class MeshSurfaceFilter(Filter): @property def translation(self): translation = (c_double*3)() - _dll.openmc_mesh_filter_get_translation(self._index, translation) + _dll.openmc_meshsurface_filter_get_translation(self._index, translation) return tuple(translation) @translation.setter def translation(self, translation): - _dll.openmc_mesh_filter_set_translation(self._index, (c_double*3)(*translation)) + _dll.openmc_meshsurface_filter_set_translation(self._index, (c_double*3)(*translation)) class MuFilter(Filter): filter_type = 'mu' +class MuSurfaceFilter(Filter): + filter_type = 'musurface' + + +class ParentNuclideFilter(Filter): + filter_type = 'parentnuclide' + + class ParticleFilter(Filter): filter_type = 'particle' + @property + def bins(self): + particle_i = np.zeros((self.n_bins,), dtype=c_int) + _dll.openmc_particle_filter_get_bins( + self._index, particle_i.ctypes.data_as(POINTER(c_int))) + return [ParticleType(i) for i in particle_i] + class PolarFilter(Filter): filter_type = 'polar' @@ -425,10 +610,18 @@ class SurfaceFilter(Filter): filter_type = 'surface' +class TimeFilter(Filter): + filter_type = 'time' + + class UniverseFilter(Filter): filter_type = 'universe' +class WeightFilter(Filter): + filter_type = 'weight' + + class ZernikeFilter(Filter): filter_type = 'zernike' @@ -458,6 +651,7 @@ _FILTER_TYPE_MAP = { 'cellborn': CellbornFilter, 'cellfrom': CellfromFilter, 'cellinstance': CellInstanceFilter, + 'collision': CollisionFilter, 'delayedgroup': DelayedGroupFilter, 'distribcell': DistribcellFilter, 'energy': EnergyFilter, @@ -465,15 +659,22 @@ _FILTER_TYPE_MAP = { 'energyfunction': EnergyFunctionFilter, 'legendre': LegendreFilter, 'material': MaterialFilter, + 'materialfrom': MaterialFromFilter, 'mesh': MeshFilter, + 'meshborn': MeshBornFilter, + 'meshmaterial': MeshMaterialFilter, 'meshsurface': MeshSurfaceFilter, 'mu': MuFilter, + 'musurface': MuSurfaceFilter, + 'parentnuclide': ParentNuclideFilter, 'particle': ParticleFilter, 'polar': PolarFilter, 'sphericalharmonics': SphericalHarmonicsFilter, 'spatiallegendre': SpatialLegendreFilter, 'surface': SurfaceFilter, + 'time': TimeFilter, 'universe': UniverseFilter, + 'weight': WeightFilter, 'zernike': ZernikeFilter, 'zernikeradial': ZernikeRadialFilter } diff --git a/openmc/lib/material.py b/openmc/lib/material.py index fde197d3d8..0ed8932da8 100644 --- a/openmc/lib/material.py +++ b/openmc/lib/material.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from ctypes import c_int, c_int32, c_double, c_char_p, POINTER, c_size_t +from ctypes import c_bool, c_int, c_int32, c_double, c_char_p, POINTER, c_size_t from weakref import WeakValueDictionary import numpy as np @@ -60,6 +60,12 @@ _dll.openmc_material_set_name.errcheck = _error_handler _dll.openmc_material_set_volume.argtypes = [c_int32, c_double] _dll.openmc_material_set_volume.restype = c_int _dll.openmc_material_set_volume.errcheck = _error_handler +_dll.openmc_material_get_depletable.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_material_get_depletable.restype = c_int +_dll.openmc_material_get_depletable.errcheck = _error_handler +_dll.openmc_material_set_depletable.argtypes = [c_int32, c_bool] +_dll.openmc_material_set_depletable.restype = c_int +_dll.openmc_material_set_depletable.errcheck = _error_handler _dll.n_materials.argtypes = [] _dll.n_materials.restype = c_size_t @@ -89,6 +95,8 @@ class Material(_FortranObjectWithID): List of nuclides in the material densities : numpy.ndarray Array of densities in atom/b-cm + depletable : bool + Whether this material is marked as depletable name : str Name of the material temperature : float @@ -169,6 +177,16 @@ class Material(_FortranObjectWithID): def volume(self, volume): _dll.openmc_material_set_volume(self._index, volume) + @property + def depletable(self): + depletable = c_bool() + _dll.openmc_material_get_depletable(self._index, depletable) + return depletable.value + + @depletable.setter + def depletable(self, depletable): + _dll.openmc_material_set_depletable(self._index, depletable) + @property def nuclides(self): return self._get_densities()[0] diff --git a/openmc/lib/math.py b/openmc/lib/math.py index bdb09b2d1f..8c62f24162 100644 --- a/openmc/lib/math.py +++ b/openmc/lib/math.py @@ -1,24 +1,10 @@ -from ctypes import c_int, c_double, POINTER, c_uint64 +from ctypes import c_int, c_double import numpy as np from numpy.ctypeslib import ndpointer from . import _dll -from random import getrandbits - - -_dll.t_percentile.restype = c_double -_dll.t_percentile.argtypes = [c_double, c_int] - -_dll.calc_pn_c.restype = None -_dll.calc_pn_c.argtypes = [c_int, c_double, ndpointer(c_double)] - -_dll.evaluate_legendre.restype = c_double -_dll.evaluate_legendre.argtypes = [c_int, POINTER(c_double), c_double] - -_dll.calc_rn_c.restype = None -_dll.calc_rn_c.argtypes = [c_int, ndpointer(c_double), ndpointer(c_double)] _dll.calc_zn.restype = None _dll.calc_zn.argtypes = [c_int, c_double, c_double, ndpointer(c_double)] @@ -26,119 +12,12 @@ _dll.calc_zn.argtypes = [c_int, c_double, c_double, ndpointer(c_double)] _dll.calc_zn_rad.restype = None _dll.calc_zn_rad.argtypes = [c_int, c_double, ndpointer(c_double)] -_dll.rotate_angle_c.restype = None -_dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double, - POINTER(c_double), POINTER(c_uint64)] -_dll.maxwell_spectrum.restype = c_double -_dll.maxwell_spectrum.argtypes = [c_double, POINTER(c_uint64)] - -_dll.watt_spectrum.restype = c_double -_dll.watt_spectrum.argtypes = [c_double, c_double, POINTER(c_uint64)] - -_dll.broaden_wmp_polynomials.restype = None -_dll.broaden_wmp_polynomials.argtypes = [c_double, c_double, c_int, - ndpointer(c_double)] - -_dll.normal_variate.restype = c_double -_dll.normal_variate.argtypes = [c_double, c_double, POINTER(c_uint64)] - -def t_percentile(p, df): - """ Calculate the percentile of the Student's t distribution with a - specified probability level and number of degrees of freedom - - Parameters - ---------- - p : float - Probability level - df : int - Degrees of freedom - - Returns - ------- - float - Corresponding t-value - - """ - - return _dll.t_percentile(p, df) - - -def calc_pn(n, x): - """ Calculate the n-th order Legendre polynomial at the value of x. - - Parameters - ---------- - n : int - Legendre order - x : float - Independent variable to evaluate the Legendre at - - Returns - ------- - float - Corresponding Legendre polynomial result - - """ - - pnx = np.empty(n + 1, dtype=np.float64) - _dll.calc_pn_c(n, x, pnx) - return pnx - - -def evaluate_legendre(data, x): - """ Finds the value of f(x) given a set of Legendre coefficients - and the value of x. - - Parameters - ---------- - data : iterable of float - Legendre coefficients - x : float - Independent variable to evaluate the Legendre at - - Returns - ------- - float - Corresponding Legendre expansion result - - """ - - data_arr = np.array(data, dtype=np.float64) - return _dll.evaluate_legendre(len(data), - data_arr.ctypes.data_as(POINTER(c_double)), x) - - -def calc_rn(n, uvw): - """ Calculate the n-th order real Spherical Harmonics for a given angle; - all Rn,m values are provided for all n (where -n <= m <= n). - - Parameters - ---------- - n : int - Harmonics order - uvw : iterable of float - Independent variable to evaluate the Legendre at - - Returns - ------- - numpy.ndarray - Corresponding real harmonics value - - """ - - num_nm = (n + 1) * (n + 1) - rn = np.empty(num_nm, dtype=np.float64) - uvw_arr = np.array(uvw, dtype=np.float64) - _dll.calc_rn_c(n, uvw_arr, rn) - return rn - def calc_zn(n, rho, phi): """ Calculate the n-th order modified Zernike polynomial moment for a given angle (rho, theta) location in the unit disk. The normalization of the polynomials is such that the integral of Z_pq*Z_pq over the unit disk is exactly pi - Parameters ---------- n : int @@ -147,12 +26,10 @@ def calc_zn(n, rho, phi): Radial location in the unit disk phi : float Theta (radians) location in the unit disk - Returns ------- numpy.ndarray Corresponding resulting list of coefficients - """ num_bins = ((n + 1) * (n + 2)) // 2 @@ -166,157 +43,19 @@ def calc_zn_rad(n, rho): moment with no azimuthal dependency (m=0) for a given radial location in the unit disk. The normalization of the polynomials is such that the integral of Z_pq*Z_pq over the unit disk is exactly pi. - Parameters ---------- n : int Maximum order rho : float Radial location in the unit disk - Returns ------- numpy.ndarray Corresponding resulting list of coefficients - """ num_bins = n // 2 + 1 zn_rad = np.zeros(num_bins, dtype=np.float64) _dll.calc_zn_rad(n, rho, zn_rad) return zn_rad - - -def rotate_angle(uvw0, mu, phi, prn_seed=None): - """ Rotates direction cosines through a polar angle whose cosine is - mu and through an azimuthal angle sampled uniformly. - - Parameters - ---------- - uvw0 : iterable of float - Original direction cosine - mu : float - Polar angle cosine to rotate - phi : float - Azimuthal angle; if None, one will be sampled uniformly - prn_seed : int - PRNG seed; if None, one will be generated randomly - - Returns - ------- - numpy.ndarray - Rotated direction cosine - - """ - - if prn_seed is None: - prn_seed = getrandbits(63) - - uvw0_arr = np.array(uvw0, dtype=np.float64) - if phi is None: - _dll.rotate_angle_c(uvw0_arr, mu, None, c_uint64(prn_seed)) - else: - _dll.rotate_angle_c(uvw0_arr, mu, c_double(phi), c_uint64(prn_seed)) - - uvw = uvw0_arr - - return uvw - - -def maxwell_spectrum(T, prn_seed=None): - """ Samples an energy from the Maxwell fission distribution based - on a direct sampling scheme. - - Parameters - ---------- - T : float - Spectrum parameter - prn_seed : int - PRNG seed; if None, one will be generated randomly - - Returns - ------- - float - Sampled outgoing energy - - """ - - if prn_seed is None: - prn_seed = getrandbits(63) - - return _dll.maxwell_spectrum(T, c_uint64(prn_seed)) - - -def watt_spectrum(a, b, prn_seed=None): - """ Samples an energy from the Watt energy-dependent fission spectrum. - - Parameters - ---------- - a : float - Spectrum parameter a - b : float - Spectrum parameter b - prn_seed : int - PRNG seed; if None, one will be generated randomly - - Returns - ------- - float - Sampled outgoing energy - - """ - - if prn_seed is None: - prn_seed = getrandbits(63) - - return _dll.watt_spectrum(a, b, c_uint64(prn_seed)) - - -def normal_variate(mean_value, std_dev, prn_seed=None): - """ Samples an energy from the Normal distribution. - - Parameters - ---------- - mean_value : float - Mean of the Normal distribution - std_dev : float - Standard deviation of the normal distribution - prn_seed : int - PRNG seed; if None, one will be generated randomly - - Returns - ------- - float - Sampled outgoing normally distributed value - - """ - - if prn_seed is None: - prn_seed = getrandbits(63) - - return _dll.normal_variate(mean_value, std_dev, c_uint64(prn_seed)) - - -def broaden_wmp_polynomials(E, dopp, n): - """ Doppler broadens the windowed multipole curvefit. The curvefit is a - polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ... - - Parameters - ---------- - E : float - Energy to evaluate at - dopp : float - sqrt(atomic weight ratio / kT), with kT given in eV - n : int - Number of components to the polynomial - - Returns - ------- - numpy.ndarray - Resultant leading coefficients - - """ - - factors = np.zeros(n, dtype=np.float64) - _dll.broaden_wmp_polynomials(E, dopp, n, factors) - return factors diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index 84571628c6..3a720f98a3 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -1,6 +1,8 @@ -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER, - create_string_buffer) + create_string_buffer, c_size_t) +from math import sqrt +import sys from weakref import WeakValueDictionary import numpy as np @@ -8,10 +10,20 @@ from numpy.ctypeslib import as_array from ..exceptions import AllocationError, InvalidIDError from . import _dll -from .core import _FortranObjectWithID +from .core import _FortranObjectWithID, quiet_dll from .error import _error_handler +from .plot import _Position +from ..bounding_box import BoundingBox +from ..mesh import MeshMaterialVolumes -__all__ = ['RegularMesh', 'RectilinearMesh', 'CylindricalMesh', 'SphericalMesh', 'meshes'] +__all__ = [ + 'Mesh', 'RegularMesh', 'RectilinearMesh', 'CylindricalMesh', + 'SphericalMesh', 'UnstructuredMesh', 'meshes', 'MeshMaterialVolumes' +] + + +arr_2d_int32 = np.ctypeslib.ndpointer(dtype=np.int32, ndim=2, flags='CONTIGUOUS') +arr_2d_double = np.ctypeslib.ndpointer(dtype=np.double, ndim=2, flags='CONTIGUOUS') # Mesh functions _dll.openmc_extend_meshes.argtypes = [c_int32, c_char_p, POINTER(c_int32), @@ -24,6 +36,25 @@ _dll.openmc_mesh_get_id.errcheck = _error_handler _dll.openmc_mesh_set_id.argtypes = [c_int32, c_int32] _dll.openmc_mesh_set_id.restype = c_int _dll.openmc_mesh_set_id.errcheck = _error_handler +_dll.openmc_mesh_get_n_elements.argtypes = [c_int32, POINTER(c_size_t)] +_dll.openmc_mesh_get_n_elements.restype = c_int +_dll.openmc_mesh_get_n_elements.errcheck = _error_handler +_dll.openmc_mesh_get_volumes.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_mesh_get_volumes.restype = c_int +_dll.openmc_mesh_get_volumes.errcheck = _error_handler +_dll.openmc_mesh_bounding_box.argtypes = [ + c_int32, POINTER(c_double), POINTER(c_double)] +_dll.openmc_mesh_bounding_box.restype = c_int +_dll.openmc_mesh_bounding_box.errcheck = _error_handler +_dll.openmc_mesh_material_volumes.argtypes = [ + c_int32, c_int, c_int, c_int, c_int, arr_2d_int32, arr_2d_double] +_dll.openmc_mesh_material_volumes.restype = c_int +_dll.openmc_mesh_material_volumes.errcheck = _error_handler +_dll.openmc_mesh_get_plot_bins.argtypes = [ + c_int32, _Position, _Position, c_int, POINTER(c_int), POINTER(c_int32) +] +_dll.openmc_mesh_get_plot_bins.restype = c_int +_dll.openmc_mesh_get_plot_bins.errcheck = _error_handler _dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_mesh_index.restype = c_int _dll.openmc_get_mesh_index.errcheck = _error_handler @@ -123,6 +154,153 @@ class Mesh(_FortranObjectWithID): def id(self, mesh_id): _dll.openmc_mesh_set_id(self._index, mesh_id) + @property + def n_elements(self) -> int: + n = c_size_t() + _dll.openmc_mesh_get_n_elements(self._index, n) + return n.value + + @property + def volumes(self) -> np.ndarray: + volumes = np.empty((self.n_elements,)) + _dll.openmc_mesh_get_volumes( + self._index, volumes.ctypes.data_as(POINTER(c_double))) + return volumes + + @property + def bounding_box(self) -> BoundingBox: + inf = sys.float_info.max + ll = np.zeros(3) + ur = np.zeros(3) + _dll.openmc_mesh_bounding_box( + self._index, + ll.ctypes.data_as(POINTER(c_double)), + ur.ctypes.data_as(POINTER(c_double)) + ) + ll[ll == inf] = np.inf + ur[ur == inf] = np.inf + ll[ll == -inf] = -np.inf + ur[ur == -inf] = -np.inf + return BoundingBox(ll, ur) + + def material_volumes( + self, + n_samples: int | tuple[int, int, int] = 10_000, + max_materials: int = 4, + output: bool = True, + ) -> MeshMaterialVolumes: + """Determine volume of materials in each mesh element. + + This method works by raytracing repeatedly through the mesh to count the + estimated volume of each material in all mesh elements. Three sets of + rays are used: one set parallel to the x-axis, one parallel to the + y-axis, and one parallel to the z-axis. + + .. versionadded:: 0.15.0 + + .. versionchanged:: 0.15.1 + Material volumes are now determined by raytracing rather than by + point sampling. + + Parameters + ---------- + n_samples : int or 3-tuple of int + Total number of rays to sample. The number of rays in each direction + is determined by the aspect ratio of the mesh bounding box. When + specified as a 3-tuple, it is interpreted as the number of rays in + the x, y, and z dimensions. + max_materials : int, optional + Estimated maximum number of materials in any given mesh element. + output : bool, optional + Whether or not to show output. + + Returns + ------- + MeshMaterialVolumes + Dictionary-like object that maps material IDs to an array of volumes + equal in size to the number of mesh elements. + + """ + if isinstance(n_samples, int): + # Determine number of rays in each direction based on aspect ratios + # and using the relation (nx*ny + ny*nz + nx*nz) = n_samples + width_x, width_y, width_z = self.bounding_box.width + ax = width_x / width_z + ay = width_y / width_z + f = sqrt(n_samples/(ax*ay + ax + ay)) + nx = round(f * ax) + ny = round(f * ay) + nz = round(f) + else: + nx, ny, nz = n_samples + + # Value indicating an empty slot in the hash table (matches C++) + EMPTY_SLOT = -2 + + # Preallocate arrays for material indices and volumes + n = self.n_elements + slot_factor = 2 + table_size = slot_factor*max_materials + materials = np.full((n, table_size), EMPTY_SLOT, dtype=np.int32) + volumes = np.zeros((n, table_size), dtype=np.float64) + + # Run material volume calculation + while True: + try: + with quiet_dll(output): + _dll.openmc_mesh_material_volumes( + self._index, nx, ny, nz, table_size, materials, volumes) + except AllocationError: + # Increase size of result array and try again + table_size *= 2 + materials = np.full((n, table_size), EMPTY_SLOT, dtype=np.int32) + volumes = np.zeros((n, table_size), dtype=np.float64) + else: + # If no error, break out of loop + break + + return MeshMaterialVolumes(materials, volumes) + + def get_plot_bins( + self, + origin: Sequence[float], + width: Sequence[float], + basis: str, + pixels: Sequence[int] + ) -> np.ndarray: + """Get mesh bin indices for a rasterized plot. + + .. versionadded:: 0.15.0 + + Parameters + ---------- + origin : iterable of float + Origin of the plotting view. Should have length 3. + width : iterable of float + Width of the plotting view. Should have length 2. + basis : {'xy', 'xz', 'yz'} + Plotting basis. + pixels : iterable of int + Number of pixels in each direction. Should have length 2. + + Returns + ------- + 2D numpy array with mesh bin indices corresponding to each pixel within + the plotting view. + + """ + origin = _Position(*origin) + width = _Position(*width) + basis = {'xy': 1, 'xz': 2, 'yz': 3}[basis] + pixel_array = (c_int*2)(*pixels) + img_data = np.zeros((pixels[1], pixels[0]), dtype=np.dtype('int32')) + + _dll.openmc_mesh_get_plot_bins( + self._index, origin, width, basis, pixel_array, + img_data.ctypes.data_as(POINTER(c_int32)) + ) + return img_data + class RegularMesh(Mesh): """RegularMesh stored internally. @@ -146,10 +324,16 @@ class RegularMesh(Mesh): The lower-left corner of the structured mesh. If only two coordinate are given, it is assumed that the mesh is an x-y mesh. upper_right : numpy.ndarray - The upper-right corner of the structrued mesh. If only two coordinate + The upper-right corner of the structured mesh. If only two coordinate are given, it is assumed that the mesh is an x-y mesh. width : numpy.ndarray The width of mesh cells in each direction. + n_elements : int + Total number of mesh elements. + volumes : numpy.ndarray + Volume of each mesh element in [cm^3] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh """ mesh_type = 'regular' @@ -229,9 +413,15 @@ class RectilinearMesh(Mesh): lower_left : numpy.ndarray The lower-left corner of the structured mesh. upper_right : numpy.ndarray - The upper-right corner of the structrued mesh. + The upper-right corner of the structured mesh. width : numpy.ndarray The width of mesh cells in each direction. + n_elements : int + Total number of mesh elements. + volumes : numpy.ndarray + Volume of each mesh element in [cm^3] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh """ mesh_type = 'rectilinear' @@ -328,9 +518,15 @@ class CylindricalMesh(Mesh): lower_left : numpy.ndarray The lower-left corner of the structured mesh. upper_right : numpy.ndarray - The upper-right corner of the structrued mesh. + The upper-right corner of the structured mesh. width : numpy.ndarray The width of mesh cells in each direction. + n_elements : int + Total number of mesh elements. + volumes : numpy.ndarray + Volume of each mesh element in [cm^3] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh """ mesh_type = 'cylindrical' @@ -405,6 +601,7 @@ class CylindricalMesh(Mesh): _dll.openmc_cylindrical_mesh_set_grid(self._index, r_grid, nr, phi_grid, nphi, z_grid, nz) + class SphericalMesh(Mesh): """SphericalMesh stored internally. @@ -426,9 +623,15 @@ class SphericalMesh(Mesh): lower_left : numpy.ndarray The lower-left corner of the structured mesh. upper_right : numpy.ndarray - The upper-right corner of the structrued mesh. + The upper-right corner of the structured mesh. width : numpy.ndarray The width of mesh cells in each direction. + n_elements : int + Total number of mesh elements. + volumes : numpy.ndarray + Volume of each mesh element in [cm^3] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh """ mesh_type = 'spherical' @@ -504,11 +707,16 @@ class SphericalMesh(Mesh): ntheta, phi_grid, nphi) +class UnstructuredMesh(Mesh): + pass + + _MESH_TYPE_MAP = { 'regular': RegularMesh, 'rectilinear': RectilinearMesh, 'cylindrical': CylindricalMesh, - 'spherical': SphericalMesh + 'spherical': SphericalMesh, + 'unstructured': UnstructuredMesh } @@ -529,7 +737,6 @@ class _MeshMapping(Mapping): raise KeyError(str(e)) return _get_mesh(index.value) - def __iter__(self): for i in range(len(self)): yield _get_mesh(i).id diff --git a/openmc/lib/nuclide.py b/openmc/lib/nuclide.py index 399bb34652..ef1287cf34 100644 --- a/openmc/lib/nuclide.py +++ b/openmc/lib/nuclide.py @@ -40,8 +40,14 @@ def load_nuclide(name): name : str Name of the nuclide, e.g. 'U235' + Returns + ------- + Nuclide + The class:`Nuclide` that was just loaded. + """ _dll.openmc_load_nuclide(name.encode(), None, 0) + return nuclides[name] class Nuclide(_FortranObject): @@ -91,7 +97,7 @@ class Nuclide(_FortranObject): energy : iterable of float Energy group boundaries in [eV] flux : iterable of float - Flux in each energt group (not normalized per eV) + Flux in each energy group (not normalized per eV) Returns ------- diff --git a/openmc/lib/plot.py b/openmc/lib/plot.py index da42ea49e0..f97348b20b 100644 --- a/openmc/lib/plot.py +++ b/openmc/lib/plot.py @@ -31,7 +31,7 @@ class _Position(Structure): elif idx == 2: return self.z else: - raise IndexError("{} index is invalid for _Position".format(idx)) + raise IndexError(f"{idx} index is invalid for _Position") def __setitem__(self, idx, val): if idx == 0: @@ -41,10 +41,10 @@ class _Position(Structure): elif idx == 2: self.z = val else: - raise IndexError("{} index is invalid for _Position".format(idx)) + raise IndexError(f"{idx} index is invalid for _Position") def __repr__(self): - return "({}, {}, {})".format(self.x, self.y, self.z) + return f"({self.x}, {self.y}, {self.z})" class _PlotBase(Structure): @@ -90,20 +90,35 @@ class _PlotBase(Structure): def __init__(self): self.level_ = -1 + self.basis_ = 1 self.color_overlaps_ = False @property def origin(self): return self.origin_ + @origin.setter + def origin(self, origin): + self.origin_.x = origin[0] + self.origin_.y = origin[1] + self.origin_.z = origin[2] + @property def width(self): return self.width_.x + @width.setter + def width(self, width): + self.width_.x = width + @property def height(self): return self.width_.y + @height.setter + def height(self, height): + self.width_.y = height + @property def basis(self): if self.basis_ == 1: @@ -113,7 +128,7 @@ class _PlotBase(Structure): elif self.basis_ == 3: return 'yz' - raise ValueError("Plot basis {} is invalid".format(self.basis_)) + raise ValueError(f"Plot basis {self.basis_} is invalid") @basis.setter def basis(self, basis): @@ -121,7 +136,7 @@ class _PlotBase(Structure): valid_bases = ('xy', 'xz', 'yz') basis = basis.lower() if basis not in valid_bases: - raise ValueError("{} is not a valid plot basis.".format(basis)) + raise ValueError(f"{basis} is not a valid plot basis.") if basis == 'xy': self.basis_ = 1 @@ -134,25 +149,36 @@ class _PlotBase(Structure): if isinstance(basis, int): valid_bases = (1, 2, 3) if basis not in valid_bases: - raise ValueError("{} is not a valid plot basis.".format(basis)) + raise ValueError(f"{basis} is not a valid plot basis.") self.basis_ = basis return - raise ValueError("{} of type {} is an" - " invalid plot basis".format(basis, type(basis))) + raise ValueError(f"{basis} of type {type(basis)} is an invalid plot basis") @property def h_res(self): return self.pixels_[0] + @h_res.setter + def h_res(self, h_res): + self.pixels_[0] = h_res + @property def v_res(self): return self.pixels_[1] + @v_res.setter + def v_res(self, v_res): + self.pixels_[1] = v_res + @property def level(self): return int(self.level_) + @level.setter + def level(self, level): + self.level_ = level + @property def color_overlaps(self): return self.color_overlaps_ @@ -161,32 +187,6 @@ class _PlotBase(Structure): def color_overlaps(self, color_overlaps): self.color_overlaps_ = color_overlaps - @origin.setter - def origin(self, origin): - self.origin_.x = origin[0] - self.origin_.y = origin[1] - self.origin_.z = origin[2] - - @width.setter - def width(self, width): - self.width_.x = width - - @height.setter - def height(self, height): - self.width_.y = height - - @h_res.setter - def h_res(self, h_res): - self.pixels_[0] = h_res - - @v_res.setter - def v_res(self, v_res): - self.pixels_[1] = v_res - - @level.setter - def level(self, level): - self.level_ = level - @property def color_overlaps(self): return self.color_overlaps_ @@ -199,14 +199,14 @@ class _PlotBase(Structure): out_str = ["-----", "Plot:", "-----", - "Origin: {}".format(self.origin), - "Width: {}".format(self.width), - "Height: {}".format(self.height), - "Basis: {}".format(self.basis), - "HRes: {}".format(self.h_res), - "VRes: {}".format(self.v_res), - "Color Overlaps: {}".format(self.color_overlaps), - "Level: {}".format(self.level)] + f"Origin: {self.origin}", + f"Width: {self.width}", + f"Height: {self.height}", + f"Basis: {self.basis}", + f"HRes: {self.h_res}", + f"VRes: {self.v_res}", + f"Color Overlaps: {self.color_overlaps}", + f"Level: {self.level}"] return '\n'.join(out_str) @@ -229,7 +229,8 @@ def id_map(plot): ------- id_map : numpy.ndarray A NumPy array with shape (vertical pixels, horizontal pixels, 3) of - OpenMC property ids with dtype int32 + OpenMC property ids with dtype int32. The last dimension of the array + contains, in order, cell IDs, cell instances, and material IDs. """ img_data = np.zeros((plot.v_res, plot.h_res, 3), diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index 2e9dd18df7..4fba8d48b6 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -12,6 +12,8 @@ _RUN_MODES = {1: 'fixed source', _dll.openmc_set_seed.argtypes = [c_int64] _dll.openmc_get_seed.restype = c_int64 +_dll.openmc_set_stride.argtypes = [c_int64] +_dll.openmc_get_stride.restype = c_int64 _dll.openmc_get_n_batches.argtypes = [POINTER(c_int), c_bool] _dll.openmc_get_n_batches.restype = c_int _dll.openmc_get_n_batches.errcheck = _error_handler @@ -35,6 +37,7 @@ class _Settings: run_CE = _DLLGlobal(c_bool, 'run_CE') verbosity = _DLLGlobal(c_int, 'verbosity') event_based = _DLLGlobal(c_bool, 'event_based') + weight_windows_on = _DLLGlobal(c_bool, 'weight_windows_on') @property def run_mode(self): @@ -52,11 +55,11 @@ class _Settings: current_idx.value = idx break else: - raise ValueError('Invalid run mode: {}'.format(mode)) + raise ValueError(f'Invalid run mode: {mode}') @property def path_statepoint(self): - path = c_char_p.in_dll(_dll, 'path_statepoint').value + path = c_char_p.in_dll(_dll, 'path_statepoint_c').value return path.decode() @property @@ -67,6 +70,14 @@ class _Settings: def seed(self, seed): _dll.openmc_set_seed(seed) + @property + def stride(self): + return _dll.openmc_get_stride() + + @stride.setter + def stride(self, stride): + _dll.openmc_set_stride(stride) + def set_batches(self, n_batches, set_max_batches=True, add_sp_batch=True): """Set number of batches or maximum number of batches diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index df28cb5dca..c17b16597f 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -39,6 +39,9 @@ _dll.openmc_tally_get_filters.argtypes = [ c_int32, POINTER(POINTER(c_int32)), POINTER(c_size_t)] _dll.openmc_tally_get_filters.restype = c_int _dll.openmc_tally_get_filters.errcheck = _error_handler +_dll.openmc_tally_get_multiply_density.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_tally_get_multiply_density.restype = c_int +_dll.openmc_tally_get_multiply_density.errcheck = _error_handler _dll.openmc_tally_get_n_realizations.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_n_realizations.restype = c_int _dll.openmc_tally_get_n_realizations.errcheck = _error_handler @@ -75,6 +78,9 @@ _dll.openmc_tally_set_estimator.errcheck = _error_handler _dll.openmc_tally_set_id.argtypes = [c_int32, c_int32] _dll.openmc_tally_set_id.restype = c_int _dll.openmc_tally_set_id.errcheck = _error_handler +_dll.openmc_tally_set_multiply_density.argtypes = [c_int32, c_bool] +_dll.openmc_tally_set_multiply_density.restype = c_int +_dll.openmc_tally_set_multiply_density.errcheck = _error_handler _dll.openmc_tally_set_nuclides.argtypes = [c_int32, c_int, POINTER(c_char_p)] _dll.openmc_tally_set_nuclides.restype = c_int _dll.openmc_tally_set_nuclides.errcheck = _error_handler @@ -87,6 +93,9 @@ _dll.openmc_tally_set_type.errcheck = _error_handler _dll.openmc_tally_set_writable.argtypes = [c_int32, c_bool] _dll.openmc_tally_set_writable.restype = c_int _dll.openmc_tally_set_writable.errcheck = _error_handler +_dll.openmc_remove_tally.argtypes = [c_int32] +_dll.openmc_remove_tally.restype = c_int +_dll.openmc_remove_tally.errcheck = _error_handler _dll.tallies_size.restype = c_size_t @@ -95,13 +104,15 @@ _SCORES = { -5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission', -9: 'current', -10: 'events', -11: 'delayed-nu-fission', -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', - -15: 'fission-q-recoverable', -16: 'decay-rate' + -15: 'fission-q-recoverable', -16: 'decay-rate', -17: 'pulse-height', + -18: 'ifp-time-numerator', -19: 'ifp-beta-numerator', + -20: 'ifp-denominator', } _ESTIMATORS = { 0: 'analog', 1: 'tracklength', 2: 'collision' } _TALLY_TYPES = { - 0: 'volume', 1: 'mesh-surface', 2: 'surface' + 0: 'volume', 1: 'mesh-surface', 2: 'surface', 3: 'pulse-height' } @@ -171,6 +182,10 @@ class Tally(_FortranObjectWithID): List of tally filters mean : numpy.ndarray An array containing the sample mean for each bin + multiply_density : bool + Whether reaction rates should be multiplied by atom density + + .. versionadded:: 0.14.0 nuclides : list of str List of nuclides to score results for num_realizations : int @@ -218,6 +233,10 @@ class Tally(_FortranObjectWithID): _dll.openmc_tally_get_active(self._index, active) return active.value + @active.setter + def active(self, active): + _dll.openmc_tally_set_active(self._index, active) + @property def type(self): type = c_int32() @@ -238,10 +257,6 @@ class Tally(_FortranObjectWithID): def estimator(self, estimator): _dll.openmc_tally_set_estimator(self._index, estimator.encode()) - @active.setter - def active(self, active): - _dll.openmc_tally_set_active(self._index, active) - @property def id(self): tally_id = c_int32() @@ -267,6 +282,30 @@ class Tally(_FortranObjectWithID): _dll.openmc_tally_set_filters(self._index, n, indices) + def find_filter(self, filter_type): + """ + Returns the first instance of a filter matching the specified type + + Parameters + ---------- + filter_type : subclass of openmc.lib.Filter + The filter type to match when retrieving a filter instance + + Returns + ------- + filter : openmc.lib.Filter + The filter instance matching the input filter type + + Raises + ------ + ValueError if a filter instance matching the input filter type cannot be found. + """ + for filter in self.filters: + if isinstance(filter, filter_type): + return filter + + raise ValueError(f'No filter of type {filter_type} on tally {self.id}') + @property def mean(self): n = self.num_realizations @@ -360,6 +399,16 @@ class Tally(_FortranObjectWithID): def writable(self, writable): _dll.openmc_tally_set_writable(self._index, writable) + @property + def multiply_density(self): + multiply_density = c_bool() + _dll.openmc_tally_get_multiply_density(self._index, multiply_density) + return multiply_density.value + + @multiply_density.setter + def multiply_density(self, multiply_density): + _dll.openmc_tally_set_multiply_density(self._index, multiply_density) + def reset(self): """Reset results and num_realizations of tally""" _dll.openmc_tally_reset(self._index) @@ -405,4 +454,8 @@ class _TallyMapping(Mapping): def __repr__(self): return repr(dict(self)) + def __delitem__(self, key): + """Delete a tally from tally vector and remove the ID,index pair from tally""" + _dll.openmc_remove_tally(self[key]._index) + tallies = _TallyMapping() diff --git a/openmc/lib/weight_windows.py b/openmc/lib/weight_windows.py new file mode 100644 index 0000000000..ed442d33ff --- /dev/null +++ b/openmc/lib/weight_windows.py @@ -0,0 +1,395 @@ +from collections.abc import Mapping +from ctypes import c_double, c_int, c_int32, c_char_p, c_size_t, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc import ParticleType +from openmc.exceptions import AllocationError, InvalidIDError +from . import _dll +from .core import _FortranObjectWithID +from .error import _error_handler +from .filter import EnergyFilter, MeshFilter, ParticleFilter +from .mesh import _get_mesh +from .mesh import meshes + + +__all__ = ['WeightWindows', 'weight_windows'] + +_dll.openmc_extend_weight_windows.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] + +_dll.openmc_weight_windows_update_magic.argtypes = 2*[c_int32] + [c_char_p] + 2*[c_double] +_dll.openmc_weight_windows_update_magic.restype = c_int +_dll.openmc_weight_windows_update_magic.errcheck = _error_handler + +_dll.openmc_weight_windows_size.restype = c_size_t + +_dll.openmc_get_weight_windows_index.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_weight_windows_index.restype = c_int +_dll.openmc_get_weight_windows_index.errcheck = _error_handler + +_dll.openmc_weight_windows_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_weight_windows_get_id.restype = c_int +_dll.openmc_weight_windows_get_id.errcheck = _error_handler + +_dll.openmc_weight_windows_set_id.argtypes = [c_int32, c_int32] +_dll.openmc_weight_windows_set_id.restype = c_int +_dll.openmc_weight_windows_set_id.errcheck = _error_handler + +_dll.openmc_weight_windows_set_mesh.argtypes = [c_int32, c_int32] +_dll.openmc_weight_windows_set_mesh.restype = c_int +_dll.openmc_weight_windows_set_mesh.errcheck = _error_handler + +_dll.openmc_weight_windows_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_weight_windows_get_mesh.restype = c_int +_dll.openmc_weight_windows_get_mesh.errcheck = _error_handler + +_dll.openmc_weight_windows_set_energy_bounds.argtypes = [c_int32, POINTER(c_double), c_size_t] +_dll.openmc_weight_windows_set_energy_bounds.restype = c_int +_dll.openmc_weight_windows_set_energy_bounds.errcheck = _error_handler + +_dll.openmc_weight_windows_get_energy_bounds.argtypes = [c_int32, POINTER(POINTER(c_double)), POINTER(c_size_t)] +_dll.openmc_weight_windows_get_energy_bounds.restype = c_int +_dll.openmc_weight_windows_get_energy_bounds.errcheck = _error_handler + +_dll.openmc_weight_windows_set_particle.argtypes = [c_int32, c_int] +_dll.openmc_weight_windows_set_particle.restype = c_int +_dll.openmc_weight_windows_set_particle.errcheck = _error_handler + +_dll.openmc_weight_windows_get_particle.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_weight_windows_get_particle.restype = c_int +_dll.openmc_weight_windows_get_particle.errcheck = _error_handler + +_dll.openmc_weight_windows_set_bounds.argtypes = [c_int32, POINTER(c_double), POINTER(c_double), c_size_t] +_dll.openmc_weight_windows_set_bounds.restype = c_int +_dll.openmc_weight_windows_set_bounds.errcheck = _error_handler + +_dll.openmc_weight_windows_get_bounds.argtypes = [c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), POINTER(c_size_t)] +_dll.openmc_weight_windows_get_bounds.restype = c_int +_dll.openmc_weight_windows_get_bounds.errcheck = _error_handler + +_dll.openmc_weight_windows_get_survival_ratio.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_weight_windows_get_survival_ratio.restype = c_int +_dll.openmc_weight_windows_get_survival_ratio.errcheck = _error_handler + +_dll.openmc_weight_windows_set_survival_ratio.argtypes = [c_int32, c_double] +_dll.openmc_weight_windows_set_survival_ratio.restype = c_int +_dll.openmc_weight_windows_set_survival_ratio.errcheck = _error_handler + +_dll.openmc_weight_windows_get_max_lower_bound_ratio.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_weight_windows_get_max_lower_bound_ratio.restype = c_int +_dll.openmc_weight_windows_get_max_lower_bound_ratio.errcheck = _error_handler + +_dll.openmc_weight_windows_set_max_lower_bound_ratio.argtypes = [c_int32, c_double] +_dll.openmc_weight_windows_set_max_lower_bound_ratio.restype = c_int +_dll.openmc_weight_windows_set_max_lower_bound_ratio.errcheck = _error_handler + +_dll.openmc_weight_windows_get_weight_cutoff.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_weight_windows_get_weight_cutoff.restype = c_int +_dll.openmc_weight_windows_get_weight_cutoff.errcheck = _error_handler + +_dll.openmc_weight_windows_set_weight_cutoff.argtypes = [c_int32, c_double] +_dll.openmc_weight_windows_set_weight_cutoff.restype = c_int +_dll.openmc_weight_windows_set_weight_cutoff.errcheck = _error_handler + +_dll.openmc_weight_windows_get_max_split.argtypes = [c_int32, POINTER(c_int)] +_dll.openmc_weight_windows_get_max_split.restype = c_int +_dll.openmc_weight_windows_get_max_split.errcheck = _error_handler + +_dll.openmc_weight_windows_set_max_split.argtypes = [c_int32, c_int] +_dll.openmc_weight_windows_set_max_split.restype = c_int +_dll.openmc_weight_windows_set_max_split.errcheck = _error_handler + + +class WeightWindows(_FortranObjectWithID): + """WeightWindows stored internally. + + This class exposes a weight windows object that is stored internally in the + OpenMC library. To obtain a view of a weight windows object with a given ID, + use the :data:`openmc.lib.weight_windows` mapping. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + id : int or None + Unique ID of the weight windows + new : bool + When `index` is None, this argument controls whether a new object is + created or a view of an existing object is returned. + index : int or None + Index in the `weight_windows` array. + + Attributes + ---------- + id : int + ID of the weight windows object + mesh : openmc.lib.Mesh + Mesh used for the weight windows + particle : openmc.ParticleType + The particle type to which these weight windows apply + energy_bounds : numpy.ndarray + The energy bounds for the weight windows + bounds : numpy.ndarray + The weight window bounds + """ + __instances = WeakValueDictionary() + + def __new__(cls, id=None, new=True, index=None): + mapping = weight_windows + + if index is None: + if new: + # Determine ID to assign + if id is None: + id = max(mapping, default=0) + 1 + else: + if id in mapping: + raise AllocationError(f'A weight windows object with ID={id} ' + 'has already been allocated.') + + index = c_int32() + _dll.openmc_extend_weight_windows(1, index, None) + index = index.value + else: + index = mapping[id]._index + + if index not in cls.__instances: + instance = super().__new__(cls) + instance._index = index + if id is not None: + instance.id = id + cls.__instances[index] = instance + + return cls.__instances[index] + + @property + def id(self): + ww_id = c_int32() + _dll.openmc_weight_windows_get_id(self._index, ww_id) + return ww_id.value + + @id.setter + def id(self, ww_id): + _dll.openmc_weight_windows_set_id(self._index, ww_id) + + @property + def mesh(self): + mesh_idx = c_int32() + _dll.openmc_weight_windows_get_mesh(self._index, mesh_idx) + return _get_mesh(mesh_idx.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_weight_windows_set_mesh( + weight_windows[self.id]._index, meshes[mesh.id]._index) + + @property + def energy_bounds(self): + data = POINTER(c_double)() + n = c_size_t() + _dll.openmc_weight_windows_get_energy_bounds(self._index, data, n) + return as_array(data, (n.value,)) + + @energy_bounds.setter + def energy_bounds(self, e_bounds): + e_bounds_arr = np.asarray(e_bounds, dtype=float) + e_bounds_ptr = e_bounds_arr.ctypes.data_as(POINTER(c_double)) + _dll.openmc_weight_windows_set_energy_bounds( + self._index, e_bounds_ptr, e_bounds_arr.size) + + @property + def particle(self): + val = c_int() + _dll.openmc_weight_windows_get_particle(self._index, val) + return ParticleType(val.value) + + @particle.setter + def particle(self, p): + if isinstance(p, str): + p = ParticleType.from_string(p) + else: + p = ParticleType(p) + _dll.openmc_weight_windows_set_particle(self._index, int(p)) + + @property + def bounds(self): + upper = POINTER(c_double)() + lower = POINTER(c_double)() + size = c_size_t() + _dll.openmc_weight_windows_get_bounds(self._index, lower, upper, size) + lower_arr = as_array(lower, (size.value,)) + upper_arr = as_array(upper, (size.value,)) + return (lower_arr, upper_arr) + + @bounds.setter + def bounds(self, bounds): + lower = np.asarray(bounds[0]) + upper = np.asarray(bounds[1]) + + lower_p = lower.ctypes.data_as(POINTER(c_double)) + upper_p = upper.ctypes.data_as(POINTER(c_double)) + + _dll.openmc_weight_windows_set_bounds(self._index, lower_p, upper_p, lower.size) + + @property + def survival_ratio(self): + ratio = c_double() + _dll.openmc_weight_windows_get_survival_ratio(self._index, ratio) + return ratio.value + + @survival_ratio.setter + def survival_ratio(self, ratio): + _dll.openmc_weight_windows_set_survival_ratio(self._index, ratio) + + @property + def max_lower_bound_ratio(self): + lb_ratio = c_double() + _dll.openmc_weight_windows_get_max_lower_bound_ratio(self._index, lb_ratio) + return lb_ratio.value + + @max_lower_bound_ratio.setter + def max_lower_bound_ratio(self, lb_ratio): + _dll.openmc_weight_windows_set_max_lower_bound_ratio(self._index, lb_ratio) + + @property + def weight_cutoff(self): + cutoff = c_double() + _dll.openmc_weight_windows_get_weight_cutoff(self._index, cutoff) + return cutoff.value + + @weight_cutoff.setter + def weight_cutoff(self, cutoff): + _dll.openmc_weight_windows_set_weight_cutoff(self._index, cutoff) + + @property + def max_split(self): + max_split = c_int() + _dll.openmc_weight_windows_get_max_split(self._index, max_split) + return max_split.value + + @max_split.setter + def max_split(self, max_split): + _dll.openmc_weight_windows_set_max_split(self._index, max_split) + + def update_magic(self, tally, value='mean', threshold=1.0, ratio=5.0): + """Update weight window values using the MAGIC method + + Reference: https://inis.iaea.org/records/231pm-zzy35 + + Parameters + ---------- + tally : openmc.lib.Tally object + The tally used to update weight window information + value : str + Value type used to generate weight windows. One of {'mean', 'rel_err'}. + threshold : float + Threshold for relative error of results used to generate weight window bounds + ratio : float + Ratio of the lower to upper weight window bounds + + """ + _dll.openmc_weight_windows_update_magic(self._index, + tally._index, + c_char_p(value.encode()), + threshold, + ratio) + + @classmethod + def from_tally(cls, tally, particle=ParticleType.NEUTRON): + """Create an instance of the WeightWindows class based on the specified tally. + + Parameters + ---------- + tally : openmc.lib.Tally + The tally used to create the WeightWindows instance. + particle : openmc.ParticleType or str, optional + The particle type to use for the WeightWindows instance. Should be + specified as an instance of ParticleType or as a string with a value of + 'neutron' or 'photon'. + + Returns + ------- + WeightWindows + The WeightWindows instance created from the specified tally. + + Raises + ------ + ValueError + If the particle parameter is not an instance of ParticleType or a string. + ValueError + If the particle parameter is not a valid particle type (i.e., not 'neutron' + or 'photon'). + ValueError + If the specified particle is not included in the bins of the ParticleFilter + of the tally. + ValueError + If the tally does not have a MeshFilter. + """ + # do some checks on particle value + if not isinstance(particle, (ParticleType, str)): + raise ValueError(f"Parameter 'particle' must be {ParticleType} or one of ('neutron', 'photon').") + + # convert particle type if needed + if isinstance(particle, str): + particle = ParticleType.from_string(particle) + + if particle not in (ParticleType.NEUTRON, ParticleType.PHOTON): + raise ValueError('Weight windows can only be applied for neutrons or photons') + + try: + particle_filter = tally.find_filter(ParticleFilter) + except ValueError: + particle_filter = None + + # ensure that the tally won't filter out the specified particle + if particle_filter is not None and particle not in particle_filter.bins: + raise ValueError(f'Specified tally for weight windows (Tally {tally.id})' + f' does not track the requested particle: "{particle}"') + + # tally must have a mesh filter + mesh_filter = tally.find_filter(MeshFilter) + + # create a new weight windows instance + out = cls() + + # set mesh and particle + out.mesh = mesh_filter.mesh + out.particle = particle + + # set energy bounds if needed + try: + energy_filter = tally.find_filter(EnergyFilter) + except ValueError: + energy_filter = None + + if energy_filter is not None: + out.energy_bounds = energy_filter.bins + + return out + + +class _WeightWindowsMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + try: + _dll.openmc_get_weight_windows_index(key, index) + except (AllocationError, InvalidIDError) as e: + raise KeyError(str(e)) + return WeightWindows(index=index.value) + + def __iter__(self): + for i in range(len(self)): + yield WeightWindows(index=i).id + + def __len__(self): + return _dll.openmc_weight_windows_size() + + def __repr__(self): + return repr(dict(self)) + + def __delitem__(self): + raise NotImplementedError("WeightWindows object remove not implemented") + +weight_windows = _WeightWindowsMapping() diff --git a/openmc/material.py b/openmc/material.py index e5c3f8e4b4..c7b954b666 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1,25 +1,39 @@ -from collections import OrderedDict, defaultdict, namedtuple, Counter +from __future__ import annotations +from collections import defaultdict, namedtuple, Counter from collections.abc import Iterable from copy import deepcopy from numbers import Real from pathlib import Path import re +import sys +import tempfile +from typing import Sequence, Dict import warnings -from xml.etree import ElementTree as ET +import lxml.etree as ET import numpy as np +import h5py import openmc import openmc.data import openmc.checkvalue as cv -from ._xml import clean_indentation, reorder_attributes +from ._xml import clean_indentation, get_elem_list, get_text from .mixin import IDManagerMixin +from .utility_funcs import input_path +from . import waste +from openmc.checkvalue import PathLike +from openmc.stats import Univariate, Discrete, Mixture +from openmc.data.data import _get_element_symbol # Units for density supported by OpenMC DENSITY_UNITS = ('g/cm3', 'g/cc', 'kg/m3', 'atom/b-cm', 'atom/cm3', 'sum', 'macro') +# Smallest normalized floating point number +_SMALLEST_NORMAL = sys.float_info.min + +_BECQUEREL_PER_CURIE = 3.7e10 NuclideTuple = namedtuple('NuclideTuple', ['name', 'percent', 'percent_type']) @@ -29,9 +43,11 @@ class Material(IDManagerMixin): To create a material, one should create an instance of this class, add nuclides or elements with :meth:`Material.add_nuclide` or - `Material.add_element`, respectively, and set the total material density - with `Material.set_density()`. The material can then be assigned to a cell - using the :attr:`Cell.fill` attribute. + :meth:`Material.add_element`, respectively, and set the total material + density with :meth:`Material.set_density()`. Alternatively, you can use + :meth:`Material.add_components()` to pass a dictionary containing all the + component information. The material can then be assigned to a cell using the + :attr:`Cell.fill` attribute. Parameters ---------- @@ -85,6 +101,10 @@ class Material(IDManagerMixin): fissionable_mass : float Mass of fissionable nuclides in the material in [g]. Requires that the :attr:`volume` attribute is set. + ncrystal_cfg : str + NCrystal configuration string + + .. versionadded:: 0.13.3 """ @@ -104,6 +124,7 @@ class Material(IDManagerMixin): self._volume = None self._atoms = {} self._isotropic = [] + self._ncrystal_cfg = None # A list of tuples (nuclide, percent, percent type) self._nuclides = [] @@ -115,7 +136,7 @@ class Material(IDManagerMixin): # If specified, a list of table names self._sab = [] - def __repr__(self): + def __repr__(self) -> str: string = 'Material\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) string += '{: <16}=\t{}\n'.format('\tName', self._name) @@ -124,8 +145,14 @@ class Material(IDManagerMixin): string += '{: <16}=\t{}'.format('\tDensity', self._density) string += f' [{self._density_units}]\n' + string += '{: <16}=\t{} [cm^3]\n'.format('\tVolume', self._volume) + string += '{: <16}=\t{}\n'.format('\tDepletable', self._depletable) + string += '{: <16}\n'.format('\tS(a,b) Tables') + if self._ncrystal_cfg: + string += '{: <16}=\t{}\n'.format('\tNCrystal conf', self._ncrystal_cfg) + for sab in self._sab: string += '{: <16}=\t{}\n'.format('\tS(a,b)', sab) @@ -133,7 +160,7 @@ class Material(IDManagerMixin): for nuclide, percent, percent_type in self._nuclides: string += '{: <16}'.format('\t{}'.format(nuclide)) - string += '=\t{: <12} [{}]\n'.format(percent, percent_type) + string += f'=\t{percent: <12} [{percent_type}]\n' if self._macroscopic is not None: string += '{: <16}\n'.format('\tMacroscopic Data') @@ -142,34 +169,55 @@ class Material(IDManagerMixin): return string @property - def name(self): + def name(self) -> str | None: return self._name - @property - def temperature(self): - return self._temperature + @name.setter + def name(self, name: str | None): + if name is not None: + cv.check_type(f'name for Material ID="{self._id}"', + name, str) + self._name = name + else: + self._name = '' @property - def density(self): + def temperature(self) -> float | None: + return self._temperature + + @temperature.setter + def temperature(self, temperature: Real | None): + cv.check_type(f'Temperature for Material ID="{self._id}"', + temperature, (Real, type(None))) + self._temperature = temperature + + @property + def density(self) -> float | None: return self._density @property - def density_units(self): + def density_units(self) -> str: return self._density_units @property - def depletable(self): + def depletable(self) -> bool: return self._depletable + @depletable.setter + def depletable(self, depletable: bool): + cv.check_type(f'Depletable flag for Material ID="{self._id}"', + depletable, bool) + self._depletable = depletable + @property - def paths(self): + def paths(self) -> list[str]: if self._paths is None: raise ValueError('Material instance paths have not been determined. ' 'Call the Geometry.determine_paths() method.') return self._paths @property - def num_instances(self): + def num_instances(self) -> int: if self._num_instances is None: raise ValueError( 'Number of material instances have not been determined. Call ' @@ -177,15 +225,21 @@ class Material(IDManagerMixin): return self._num_instances @property - def nuclides(self): + def nuclides(self) -> list[namedtuple]: return self._nuclides @property - def isotropic(self): + def isotropic(self) -> list[str]: return self._isotropic + @isotropic.setter + def isotropic(self, isotropic: Iterable[str]): + cv.check_iterable_type('Isotropic scattering nuclides', isotropic, + str) + self._isotropic = list(isotropic) + @property - def average_molar_mass(self): + def average_molar_mass(self) -> float: # Using the sum of specified atomic or weight amounts as a basis, sum # the mass and moles of the material mass = 0. @@ -202,56 +256,105 @@ class Material(IDManagerMixin): return mass / moles @property - def volume(self): + def volume(self) -> float | None: return self._volume - @name.setter - def name(self, name): - if name is not None: - cv.check_type(f'name for Material ID="{self._id}"', - name, str) - self._name = name - else: - self._name = '' - - @temperature.setter - def temperature(self, temperature): - cv.check_type(f'Temperature for Material ID="{self._id}"', - temperature, (Real, type(None))) - self._temperature = temperature - - @depletable.setter - def depletable(self, depletable): - cv.check_type(f'Depletable flag for Material ID="{self._id}"', - depletable, bool) - self._depletable = depletable - @volume.setter - def volume(self, volume): + def volume(self, volume: Real): if volume is not None: cv.check_type('material volume', volume, Real) self._volume = volume - @isotropic.setter - def isotropic(self, isotropic): - cv.check_iterable_type('Isotropic scattering nuclides', isotropic, - str) - self._isotropic = list(isotropic) + @property + def ncrystal_cfg(self) -> str | None: + return self._ncrystal_cfg @property - def fissionable_mass(self): + def fissionable_mass(self) -> float: if self.volume is None: raise ValueError("Volume must be set in order to determine mass.") density = 0.0 - for nuc, atoms_per_cc in self.get_nuclide_atom_densities().values(): + for nuc, atoms_per_bcm in self.get_nuclide_atom_densities().items(): Z = openmc.data.zam(nuc)[0] if Z >= 90: - density += 1e24 * atoms_per_cc * openmc.data.atomic_mass(nuc) \ + density += 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ / openmc.data.AVOGADRO return density*self.volume + @property + def decay_photon_energy(self) -> Univariate | None: + warnings.warn( + "The 'decay_photon_energy' property has been replaced by the " + "get_decay_photon_energy() method and will be removed in a future " + "version.", FutureWarning) + return self.get_decay_photon_energy(0.0) + + def get_decay_photon_energy( + self, + clip_tolerance: float = 1e-6, + units: str = 'Bq', + volume: float | None = None + ) -> Univariate | None: + r"""Return energy distribution of decay photons from unstable nuclides. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + clip_tolerance : float + Maximum fraction of :math:`\sum_i x_i p_i` for discrete + distributions that will be discarded. + units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'} + Specifies the units on the integral of the distribution. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + Returns + ------- + Univariate or None + Decay photon energy distribution. The integral of this distribution + is the total intensity of the photon source in the requested units. + + """ + cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'}) + if units == 'Bq': + multiplier = volume if volume is not None else self.volume + if multiplier is None: + raise ValueError("volume must be specified if units='Bq'") + elif units == 'Bq/cm3': + multiplier = 1 + elif units == 'Bq/g': + multiplier = 1.0 / self.get_mass_density() + elif units == 'Bq/kg': + multiplier = 1000.0 / self.get_mass_density() + + dists = [] + probs = [] + for nuc, atoms_per_bcm in self.get_nuclide_atom_densities().items(): + source_per_atom = openmc.data.decay_photon_energy(nuc) + if source_per_atom is not None and atoms_per_bcm > 0.0: + dists.append(source_per_atom) + probs.append(1e24 * atoms_per_bcm * multiplier) + + # If no photon sources, exit early + if not dists: + return None + + # Get combined distribution, clip low-intensity values in discrete spectra + combined = openmc.data.combine_distributions(dists, probs) + if isinstance(combined, (Discrete, Mixture)): + combined.clip(clip_tolerance, inplace=True) + + # If clipping resulted in a single distribution within a mixture, pick + # out that single distribution + if isinstance(combined, Mixture) and len(combined.distribution) == 1: + combined = combined.distribution[0] + + return combined + @classmethod - def from_hdf5(cls, group): + def from_hdf5(cls, group: h5py.Group) -> Material: """Create material from HDF5 group Parameters @@ -305,6 +408,70 @@ class Material(IDManagerMixin): return material + @classmethod + def from_ncrystal(cls, cfg, **kwargs) -> Material: + """Create material from NCrystal configuration string. + + Density, temperature, and material composition, and (ultimately) thermal + neutron scattering will be automatically be provided by NCrystal based + on this string. The name and material_id parameters are simply passed on + to the Material constructor. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + cfg : str + NCrystal configuration string + **kwargs + Keyword arguments passed to :class:`openmc.Material` + + Returns + ------- + openmc.Material + Material instance + + """ + + try: + import NCrystal + except ModuleNotFoundError as e: + raise RuntimeError('The .from_ncrystal method requires' + ' NCrystal to be installed.') from e + nc_mat = NCrystal.createInfo(cfg) + + def openmc_natabund(Z): + #nc_mat.getFlattenedComposition might need natural abundancies. + #This call-back function is used so NCrystal can flatten composition + #using OpenMC's natural abundancies. In practice this function will + #only get invoked in the unlikely case where a material is specified + #by referring both to natural elements and specific isotopes of the + #same element. + elem_name = openmc.data.ATOMIC_SYMBOL[Z] + return [ + (int(iso_name[len(elem_name):]), abund) + for iso_name, abund in openmc.data.isotopes(elem_name) + ] + + flat_compos = nc_mat.getFlattenedComposition( + preferNaturalElements=True, naturalAbundProvider=openmc_natabund) + + # Create the Material + material = cls(temperature=nc_mat.getTemperature(), **kwargs) + + for Z, A_vals in flat_compos: + elemname = openmc.data.ATOMIC_SYMBOL[Z] + for A, frac in A_vals: + if A: + material.add_nuclide(f'{elemname}{A}', frac) + else: + material.add_element(elemname, frac) + + material.set_density('g/cm3', nc_mat.getDensity()) + material._ncrystal_cfg = NCrystal.normaliseCfg(cfg) + + return material + def add_volume_information(self, volume_calc): """Add volume information to a material. @@ -320,12 +487,11 @@ class Material(IDManagerMixin): self._atoms = volume_calc.atoms[self.id] else: raise ValueError('No volume information found for material ID={}.' - .format(self.id)) + .format(self.id)) else: - raise ValueError('No volume information found for material ID={}.' - .format(self.id)) + raise ValueError(f'No volume information found for material ID={self.id}.') - def set_density(self, units, density=None): + def set_density(self, units: str, density: float | None = None): """Set the density of the material Parameters @@ -353,11 +519,11 @@ class Material(IDManagerMixin): '"sum" unit'.format(self.id) raise ValueError(msg) - cv.check_type('the density for Material ID="{}"'.format(self.id), + cv.check_type(f'the density for Material ID="{self.id}"', density, Real) self._density = density - def add_nuclide(self, nuclide, percent, percent_type='ao'): + def add_nuclide(self, nuclide: str, percent: float, percent_type: str = 'ao'): """Add a nuclide to the material Parameters @@ -373,12 +539,16 @@ class Material(IDManagerMixin): cv.check_type('nuclide', nuclide, str) cv.check_type('percent', percent, Real) cv.check_value('percent type', percent_type, {'ao', 'wo'}) + cv.check_greater_than('percent', percent, 0, equality=True) if self._macroscopic is not None: msg = 'Unable to add a Nuclide to Material ID="{}" as a ' \ 'macroscopic data-set has already been added'.format(self._id) raise ValueError(msg) + if self._ncrystal_cfg is not None: + raise ValueError("Cannot add nuclides to NCrystal material") + # If nuclide name doesn't look valid, give a warning try: Z, _, _ = openmc.data.zam(nuclide) @@ -391,7 +561,54 @@ class Material(IDManagerMixin): self._nuclides.append(NuclideTuple(nuclide, percent, percent_type)) - def remove_nuclide(self, nuclide): + def add_components(self, components: dict, percent_type: str = 'ao'): + """ Add multiple elements or nuclides to a material + + .. versionadded:: 0.13.1 + + Parameters + ---------- + components : dict of str to float or dict + Dictionary mapping element or nuclide names to their atom or weight + percent. To specify enrichment of an element, the entry of + ``components`` for that element must instead be a dictionary + containing the keyword arguments as well as a value for + ``'percent'`` + percent_type : {'ao', 'wo'} + 'ao' for atom percent and 'wo' for weight percent + + Examples + -------- + >>> mat = openmc.Material() + >>> components = {'Li': {'percent': 1.0, + >>> 'enrichment': 60.0, + >>> 'enrichment_target': 'Li7'}, + >>> 'Fl': 1.0, + >>> 'Be6': 0.5} + >>> mat.add_components(components) + + """ + + for component, params in components.items(): + cv.check_type('component', component, str) + if isinstance(params, Real): + params = {'percent': params} + + else: + cv.check_type('params', params, dict) + if 'percent' not in params: + raise ValueError("An entry in the dictionary does not have " + "a required key: 'percent'") + + params['percent_type'] = percent_type + + # check if nuclide + if not component.isalpha(): + self.add_nuclide(component, **params) + else: + self.add_element(component, **params) + + def remove_nuclide(self, nuclide: str): """Remove a nuclide from the material Parameters @@ -407,7 +624,26 @@ class Material(IDManagerMixin): if nuclide == nuc.name: self.nuclides.remove(nuc) - def add_macroscopic(self, macroscopic): + def remove_element(self, element): + """Remove an element from the material + + .. versionadded:: 0.13.1 + + Parameters + ---------- + element : str + Element to remove + + """ + cv.check_type('element', element, str) + + # If the Material contains the element, delete it + for nuc in reversed(self.nuclides): + element_name = re.split(r'\d+', nuc.name)[0] + if element_name == element: + self.nuclides.remove(nuc) + + def add_macroscopic(self, macroscopic: str): """Add a macroscopic to the material. This will also set the density of the material to 1.0, unless it has been otherwise set, as a default for Macroscopic cross sections. @@ -449,7 +685,7 @@ class Material(IDManagerMixin): if self._density is None: self.set_density('macro', 1.0) - def remove_macroscopic(self, macroscopic): + def remove_macroscopic(self, macroscopic: str): """Remove a macroscopic from the material Parameters @@ -468,8 +704,11 @@ class Material(IDManagerMixin): if macroscopic == self._macroscopic: self._macroscopic = None - def add_element(self, element, percent, percent_type='ao', enrichment=None, - enrichment_target=None, enrichment_type=None): + def add_element(self, element: str, percent: float, percent_type: str = 'ao', + enrichment: float | None = None, + enrichment_target: str | None = None, + enrichment_type: str | None = None, + cross_sections: str | None = None): """Add a natural element to the material Parameters @@ -496,6 +735,8 @@ class Material(IDManagerMixin): Default is: 'ao' for two-isotope enrichment; 'wo' for U enrichment .. versionadded:: 0.12 + cross_sections : str, optional + Location of cross_sections.xml file. Notes ----- @@ -507,6 +748,7 @@ class Material(IDManagerMixin): cv.check_type('nuclide', element, str) cv.check_type('percent', percent, Real) + cv.check_greater_than('percent', percent, 0, equality=True) cv.check_value('percent type', percent_type, {'ao', 'wo'}) # Make sure element name is just that @@ -514,25 +756,26 @@ class Material(IDManagerMixin): raise ValueError("Element name should be given by the " "element's symbol or name, e.g., 'Zr', 'zirconium'") + if self._ncrystal_cfg is not None: + raise ValueError("Cannot add elements to NCrystal material") + # Allow for element identifier to be given as a symbol or name if len(element) > 2: el = element.lower() element = openmc.data.ELEMENT_SYMBOL.get(el) if element is None: - msg = 'Element name "{}" not recognised'.format(el) + msg = f'Element name "{el}" not recognised' raise ValueError(msg) else: if element[0].islower(): - msg = 'Element name "{}" should start with an uppercase ' \ - 'letter'.format(element) + msg = f'Element name "{element}" should start with an uppercase letter' raise ValueError(msg) if len(element) == 2 and element[1].isupper(): - msg = 'Element name "{}" should end with a lowercase ' \ - 'letter'.format(element) + msg = f'Element name "{element}" should end with a lowercase letter' raise ValueError(msg) # skips the first entry of ATOMIC_SYMBOL which is n for neutron if element not in list(openmc.data.ATOMIC_SYMBOL.values())[1:]: - msg = 'Element name "{}" not recognised'.format(element) + msg = f'Element name "{element}" not recognised' raise ValueError(msg) if self._macroscopic is not None: @@ -571,11 +814,14 @@ class Material(IDManagerMixin): percent_type, enrichment, enrichment_target, - enrichment_type): + enrichment_type, + cross_sections): self.add_nuclide(*nuclide) - def add_elements_from_formula(self, formula, percent_type='ao', enrichment=None, - enrichment_target=None, enrichment_type=None): + def add_elements_from_formula(self, formula: str, percent_type: str = 'ao', + enrichment: float | None = None, + enrichment_target: str | None = None, + enrichment_type: str | None = None): """Add a elements from a chemical formula to the material. .. versionadded:: 0.12 @@ -620,8 +866,7 @@ class Material(IDManagerMixin): for token in row: if token.isalpha(): if token == "n" or token not in openmc.data.ATOMIC_NUMBER: - msg = 'Formula entry {} not an element symbol.' \ - .format(token) + msg = f'Formula entry {token} not an element symbol.' raise ValueError(msg) elif token not in ['(', ')', ''] and not token.isdigit(): msg = 'Formula must be made from a sequence of ' \ @@ -672,7 +917,7 @@ class Material(IDManagerMixin): else: self.add_element(element, percent, percent_type) - def add_s_alpha_beta(self, name, fraction=1.0): + def add_s_alpha_beta(self, name: str, fraction: float = 1.0): r"""Add an :math:`S(\alpha,\beta)` table to the material Parameters @@ -705,7 +950,7 @@ class Material(IDManagerMixin): def make_isotropic_in_lab(self): self.isotropic = [x.name for x in self._nuclides] - def get_elements(self): + def get_elements(self) -> list[str]: """Returns all elements in the material .. versionadded:: 0.12 @@ -719,18 +964,37 @@ class Material(IDManagerMixin): return sorted({re.split(r'(\d+)', i)[0] for i in self.get_nuclides()}) - def get_nuclides(self): - """Returns all nuclides in the material + def get_nuclides(self, element: str | None = None) -> list[str]: + """Returns a list of all nuclides in the material, if the element + argument is specified then just nuclides of that element are returned. + + Parameters + ---------- + element : str + Specifies the element to match when searching through the nuclides + + .. versionadded:: 0.13.2 Returns ------- nuclides : list of str List of nuclide names - """ - return [x.name for x in self._nuclides] - def get_nuclide_densities(self): + matching_nuclides = [] + if element: + for nuclide in self._nuclides: + if re.split(r'(\d+)', nuclide.name)[0] == element: + if nuclide.name not in matching_nuclides: + matching_nuclides.append(nuclide.name) + else: + for nuclide in self._nuclides: + if nuclide.name not in matching_nuclides: + matching_nuclides.append(nuclide.name) + + return matching_nuclides + + def get_nuclide_densities(self) -> dict[str, tuple]: """Returns all nuclides in the material and their densities Returns @@ -741,23 +1005,34 @@ class Material(IDManagerMixin): """ - # keep ordered dictionary for testing purposes - nuclides = OrderedDict() + nuclides = {} for nuclide in self._nuclides: nuclides[nuclide.name] = nuclide return nuclides - def get_nuclide_atom_densities(self): - """Returns all nuclides in the material and their atomic densities in - units of atom/b-cm + def get_nuclide_atom_densities(self, nuclide: str | None = None) -> dict[str, float]: + """Returns one or all nuclides in the material and their atomic + densities in units of atom/b-cm + + .. versionchanged:: 0.13.1 + The values in the dictionary were changed from a tuple containing + the nuclide name and the density to just the density. + + Parameters + ---------- + nuclides : str, optional + Nuclide for which atom density is desired. If not specified, the + atom density for each nuclide in the material is given. + + .. versionadded:: 0.13.2 Returns ------- nuclides : dict - Dictionary whose keys are nuclide names and values are tuples of - (nuclide, density in atom/b-cm) + Dictionary whose keys are nuclide names and values are densities in + [atom/b-cm] """ @@ -774,7 +1049,7 @@ class Material(IDManagerMixin): elif self.density_units == 'atom/b-cm': density = self.density elif self.density_units == 'atom/cm3' or self.density_units == 'atom/cc': - density = 1.E-24 * self.density + density = 1.e-24 * self.density # For ease of processing split out nuc, nuc_density, # and nuc_density_type into separate arrays @@ -782,12 +1057,11 @@ class Material(IDManagerMixin): nuc_densities = [] nuc_density_types = [] - for nuclide in self.nuclides: - nucs.append(nuclide.name) - nuc_densities.append(nuclide.percent) - nuc_density_types.append(nuclide.percent_type) + for nuc in self.nuclides: + nucs.append(nuc.name) + nuc_densities.append(nuc.percent) + nuc_density_types.append(nuc.percent_type) - nucs = np.array(nucs) nuc_densities = np.array(nuc_densities) nuc_density_types = np.array(nuc_density_types) @@ -810,18 +1084,196 @@ class Material(IDManagerMixin): # Convert the mass density to an atom density if not density_in_atom: - density = -density / self.average_molar_mass * 1.E-24 \ + density = -density / self.average_molar_mass * 1.e-24 \ * openmc.data.AVOGADRO nuc_densities = density * nuc_densities - nuclides = OrderedDict() + nuclides = {} for n, nuc in enumerate(nucs): - nuclides[nuc] = (nuc, nuc_densities[n]) + if nuclide is None or nuclide == nuc: + nuclides[nuc] = nuc_densities[n] return nuclides - def get_mass_density(self, nuclide=None): + def get_element_atom_densities(self, element: str | None = None) -> dict[str, float]: + """Returns one or all elements in the material and their atomic + densities in units of atom/b-cm + + .. versionadded:: 0.15.1 + + Parameters + ---------- + element : str, optional + Element for which atom density is desired. If not specified, the + atom density for each element in the material is given. + + Returns + ------- + elements : dict + Dictionary whose keys are element names and values are densities in + [atom/b-cm] + + """ + if element is not None: + element = _get_element_symbol(element) + + nuc_densities = self.get_nuclide_atom_densities() + + # Initialize an empty dictionary for summed values + densities = {} + + # Accumulate densities for each nuclide + for nuclide, density in nuc_densities.items(): + nuc_element = openmc.data.ATOMIC_SYMBOL[openmc.data.zam(nuclide)[0]] + if element is None or element == nuc_element: + if nuc_element not in densities: + densities[nuc_element] = 0.0 + densities[nuc_element] += float(density) + + # If specific element was requested, make sure it is present + if element is not None and element not in densities: + raise ValueError(f'Element {element} not found in material.') + + return densities + + + def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, + volume: float | None = None) -> dict[str, float] | float: + """Returns the activity of the material or of each nuclide within. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Ci', 'Ci/m3'} + Specifies the type of activity to return, options include total + activity [Bq,Ci], specific [Bq/g, Bq/kg] or volumetric activity + [Bq/cm3,Ci/m3]. Default is volumetric activity [Bq/cm3]. + by_nuclide : bool + Specifies if the activity should be returned for the material as a + whole or per nuclide. Default is False. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + .. versionadded:: 0.13.3 + + Returns + ------- + Union[dict, float] + If by_nuclide is True then a dictionary whose keys are nuclide + names and values are activity is returned. Otherwise the activity + of the material is returned as a float. + """ + + cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Ci', 'Ci/m3'}) + cv.check_type('by_nuclide', by_nuclide, bool) + + if volume is None: + volume = self.volume + + if units == 'Bq': + multiplier = volume + elif units == 'Bq/cm3': + multiplier = 1 + elif units == 'Bq/g': + multiplier = 1.0 / self.get_mass_density() + elif units == 'Bq/kg': + multiplier = 1000.0 / self.get_mass_density() + elif units == 'Ci': + multiplier = volume / _BECQUEREL_PER_CURIE + elif units == 'Ci/m3': + multiplier = 1e6 / _BECQUEREL_PER_CURIE + + activity = {} + for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items(): + inv_seconds = openmc.data.decay_constant(nuclide) + activity[nuclide] = inv_seconds * 1e24 * atoms_per_bcm * multiplier + + return activity if by_nuclide else sum(activity.values()) + + def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, + volume: float | None = None) -> dict[str, float] | float: + """Returns the decay heat of the material or for each nuclide in the + material in units of [W], [W/g], [W/kg] or [W/cm3]. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + units : {'W', 'W/g', 'W/kg', 'W/cm3'} + Specifies the units of decay heat to return. Options include total + heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3]. + Default is total heat [W]. + by_nuclide : bool + Specifies if the decay heat should be returned for the material as a + whole or per nuclide. Default is False. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + .. versionadded:: 0.13.3 + + Returns + ------- + Union[dict, float] + If `by_nuclide` is True then a dictionary whose keys are nuclide + names and values are decay heat is returned. Otherwise the decay heat + of the material is returned as a float. + """ + + cv.check_value('units', units, {'W', 'W/g', 'W/kg', 'W/cm3'}) + cv.check_type('by_nuclide', by_nuclide, bool) + + if units == 'W': + multiplier = volume if volume is not None else self.volume + elif units == 'W/cm3': + multiplier = 1 + elif units == 'W/g': + multiplier = 1.0 / self.get_mass_density() + elif units == 'W/kg': + multiplier = 1000.0 / self.get_mass_density() + + decayheat = {} + for nuclide, atoms_per_bcm in self.get_nuclide_atom_densities().items(): + decay_erg = openmc.data.decay_energy(nuclide) + inv_seconds = openmc.data.decay_constant(nuclide) + decay_erg *= openmc.data.JOULE_PER_EV + decayheat[nuclide] = inv_seconds * decay_erg * 1e24 * atoms_per_bcm * multiplier + + return decayheat if by_nuclide else sum(decayheat.values()) + + def get_nuclide_atoms(self, volume: float | None = None) -> dict[str, float]: + """Return number of atoms of each nuclide in the material + + .. versionadded:: 0.13.1 + + Parameters + ---------- + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + .. versionadded:: 0.13.3 + + Returns + ------- + dict + Dictionary whose keys are nuclide names and values are number of + atoms present in the material. + + """ + if volume is None: + volume = self.volume + if volume is None: + raise ValueError("Volume must be set in order to determine atoms.") + atoms = {} + for nuclide, atom_per_bcm in self.get_nuclide_atom_densities().items(): + atoms[nuclide] = 1.0e24 * atom_per_bcm * volume + return atoms + + def get_mass_density(self, nuclide: str | None = None) -> float: """Return mass density of one or all nuclides Parameters @@ -837,14 +1289,13 @@ class Material(IDManagerMixin): """ mass_density = 0.0 - for nuc, atoms_per_cc in self.get_nuclide_atom_densities().values(): - if nuclide is None or nuclide == nuc: - density_i = 1e24 * atoms_per_cc * openmc.data.atomic_mass(nuc) \ - / openmc.data.AVOGADRO - mass_density += density_i + for nuc, atoms_per_bcm in self.get_nuclide_atom_densities(nuclide=nuclide).items(): + density_i = 1e24 * atoms_per_bcm * openmc.data.atomic_mass(nuc) \ + / openmc.data.AVOGADRO + mass_density += density_i return mass_density - def get_mass(self, nuclide=None): + def get_mass(self, nuclide: str | None = None, volume: float | None = None) -> float: """Return mass of one or all nuclides. Note that this method requires that the :attr:`Material.volume` has @@ -855,6 +1306,12 @@ class Material(IDManagerMixin): nuclides : str, optional Nuclide for which mass is desired. If not specified, the density for the entire material is given. + volume : float, optional + Volume of the material. If not passed, defaults to using the + :attr:`Material.volume` attribute. + + .. versionadded:: 0.13.3 + Returns ------- @@ -862,11 +1319,98 @@ class Material(IDManagerMixin): Mass of the nuclide/material in [g] """ - if self.volume is None: + if volume is None: + volume = self.volume + if volume is None: raise ValueError("Volume must be set in order to determine mass.") - return self.volume*self.get_mass_density(nuclide) + return volume*self.get_mass_density(nuclide) - def clone(self, memo=None): + def waste_classification(self, metal: bool = False) -> str: + """Classify the material for near-surface waste disposal. + + This method determines a waste classification for the material based on + the NRC regulations (10 CFR 61.55). Note that the NRC regulations do not + consider many long-lived radionuclides relevant to fusion systems; for + fusion applications, it is recommended to calculate a waste disposal + rating based on limits by Fetter et al. using the + :meth:`~openmc.Material.waste_disposal_rating` method. + + Parameters + ---------- + metal : bool, optional + Whether or not the material is in metal form. + + Returns + ------- + str + The waste disposal classification, which can be "Class A", "Class + B", "Class C", or "GTCC" (greater than class C). + + """ + return waste._waste_classification(self, metal=metal) + + def waste_disposal_rating( + self, + limits: str | dict[str, float] = 'Fetter', + metal: bool = False, + by_nuclide: bool = False, + ) -> float | dict[str, float]: + """Return the waste disposal rating for the material. + + This method returns a waste disposal rating for the material based on a + set of specific activity limits. The waste disposal rating is a single + number that represents the sum of the ratios of the specific activity + for each radionuclide in the material against a nuclide-specific limit. + A value less than 1.0 indicates that the material "meets" the limits + whereas a value greater than 1.0 exceeds the limits. + + Note that the limits for NRC do not consider many long-lived + radionuclides relevant to fusion systems. A paper by `Fetter et al. + `_ applies the NRC + methodology to calculate specific activity limits for an expanded set of + radionuclides. + + Parameters + ---------- + limits : str or dict, optional + The name of a predefined set of specific activity limits or a + dictionary that contains specific activity limits for radionuclides, + where keys are nuclide names and values are activities in units of + [Ci/m3]. The predefined options are: + + - 'Fetter': Uses limits from Fetter et al. (1990) + - 'NRC_long': Uses the 10 CFR 61.55 limits for long-lived + radionuclides + - 'NRC_short_A': Uses the 10 CFR 61.55 class A limits for + short-lived radionuclides + - 'NRC_short_B': Uses the 10 CFR 61.55 class B limits for + short-lived radionuclides + - 'NRC_short_C': Uses the 10 CFR 61.55 class C limits for + short-lived radionuclides + metal : bool, optional + Whether or not the material is in metal form (only applicable for + NRC based limits) + by_nuclide : bool, optional + Whether to return the waste disposal rating for each nuclide in the + material. If True, a dictionary is returned where the keys are the + nuclide names and the values are the waste disposal ratings for each + nuclide. If False, a single float value is returned that represents + the overall waste disposal rating for the material. + + Returns + ------- + float or dict + The waste disposal rating for the material or its constituent + nuclides. + + See also + -------- + Material.waste_classification() + + """ + return waste._waste_disposal_rating(self, limits, metal, by_nuclide) + + def clone(self, memo: dict | None = None) -> Material: """Create a copy of this material with a new unique ID. Parameters @@ -905,35 +1449,54 @@ class Material(IDManagerMixin): return memo[self] - def _get_nuclide_xml(self, nuclide): + def _get_nuclide_xml(self, nuclide: NuclideTuple) -> ET.Element: xml_element = ET.Element("nuclide") xml_element.set("name", nuclide.name) + # Prevent subnormal numbers from being written to XML, which causes an + # exception on the C++ side when calling std::stod + val = nuclide.percent + if abs(val) < _SMALLEST_NORMAL: + val = 0.0 + if nuclide.percent_type == 'ao': - xml_element.set("ao", str(nuclide.percent)) + xml_element.set("ao", str(val)) else: - xml_element.set("wo", str(nuclide.percent)) + xml_element.set("wo", str(val)) return xml_element - def _get_macroscopic_xml(self, macroscopic): + def _get_macroscopic_xml(self, macroscopic: str) -> ET.Element: xml_element = ET.Element("macroscopic") xml_element.set("name", macroscopic) return xml_element - def _get_nuclides_xml(self, nuclides): + def _get_nuclides_xml( + self, nuclides: Iterable[NuclideTuple], + nuclides_to_ignore: Iterable[str] | None = None)-> list[ET.Element]: xml_elements = [] - for nuclide in nuclides: - xml_elements.append(self._get_nuclide_xml(nuclide)) + + # Remove any nuclides to ignore from the XML export + if nuclides_to_ignore: + nuclides = [nuclide for nuclide in nuclides if nuclide.name not in nuclides_to_ignore] + + xml_elements = [self._get_nuclide_xml(nuclide) for nuclide in nuclides] + return xml_elements - def to_xml_element(self): + def to_xml_element( + self, nuclides_to_ignore: Iterable[str] | None = None) -> ET.Element: """Return XML representation of the material + Parameters + ---------- + nuclides_to_ignore : list of str + Nuclides to ignore when exporting to XML. + Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing material data """ @@ -951,6 +1514,14 @@ class Material(IDManagerMixin): if self._volume: element.set("volume", str(self._volume)) + if self._ncrystal_cfg: + if self._sab: + raise ValueError("NCrystal materials are not compatible with S(a,b).") + if self._macroscopic is not None: + raise ValueError("NCrystal materials are not compatible with macroscopic cross sections.") + + element.set("cfg", str(self._ncrystal_cfg)) + # Create temperature XML subelement if self.temperature is not None: element.set("temperature", str(self.temperature)) @@ -962,12 +1533,12 @@ class Material(IDManagerMixin): subelement.set("value", str(self._density)) subelement.set("units", self._density_units) else: - raise ValueError('Density has not been set for material {}!' - .format(self.id)) + raise ValueError(f'Density has not been set for material {self.id}!') if self._macroscopic is None: # Create nuclide XML subelements - subelements = self._get_nuclides_xml(self._nuclides) + subelements = self._get_nuclides_xml(self._nuclides, + nuclides_to_ignore=nuclides_to_ignore) for subelement in subelements: element.append(subelement) else: @@ -989,7 +1560,8 @@ class Material(IDManagerMixin): return element @classmethod - def mix_materials(cls, materials, fracs, percent_type='ao', name=None): + def mix_materials(cls, materials, fracs: Iterable[float], + percent_type: str = 'ao', **kwargs) -> Material: """Mix materials together based on atom, weight, or volume fractions .. versionadded:: 0.12 @@ -1004,10 +1576,8 @@ class Material(IDManagerMixin): Type of percentage, must be one of 'ao', 'wo', or 'vo', to signify atom percent (molar percent), weight percent, or volume percent, optional. Defaults to 'ao' - name : str - The name for the new material, optional. Defaults to concatenated - names of input materials with percentages indicated inside - parentheses. + **kwargs + Keyword arguments passed to :class:`openmc.Material` Returns ------- @@ -1059,17 +1629,18 @@ class Material(IDManagerMixin): nuclides_per_cc = defaultdict(float) mass_per_cc = defaultdict(float) for mat, wgt in zip(materials, wgts): - for nuc, atoms_per_bcm in mat.get_nuclide_atom_densities().values(): + for nuc, atoms_per_bcm in mat.get_nuclide_atom_densities().items(): nuc_per_cc = wgt*1.e24*atoms_per_bcm nuclides_per_cc[nuc] += nuc_per_cc mass_per_cc[nuc] += nuc_per_cc*openmc.data.atomic_mass(nuc) / \ openmc.data.AVOGADRO # Create the new material with the desired name - if name is None: - name = '-'.join(['{}({})'.format(m.name, f) for m, f in + if "name" not in kwargs: + kwargs["name"] = '-'.join([f'{m.name}({f})' for m, f in zip(materials, fracs)]) - new_mat = openmc.Material(name=name) + + new_mat = cls(**kwargs) # Compute atom fractions of nuclides and add them to the new material tot_nuclides_per_cc = np.sum([dens for dens in nuclides_per_cc.values()]) @@ -1087,12 +1658,12 @@ class Material(IDManagerMixin): return new_mat @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element) -> Material: """Generate material from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -1101,54 +1672,156 @@ class Material(IDManagerMixin): Material generated from XML element """ - mat_id = int(elem.get('id')) + mat_id = int(get_text(elem, 'id')) + + # Add NCrystal material from cfg string + cfg = get_text(elem, "cfg") + if cfg is not None: + return Material.from_ncrystal(cfg, material_id=mat_id) + mat = cls(mat_id) - mat.name = elem.get('name') + mat.name = get_text(elem, 'name') - if "temperature" in elem.attrib: - mat.temperature = float(elem.get("temperature")) + temperature = get_text(elem, "temperature") + if temperature is not None: + mat.temperature = float(temperature) - if 'volume' in elem.attrib: - mat.volume = float(elem.get('volume')) - mat.depletable = bool(elem.get('depletable')) + volume = get_text(elem, "volume") + if volume is not None: + mat.volume = float(volume) # Get each nuclide for nuclide in elem.findall('nuclide'): - name = nuclide.attrib['name'] + name = get_text(nuclide, "name") if 'ao' in nuclide.attrib: mat.add_nuclide(name, float(nuclide.attrib['ao'])) elif 'wo' in nuclide.attrib: mat.add_nuclide(name, float(nuclide.attrib['wo']), 'wo') + # Get depletable attribute + depletable = get_text(elem, "depletable") + mat.depletable = depletable in ('true', '1') + # Get each S(a,b) table for sab in elem.findall('sab'): - fraction = float(sab.get('fraction', 1.0)) - mat.add_s_alpha_beta(sab.get('name'), fraction) + fraction = float(get_text(sab, "fraction", 1.0)) + name = get_text(sab, "name") + mat.add_s_alpha_beta(name, fraction) # Get total material density density = elem.find('density') - units = density.get('units') + units = get_text(density, "units") if units == 'sum': mat.set_density(units) else: - value = float(density.get('value')) + value = float(get_text(density, 'value')) mat.set_density(units, value) # Check for isotropic scattering nuclides - isotropic = elem.find('isotropic') + isotropic = get_elem_list(elem, "isotropic", str) if isotropic is not None: - mat.isotropic = isotropic.text.split() + mat.isotropic = isotropic return mat + def deplete( + self, + multigroup_flux: Sequence[float], + energy_group_structure: Sequence[float] | str, + timesteps: Sequence[float] | Sequence[tuple[float, str]], + source_rates: float | Sequence[float], + timestep_units: str = 's', + chain_file: cv.PathLike | "openmc.deplete.Chain" | None = None, + reactions: Sequence[str] | None = None, + ) -> list[openmc.Material]: + """Depletes that material, evolving the nuclide densities + + .. versionadded:: 0.15.3 + + Parameters + ---------- + multigroup_flux: Sequence[float] + Energy-dependent multigroup flux values, where each sublist corresponds + to a specific material. Will be normalized so that it sums to 1. + energy_group_structure : Sequence[float] | str + Energy group boundaries in [eV] or the name of the group structure. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. + source_rates : float or iterable of float, optional + Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for + each interval in :attr:`timesteps` + timestep_units : {'s', 'min', 'h', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'min' means minutes, 'h' means hours, 'a' means Julian years + and 'MWd/kg' indicates that the values are given in burnup (MW-d of + energy deposited per kilogram of initial heavy metal). + chain_file : PathLike or Chain + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Defaults to ``openmc.config['chain_file']``. + reactions : list of str, optional + Reactions to get cross sections for. If not specified, all neutron + reactions listed in the depletion chain file are used. + + Returns + ------- + list of openmc.Material, one for each timestep + + """ + + materials = openmc.Materials([self]) + + depleted_materials_dict = materials.deplete( + multigroup_fluxes=[multigroup_flux], + energy_group_structures=[energy_group_structure], + timesteps=timesteps, + source_rates=source_rates, + timestep_units=timestep_units, + chain_file=chain_file, + reactions=reactions, + ) + + return depleted_materials_dict[self.id] + + + def mean_free_path(self, energy: float) -> float: + """Calculate the mean free path of neutrons in the material at a given + energy. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + energy : float + Neutron energy in eV + + Returns + ------- + float + Mean free path in cm + + """ + from openmc.plotter import _calculate_cexs_elem_mat + + energy_grid, cexs = _calculate_cexs_elem_mat( + this=self, + types=["total"], + ) + total_cexs = cexs[0] + + interpolated_cexs = float(np.interp(energy, energy_grid, total_cexs)) + + return 1.0 / interpolated_cexs + class Materials(cv.CheckedList): """Collection of Materials used for an OpenMC simulation. This class corresponds directly to the materials.xml input file. It can be - thought of as a normal Python list where each member is a - :class:`Material`. It behaves like a list as the following example - demonstrates: + thought of as a normal Python list where each member is a :class:`Material`. + It behaves like a list as the following example demonstrates: >>> fuel = openmc.Material() >>> clad = openmc.Material() @@ -1168,9 +1841,9 @@ class Materials(cv.CheckedList): Indicates the path to an XML cross section listing file (usually named cross_sections.xml). If it is not set, the :envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used for - continuous-energy calculations and - :envvar:`OPENMC_MG_CROSS_SECTIONS` will be used for multi-group - calculations to find the path to the HDF5 cross section file. + continuous-energy calculations and :envvar:`OPENMC_MG_CROSS_SECTIONS` + will be used for multi-group calculations to find the path to the HDF5 + cross section file. """ @@ -1182,13 +1855,13 @@ class Materials(cv.CheckedList): self += materials @property - def cross_sections(self): + def cross_sections(self) -> Path | None: return self._cross_sections @cross_sections.setter def cross_sections(self, cross_sections): if cross_sections is not None: - self._cross_sections = Path(cross_sections) + self._cross_sections = input_path(cross_sections) def append(self, material): """Append material to collection @@ -1201,7 +1874,7 @@ class Materials(cv.CheckedList): """ super().append(material) - def insert(self, index, material): + def insert(self, index: int, material): """Insert material before index Parameters @@ -1218,13 +1891,67 @@ class Materials(cv.CheckedList): for material in self: material.make_isotropic_in_lab() - def export_to_xml(self, path='materials.xml'): + def _write_xml(self, file, header=True, level=0, spaces_per_level=2, + trailing_indent=True, nuclides_to_ignore=None): + """Writes XML content of the materials to an open file handle. + + Parameters + ---------- + file : IOTextWrapper + Open file handle to write content into. + header : bool + Whether or not to write the XML header + level : int + Indentation level of materials element + spaces_per_level : int + Number of spaces per indentation + trailing_indentation : bool + Whether or not to write a trailing indentation for the materials element + nuclides_to_ignore : list of str + Nuclides to ignore when exporting to XML. + + """ + indentation = level*spaces_per_level*' ' + # Write the header and the opening tag for the root element. + if header: + file.write("\n") + file.write(indentation+'\n') + + # Write the element. + if self.cross_sections is not None: + element = ET.Element('cross_sections') + element.text = str(self.cross_sections) + clean_indentation(element, level=level+1) + element.tail = element.tail.strip(' ') + file.write((level+1)*spaces_per_level*' ') + file.write(ET.tostring(element, encoding="unicode")) + + # Write the elements. + for material in sorted(set(self), key=lambda x: x.id): + element = material.to_xml_element(nuclides_to_ignore=nuclides_to_ignore) + clean_indentation(element, level=level+1) + element.tail = element.tail.strip(' ') + file.write((level+1)*spaces_per_level*' ') + file.write(ET.tostring(element, encoding="unicode")) + + # Write the closing tag for the root element. + file.write(indentation+'\n') + + # Write a trailing indentation for the next element + # at this level if needed + if trailing_indent: + file.write(indentation) + + def export_to_xml(self, path: PathLike = 'materials.xml', + nuclides_to_ignore: Iterable[str] | None = None): """Export material collection to an XML file. Parameters ---------- path : str Path to file to write. Defaults to 'materials.xml'. + nuclides_to_ignore : list of str + Nuclides to ignore when exporting to XML. """ # Check if path is a directory @@ -1237,40 +1964,42 @@ class Materials(cv.CheckedList): # one go. with open(str(p), 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: - - # Write the header and the opening tag for the root element. - fh.write("\n") - fh.write('\n') - - # Write the element. - if self.cross_sections is not None: - element = ET.Element('cross_sections') - element.text = str(self.cross_sections) - clean_indentation(element, level=1) - element.tail = element.tail.strip(' ') - fh.write(' ') - reorder_attributes(element) # TODO: Remove when support is Python 3.8+ - ET.ElementTree(element).write(fh, encoding='unicode') - - # Write the elements. - for material in sorted(self, key=lambda x: x.id): - element = material.to_xml_element() - clean_indentation(element, level=1) - element.tail = element.tail.strip(' ') - fh.write(' ') - reorder_attributes(element) # TODO: Remove when support is Python 3.8+ - ET.ElementTree(element).write(fh, encoding='unicode') - - # Write the closing tag for the root element. - fh.write('\n') + self._write_xml(fh, nuclides_to_ignore=nuclides_to_ignore) @classmethod - def from_xml(cls, path='materials.xml'): + def from_xml_element(cls, elem) -> Materials: """Generate materials collection from XML file Parameters ---------- - path : str, optional + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.Materials + Materials collection + + """ + # Generate each material + materials = cls() + for material in elem.findall('material'): + materials.append(Material.from_xml_element(material)) + + # Check for cross sections settings + xs = get_text(elem, "cross_sections") + if xs is not None: + materials.cross_sections = xs + + return materials + + @classmethod + def from_xml(cls, path: PathLike = 'materials.xml') -> Materials: + """Generate materials collection from XML file + + Parameters + ---------- + path : str Path to materials XML file Returns @@ -1279,17 +2008,119 @@ class Materials(cv.CheckedList): Materials collection """ - tree = ET.parse(path) + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) root = tree.getroot() - # Generate each material - materials = cls() - for material in root.findall('material'): - materials.append(Material.from_xml_element(material)) + return cls.from_xml_element(root) - # Check for cross sections settings - xs = tree.find('cross_sections') - if xs is not None: - materials.cross_sections = xs.text - return materials + def deplete( + self, + multigroup_fluxes: Sequence[Sequence[float]], + energy_group_structures: Sequence[Sequence[float] | str], + timesteps: Sequence[float] | Sequence[tuple[float, str]], + source_rates: float | Sequence[float], + timestep_units: str = 's', + chain_file: cv.PathLike | "openmc.deplete.Chain" | None = None, + reactions: Sequence[str] | None = None, + ) -> Dict[int, list[openmc.Material]]: + """Depletes that material, evolving the nuclide densities + + .. versionadded:: 0.15.3 + + Parameters + ---------- + multigroup_fluxes: Sequence[Sequence[float]] + Energy-dependent multigroup flux values, where each sublist corresponds + to a specific material. Will be normalized so that it sums to 1. + energy_group_structures': Sequence[Sequence[float] | str] + Energy group boundaries in [eV] or the name of the group structure. + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. + source_rates : float or iterable of float, optional + Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for + each interval in :attr:`timesteps` + timestep_units : {'s', 'min', 'h', 'd', 'a', 'MWd/kg'} + Units for values specified in the `timesteps` argument. 's' means + seconds, 'min' means minutes, 'h' means hours, 'a' means Julian years + and 'MWd/kg' indicates that the values are given in burnup (MW-d of + energy deposited per kilogram of initial heavy metal). + chain_file : PathLike or Chain + Path to the depletion chain XML file or instance of openmc.deplete.Chain. + Defaults to ``openmc.config['chain_file']``. + reactions : list of str, optional + Reactions to get cross sections for. If not specified, all neutron + reactions listed in the depletion chain file are used. + + Returns + ------- + list of openmc.Material, one for each timestep + + """ + + import openmc.deplete + from .deplete.chain import _get_chain + + # setting all materials to be depletable + for mat in self: + mat.depletable = True + + chain = _get_chain(chain_file) + + # Create MicroXS objects for all materials + micros = [] + fluxes = [] + + with openmc.lib.TemporarySession(): + for material, flux, energy in zip( + self, multigroup_fluxes, energy_group_structures + ): + temperature = material.temperature or 293.6 + micro_xs = openmc.deplete.MicroXS.from_multigroup_flux( + energies=energy, + multigroup_flux=flux, + chain_file=chain, + temperature=temperature, + reactions=reactions, + ) + micros.append(micro_xs) + fluxes.append(material.volume) + + # Create a single operator for all materials + operator = openmc.deplete.IndependentOperator( + materials=self, + fluxes=fluxes, + micros=micros, + normalization_mode="source-rate", + chain_file=chain, + ) + + integrator = openmc.deplete.PredictorIntegrator( + operator=operator, + timesteps=timesteps, + source_rates=source_rates, + timestep_units=timestep_units, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + # Run integrator + results_path = Path(tmpdir) / "depletion_results.h5" + integrator.integrate(path=results_path) + + # Load depletion results + results = openmc.deplete.Results(results_path) + + # For each material, get activated composition at each timestep + all_depleted_materials = { + material.id: [ + result.get_material(str(material.id)) + for result in results + ] + for material in self + } + + return all_depleted_materials diff --git a/openmc/mesh.py b/openmc/mesh.py index 1f17dbae29..9601207e91 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -1,17 +1,139 @@ -from abc import ABC -from collections.abc import Iterable -from math import pi -from numbers import Real, Integral +from __future__ import annotations import warnings -from xml.etree import ElementTree as ET +from abc import ABC, abstractmethod +from collections.abc import Iterable, Sequence, Mapping +from functools import wraps +from math import pi, sqrt, atan2 +from numbers import Integral, Real +from typing import Protocol +import h5py +import lxml.etree as ET import numpy as np +from pathlib import Path -import openmc.checkvalue as cv import openmc -from ._xml import get_text +import openmc.checkvalue as cv +from openmc.checkvalue import PathLike +from openmc.utility_funcs import change_directory +from ._xml import get_elem_list, get_text from .mixin import IDManagerMixin from .surface import _BOUNDARY_TYPES +from .utility_funcs import input_path + + +class MeshMaterialVolumes(Mapping): + """Results from a material volume in mesh calculation. + + This class provides multiple ways of accessing information about material + volumes in individual mesh elements. First, the class behaves like a + dictionary that maps material IDs to an array of volumes equal in size to + the number of mesh elements. Second, the class provides a :meth:`by_element` + method that gives all the material volumes for a specific mesh element. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + materials : numpy.ndarray + Array of shape (elements, max_materials) storing material IDs + volumes : numpy.ndarray + Array of shape (elements, max_materials) storing material volumes + + See Also + -------- + openmc.MeshBase.material_volumes + + Examples + -------- + If you want to get the volume of a specific material in every mesh element, + index the object with the material ID: + + >>> volumes = mesh.material_volumes(...) + >>> volumes + {1: <32121 nonzero volumes> + 2: <338186 nonzero volumes> + 3: <49120 nonzero volumes>} + + If you want the volume of all materials in a specific mesh element, use the + :meth:`by_element` method: + + >>> volumes = mesh.material_volumes(...) + >>> volumes.by_element(42) + [(2, 31.87963824195591), (1, 6.129949130817542)] + + """ + def __init__(self, materials: np.ndarray, volumes: np.ndarray): + self._materials = materials + self._volumes = volumes + + @property + def num_elements(self) -> int: + return self._volumes.shape[0] + + def __iter__(self): + for mat in np.unique(self._materials): + if mat > 0: + yield mat + + def __len__(self) -> int: + return (np.unique(self._materials) > 0).sum() + + def __repr__(self) -> str: + ids, counts = np.unique(self._materials, return_counts=True) + return '{' + '\n '.join( + f'{id}: <{count} nonzero volumes>' for id, count in zip(ids, counts) if id > 0) + '}' + + def __getitem__(self, material_id: int) -> np.ndarray: + volumes = np.zeros(self.num_elements) + for i in range(self._volumes.shape[1]): + indices = (self._materials[:, i] == material_id) + volumes[indices] = self._volumes[indices, i] + return volumes + + def by_element(self, index_elem: int) -> list[tuple[int | None, float]]: + """Get a list of volumes for each material within a specific element. + + Parameters + ---------- + index_elem : int + Mesh element index + + Returns + ------- + list of tuple of (material ID, volume) + + """ + table_size = self._volumes.shape[1] + return [ + (m if m > -1 else None, self._volumes[index_elem, i]) + for i in range(table_size) + if (m := self._materials[index_elem, i]) != -2 + ] + + def save(self, filename: PathLike): + """Save material volumes to a .npz file. + + Parameters + ---------- + filename : path-like + Filename where data will be saved + """ + np.savez_compressed( + filename, materials=self._materials, volumes=self._volumes) + + @classmethod + def from_npz(cls, filename: PathLike) -> MeshMaterialVolumes: + """Generate material volumes from a .npz file + + Parameters + ---------- + filename : path-like + File where data will be read from + + """ + filedata = np.load(filename) + return cls(filedata['materials'], filedata['volumes']) class MeshBase(IDManagerMixin, ABC): @@ -30,13 +152,17 @@ class MeshBase(IDManagerMixin, ABC): Unique identifier for the mesh name : str Name of the mesh - + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. + indices : Iterable of tuple + An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1, 1), ...] """ next_id = 1 used_ids = set() - def __init__(self, mesh_id=None, name=''): + def __init__(self, mesh_id: int | None = None, name: str = ''): # Initialize Mesh class attributes self.id = mesh_id self.name = name @@ -46,21 +172,36 @@ class MeshBase(IDManagerMixin, ABC): return self._name @name.setter - def name(self, name): + def name(self, name: str): if name is not None: cv.check_type(f'name for mesh ID="{self._id}"', name, str) self._name = name else: self._name = '' + @property + def bounding_box(self) -> openmc.BoundingBox: + return openmc.BoundingBox(self.lower_left, self.upper_right) + + @property + @abstractmethod + def indices(self): + pass + def __repr__(self): string = type(self).__name__ + '\n' string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) return string + def _volume_dim_check(self): + if self.n_dimension != 3 or \ + any([d == 0 for d in self.dimension]): + raise RuntimeError(f'Mesh {self.id} is not 3D. ' + 'Volumes cannot be provided.') + @classmethod - def from_hdf5(cls, group): + def from_hdf5(cls, group: h5py.Group): """Create mesh from HDF5 group Parameters @@ -74,28 +215,47 @@ class MeshBase(IDManagerMixin, ABC): Instance of a MeshBase subclass """ + mesh_type = 'regular' if 'type' not in group.keys() else group['type'][()].decode() + mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + mesh_name = '' if not 'name' in group else group['name'][()].decode() - mesh_type = group['type'][()].decode() if mesh_type == 'regular': - return RegularMesh.from_hdf5(group) + return RegularMesh.from_hdf5(group, mesh_id, mesh_name) elif mesh_type == 'rectilinear': - return RectilinearMesh.from_hdf5(group) + return RectilinearMesh.from_hdf5(group, mesh_id, mesh_name) elif mesh_type == 'cylindrical': - return CylindricalMesh.from_hdf5(group) + return CylindricalMesh.from_hdf5(group, mesh_id, mesh_name) elif mesh_type == 'spherical': - return SphericalMesh.from_hdf5(group) + return SphericalMesh.from_hdf5(group, mesh_id, mesh_name) elif mesh_type == 'unstructured': - return UnstructuredMesh.from_hdf5(group) + return UnstructuredMesh.from_hdf5(group, mesh_id, mesh_name) else: raise ValueError('Unrecognized mesh type: "' + mesh_type + '"') + def to_xml_element(self): + """Return XML representation of the mesh + + Returns + ------- + element : lxml.etree._Element + XML element containing mesh data + + """ + elem = ET.Element("mesh") + + elem.set("id", str(self._id)) + if self.name: + elem.set("name", self.name) + + return elem + @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generates a mesh from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -107,20 +267,594 @@ class MeshBase(IDManagerMixin, ABC): mesh_type = get_text(elem, 'type') if mesh_type == 'regular' or mesh_type is None: - return RegularMesh.from_xml_element(elem) + mesh = RegularMesh.from_xml_element(elem) elif mesh_type == 'rectilinear': - return RectilinearMesh.from_xml_element(elem) + mesh = RectilinearMesh.from_xml_element(elem) elif mesh_type == 'cylindrical': - return CylindricalMesh.from_xml_element(elem) + mesh = CylindricalMesh.from_xml_element(elem) elif mesh_type == 'spherical': - return SphericalMesh.from_xml_element(elem) + mesh = SphericalMesh.from_xml_element(elem) elif mesh_type == 'unstructured': - return UnstructuredMesh.from_xml_element(elem) + mesh = UnstructuredMesh.from_xml_element(elem) else: raise ValueError(f'Unrecognized mesh type "{mesh_type}" found.') + mesh.name = get_text(elem, 'name', default='') + return mesh -class RegularMesh(MeshBase): + def get_homogenized_materials( + self, + model: openmc.Model, + n_samples: int | tuple[int, int, int] = 10_000, + include_void: bool = True, + material_volumes: MeshMaterialVolumes | None = None, + **kwargs + ) -> list[openmc.Material]: + """Generate homogenized materials over each element in a mesh. + + .. versionadded:: 0.15.0 + + Parameters + ---------- + model : openmc.Model + Model containing materials to be homogenized and the associated + geometry. + n_samples : int or 2-tuple of int + Total number of rays to sample. The number of rays in each direction + is determined by the aspect ratio of the mesh bounding box. When + specified as a 3-tuple, it is interpreted as the number of rays in + the x, y, and z dimensions. + include_void : bool, optional + Whether homogenization should include voids. + material_volumes : MeshMaterialVolumes, optional + Previously computed mesh material volumes to use for homogenization. + If not provided, they will be computed by calling + :meth:`material_volumes`. + **kwargs + Keyword-arguments passed to :meth:`material_volumes`. + + Returns + ------- + list of openmc.Material + Homogenized material in each mesh element + + """ + if material_volumes is None: + vols = self.material_volumes(model, n_samples, **kwargs) + else: + vols = material_volumes + mat_volume_by_element = [vols.by_element(i) for i in range(vols.num_elements)] + + # Create homogenized material for each element + materials = model.geometry.get_all_materials() + + # Account for materials in DAGMC universes + # TODO: This should really get incorporated in lower-level calls to + # get_all_materials, but right now it requires information from the + # Model object + for cell in model.geometry.get_all_cells().values(): + if isinstance(cell.fill, openmc.DAGMCUniverse): + names = cell.fill.material_names + materials.update({ + mat.id: mat for mat in model.materials if mat.name in names + }) + + homogenized_materials = [] + for mat_volume_list in mat_volume_by_element: + material_ids, volumes = [list(x) for x in zip(*mat_volume_list)] + total_volume = sum(volumes) + + # Check for void material and remove + try: + index_void = material_ids.index(None) + except ValueError: + pass + else: + material_ids.pop(index_void) + volumes.pop(index_void) + + # If void should be excluded, adjust total volume + if not include_void: + total_volume = sum(volumes) + + # Compute volume fractions + volume_fracs = np.array(volumes) / total_volume + + # Get list of materials and mix 'em up! + mats = [materials[uid] for uid in material_ids] + homogenized_mat = openmc.Material.mix_materials( + mats, volume_fracs, 'vo' + ) + homogenized_mat.volume = total_volume + homogenized_materials.append(homogenized_mat) + + return homogenized_materials + + def material_volumes( + self, + model: openmc.Model, + n_samples: int | tuple[int, int, int] = 10_000, + max_materials: int = 4, + **kwargs + ) -> MeshMaterialVolumes: + """Determine volume of materials in each mesh element. + + This method works by raytracing repeatedly through the mesh to count the + estimated volume of each material in all mesh elements. Three sets of + rays are used: one set parallel to the x-axis, one parallel to the + y-axis, and one parallel to the z-axis. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + model : openmc.Model + Model containing materials. + n_samples : int or 3-tuple of int + Total number of rays to sample. The number of rays in each direction + is determined by the aspect ratio of the mesh bounding box. When + specified as a 3-tuple, it is interpreted as the number of rays in + the x, y, and z dimensions. + max_materials : int, optional + Estimated maximum number of materials in any given mesh element. + **kwargs : dict + Keyword arguments passed to :func:`openmc.lib.init` + + Returns + ------- + Dictionary-like object that maps material IDs to an array of volumes + equal in size to the number of mesh elements. + + """ + import openmc.lib + + # In order to get mesh into model, we temporarily replace the + # tallies with a single mesh tally using the current mesh + original_tallies = model.tallies + new_tally = openmc.Tally() + new_tally.filters = [openmc.MeshFilter(self)] + new_tally.scores = ['flux'] + model.tallies = [new_tally] + + # Set default arguments + kwargs.setdefault('output', True) + if 'args' in kwargs: + kwargs['args'] = ['-c'] + kwargs['args'] + kwargs.setdefault('args', ['-c']) + + with openmc.lib.TemporarySession(model, **kwargs): + # Get mesh from single tally + mesh = openmc.lib.tallies[new_tally.id].filters[0].mesh + + # Compute material volumes + volumes = mesh.material_volumes( + n_samples, max_materials, output=kwargs['output']) + + # Restore original tallies + model.tallies = original_tallies + return volumes + + +class StructuredMesh(MeshBase): + """A base class for structured mesh functionality + + Parameters + ---------- + mesh_id : int + Unique identifier for the mesh + name : str + Name of the mesh + + Attributes + ---------- + id : int + Unique identifier for the mesh + name : str + Name of the mesh + + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + @property + @abstractmethod + def dimension(self): + pass + + @property + @abstractmethod + def n_dimension(self): + pass + + @property + @abstractmethod + def _grids(self): + pass + + @property + def vertices(self): + """Return coordinates of mesh vertices in Cartesian coordinates. Also + see :meth:`CylindricalMesh.vertices_cylindrical` and + :meth:`SphericalMesh.vertices_spherical` for coordinates in other coordinate + systems. + + Returns + ------- + vertices : numpy.ndarray + Returns a numpy.ndarray representing the coordinates of the mesh + vertices with a shape equal to (dim1 + 1, ..., dimn + 1, ndim). X, Y, Z values + can be unpacked with xx, yy, zz = np.rollaxis(mesh.vertices, -1). + + """ + return self._generate_vertices(*self._grids) + + @staticmethod + def _generate_vertices(i_grid, j_grid, k_grid): + """Returns an array with shape (i_grid.size, j_grid.size, k_grid.size, 3) + containing the corner vertices of mesh elements. + """ + return np.stack(np.meshgrid(i_grid, j_grid, k_grid, indexing='ij'), axis=-1) + + @staticmethod + def _generate_edge_midpoints(grids): + """Generates the midpoints of mesh element edges for each dimension of the mesh. + + Parameters + ---------- + grids : numpy.ndarray + The vertex grids along each dimension of the mesh. + + Returns + ------- + midpoint_grids : list of numpy.ndarray + The edge midpoints for the i, j, and k midpoints of each element in + i, j, k ordering. The shapes of the resulting grids are + [(ni-1, nj, nk, 3), (ni, nj-1, nk, 3), (ni, nj, nk-1, 3)] + """ + # generate a set of edge midpoints for each dimension + midpoint_grids = [] + # generate the element edge midpoints in order s.t. + # the epxected element ordering is preserved with respect to the corner vertices + + # each grid is comprised of the mid points for one dimension and the + # corner vertices of the other two + for dims in ((0, 1, 2), (1, 0, 2), (2, 0, 1)): + # compute the midpoints along the last dimension + midpoints = grids[dims[0]][:-1] + 0.5 * np.diff(grids[dims[0]]) + + coords = (midpoints, grids[dims[1]], grids[dims[2]]) + + i_grid, j_grid, k_grid = [coords[dims.index(i)] for i in range(3)] + + # re-use the generate vertices method to create the full mesh grid + # transpose to get (i, j, k) ordering of the gridpoints + midpoint_grid = StructuredMesh._generate_vertices(i_grid, j_grid, k_grid) + midpoint_grids.append(midpoint_grid) + + return midpoint_grids + + @property + def midpoint_vertices(self): + """Create vertices that lie on the midpoint of element edges + """ + # generate edge midpoints needed for curvilinear element definition + midpoint_vertices = self._generate_edge_midpoints(self._grids) + + # convert each of the midpoint grids to cartesian coordinates + for vertices in midpoint_vertices: + self._convert_to_cartesian(vertices, self.origin) + + return midpoint_vertices + + @property + def centroids(self): + """Return coordinates of mesh element centroids. + + Returns + ------- + centroids : numpy.ndarray + Returns a numpy.ndarray representing the mesh element centroid + coordinates with a shape equal to (dim1, ..., dimn, ndim). X, + Y, Z values can be unpacked with xx, yy, zz = + np.rollaxis(mesh.centroids, -1). + """ + ndim = self.n_dimension + # this line ensures that the vertices aren't adjusted by the origin or + # converted to the Cartesian system for cylindrical and spherical meshes + vertices = StructuredMesh.vertices.fget(self) + s0 = (slice(0, -1),)*ndim + (slice(None),) + s1 = (slice(1, None),)*ndim + (slice(None),) + return (vertices[s0] + vertices[s1]) / 2 + + @property + def num_mesh_cells(self): + return np.prod(self.dimension) + + def write_data_to_vtk(self, + filename: PathLike, + datasets: dict | None = None, + volume_normalization: bool = True, + curvilinear: bool = False): + """Creates a VTK object of the mesh + + Parameters + ---------- + filename : str + Name of the VTK file to write. + datasets : dict + Dictionary whose keys are the data labels and values are the data + sets. 1D datasets are expected to be extracted directly from + statepoint data without reordering/reshaping. Multidimensional + datasets are expected to have the same dimensions as the mesh itself + with structured indexing in "C" ordering. See the "expand_dims" flag + of :meth:`~openmc.Tally.get_reshaped_data` on reshaping tally data when using + :class:`~openmc.MeshFilter`'s. + volume_normalization : bool, optional + Whether or not to normalize the data by the volume of the mesh + elements. + curvilinear : bool + Whether or not to write curvilinear elements. Only applies to + ``SphericalMesh`` and ``CylindricalMesh``. + + Raises + ------ + ValueError + When the size of a dataset doesn't match the number of mesh cells + + Returns + ------- + vtk.StructuredGrid or vtk.UnstructuredGrid + a VTK grid object representing the mesh + + Examples + -------- + 1D data from a tally with only a mesh filter and heating score: + + # pass the tally mean property of shape (N, 1, 1) directly to this + # method; dimensions of size 1 will automatically removed + >>> heating = tally.mean + >>> mesh.write_data_to_vtk({'heating': heating}) + + Multidimensional data from a tally with only a mesh + + # retrieve a data array with the mesh filter expanded into three + # dimensions, ijk; additional dimensions of size one will + # automatically be removed + >>> heating = tally.get_reshaped_data(expand_dims=True) + >>> mesh.write_data_to_vtk({'heating': heating}) + """ + import vtk + from vtk.util import numpy_support as nps + + # write linear elements using a structured grid + if not curvilinear or isinstance(self, (RegularMesh, RectilinearMesh)): + vtk_grid = self._create_vtk_structured_grid() + writer = vtk.vtkStructuredGridWriter() + # write curvilinear elements using an unstructured grid + else: + vtk_grid = self._create_vtk_unstructured_grid() + writer = vtk.vtkUnstructuredGridWriter() + + if datasets is not None: + # maintain a list of the datasets as added to the VTK arrays to + # ensure they persist in memory until the file is written + datasets_out = [] + for label, dataset in datasets.items(): + dataset = self._reshape_vtk_dataset(dataset) + self._check_vtk_dataset(label, dataset) + # If the array data is 3D, assume is in C ordering and transpose + # before flattening to match the ordering expected by the VTK + # array based on the way mesh indices are ordered in the Python + # API + # TODO: update to "C" ordering throughout + if dataset.ndim == 3: + dataset = dataset.T.ravel() + datasets_out.append(dataset) + + if volume_normalization: + dataset /= self.volumes.T.ravel() + + dataset_array = vtk.vtkDoubleArray() + dataset_array.SetName(label) + dataset_array.SetArray(nps.numpy_to_vtk(dataset), dataset.size, True) + vtk_grid.GetCellData().AddArray(dataset_array) + + writer.SetFileName(str(filename)) + writer.SetInputData(vtk_grid) + writer.Write() + + return vtk_grid + + def _create_vtk_structured_grid(self): + """Create a structured grid + + Returns + ------- + vtk.vtkStructuredGrid + a VTK structured grid object representing the mesh + """ + import vtk + from vtk.util import numpy_support as nps + + vtkPts = vtk.vtkPoints() + vtkPts.SetData(nps.numpy_to_vtk(np.swapaxes(self.vertices, 0, 2).reshape(-1, 3), deep=True)) + vtk_grid = vtk.vtkStructuredGrid() + vtk_grid.SetPoints(vtkPts) + vtk_grid.SetDimensions(*[dim + 1 for dim in self.dimension]) + + return vtk_grid + + def _create_vtk_unstructured_grid(self): + """Create an unstructured grid of curvilinear elements + representing the mesh + + Returns + ------- + vtk.vtkUnstructuredGrid + a VTK unstructured grid object representing the mesh + """ + import vtk + from vtk.util import numpy_support as nps + + corner_vertices = np.swapaxes(self.vertices, 0, 2).reshape(-1, 3) + + vtkPts = vtk.vtkPoints() + vtk_grid = vtk.vtkUnstructuredGrid() + vtk_grid.SetPoints(vtkPts) + # add corner vertices to the point set for the unstructured grid + # only insert unique points, we'll get their IDs in the point set to + # define element connectivity later + vtkPts.SetData(nps.numpy_to_vtk(np.unique(corner_vertices, axis=0), deep=True)) + + # create a locator to assist with duplicate points + locator = vtk.vtkPointLocator() + locator.SetDataSet(vtk_grid) + locator.AutomaticOn() # autmoatically adds points to locator + locator.InitPointInsertion(vtkPts, vtkPts.GetBounds()) + locator.BuildLocator() + + # this function is used to add new points to the unstructured + # grid. It will return an existing point ID if the point is alread present + def _insert_point(pnt): + result = locator.IsInsertedPoint(pnt) + if result == -1: + point_id = vtkPts.InsertNextPoint(pnt) + locator.InsertPoint(point_id, pnt) + return point_id + else: + return result + + # Add all points to the unstructured grid, maintaining a flat list of IDs as we go ### + + # flat array storing point IDs for a given vertex + # in the grid + point_ids = [] + + # add element corner vertices to array + for pnt in corner_vertices: + point_ids.append(_insert_point(pnt)) + + # get edge midpoints and add them to the + # list of point IDs + midpoint_vertices = self.midpoint_vertices + for edge_grid in midpoint_vertices: + for pnt in np.swapaxes(edge_grid, 0, 2).reshape(-1, 3): + point_ids.append(_insert_point(pnt)) + + # determine how many elements in each dimension + # and how many points in each grid + n_elem = np.asarray(self.dimension) + n_pnts = n_elem + 1 + + # create hexes and set points for corner + # vertices + for i, j, k in self.indices: + # handle indices indexed from one + i -= 1 + j -= 1 + k -= 1 + + # create a new vtk hex + hex = vtk.vtkQuadraticHexahedron() + + # set connectivity the hex corners + for n, (di, dj, dk) in enumerate(_HEX_VERTEX_CONN): + # compute flat index into the point ID list based on i, j, k + # of the vertex + flat_idx = np.ravel_multi_index((i+di, j+dj, k+dk), n_pnts, order='F') + # set corner vertices + hex.GetPointIds().SetId(n, point_ids[flat_idx]) + + # set connectivity of the hex midpoints + n_midpoint_vertices = [v.size // 3 for v in midpoint_vertices] + for n, (dim, (di, dj, dk)) in enumerate(_HEX_MIDPOINT_CONN): + # initial offset for corner vertices and midpoint dimension + flat_idx = corner_vertices.shape[0] + sum(n_midpoint_vertices[:dim]) + # generate a flat index into the table of point IDs + midpoint_shape = midpoint_vertices[dim].shape[:-1] + flat_idx += np.ravel_multi_index((i+di, j+dj, k+dk), + midpoint_shape, + order='F') + # set hex midpoint connectivity + hex.GetPointIds().SetId(_N_HEX_VERTICES + n, point_ids[flat_idx]) + + # add the hex to the grid + vtk_grid.InsertNextCell(hex.GetCellType(), hex.GetPointIds()) + + return vtk_grid + + @staticmethod + def _reshape_vtk_dataset(dataset): + """Reshape a dataset to be compatible with VTK output + + This method performs the following operations on a dataset: + 1. Convert to numpy array if not already + 2. Remove any trailing dimensions of size 1 + 3. Squeeze out any extra dimensions of size 1 beyond the first 3 + + Parameters + ---------- + dataset : array-like + The dataset to reshape + + Returns + ------- + numpy.ndarray + The reshaped dataset + """ + reshaped_data = np.asarray(dataset) + + # detect flat array with extra dims + if all(d == 1 for d in reshaped_data.shape[1:]): + reshaped_data = reshaped_data.squeeze() + + # remove any higher dimensions with size 1 + if reshaped_data.ndim > 3 and all(d == 1 for d in reshaped_data.shape[3:]): + reshaped_data = reshaped_data.reshape(reshaped_data.shape[:3]) + + if np.shares_memory(reshaped_data, dataset): + return np.copy(reshaped_data) + else: + return reshaped_data + + def _check_vtk_dataset(self, label: str, dataset: np.ndarray): + """Perform some basic checks that a dataset is valid for this Mesh + + Parameters + ---------- + label : str + The label for the dataset being checked + dataset : numpy.ndarray + The dataset array to check against this mesh's dimensions + + """ + cv.check_type('data label', label, str) + + if dataset.size != self.num_mesh_cells: + raise ValueError( + f"The size of the dataset '{label}' ({dataset.size}) should be" + f" equal to the number of mesh cells ({self.num_mesh_cells})" + ) + + # accept a flat array as-is, assuming it is in the correct order + if dataset.ndim == 1: + return + + if dataset.shape != self.dimension: + raise ValueError( + f'Cannot apply multidimensional dataset "{label}" with ' + f"shape {dataset.shape} to mesh {self.id} " + f"with dimensions {self.dimension}" + ) + + +class HasBoundingBox(Protocol): + """Object that has a ``bounding_box`` attribute.""" + bounding_box: openmc.BoundingBox + + +class RegularMesh(StructuredMesh): """A regular Cartesian mesh in one, two, or three dimensions Parameters @@ -137,7 +871,7 @@ class RegularMesh(MeshBase): name : str Name of the mesh dimension : Iterable of int - The number of mesh cells in each direction. + The number of mesh cells in each direction (x, y, z). n_dimension : int Number of mesh dimensions. lower_left : Iterable of float @@ -146,6 +880,9 @@ class RegularMesh(MeshBase): upper_right : Iterable of float The upper-right corner of the structured mesh. If only two coordinate are given, it is assumed that the mesh is an x-y mesh. + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. width : Iterable of float The width of mesh cells in each direction. indices : Iterable of tuple @@ -154,7 +891,7 @@ class RegularMesh(MeshBase): """ - def __init__(self, mesh_id=None, name=''): + def __init__(self, mesh_id: int | None = None, name: str = ''): super().__init__(mesh_id, name) self._dimension = None @@ -164,7 +901,13 @@ class RegularMesh(MeshBase): @property def dimension(self): - return self._dimension + return tuple(self._dimension) + + @dimension.setter + def dimension(self, dimension: Iterable[int]): + cv.check_type('mesh dimension', dimension, Iterable, Integral) + cv.check_length('mesh dimension', dimension, 1, 3) + self._dimension = dimension @property def n_dimension(self): @@ -177,6 +920,15 @@ class RegularMesh(MeshBase): def lower_left(self): return self._lower_left + @lower_left.setter + def lower_left(self, lower_left: Iterable[Real]): + cv.check_type('mesh lower_left', lower_left, Iterable, Real) + cv.check_length('mesh lower_left', lower_left, 1, 3) + self._lower_left = lower_left + + if self.upper_right is not None and any(np.isclose(self.upper_right, lower_left)): + raise ValueError("Mesh cannot have zero thickness in any dimension") + @property def upper_right(self): if self._upper_right is not None: @@ -188,6 +940,19 @@ class RegularMesh(MeshBase): dims = self._dimension return [l + w * d for l, w, d in zip(ls, ws, dims)] + @upper_right.setter + def upper_right(self, upper_right: Iterable[Real]): + cv.check_type('mesh upper_right', upper_right, Iterable, Real) + cv.check_length('mesh upper_right', upper_right, 1, 3) + self._upper_right = upper_right + + if self._width is not None: + self._width = None + warnings.warn("Unsetting width attribute.") + + if self.lower_left is not None and any(np.isclose(self.lower_left, upper_right)): + raise ValueError("Mesh cannot have zero thickness in any dimension") + @property def width(self): if self._width is not None: @@ -199,9 +964,32 @@ class RegularMesh(MeshBase): dims = self._dimension return [(u - l) / d for u, l, d in zip(us, ls, dims)] + @width.setter + def width(self, width: Iterable[Real]): + cv.check_type('mesh width', width, Iterable, Real) + cv.check_length('mesh width', width, 1, 3) + self._width = width + + if self._upper_right is not None: + self._upper_right = None + warnings.warn("Unsetting upper_right attribute.") + @property - def num_mesh_cells(self): - return np.prod(self._dimension) + def volumes(self): + """Return Volumes for every mesh cell + + Returns + ------- + volumes : numpy.ndarray + Volumes + + """ + self._volume_dim_check() + return np.full(self.dimension, np.prod(self.width)) + + @property + def total_volume(self): + return np.prod(self.dimension) * np.prod(self.width) @property def indices(self): @@ -221,53 +1009,43 @@ class RegularMesh(MeshBase): nx, = self.dimension return ((x,) for x in range(1, nx + 1)) - @dimension.setter - def dimension(self, dimension): - cv.check_type('mesh dimension', dimension, Iterable, Integral) - cv.check_length('mesh dimension', dimension, 1, 3) - self._dimension = dimension - - @lower_left.setter - def lower_left(self, lower_left): - cv.check_type('mesh lower_left', lower_left, Iterable, Real) - cv.check_length('mesh lower_left', lower_left, 1, 3) - self._lower_left = lower_left - - @upper_right.setter - def upper_right(self, upper_right): - cv.check_type('mesh upper_right', upper_right, Iterable, Real) - cv.check_length('mesh upper_right', upper_right, 1, 3) - self._upper_right = upper_right - - if self._width is not None: - self._width = None - warnings.warn("Unsetting width attribute.") - - @width.setter - def width(self, width): - cv.check_type('mesh width', width, Iterable, Real) - cv.check_length('mesh width', width, 1, 3) - self._width = width - - if self._upper_right is not None: - self._upper_right = None - warnings.warn("Unsetting upper_right attribute.") + @property + def _grids(self): + ndim = len(self._dimension) + if ndim == 3: + x0, y0, z0 = self.lower_left + x1, y1, z1 = self.upper_right + nx, ny, nz = self.dimension + xarr = np.linspace(x0, x1, nx + 1) + yarr = np.linspace(y0, y1, ny + 1) + zarr = np.linspace(z0, z1, nz + 1) + return (xarr, yarr, zarr) + elif ndim == 2: + x0, y0 = self.lower_left + x1, y1 = self.upper_right + nx, ny = self.dimension + xarr = np.linspace(x0, x1, nx + 1) + yarr = np.linspace(y0, y1, ny + 1) + return (xarr, yarr) + else: + nx, = self.dimension + x0, = self.lower_left + x1, = self.upper_right + return (np.linspace(x0, x1, nx + 1),) def __repr__(self): string = super().__repr__() string += '{0: <16}{1}{2}\n'.format('\tDimensions', '=\t', self.n_dimension) string += '{0: <16}{1}{2}\n'.format('\tVoxels', '=\t', self._dimension) string += '{0: <16}{1}{2}\n'.format('\tLower left', '=\t', self._lower_left) - string += '{0: <16}{1}{2}\n'.format('\tUpper Right', '=\t', self._upper_right) - string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self._width) + string += '{0: <16}{1}{2}\n'.format('\tUpper Right', '=\t', self.upper_right) + string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self.width) return string @classmethod - def from_hdf5(cls, group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) - + def from_hdf5(cls, group: h5py.Group, mesh_id: int, name: str): # Read and assign mesh properties - mesh = cls(mesh_id) + mesh = cls(mesh_id=mesh_id, name=name) mesh.dimension = group['dimension'][()] mesh.lower_left = group['lower_left'][()] if 'width' in group: @@ -280,7 +1058,13 @@ class RegularMesh(MeshBase): return mesh @classmethod - def from_rect_lattice(cls, lattice, division=1, mesh_id=None, name=''): + def from_rect_lattice( + cls, + lattice: 'openmc.RectLattice', + division: int = 1, + mesh_id: int | None = None, + name: str = '' + ): """Create mesh from an existing rectangular lattice Parameters @@ -306,25 +1090,75 @@ class RegularMesh(MeshBase): shape = np.array(lattice.shape) width = lattice.pitch*shape - mesh = cls(mesh_id, name) + mesh = cls(mesh_id=mesh_id, name=name) mesh.lower_left = lattice.lower_left mesh.upper_right = lattice.lower_left + width mesh.dimension = shape*division return mesh + @classmethod + def from_domain( + cls, + domain: HasBoundingBox, + dimension: Sequence[int] | int = 1000, + mesh_id: int | None = None, + name: str = '' + ): + """Create RegularMesh from a domain using its bounding box. + + Parameters + ---------- + domain : HasBoundingBox + The object passed in will be used as a template for this mesh. The + bounding box of the property of the object passed will be used to + set the lower_left and upper_right and of the mesh instance + dimension : Iterable of int | int + The number of mesh cells in total or number of mesh cells in each + direction (x, y, z). If a single integer is provided, the domain + will will be divided into that many mesh cells with roughly equal + lengths in each direction (cubes). + mesh_id : int + Unique identifier for the mesh + name : str + Name of the mesh + + Returns + ------- + openmc.RegularMesh + RegularMesh instance + + """ + if not hasattr(domain, 'bounding_box'): + raise TypeError("Domain must have a bounding_box property") + + mesh = cls(mesh_id=mesh_id, name=name) + mesh.lower_left = domain.bounding_box[0] + mesh.upper_right = domain.bounding_box[1] + if isinstance(dimension, int): + cv.check_greater_than("dimension", dimension, 1, equality=True) + # If a single integer is provided, divide the domain into that many + # mesh cells with roughly equal lengths in each direction + ideal_cube_volume = domain.bounding_box.volume / dimension + ideal_cube_size = ideal_cube_volume ** (1 / 3) + dimension = [ + max(1, int(round(side / ideal_cube_size))) + for side in domain.bounding_box.width + ] + mesh.dimension = dimension + + return mesh + def to_xml_element(self): """Return XML representation of the mesh Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mesh data """ - - element = ET.Element("mesh") - element.set("id", str(self._id)) + element = super().to_xml_element() if self._dimension is not None: subelement = ET.SubElement(element, "dimension") @@ -343,12 +1177,12 @@ class RegularMesh(MeshBase): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate mesh from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -358,31 +1192,27 @@ class RegularMesh(MeshBase): """ mesh_id = int(get_text(elem, 'id')) - mesh = cls(mesh_id) + mesh = cls(mesh_id=mesh_id) - mesh_type = get_text(elem, 'type') - if mesh_type is not None: - mesh.type = mesh_type - - dimension = get_text(elem, 'dimension') + dimension = get_elem_list(elem, "dimension", int) if dimension is not None: - mesh.dimension = [int(x) for x in dimension.split()] + mesh.dimension = dimension - lower_left = get_text(elem, 'lower_left') + lower_left = get_elem_list(elem, "lower_left", float) if lower_left is not None: - mesh.lower_left = [float(x) for x in lower_left.split()] + mesh.lower_left = lower_left - upper_right = get_text(elem, 'upper_right') + upper_right = get_elem_list(elem, "upper_right", float) if upper_right is not None: - mesh.upper_right = [float(x) for x in upper_right.split()] + mesh.upper_right = upper_right - width = get_text(elem, 'width') + width = get_elem_list(elem, "width", float) if width is not None: - mesh.width = [float(x) for x in width.split()] + mesh.width = width return mesh - def build_cells(self, bc=None): + def build_cells(self, bc: str | None = None): """Generates a lattice of universes with the same dimensionality as the mesh object. The individual cells/universes produced will not have material definitions applied and so downstream code @@ -417,7 +1247,7 @@ class RegularMesh(MeshBase): for entry in bc: cv.check_value('bc', entry, _BOUNDARY_TYPES) - n_dim = len(self.dimension) + n_dim = self.n_dimension # Build the cell which will contain the lattice xplanes = [openmc.XPlane(self.lower_left[0], boundary_type=bc[0]), @@ -514,7 +1344,7 @@ def Mesh(*args, **kwargs): return RegularMesh(*args, **kwargs) -class RectilinearMesh(MeshBase): +class RectilinearMesh(StructuredMesh): """A 3D rectilinear Cartesian mesh Parameters @@ -531,22 +1361,25 @@ class RectilinearMesh(MeshBase): name : str Name of the mesh dimension : Iterable of int - The number of mesh cells in each direction. + The number of mesh cells in each direction (x, y, z). n_dimension : int Number of mesh dimensions (always 3 for a RectilinearMesh). - x_grid : Iterable of float - Mesh boundary points along the x-axis. - y_grid : Iterable of float - Mesh boundary points along the y-axis. - z_grid : Iterable of float - Mesh boundary points along the z-axis. + x_grid : numpy.ndarray + 1-D array of mesh boundary points along the x-axis. + y_grid : numpy.ndarray + 1-D array of mesh boundary points along the y-axis. + z_grid : numpy.ndarray + 1-D array of mesh boundary points along the z-axis. indices : Iterable of tuple An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1, 1), ...] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. """ - def __init__(self, mesh_id=None, name=''): + def __init__(self, mesh_id: int = None, name: str = ''): super().__init__(mesh_id, name) self._x_grid = None @@ -567,14 +1400,62 @@ class RectilinearMesh(MeshBase): def x_grid(self): return self._x_grid + @x_grid.setter + def x_grid(self, grid): + cv.check_type('mesh x_grid', grid, Iterable, Real) + self._x_grid = np.asarray(grid, dtype=float) + @property def y_grid(self): return self._y_grid + @y_grid.setter + def y_grid(self, grid): + cv.check_type('mesh y_grid', grid, Iterable, Real) + self._y_grid = np.asarray(grid, dtype=float) + @property def z_grid(self): return self._z_grid + @z_grid.setter + def z_grid(self, grid): + cv.check_type('mesh z_grid', grid, Iterable, Real) + self._z_grid = np.asarray(grid, dtype=float) + + @property + def _grids(self): + return (self.x_grid, self.y_grid, self.z_grid) + + @property + def lower_left(self): + return np.array([self.x_grid[0], self.y_grid[0], self.z_grid[0]]) + + @property + def upper_right(self): + return np.array([self.x_grid[-1], self.y_grid[-1], self.z_grid[-1]]) + + @property + def volumes(self): + """Return Volumes for every mesh cell + + Returns + ------- + volumes : numpy.ndarray + Volumes + + """ + self._volume_dim_check() + V_x = np.diff(self.x_grid) + V_y = np.diff(self.y_grid) + V_z = np.diff(self.z_grid) + + return np.multiply.outer(np.outer(V_x, V_y), V_z) + + @property + def total_volume(self): + return np.sum(self.volumes) + @property def indices(self): nx = len(self.x_grid) - 1 @@ -585,21 +1466,6 @@ class RectilinearMesh(MeshBase): for y in range(1, ny + 1) for x in range(1, nx + 1)) - @x_grid.setter - def x_grid(self, grid): - cv.check_type('mesh x_grid', grid, Iterable, Real) - self._x_grid = grid - - @y_grid.setter - def y_grid(self, grid): - cv.check_type('mesh y_grid', grid, Iterable, Real) - self._y_grid = grid - - @z_grid.setter - def z_grid(self, grid): - cv.check_type('mesh z_grid', grid, Iterable, Real) - self._z_grid = grid - def __repr__(self): fmt = '{0: <16}{1}{2}\n' string = super().__repr__() @@ -622,11 +1488,9 @@ class RectilinearMesh(MeshBase): return string @classmethod - def from_hdf5(cls, group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) - + def from_hdf5(cls, group: h5py.Group, mesh_id: int, name: str): # Read and assign mesh properties - mesh = cls(mesh_id) + mesh = cls(mesh_id=mesh_id, name=name) mesh.x_grid = group['x_grid'][()] mesh.y_grid = group['y_grid'][()] mesh.z_grid = group['z_grid'][()] @@ -634,12 +1498,12 @@ class RectilinearMesh(MeshBase): return mesh @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate a rectilinear mesh from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -648,11 +1512,11 @@ class RectilinearMesh(MeshBase): Rectilinear mesh object """ - id = int(get_text(elem, 'id')) - mesh = cls(id) - mesh.x_grid = [float(x) for x in get_text(elem, 'x_grid').split()] - mesh.y_grid = [float(y) for y in get_text(elem, 'y_grid').split()] - mesh.z_grid = [float(z) for z in get_text(elem, 'z_grid').split()] + mesh_id = int(get_text(elem, 'id')) + mesh = cls(mesh_id=mesh_id) + mesh.x_grid = get_elem_list(elem, "x_grid", float) + mesh.y_grid = get_elem_list(elem, "y_grid", float) + mesh.z_grid = get_elem_list(elem, "z_grid", float) return mesh @@ -661,13 +1525,12 @@ class RectilinearMesh(MeshBase): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mesh data """ - element = ET.Element("mesh") - element.set("id", str(self._id)) + element = super().to_xml_element() element.set("type", "rectilinear") subelement = ET.SubElement(element, "x_grid") @@ -681,11 +1544,24 @@ class RectilinearMesh(MeshBase): return element -class CylindricalMesh(MeshBase): + +class CylindricalMesh(StructuredMesh): """A 3D cylindrical mesh Parameters ---------- + r_grid : numpy.ndarray + 1-D array of mesh boundary points along the r-axis + Requirement is r >= 0. + z_grid : numpy.ndarray + 1-D array of mesh boundary points along the z-axis relative to the + origin. + phi_grid : numpy.ndarray + 1-D array of mesh boundary points along the phi-axis in radians. + The default value is [0, 2π], i.e. the full phi range. + origin : numpy.ndarray + 1-D array of length 3 the (x,y,z) origin of the mesh in + cartesian coordinates mesh_id : int Unique identifier for the mesh name : str @@ -698,29 +1574,51 @@ class CylindricalMesh(MeshBase): name : str Name of the mesh dimension : Iterable of int - The number of mesh cells in each direction. + The number of mesh cells in each direction (r_grid, phi_grid, z_grid). n_dimension : int Number of mesh dimensions (always 3 for a CylindricalMesh). - r_grid : Iterable of float - Mesh boundary points along the r-axis. + r_grid : numpy.ndarray + 1-D array of mesh boundary points along the r-axis. Requirement is r >= 0. - phi_grid : Iterable of float - Mesh boundary points along the phi-axis. + phi_grid : numpy.ndarray + 1-D array of mesh boundary points along the phi-axis in radians. The default value is [0, 2π], i.e. the full phi range. - z_grid : Iterable of float - Mesh boundary points along the z-axis. + z_grid : numpy.ndarray + 1-D array of mesh boundary points along the z-axis relative to the + origin. + origin : numpy.ndarray + 1-D array of length 3 the (x,y,z) origin of the mesh in + cartesian coordinates indices : Iterable of tuple An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1, 1), ...] + lower_left : Iterable of float + The lower-left corner of the structured mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + upper_right : Iterable of float + The upper-right corner of the structured mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. """ - def __init__(self, mesh_id=None, name=''): + def __init__( + self, + r_grid: Sequence[float], + z_grid: Sequence[float], + phi_grid: Sequence[float] = (0, 2*pi), + origin: Sequence[float] = (0., 0., 0.), + mesh_id: int | None = None, + name: str = '', + ): super().__init__(mesh_id, name) - self._r_grid = None - self._phi_grid = [0.0, 2*pi] - self._z_grid = None + self.r_grid = r_grid + self.phi_grid = phi_grid + self.z_grid = z_grid + self.origin = origin @property def dimension(self): @@ -732,18 +1630,56 @@ class CylindricalMesh(MeshBase): def n_dimension(self): return 3 + @property + def origin(self): + return self._origin + + @origin.setter + def origin(self, coords): + cv.check_type('mesh origin', coords, Iterable, Real) + cv.check_length("mesh origin", coords, 3) + self._origin = np.asarray(coords) + @property def r_grid(self): return self._r_grid + @r_grid.setter + def r_grid(self, grid): + cv.check_type('mesh r_grid', grid, Iterable, Real) + cv.check_length('mesh r_grid', grid, 2) + cv.check_increasing('mesh r_grid', grid) + self._r_grid = np.asarray(grid, dtype=float) + @property def phi_grid(self): return self._phi_grid + @phi_grid.setter + def phi_grid(self, grid): + cv.check_type('mesh phi_grid', grid, Iterable, Real) + cv.check_length('mesh phi_grid', grid, 2) + cv.check_increasing('mesh phi_grid', grid) + grid = np.asarray(grid, dtype=float) + if np.any((grid < 0.0) | (grid > 2*pi)): + raise ValueError("phi_grid values must be in [0, 2π].") + self._phi_grid = grid + @property def z_grid(self): return self._z_grid + @z_grid.setter + def z_grid(self, grid): + cv.check_type('mesh z_grid', grid, Iterable, Real) + cv.check_length('mesh z_grid', grid, 2) + cv.check_increasing('mesh z_grid', grid) + self._z_grid = np.asarray(grid, dtype=float) + + @property + def _grids(self): + return (self.r_grid, self.phi_grid, self.z_grid) + @property def indices(self): nr, np, nz = self.dimension @@ -754,25 +1690,27 @@ class CylindricalMesh(MeshBase): for p in range(1, np + 1) for r in range(1, nr + 1)) - @r_grid.setter - def r_grid(self, grid): - cv.check_type('mesh r_grid', grid, Iterable, Real) - self._r_grid = grid + @property + def lower_left(self): + return np.array(( + self.origin[0] - self.r_grid[-1], + self.origin[1] - self.r_grid[-1], + self.origin[2] + self.z_grid[0] + )) - @phi_grid.setter - def phi_grid(self, grid): - cv.check_type('mesh phi_grid', grid, Iterable, Real) - self._phi_grid = np.asarray(grid) - - @z_grid.setter - def z_grid(self, grid): - cv.check_type('mesh z_grid', grid, Iterable, Real) - self._z_grid = np.asarray(grid) + @property + def upper_right(self): + return np.array(( + self.origin[0] + self.r_grid[-1], + self.origin[1] + self.r_grid[-1], + self.origin[2] + self.z_grid[-1] + )) def __repr__(self): fmt = '{0: <16}{1}{2}\n' string = super().__repr__() string += fmt.format('\tDimensions', '=\t', self.n_dimension) + string += fmt.format('\tOrigin', '=\t', self.origin) r_grid_str = str(self._r_grid) if self._r_grid is None else len(self._r_grid) string += fmt.format('\tN R pnts:', '=\t', r_grid_str) if self._r_grid is not None: @@ -790,15 +1728,160 @@ class CylindricalMesh(MeshBase): string += fmt.format('\tZ Max:', '=\t', self._z_grid[-1]) return string - @classmethod - def from_hdf5(cls, group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + def get_indices_at_coords( + self, + coords: Sequence[float] + ) -> tuple[int, int, int]: + """Finds the index of the mesh voxel at the specified x,y,z coordinates. + .. versionadded:: 0.15.0 + + Parameters + ---------- + coords : Sequence[float] + The x, y, z axis coordinates + + Returns + ------- + tuple[int, int, int] + The r, phi, z indices + + """ + r_value_from_origin = sqrt((coords[0]-self.origin[0])**2 + (coords[1]-self.origin[1])**2) + + if r_value_from_origin < self.r_grid[0] or r_value_from_origin > self.r_grid[-1]: + raise ValueError( + f'The specified x, y ({coords[0]}, {coords[1]}) combine to give an r value of ' + f'{r_value_from_origin} from the origin of {self.origin}.which ' + f'is outside the origin absolute r grid values {self.r_grid}.' + ) + + r_index = np.searchsorted(self.r_grid, r_value_from_origin) - 1 + + z_grid_values = np.array(self.z_grid) + self.origin[2] + + if coords[2] < z_grid_values[0] or coords[2] > z_grid_values[-1]: + raise ValueError( + f'The specified z value ({coords[2]}) from the z origin of ' + f'{self.origin[-1]} is outside of the absolute z grid range {z_grid_values}.' + ) + + z_index = np.argmax(z_grid_values > coords[2]) - 1 + + delta_x = coords[0] - self.origin[0] + delta_y = coords[1] - self.origin[1] + # atan2 returns values in -pi to +pi range + phi_value = atan2(delta_y, delta_x) + if delta_x < 0 and delta_y < 0: + # returned phi_value anticlockwise and negative + phi_value += 2 * pi + if delta_x > 0 and delta_y < 0: + # returned phi_value anticlockwise and negative + phi_value += 2 * pi + + phi_grid_values = np.array(self.phi_grid) + + if phi_value < phi_grid_values[0] or phi_value > phi_grid_values[-1]: + raise ValueError( + f'The phi value ({phi_value}) resulting from the specified x, y ' + f'values is outside of the absolute phi grid range {phi_grid_values}.' + ) + phi_index = np.argmax(phi_grid_values > phi_value) - 1 + + return (r_index, phi_index, z_index) + + @classmethod + def from_hdf5(cls, group: h5py.Group, mesh_id: int, name: str): # Read and assign mesh properties - mesh = cls(mesh_id) - mesh.r_grid = group['r_grid'][()] - mesh.phi_grid = group['phi_grid'][()] - mesh.z_grid = group['z_grid'][()] + mesh = cls( + mesh_id=mesh_id, + name=name, + r_grid = group['r_grid'][()], + phi_grid = group['phi_grid'][()], + z_grid = group['z_grid'][()], + ) + if 'origin' in group: + mesh.origin = group['origin'][()] + + return mesh + + @classmethod + def from_domain( + cls, + domain: HasBoundingBox, + dimension: Sequence[int] = (10, 10, 10), + mesh_id: int | None = None, + phi_grid_bounds: Sequence[float] = (0.0, 2*pi), + name: str = '', + enclose_domain: bool = False + ): + """Create CylindricalMesh from a domain using its bounding box. + + Parameters + ---------- + domain : HasBoundingBox + The object passed in will be used as a template for this mesh. The + bounding box of the property of the object passed will be used to + set the r_grid, z_grid ranges. + dimension : Iterable of int + The number of equally spaced mesh cells in each direction (r_grid, + phi_grid, z_grid) + mesh_id : int + Unique identifier for the mesh + phi_grid_bounds : numpy.ndarray + Mesh bounds points along the phi-axis in radians. The default value + is (0, 2π), i.e., the full phi range. + name : str + Name of the mesh + enclose_domain : bool + If True, the mesh will encompass the bounding box of the domain. If + False, the mesh will be inscribed within the domain's bounding box. + + Returns + ------- + openmc.CylindricalMesh + CylindricalMesh instance + + """ + if not hasattr(domain, 'bounding_box'): + raise TypeError("Domain must have a bounding_box property") + + # loaded once to avoid recalculating bounding box + cached_bb = domain.bounding_box + + if enclose_domain: + outer_radius = 0.5 * np.linalg.norm(cached_bb.width[:2]) + else: + outer_radius = 0.5 * min(cached_bb.width[:2]) + + r_grid = np.linspace( + 0, + outer_radius, + num=dimension[0]+1 + ) + phi_grid = np.linspace( + phi_grid_bounds[0], + phi_grid_bounds[1], + num=dimension[1]+1 + ) + z_grid = np.linspace( + cached_bb[0][2], + cached_bb[1][2], + num=dimension[2]+1 + ) + origin = (cached_bb.center[0], cached_bb.center[1], z_grid[0]) + + # make z-grid relative to the origin + z_grid -= origin[2] + + mesh = cls( + r_grid=r_grid, + z_grid=z_grid, + phi_grid=phi_grid, + mesh_id=mesh_id, + name=name, + origin=origin + ) return mesh @@ -807,13 +1890,12 @@ class CylindricalMesh(MeshBase): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mesh data """ - element = ET.Element("mesh") - element.set("id", str(self._id)) + element = super().to_xml_element() element.set("type", "cylindrical") subelement = ET.SubElement(element, "r_grid") @@ -825,15 +1907,18 @@ class CylindricalMesh(MeshBase): subelement = ET.SubElement(element, "z_grid") subelement.text = ' '.join(map(str, self.z_grid)) + subelement = ET.SubElement(element, "origin") + subelement.text = ' '.join(map(str, self.origin)) + return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate a cylindrical mesh from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -844,13 +1929,18 @@ class CylindricalMesh(MeshBase): """ mesh_id = int(get_text(elem, 'id')) - mesh = cls(mesh_id) - mesh.r_grid = [float(x) for x in get_text(elem, "r_grid").split()] - mesh.phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()] - mesh.z_grid = [float(x) for x in get_text(elem, "z_grid").split()] + mesh = cls( + r_grid = get_elem_list(elem, "r_grid", float), + phi_grid = get_elem_list(elem, "phi_grid", float), + z_grid = get_elem_list(elem, "z_grid", float), + origin = get_elem_list(elem, "origin", float) or [0., 0., 0.], + mesh_id=mesh_id, + ) + return mesh - def calc_mesh_volumes(self): + @property + def volumes(self): """Return Volumes for every mesh cell Returns @@ -859,19 +1949,65 @@ class CylindricalMesh(MeshBase): Volumes """ - + self._volume_dim_check() V_r = np.diff(np.asarray(self.r_grid)**2 / 2) V_p = np.diff(self.phi_grid) V_z = np.diff(self.z_grid) return np.multiply.outer(np.outer(V_r, V_p), V_z) + @property + def vertices(self): + warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + return self._convert_to_cartesian(self.vertices_cylindrical, self.origin) -class SphericalMesh(MeshBase): + @property + def vertices_cylindrical(self): + """Returns vertices of the mesh in cylindrical coordinates. + """ + return super().vertices + + @property + def centroids(self): + warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + return self._convert_to_cartesian(self.centroids_cylindrical, self.origin) + + @property + def centroids_cylindrical(self): + """Returns centroids of the mesh in cylindrical coordinates. + """ + return super().centroids + + @staticmethod + def _convert_to_cartesian(arr, origin: Sequence[float]): + """Converts an array with r, phi, z values in the last dimension (shape (..., 3)) + to Cartesian coordinates. + """ + x = arr[..., 0] * np.cos(arr[..., 1]) + origin[0] + y = arr[..., 0] * np.sin(arr[..., 1]) + origin[1] + arr[..., 0] = x + arr[..., 1] = y + arr[..., 2] += origin[2] + return arr + + +class SphericalMesh(StructuredMesh): """A 3D spherical mesh Parameters ---------- + r_grid : numpy.ndarray + 1-D array of mesh boundary points along the r-axis. + Requirement is r >= 0. + phi_grid : numpy.ndarray + 1-D array of mesh boundary points along the phi-axis in radians. + The default value is [0, 2π], i.e. the full phi range. + theta_grid : numpy.ndarray + 1-D array of mesh boundary points along the theta-axis in radians. + The default value is [0, π], i.e. the full theta range. + origin : numpy.ndarray + 1-D array of length 3 the (x,y,z) origin of the mesh in + cartesian coordinates mesh_id : int Unique identifier for the mesh name : str @@ -884,30 +2020,52 @@ class SphericalMesh(MeshBase): name : str Name of the mesh dimension : Iterable of int - The number of mesh cells in each direction. + The number of mesh cells in each direction (r_grid, + theta_grid, phi_grid). n_dimension : int Number of mesh dimensions (always 3 for a SphericalMesh). - r_grid : Iterable of float - Mesh boundary points along the r-axis. + r_grid : numpy.ndarray + 1-D array of mesh boundary points along the r-axis. Requirement is r >= 0. - theta_grid : Iterable of float - Mesh boundary points along the theta-axis in degrees. + theta_grid : numpy.ndarray + 1-D array of mesh boundary points along the theta-axis in radians. The default value is [0, π], i.e. the full theta range. - phi_grid : Iterable of float - Mesh boundary points along the phi-axis in degrees. + phi_grid : numpy.ndarray + 1-D array of mesh boundary points along the phi-axis in radians. The default value is [0, 2π], i.e. the full phi range. + origin : numpy.ndarray + 1-D array of length 3 the (x,y,z) origin of the mesh in + cartesian coordinates indices : Iterable of tuple An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1, 1), ...] + lower_left : numpy.ndarray + The lower-left corner of the structured mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + upper_right : numpy.ndarray + The upper-right corner of the structured mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. """ - def __init__(self, mesh_id=None, name=''): + def __init__( + self, + r_grid: Sequence[float], + phi_grid: Sequence[float] = (0, 2*pi), + theta_grid: Sequence[float] = (0, pi), + origin: Sequence[float] = (0., 0., 0.), + mesh_id: int | None = None, + name: str = '', + ): super().__init__(mesh_id, name) - self._r_grid = None - self._theta_grid = [0, pi] - self._phi_grid = [0, 2*pi] + self.r_grid = r_grid + self.theta_grid = theta_grid + self.phi_grid = phi_grid + self.origin = origin @property def dimension(self): @@ -919,18 +2077,59 @@ class SphericalMesh(MeshBase): def n_dimension(self): return 3 + @property + def origin(self): + return self._origin + + @origin.setter + def origin(self, coords): + cv.check_type('mesh origin', coords, Iterable, Real) + cv.check_length("mesh origin", coords, 3) + self._origin = np.asarray(coords, dtype=float) + @property def r_grid(self): return self._r_grid + @r_grid.setter + def r_grid(self, grid): + cv.check_type('mesh r_grid', grid, Iterable, Real) + cv.check_length('mesh r_grid', grid, 2) + cv.check_increasing('mesh r_grid', grid) + self._r_grid = np.asarray(grid, dtype=float) + @property def theta_grid(self): return self._theta_grid + @theta_grid.setter + def theta_grid(self, grid): + cv.check_type('mesh theta_grid', grid, Iterable, Real) + cv.check_length('mesh theta_grid', grid, 2) + cv.check_increasing('mesh theta_grid', grid) + grid = np.asarray(grid, dtype=float) + if np.any((grid < 0.0) | (grid > pi)): + raise ValueError("theta_grid values must be in [0, π].") + self._theta_grid = grid + @property def phi_grid(self): return self._phi_grid + @phi_grid.setter + def phi_grid(self, grid): + cv.check_type('mesh phi_grid', grid, Iterable, Real) + cv.check_length('mesh phi_grid', grid, 2) + cv.check_increasing('mesh phi_grid', grid) + grid = np.asarray(grid, dtype=float) + if np.any((grid < 0.0) | (grid > 2*pi)): + raise ValueError("phi_grid values must be in [0, 2π].") + self._phi_grid = grid + + @property + def _grids(self): + return (self.r_grid, self.theta_grid, self.phi_grid) + @property def indices(self): nr, nt, np = self.dimension @@ -941,66 +2140,135 @@ class SphericalMesh(MeshBase): for t in range(1, nt + 1) for r in range(1, nr + 1)) - @r_grid.setter - def r_grid(self, grid): - cv.check_type('mesh r_grid', grid, Iterable, Real) - self._r_grid = grid + @property + def lower_left(self): + r = self.r_grid[-1] + return np.array((self.origin[0] - r, self.origin[1] - r, self.origin[2] - r)) - @theta_grid.setter - def theta_grid(self, grid): - cv.check_type('mesh theta_grid', grid, Iterable, Real) - self._theta_grid = np.asarray(grid) - - @phi_grid.setter - def phi_grid(self, grid): - cv.check_type('mesh phi_grid', grid, Iterable, Real) - self._phi_grid = np.asarray(grid) + @property + def upper_right(self): + r = self.r_grid[-1] + return np.array((self.origin[0] + r, self.origin[1] + r, self.origin[2] + r)) def __repr__(self): fmt = '{0: <16}{1}{2}\n' string = super().__repr__() string += fmt.format('\tDimensions', '=\t', self.n_dimension) - r_grid_str = str(self._r_grid) if not self._r_grid else len(self._r_grid) + string += fmt.format('\tOrigin', '=\t', self.origin) + r_grid_str = str(self._r_grid) if self._r_grid is None else len(self._r_grid) string += fmt.format('\tN R pnts:', '=\t', r_grid_str) - if self._r_grid: + if self._r_grid is not None: string += fmt.format('\tR Min:', '=\t', self._r_grid[0]) string += fmt.format('\tR Max:', '=\t', self._r_grid[-1]) - theta_grid_str = str(self._theta_grid) if not self._theta_grid else len(self._theta_grid) + theta_grid_str = str(self._theta_grid) if self._theta_grid is None else len(self._theta_grid) string += fmt.format('\tN Theta pnts:', '=\t', theta_grid_str) - if self._theta_grid: + if self._theta_grid is not None: string += fmt.format('\tTheta Min:', '=\t', self._theta_grid[0]) string += fmt.format('\tTheta Max:', '=\t', self._theta_grid[-1]) - phi_grid_str = str(self._phi_grid) if not self._phi_grid else len(self._phi_grid) + phi_grid_str = str(self._phi_grid) if self._phi_grid is None else len(self._phi_grid) string += fmt.format('\tN Phi pnts:', '=\t', phi_grid_str) - if self._phi_grid: + if self._phi_grid is not None: string += fmt.format('\tPhi Min:', '=\t', self._phi_grid[0]) string += fmt.format('\tPhi Max:', '=\t', self._phi_grid[-1]) return string @classmethod - def from_hdf5(cls, group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) - + def from_hdf5(cls, group: h5py.Group, mesh_id: int, name: str): # Read and assign mesh properties - mesh = cls(mesh_id) - mesh.r_grid = group['r_grid'][()] - mesh.theta_grid = group['theta_grid'][()] - mesh.phi_grid = group['phi_grid'][()] + mesh = cls( + r_grid = group['r_grid'][()], + theta_grid = group['theta_grid'][()], + phi_grid = group['phi_grid'][()], + mesh_id=mesh_id, + name=name + ) + if 'origin' in group: + mesh.origin = group['origin'][()] return mesh + @classmethod + def from_domain( + cls, + domain: HasBoundingBox, + dimension: Sequence[int] = (10, 10, 10), + mesh_id: int | None = None, + phi_grid_bounds: Sequence[float] = (0.0, 2*pi), + theta_grid_bounds: Sequence[float] = (0.0, pi), + name: str = '', + enclose_domain: bool = False + ): + """Create SphericalMesh from a domain using its bounding box. + + Parameters + ---------- + domain : HasBoundingBox + The object passed in will be used as a template for this mesh. The + bounding box of the property of the object passed will be used to + set the r_grid, phi_grid, and theta_grid ranges. + dimension : Iterable of int + The number of equally spaced mesh cells in each direction (r_grid, + phi_grid, theta_grid). Spacing is in angular space (radians) for + phi and theta, and in absolute space for r. + mesh_id : int + Unique identifier for the mesh + phi_grid_bounds : numpy.ndarray + Mesh bounds points along the phi-axis in radians. The default value + is (0, 2π), i.e., the full phi range. + theta_grid_bounds : numpy.ndarray + Mesh bounds points along the theta-axis in radians. The default value + is (0, π), i.e., the full theta range. + name : str + Name of the mesh + enclose_domain : bool + If True, the mesh will encompass the bounding box of the domain. If + False, the mesh will be inscribed within the domain's bounding box. + + Returns + ------- + openmc.SphericalMesh + SphericalMesh instance + + """ + if not hasattr(domain, 'bounding_box'): + raise TypeError("Domain must have a bounding_box property") + + # loaded once to avoid recalculating bounding box + cached_bb = domain.bounding_box + + if enclose_domain: + outer_radius = 0.5 * np.linalg.norm(cached_bb.width) + else: + outer_radius = 0.5 * min(cached_bb.width) + + r_grid = np.linspace(0, outer_radius, num=dimension[0] + 1) + theta_grid = np.linspace( + theta_grid_bounds[0], + theta_grid_bounds[1], + num=dimension[1]+1 + ) + phi_grid = np.linspace( + phi_grid_bounds[0], + phi_grid_bounds[1], + num=dimension[2]+1 + ) + origin = np.array([ + cached_bb.center[0], cached_bb.center[1], cached_bb.center[2]]) + + return cls(r_grid=r_grid, phi_grid=phi_grid, theta_grid=theta_grid, + origin=origin, mesh_id=mesh_id, name=name) + def to_xml_element(self): """Return XML representation of the mesh Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mesh data """ - element = ET.Element("mesh") - element.set("id", str(self._id)) + element = super().to_xml_element() element.set("type", "spherical") subelement = ET.SubElement(element, "r_grid") @@ -1012,15 +2280,18 @@ class SphericalMesh(MeshBase): subelement = ET.SubElement(element, "phi_grid") subelement.text = ' '.join(map(str, self.phi_grid)) + subelement = ET.SubElement(element, "origin") + subelement.text = ' '.join(map(str, self.origin)) + return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate a spherical mesh from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -1029,15 +2300,19 @@ class SphericalMesh(MeshBase): Spherical mesh object """ - mesh_id = int(get_text(elem, 'id')) - mesh = cls(mesh_id) - mesh.r_grid = [float(x) for x in get_text(elem, "r_grid").split()] - mesh.theta_grid = [float(x) for x in get_text(elem, "theta_grid").split()] - mesh.phi_grid = [float(x) for x in get_text(elem, "phi_grid").split()] + mesh = cls( + mesh_id=mesh_id, + r_grid = get_elem_list(elem, "r_grid", float), + theta_grid = get_elem_list(elem, "theta_grid", float), + phi_grid = get_elem_list(elem, "phi_grid", float), + origin = get_elem_list(elem, "origin", float) or [0., 0., 0.], + ) + return mesh - def calc_mesh_volumes(self): + @property + def volumes(self): """Return Volumes for every mesh cell Returns @@ -1046,13 +2321,61 @@ class SphericalMesh(MeshBase): Volumes """ - + self._volume_dim_check() V_r = np.diff(np.asarray(self.r_grid)**3 / 3) V_t = np.diff(-np.cos(self.theta_grid)) V_p = np.diff(self.phi_grid) return np.multiply.outer(np.outer(V_r, V_t), V_p) + @property + def vertices(self): + warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + return self._convert_to_cartesian(self.vertices_spherical, self.origin) + + @property + def vertices_spherical(self): + """Returns vertices of the mesh in cylindrical coordinates. + """ + return super().vertices + + @property + def centroids(self): + warnings.warn('Cartesian coordinates are returned from this property as of version 0.14.0') + return self._convert_to_cartesian(self.centroids_spherical, self.origin) + + @property + def centroids_spherical(self): + """Returns centroids of the mesh in cylindrical coordinates. + """ + return super().centroids + + + @staticmethod + def _convert_to_cartesian(arr, origin: Sequence[float]): + """Converts an array with r, theta, phi values in the last dimension (shape (..., 3)) + to Cartesian coordinates. + """ + r_xy = arr[..., 0] * np.sin(arr[..., 1]) + x = r_xy * np.cos(arr[..., 2]) + y = r_xy * np.sin(arr[..., 2]) + z = arr[..., 0] * np.cos(arr[..., 1]) + arr[..., 0] = x + origin[0] + arr[..., 1] = y + origin[1] + arr[..., 2] = z + origin[2] + return arr + + +def require_statepoint_data(func): + @wraps(func) + def wrapper(self: UnstructuredMesh, *args, **kwargs): + if not self._has_statepoint_data: + raise AttributeError(f'The "{func.__name__}" property requires ' + 'information about this mesh to be loaded ' + 'from a statepoint file.') + return func(self, *args, **kwargs) + return wrapper + class UnstructuredMesh(MeshBase): """A 3D unstructured mesh @@ -1064,8 +2387,10 @@ class UnstructuredMesh(MeshBase): Parameters ---------- - filename : str - Location of the unstructured mesh file + filename : path-like + Location of the unstructured mesh file. Supported files for 'moab' + library are .h5 and .vtk. Supported files for 'libmesh' library are + exodus mesh files .exo. library : {'moab', 'libmesh'} Mesh library used for the unstructured mesh tally mesh_id : int @@ -1074,6 +2399,11 @@ class UnstructuredMesh(MeshBase): Name of the mesh length_multiplier: float Constant multiplier to apply to mesh coordinates + options : str, optional + Special options that control spatial search data structures used. This + is currently only used to set `parameters + `_ for MOAB's AdaptiveKDTree. If + None, OpenMC internally uses a default of "MAX_DEPTH=20;PLANE_SET=2;". Attributes ---------- @@ -1087,30 +2417,57 @@ class UnstructuredMesh(MeshBase): Multiplicative factor to apply to mesh coordinates library : {'moab', 'libmesh'} Mesh library used for the unstructured mesh tally + options : str + Special options that control spatial search data structures used. This + is currently only used to set `parameters + `_ for MOAB's AdaptiveKDTree. If + None, OpenMC internally uses a default of "MAX_DEPTH=20;PLANE_SET=2;". output : bool - Indicates whether or not automatic tally output should - be generated for this mesh - specified_length_multiplier: bool - Indicates whether a non-unity length multiplier has been - applied to this mesh + Indicates whether or not automatic tally output should be generated for + this mesh volumes : Iterable of float Volumes of the unstructured mesh elements + centroids : numpy.ndarray + Centroids of the mesh elements with array shape (n_elements, 3) + + vertices : numpy.ndarray + Coordinates of the mesh vertices with array shape (n_elements, 3) + + .. versionadded:: 0.13.1 + connectivity : numpy.ndarray + Connectivity of the elements with array shape (n_elements, 8) + + .. versionadded:: 0.13.1 + element_types : Iterable of integers + Mesh element types + + .. versionadded:: 0.13.1 total_volume : float Volume of the unstructured mesh in total - centroids : Iterable of tuple - An iterable of element centroid coordinates, e.g. [(0.0, 0.0, 0.0), - (1.0, 1.0, 1.0), ...] + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the mesh as defined by the upper-right and + lower-left coordinates. + """ - def __init__(self, filename, library, mesh_id=None, name='', - length_multiplier=1.0): + + _UNSUPPORTED_ELEM = -1 + _LINEAR_TET = 0 + _LINEAR_HEX = 1 + + def __init__(self, filename: PathLike, library: str, mesh_id: int | None = None, + name: str = '', length_multiplier: float = 1.0, + options: str | None = None): super().__init__(mesh_id, name) self.filename = filename self._volumes = None - self._centroids = None + self._n_elements = None + self._conectivity = None + self._vertices = None self.library = library - self._output = True - self._specified_length_multiplier = False + self._output = False self.length_multiplier = length_multiplier + self.options = options + self._has_statepoint_data = False @property def filename(self): @@ -1118,24 +2475,34 @@ class UnstructuredMesh(MeshBase): @filename.setter def filename(self, filename): - cv.check_type('Unstructured Mesh filename', filename, str) - self._filename = filename + cv.check_type('Unstructured Mesh filename', filename, PathLike) + self._filename = input_path(filename) @property def library(self): return self._library @library.setter - def library(self, lib): + def library(self, lib: str): cv.check_value('Unstructured mesh library', lib, ('moab', 'libmesh')) self._library = lib @property + def options(self) -> str | None: + return self._options + + @options.setter + def options(self, options: str | None): + cv.check_type('options', options, (str, type(None))) + self._options = options + + @property + @require_statepoint_data def size(self): return self._size @size.setter - def size(self, size): + def size(self, size: int): cv.check_type("Unstructured mesh size", size, Integral) self._size = size @@ -1144,39 +2511,66 @@ class UnstructuredMesh(MeshBase): return self._output @output.setter - def output(self, val): + def output(self, val: bool): cv.check_type("Unstructured mesh output value", val, bool) self._output = val @property + @require_statepoint_data def volumes(self): + """Return Volumes for every mesh cell if + populated by a StatePoint file + + Returns + ------- + volumes : numpy.ndarray + Volumes + + """ return self._volumes @volumes.setter - def volumes(self, volumes): + def volumes(self, volumes: Iterable[Real]): cv.check_type("Unstructured mesh volumes", volumes, Iterable, Real) self._volumes = volumes @property + @require_statepoint_data def total_volume(self): return np.sum(self.volumes) @property - def centroids(self): - return self._centroids + @require_statepoint_data + def vertices(self): + return self._vertices @property + @require_statepoint_data + def connectivity(self): + return self._connectivity + + @property + @require_statepoint_data + def element_types(self): + return self._element_types + + @property + @require_statepoint_data + def centroids(self): + return np.array([self.centroid(i) for i in range(self.n_elements)]) + + @property + @require_statepoint_data def n_elements(self): - if self._centroids is None: + if self._n_elements is None: raise RuntimeError("No information about this mesh has " "been loaded from a statepoint file.") - return len(self._centroids) + return self._n_elements - @centroids.setter - def centroids(self, centroids): - cv.check_type("Unstructured mesh centroids", centroids, - Iterable, Real) - self._centroids = centroids + @n_elements.setter + def n_elements(self, val: int): + cv.check_type('Number of elements', val, Integral) + self._n_elements = val @property def length_multiplier(self): @@ -1185,26 +2579,78 @@ class UnstructuredMesh(MeshBase): @length_multiplier.setter def length_multiplier(self, length_multiplier): cv.check_type("Unstructured mesh length multiplier", - length_multiplier, - Real) + length_multiplier, + Real) self._length_multiplier = length_multiplier - if (self._length_multiplier != 1.0): - self._specified_length_multiplier = True + @property + def dimension(self): + return (self.n_elements,) + + @property + def n_dimension(self): + return 3 + + @property + @require_statepoint_data + def indices(self): + return [(i,) for i in range(self.n_elements)] + + @property + def has_statepoint_data(self) -> bool: + return self._has_statepoint_data def __repr__(self): string = super().__repr__() string += '{: <16}=\t{}\n'.format('\tFilename', self.filename) - string += '{: <16}=\t{}\n'.format('\tMesh Library', self.mesh_lib) + string += '{: <16}=\t{}\n'.format('\tMesh Library', self.library) + if self.length_multiplier != 1.0: + string += '{: <16}=\t{}\n'.format('\tLength multiplier', + self.length_multiplier) + if self.options is not None: + string += '{: <16}=\t{}\n'.format('\tOptions', self.options) return string - def write_data_to_vtk(self, filename, datasets, volume_normalization=True): - """Map data to the unstructured mesh element centroids - to create a VTK point-cloud dataset. + @property + @require_statepoint_data + def lower_left(self): + return self.vertices.min(axis=0) + + @property + @require_statepoint_data + def upper_right(self): + return self.vertices.max(axis=0) + + @require_statepoint_data + def centroid(self, bin: int): + """Return the vertex averaged centroid of an element Parameters ---------- - filename : str + bin : int + Bin ID for the returned centroid + + Returns + ------- + numpy.ndarray + x, y, z values of the element centroid + + """ + conn = self.connectivity[bin] + # remove invalid connectivity values + conn = conn[conn >= 0] + coords = self.vertices[conn] + return coords.mean(axis=0) + + def write_vtk_mesh(self, **kwargs): + """Map data to unstructured VTK mesh elements. + + .. deprecated:: 0.13 + Use :func:`UnstructuredMesh.write_data_to_vtk` instead. + + Parameters + ---------- + filename : str or pathlib.Path Name of the VTK file to write. datasets : dict Dictionary whose keys are the data labels @@ -1213,88 +2659,148 @@ class UnstructuredMesh(MeshBase): Whether or not to normalize the data by the volume of the mesh elements """ + warnings.warn( + "The 'UnstructuredMesh.write_vtk_mesh' method has been renamed " + "to 'write_data_to_vtk' and will be removed in a future version " + " of OpenMC.", FutureWarning + ) + self.write_data_to_vtk(**kwargs) - import vtk - from vtk.util import numpy_support as vtk_npsup + def write_data_to_vtk( + self, + filename: PathLike | None = None, + datasets: dict | None = None, + volume_normalization: bool = True, + ): + """Map data to unstructured VTK mesh elements. - if self.centroids is None: - raise RuntimeError("No centroid information is present on this " - "unstructured mesh. Please load this " - "information from a relevant statepoint file.") + If filename is None, then a filename will be generated based on the mesh + ID, and exported to VTK format. - if self.volumes is None and volume_normalization: - raise RuntimeError("No volume data is present on this " - "unstructured mesh. Please load the " - " mesh information from a statepoint file.") + Parameters + ---------- + filename : str or pathlib.Path + Name of the VTK file to write. If the filename ends in '.vtu' then a + binary VTU format file will be written, if the filename ends in + '.vtk' then a legacy VTK file will be written. + datasets : dict + Dictionary whose keys are the data labels and values are numpy + appropriately sized arrays of the data + volume_normalization : bool + Whether or not to normalize the data by the volume of the mesh + elements + """ + from vtkmodules.util import numpy_support + from vtkmodules import vtkCommonCore + from vtkmodules import vtkCommonDataModel + from vtkmodules import vtkIOLegacy + from vtkmodules import vtkIOXML - # check that the data sets are appropriately sized - for label, dataset in datasets.items(): - if isinstance(dataset, np.ndarray): - assert dataset.size == self.n_elements + if self.connectivity is None or self.vertices is None: + raise RuntimeError( + "This mesh has not been loaded from a statepoint file." + ) + + if filename is None: + filename = f"mesh_{self.id}.vtk" + + if Path(filename).suffix == ".vtk": + writer = vtkIOLegacy.vtkUnstructuredGridWriter() + + elif Path(filename).suffix == ".vtu": + writer = vtkIOXML.vtkXMLUnstructuredGridWriter() + writer.SetCompressorTypeToZLib() + writer.SetDataModeToBinary() + + writer.SetFileName(str(filename)) + + grid = vtkCommonDataModel.vtkUnstructuredGrid() + + points = vtkCommonCore.vtkPoints() + points.SetData(numpy_support.numpy_to_vtk(self.vertices)) + grid.SetPoints(points) + + n_skipped = 0 + for elem_type, conn in zip(self.element_types, self.connectivity): + if elem_type == self._LINEAR_TET: + elem = vtkCommonDataModel.vtkTetra() + elif elem_type == self._LINEAR_HEX: + elem = vtkCommonDataModel.vtkHexahedron() + elif elem_type == self._UNSUPPORTED_ELEM: + n_skipped += 1 + continue else: - assert len(dataset) == self.n_elements - cv.check_type('label', label, str) + raise RuntimeError(f"Invalid element type {elem_type} found") - # create data arrays for the cells/points - cell_dim = 1 - vertices = vtk.vtkCellArray() - points = vtk.vtkPoints() + for i, c in enumerate(conn): + if c == -1: + break + elem.GetPointIds().SetId(i, c) - for centroid in self.centroids: - # create a point for each centroid - point_id = points.InsertNextPoint(centroid * self.length_multiplier) - # create a cell of type "Vertex" for each point - cell_id = vertices.InsertNextCell(cell_dim, (point_id,)) + grid.InsertNextCell(elem.GetCellType(), elem.GetPointIds()) - # create a VTK data object - poly_data = vtk.vtkPolyData() - poly_data.SetPoints(points) - poly_data.SetVerts(vertices) + if n_skipped > 0: + warnings.warn( + f"{n_skipped} elements were not written because " + "they are not of type linear tet/hex" + ) - # strange VTK nuance: - # data must be held in some container - # until the vtk file is written - data_holder = [] - - # create VTK arrays for each of - # the data sets - for label, dataset in datasets.items(): - dataset = np.asarray(dataset).flatten() + # check that datasets are the correct size + datasets_out = [] + if datasets is not None: + for name, data in datasets.items(): + if data.shape != self.dimension: + raise ValueError( + f'Cannot apply dataset "{name}" with ' + f"shape {data.shape} to mesh {self.id} " + f"with dimensions {self.dimension}" + ) if volume_normalization: - dataset /= self.volumes.flatten() + for name, data in datasets.items(): + if np.issubdtype(data.dtype, np.integer): + warnings.warn( + f'Integer data set "{name}" will ' + "not be volume-normalized." + ) + continue + data /= self.volumes - array = vtk.vtkDoubleArray() - array.SetName(label) - array.SetNumberOfComponents(1) - array.SetArray(vtk_npsup.numpy_to_vtk(dataset), - dataset.size, - True) + # add data to the mesh + for name, data in datasets.items(): + datasets_out.append(data) + arr = vtkCommonCore.vtkDoubleArray() + arr.SetName(name) + arr.SetNumberOfTuples(data.size) - data_holder.append(dataset) - poly_data.GetPointData().AddArray(array) + for i in range(data.size): + arr.SetTuple1(i, data.flat[i]) + grid.GetCellData().AddArray(arr) - # set filename - if not filename.endswith(".vtk"): - filename += ".vtk" + writer.SetInputData(grid) - writer = vtk.vtkGenericDataObjectWriter() - writer.SetFileName(filename) - writer.SetInputData(poly_data) writer.Write() @classmethod - def from_hdf5(cls, group): - mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + def from_hdf5(cls, group: h5py.Group, mesh_id: int, name: str): filename = group['filename'][()].decode() library = group['library'][()].decode() + if 'options' in group.attrs: + options = group.attrs['options'].decode() + else: + options = None - mesh = cls(filename, library, mesh_id=mesh_id) + mesh = cls(filename=filename, library=library, mesh_id=mesh_id, name=name, options=options) + mesh._has_statepoint_data = True vol_data = group['volumes'][()] - centroids = group['centroids'][()] mesh.volumes = np.reshape(vol_data, (vol_data.shape[0],)) - mesh.centroids = np.reshape(centroids, (vol_data.shape[0], 3)) - mesh.size = mesh.volumes.size + mesh.n_elements = mesh.volumes.size + + vertices = group['vertices'][()] + mesh._vertices = vertices.reshape((-1, 3)) + connectivity = group['connectivity'][()] + mesh._connectivity = connectivity.reshape((-1, 8)) + mesh._element_types = group['element_types'][()] if 'length_multiplier' in group: mesh.length_multiplier = group['length_multiplier'][()] @@ -1306,30 +2812,32 @@ class UnstructuredMesh(MeshBase): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mesh data """ - element = ET.Element("mesh") - element.set("id", str(self._id)) + element = super().to_xml_element() element.set("type", "unstructured") - element.set("library", self._library) - subelement = ET.SubElement(element, "filename") - subelement.text = self.filename - if (self._specified_length_multiplier): - element.set("length_multiplier", str(self.length_multiplier)) + element.set("library", self._library) + if self.options is not None: + element.set('options', self.options) + subelement = ET.SubElement(element, "filename") + subelement.text = str(self.filename) + + if self._length_multiplier != 1.0: + element.set("length_multiplier", str(self.length_multiplier)) return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate unstructured mesh object from XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -1341,5 +2849,59 @@ class UnstructuredMesh(MeshBase): filename = get_text(elem, 'filename') library = get_text(elem, 'library') length_multiplier = float(get_text(elem, 'length_multiplier', 1.0)) + options = get_text(elem, "options") - return cls(filename, library, mesh_id, '', length_multiplier) + return cls(filename, library, mesh_id, '', length_multiplier, options) + + +def _read_meshes(elem): + """Generate dictionary of meshes from a given XML node + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + dict + A dictionary with mesh IDs as keys and openmc.MeshBase + instanaces as values + """ + out = {} + for mesh_elem in elem.findall('mesh'): + mesh = MeshBase.from_xml_element(mesh_elem) + out[mesh.id] = mesh + + return out + + +# hexahedron element connectivity +# lower-k connectivity offsets +_HEX_VERTEX_CONN = ((0, 0, 0), + (1, 0, 0), + (1, 1, 0), + (0, 1, 0)) +# upper-k connectivity offsets +_HEX_VERTEX_CONN += ((0, 0, 1), + (1, 0, 1), + (1, 1, 1), + (0, 1, 1)) + +_N_HEX_VERTICES = 8 + +# lower-k connectivity offsets +_HEX_MIDPOINT_CONN = ((0, (0, 0, 0)), + (1, (1, 0, 0)), + (0, (0, 1, 0)), + (1, (0, 0, 0))) +# upper-k connectivity offsets +_HEX_MIDPOINT_CONN += ((0, (0, 0, 1)), + (1, (1, 0, 1)), + (0, (0, 1, 1)), + (1, (0, 0, 1))) +# mid-plane k connectivity +_HEX_MIDPOINT_CONN += ((2, (0, 0, 0)), + (2, (1, 0, 0)), + (2, (1, 1, 0)), + (2, (0, 1, 0))) diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index 00b6440507..5de85afb0e 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -13,16 +13,31 @@ GROUP_STRUCTURES = {} - "XMAS-172_" designed for LWR analysis ([SAR1990]_, [SAN2004]_) - "SHEM-361_" designed for LWR analysis to eliminate self-shielding calculations of thermal resonances ([HFA2005]_, [SAN2007]_, [HEB2008]_) +- "SCALE-X" (where X is 44 which is designed for criticality analysis + and 252 is designed for thermal reactors) for the SCALE code suite + ([ZAL1999]_ and [REARDEN2013]_) +- "MPACT-X" (where X is 51 (PWR), 60 (BWR), 69 (Magnox)) from the MPACT_ reactor + physics code ([KIM2019]_ and [KIM2020]_) +- "ECCO-33" intended for fast reactor criticality benchmarks. It’s derived as a + subset of VITAMIN‑J +- "ECCO-1968_" designed for fine group reactor cell calculations for fast, + intermediate and thermal reactor applications ([SAR1990]_) - activation_ energy group structures "VITAMIN-J-42", "VITAMIN-J-175", - "TRIPOLI-315", "CCFE-709_" and "UKAEA-1102_" + "TRIPOLI-315", "LLNL-616", "CCFE-709_" and "UKAEA-1102_" -.. _CASMO: https://www.studsvik.com/SharepointFiles/CASMO-5%20Development%20and%20Applications.pdf +.. _CASMO: http://large.stanford.edu/courses/2013/ph241/dalvi1/docs/c5.physor2006.pdf +.. _SCALE44: https://www-nds.iaea.org/publications/indc/indc-czr-0001.pdf +.. _ECCO-33: https://serpent.vtt.fi/mediawiki/index.php/ECCO_33-group_structure +.. _SCALE252: https://oecd-nea.org/science/wpncs/amct/workingarea/meeting2013/EGAMCT2013_08.pdf +.. _MPACT: https://vera.ornl.gov/mpact/ .. _XMAS-172: https://www-nds.iaea.org/wimsd/energy.htm -.. _SHEM-361: https://www.polymtl.ca/merlin/downloads/FP214.pdf +.. _SHEM-361: http://merlin.polymtl.ca/downloads/FP214.pdf .. _activation: https://fispact.ukaea.uk/wiki/Keyword:GETXS .. _VITAMIN-J-42: https://www.oecd-nea.org/dbdata/nds_jefreports/jefreport-10.pdf +.. _LLNL-616: https://fispact.ukaea.uk/manual/user_manual.pdf .. _CCFE-709: https://fispact.ukaea.uk/wiki/CCFE-709_group_structure .. _UKAEA-1102: https://fispact.ukaea.uk/wiki/UKAEA-1102_group_structure +.. _ECCO-1968: https://serpent.vtt.fi/mediawiki/index.php/ECCO_1968-group_structure .. [SAR1990] Sartori, E., OECD/NEA Data Bank: Standard Energy Group Structures of Cross Section Libraries for Reactor Shielding, Reactor Cell and Fusion Neutronics Applications: VITAMIN-J, ECCO-33, ECCO-2000 and XMAS JEF/DOC-315 @@ -41,6 +56,19 @@ GROUP_STRUCTURES = {} .. [HEB2008] Hébert, Alain & Santamarina, Alain. (2008). Refinement of the Santamarina-Hfaiedh energy mesh between 22.5 eV and 11.4 keV. International Conference on the Physics of Reactors 2008, PHYSOR 08. 2. 929-938. +.. [ZAL1999] K. Záleský and L. Marková (1999), Assessment of Nuclear Data Needs + for Broad-Group SCALE Library Related to VVER Spent Fuel Applications, IAEA. SCALE44_. +.. [REARDEN2013] B. T. Rearden, M. E. Dunn, D. Wiarda, C. Celik, K. Bekar, + M. L. Williams, D. E. Peplow, M. A. Jessee, C. M. Perfetti, + I. C. Gauld, W. A. Wieselquist, J. P. Lefebvre, R. A. Lefebvre, + W. J. Marshall, A. B. Thompson, F. Havluj, S. E. Skutnik, + K. J. Dugan. (2013). Overview of SCALE 6.2. OECD. SCALE252_. +.. [KIM2019] Kim, K.S., Williams, M., Wiarda, D., & Clarno, K. (2019). Development + of the multigroup cross section library for the CASL neutronics simulator MPACT: + Method and procedure. Annals of Nuclear Energy, 133. pp. 46-58. +.. [KIM2020] Kim, K.S., Ade, B., & Luciano, N. (2020). Development + of the MPACT 69-group Library for Magnox Reactor Analysis using VERA. + Proceedings of International Conference on Physics of Reactors PHYSOR2020. """ GROUP_STRUCTURES['CASMO-2'] = np.array([ @@ -56,6 +84,16 @@ GROUP_STRUCTURES['CASMO-25'] = np.array([ 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 9.72e-1, 1.02, 1.097, 1.15, 1.855, 4., 9.877, 1.5968e1, 1.4873e2, 5.53e3, 9.118e3, 1.11e5, 5.e5, 8.21e5, 1.353e6, 2.231e6, 3.679e6, 6.0655e6, 2.e7]) +GROUP_STRUCTURES['ECCO-33'] = np.array([ + 1.0000100000E-05, 1.0000000000E-01, 5.4000000000E-01, 4.0000000000E+00, + 8.3152870000E+00, 1.3709590000E+01, 2.2603290000E+01, 4.0169000000E+01, + 6.7904050000E+01, 9.1660880000E+01, 1.4862540000E+02, 3.0432480000E+02, + 4.5399930000E+02, 7.4851830000E+02, 1.2340980000E+03, 2.0346840000E+03, + 3.3546260000E+03, 5.5308440000E+03, 9.1188200000E+03, 1.5034390000E+04, + 2.4787520000E+04, 4.0867710000E+04, 6.7379470000E+04, 1.1109000000E+05, + 1.8315640000E+05, 3.0197380000E+05, 4.9787070000E+05, 8.2085000000E+05, + 1.3533530000E+06, 2.2313020000E+06, 3.6787940000E+06, 6.0653070000E+06, + 1.0000000000E+07, 1.9640330000E+07]) GROUP_STRUCTURES['CASMO-40'] = np.array([ 0., 1.5e-2, 3.e-2, 4.2e-2, 5.8e-2, 8.e-2, 1.e-1, 1.4e-1, 1.8e-1, 2.2e-1, 2.8e-1, 3.5e-1, 6.25e-1, 8.5e-1, 9.5e-1, @@ -68,6 +106,35 @@ GROUP_STRUCTURES['VITAMIN-J-42'] = np.array([ 1.33e6, 1.34e6, 1.5e6, 1.66e6, 2.e6, 2.5e6, 3.e6, 3.5e6, 4.e6, 4.5e6, 5.e6, 5.5e6, 6.e6, 6.5e6, 7.e6, 7.5e6, 8.e6, 10.e6, 12.e6, 14.e6, 20.e6, 30.e6, 50.e6]) +GROUP_STRUCTURES['SCALE-44'] = np.array([1e-5, 3.e-3, 7.5e-3, 1.e-2, 2.53e-2, + 3.e-2, 4.e-2, 5e-2, 7.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.25e-1, 2.5e-1, 2.75e-1, + 3.25e-1, 3.5e-1, 3.75e-1, 4.e-1, 6.25e-1, 1., 1.77, 3., 4.75, 6., 8.1, + 1.e1, 3.e1, 1.e2, 5.5e2, 3.e3, 1.7e4, 2.5e4, 1.e5, 4.e5, 9.e5, 1.4e6, + 1.85e6, 2.354e6, 2.479e6, 3.e6, 4.8e6, 6.434e6, 8.1873e6, 2.e7]) +GROUP_STRUCTURES['MPACT-51'] = np.array([ + 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.75e-1, + 3.5e-1, 5.e-1, 6.25e-1, 7.5e-1, 9.25e-1, 9.75e-1, 1.010, 1.080, 1.130, + 1.175, 1.250, 1.450, 1.860, 2.470, 3.730, 4.700, 5.000, 5.400, 6.250, + 7.150, 8.100, 1.19e+1, 1.44e+1, 3.e+1, 4.83e+1, 7.6e+1, 1.43e+2, 3.05e+2, + 9.5e+2, 2.25e+3, 9.5e+3, 2.e+4, 5.e+4, 7.3e+4, 2.e+5, 4.92e+5, 8.2e+5, + 1.356e+6, 2.354e+6, 4.304e+6, 6.434e+6, 2.e+7]) +GROUP_STRUCTURES['MPACT-60'] = np.array([ + 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 1.e-1, 1.5e-1, 2.e-1, 2.75e-1, + 3.5e-1, 5.e-1, 6.25e-1, 7.5e-1, 9.25e-1, 9.75e-1, 1.01, 1.08, 1.13, + 1.175, 1.25, 1.45, 1.86, 2.47, 3.73, 4.7, 5., 5.4, 6.25, 7.15, 8.1, + 1.19e+1, 1.44e+1, 3.e+1, 4.83e+1, 7.6e+1, 1.43e+2, 2.095e+2, 3.05e+2, + 6.7e+2, 9.5e+2, 1.55e+3, 2.25e+3, 3.9e+3, 9.5e+3, 1.3e+4, 2.e+4, 3.e+4, + 5.e+4, 7.3e+4, 1.283e+5, 2.e+5, 3.3e+5, 4.92e+5, 6.7e+5, 8.2e+5, 1.356e+6, + 2.354e+6, 4.304e+6, 6.434e+6, 2.e+7]) +GROUP_STRUCTURES['MPACT-69'] = np.array([ + 0., 1.e-2, 3.e-2, 4.e-2, 6.e-2, 8.e-2, 9.e-2, 1.e-1, 1.25e-1, 1.5e-1, + 1.75e-1, 2.e-1, 2.25e-1, 2.5e-1, 2.75e-1, 3.e-1, 3.25e-1, 3.5e-1, 3.75e-1, + 4.e-1, 4.5e-1, 5.e-1, 5.5e-1, 6.e-1, 6.25e-1, 6.5e-1, 7.5e-1, 8.5e-1, + 9.25e-1, 9.75e-1, 1.01, 1.08, 1.13, 1.175, 1.25, 1.45, 1.86, 2.47, 3., + 3.73, 4.7, 5., 5.4, 6.25, 7.15, 8.1, 1.e+1, 1.19e+1, 1.44e+1, 3.e+1, + 4.83e+1, 7.6e+1, 1.43e+2, 3.05e+2, 5.5e+2, 9.5e+2, 2.25e+3, 3.9e+3, 9.5e+3, + 2.e+4, 5.e+4, 7.3e+4, 2.e+5, 4.92e+5, 8.2e+5, 1.356e+6, 2.354e+6, 4.304e+6, + 6.434e+6, 2.e+7]) GROUP_STRUCTURES['CASMO-70'] = np.array([ 0., 5.e-3, 1.e-2, 1.5e-2, 2.e-2, 2.5e-2, 3.e-2, 3.5e-2, 4.2e-2, 5.e-2, 5.8e-2, 6.7e-2, 8.e-2, 1.e-1, 1.4e-1, 1.8e-1, 2.2e-1, @@ -145,7 +212,35 @@ GROUP_STRUCTURES['VITAMIN-J-175'] = np.array([ 1.1052e7, 1.1618e7, 1.2214e7, 1.2523e7, 1.2840e7, 1.3499e7, 1.3840e7, 1.4191e7, 1.4550e7, 1.4918e7, 1.5683e7, 1.6487e7, 1.6905e7, 1.7332e7, 1.9640e7]) -GROUP_STRUCTURES['TRIPOLI-315,'] = np.array([ +GROUP_STRUCTURES['SCALE-252'] = np.array([ + 0., 1.e-4, 5.e-4, 7.5e-4, 1.e-3, 1.2e-3, 1.5e-3, 2.e-3, 2.5e-3, 3.e-3, + 4.e-3, 5.e-3, 7.5e-3, 1.e-2, 2.53e-2, 3.e-2, 4.e-2, 5.e-2, 6.e-2, 7.e-2, + 8.e-2, 9.e-2, 1.e-1, 1.25e-1, 1.5e-1, 1.75e-1, 2.e-1, 2.25e-1, 2.5e-1, + 2.75e-1, 3.e-1, 3.25e-1, 3.5e-1, 3.75e-1, 4.e-1, 4.5e-1, 5.e-1, 5.5e-1, + 6.e-1, 6.25e-1, 6.5e-1, 7.e-1, 7.5e-1, 8.e-1, 8.5e-1, 9.e-1, 9.25e-1, + 9.5e-1, 9.75e-1, 1., 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, + 1.1, 1.11, 1.12, 1.13, 1.14, 1.15, 1.175, 1.2, 1.225, 1.25, 1.3, 1.35, 1.4, + 1.45, 1.5, 1.59, 1.68, 1.77, 1.86, 1.94, 2., 2.12, 2.21, 2.3, 2.38, 2.47, + 2.57, 2.67, 2.77, 2.87, 2.97, 3., 3.1, 3.2, 3.5, 3.73, 4.1, 4.7, 5., 5.4, + 6., 6.25, 6.5, 6.75, 6.875, 7., 7.15, 8.1, 9.1, 1.e+1, 1.15e+1, 1.19e+1, + 1.29e+1, 1.44e+1, 1.6e+1, 1.7e+1, 1.85e+1, 1.94e+1, 2.e+1, 2.05e+1, + 2.12e+1, 2.175e+1, 2.25e+1, 2.5e+1, 2.75e+1, 3.e+1, 3.125e+1, 3.175e+1, + 3.325e+1, 3.375e+1, 3.5e+1, 3.55e+1, 3.6e+1, 3.7e+1, 3.713e+1, 3.727e+1, + 3.763e+1, 3.8e+1, 3.91e+1, 3.96e+1, 4.1e+1, 4.24e+1, 4.4e+1, 4.52e+1, + 4.83e+1, 5.06e+1, 5.34e+1, 5.8e+1, 6.1e+1, 6.3e+1, 6.5e+1, 6.75e+1, 7.2e+1, + 7.6e+1, 8.e+1, 8.17e+1, 9.e+1, 9.7e+1, 1.012e+2, 1.05e+2, 1.08e+2, 1.13e+2, + 1.16e+2, 1.175e+2, 1.19e+2, 1.22e+2, 1.43e+2, 1.7e+2, 1.8e+2, 1.877e+2, + 1.885e+2, 1.915e+2, 1.93e+2, 2.02e+2, 2.074e+2, 2.095e+2, 2.2e+2, 2.4e+2, + 2.85e+2, 3.05e+2, 5.5e+2, 6.7e+2, 6.83e+2, 9.5e+2, 1.15e+3, 1.5e+3, + 1.55e+3, 1.8e+3, 2.2e+3, 2.25e+3, 2.5e+3, 3.e+3, 3.74e+3, 3.9e+3, 5.7e+3, + 8.03e+3, 9.5e+3, 1.3e+4, 1.7e+4, 2.e+4, 3.e+4, 4.5e+4, 5.e+4, 5.2e+4, + 6.e+4, 7.3e+4, 7.5e+4, 8.2e+4, 8.5e+4, 1.e+5, 1.283e+5, 1.49e+5, 2.e+5, + 2.7e+5, 3.3e+5, 4.e+5, 4.2e+5, 4.4e+5, 4.7e+5, 4.92e+5, 5.5e+5, 5.73e+5, + 6.e+5, 6.7e+5, 6.79e+5, 7.5e+5, 8.2e+5, 8.611e+5, 8.75e+5, 9.e+5, 9.2e+5, + 1.01e+6, 1.1e+6, 1.2e+6, 1.25e+6, 1.317e+6, 1.356e+6, 1.4e+6, 1.5e+6, + 1.85e+6, 2.354e+6, 2.479e+6, 3.e+6, 4.304e+6, 4.8e+6, 6.434e+6, 8.187e+6, + 1.e+7, 1.284e+7, 1.384e+7, 1.455e+7, 1.568e+7, 1.733e+7, 2.e+7]) +GROUP_STRUCTURES['TRIPOLI-315'] = np.array([ 1.0e-5, 1.1e-4, 3.000e-3, 5.500e-3, 1.000e-2, 1.500e-2, 2.000e-2, 3.000e-2, 3.200e-2, 3.238e-2, 4.300e-2, 5.900e-2, 7.700e-2, 9.500e-2, 1.000e-1, 1.150e-1, 1.340e-1, 1.600e-1, 1.890e-1, 2.200e-1, 2.480e-1, 2.825e-1, @@ -265,6 +360,97 @@ GROUP_STRUCTURES['SHEM-361'] = np.array([ 4.06569e+06, 4.96585e+06, 6.06530e+06, 6.70319e+06, 7.40817e+06, 8.18730e+06, 9.04836e+06, 9.99999e+06, 1.16183e+07, 1.38403e+07, 1.49182e+07, 1.96403e+07]) +GROUP_STRUCTURES['LLNL-616'] = np.array([ + 1.0000e-5, 1.0471e-5, 1.0965e-5, 1.1482e-5, 1.2023e-5, 1.2589e-5, 1.3183e-5, + 1.3804e-5, 1.4454e-5, 1.5136e-5, 1.5849e-5, 1.6596e-5, 1.7378e-5, 1.8197e-5, + 1.9055e-5, 1.9953e-5, 2.0893e-5, 2.1878e-5, 2.2909e-5, 2.3988e-5, 2.5119e-5, + 2.6303e-5, 2.7542e-5, 2.8840e-5, 3.0200e-5, 3.1623e-5, 3.3113e-5, 3.4674e-5, + 3.6308e-5, 3.8019e-5, 3.9811e-5, 4.1687e-5, 4.3652e-5, 4.5709e-5, 4.7863e-5, + 5.0119e-5, 5.2481e-5, 5.4954e-5, 5.7544e-5, 6.0256e-5, 6.3096e-5, 6.6069e-5, + 6.9183e-5, 7.2444e-5, 7.5858e-5, 7.9433e-5, 8.3176e-5, 8.7096e-5, 9.1201e-5, + 9.5499e-5, 1.0000e-4, 1.0471e-4, 1.0965e-4, 1.1482e-4, 1.2023e-4, 1.2589e-4, + 1.3183e-4, 1.3804e-4, 1.4454e-4, 1.5136e-4, 1.5849e-4, 1.6596e-4, 1.7378e-4, + 1.8197e-4, 1.9055e-4, 1.9953e-4, 2.0893e-4, 2.1878e-4, 2.2909e-4, 2.3988e-4, + 2.5119e-4, 2.6303e-4, 2.7542e-4, 2.8840e-4, 3.0200e-4, 3.1623e-4, 3.3113e-4, + 3.4674e-4, 3.6308e-4, 3.8019e-4, 3.9811e-4, 4.1687e-4, 4.3652e-4, 4.5709e-4, + 4.7863e-4, 5.0119e-4, 5.2481e-4, 5.4954e-4, 5.7544e-4, 6.0256e-4, 6.3096e-4, + 6.6069e-4, 6.9183e-4, 7.2444e-4, 7.5858e-4, 7.9433e-4, 8.3176e-4, 8.7096e-4, + 9.1201e-4, 9.5499e-4, 1.0000e-3, 1.0471e-3, 1.0965e-3, 1.1482e-3, 1.2023e-3, + 1.2589e-3, 1.3183e-3, 1.3804e-3, 1.4454e-3, 1.5136e-3, 1.5849e-3, 1.6596e-3, + 1.7378e-3, 1.8197e-3, 1.9055e-3, 1.9953e-3, 2.0893e-3, 2.1878e-3, 2.2909e-3, + 2.3988e-3, 2.5119e-3, 2.6303e-3, 2.7542e-3, 2.8840e-3, 3.0200e-3, 3.1623e-3, + 3.3113e-3, 3.4674e-3, 3.6308e-3, 3.8019e-3, 3.9811e-3, 4.1687e-3, 4.3652e-3, + 4.5709e-3, 4.7863e-3, 5.0119e-3, 5.2481e-3, 5.4954e-3, 5.7544e-3, 6.0256e-3, + 6.3096e-3, 6.6069e-3, 6.9183e-3, 7.2444e-3, 7.5858e-3, 7.9433e-3, 8.3176e-3, + 8.7096e-3, 9.1201e-3, 9.5499e-3, 1.0000e-2, 1.0471e-2, 1.0965e-2, 1.1482e-2, + 1.2023e-2, 1.2589e-2, 1.3183e-2, 1.3804e-2, 1.4454e-2, 1.5136e-2, 1.5849e-2, + 1.6596e-2, 1.7378e-2, 1.8197e-2, 1.9055e-2, 1.9953e-2, 2.0893e-2, 2.1878e-2, + 2.2909e-2, 2.3988e-2, 2.5119e-2, 2.6303e-2, 2.7542e-2, 2.8840e-2, 3.0200e-2, + 3.1623e-2, 3.3113e-2, 3.4674e-2, 3.6308e-2, 3.8019e-2, 3.9811e-2, 4.1687e-2, + 4.3652e-2, 4.5709e-2, 4.7863e-2, 5.0119e-2, 5.2481e-2, 5.4954e-2, 5.7544e-2, + 6.0256e-2, 6.3096e-2, 6.6069e-2, 6.9183e-2, 7.2444e-2, 7.5858e-2, 7.9433e-2, + 8.3176e-2, 8.7096e-2, 9.1201e-2, 9.5499e-2, 1.0000e-1, 1.0471e-1, 1.0965e-1, + 1.1482e-1, 1.2023e-1, 1.2589e-1, 1.3183e-1, 1.3804e-1, 1.4454e-1, 1.5136e-1, + 1.5849e-1, 1.6596e-1, 1.7378e-1, 1.8197e-1, 1.9055e-1, 1.9953e-1, 2.0893e-1, + 2.1878e-1, 2.2909e-1, 2.3988e-1, 2.5119e-1, 2.6303e-1, 2.7542e-1, 2.8840e-1, + 3.0200e-1, 3.1623e-1, 3.3113e-1, 3.4674e-1, 3.6308e-1, 3.8019e-1, 3.9811e-1, + 4.1687e-1, 4.3652e-1, 4.5709e-1, 4.7863e-1, 5.0119e-1, 5.2481e-1, 5.4954e-1, + 5.7544e-1, 6.0256e-1, 6.3096e-1, 6.6069e-1, 6.9183e-1, 7.2444e-1, 7.5858e-1, + 7.9433e-1, 8.3176e-1, 8.7096e-1, 9.1201e-1, 9.5499e-1, 1.0000e0, 1.0471e0, + 1.0965e0, 1.1482e0, 1.2023e0, 1.2589e0, 1.3183e0, 1.3804e0, 1.4454e0, + 1.5136e0, 1.5849e0, 1.6596e0, 1.7378e0, 1.8197e0, 1.9055e0, 1.9953e0, + 2.0893e0, 2.1878e0, 2.2909e0, 2.3988e0, 2.5119e0, 2.6303e0, 2.7542e0, + 2.8840e0, 3.0200e0, 3.1623e0, 3.3113e0, 3.4674e0, 3.6308e0, 3.8019e0, + 3.9811e0, 4.1687e0, 4.3652e0, 4.5709e0, 4.7863e0, 5.0119e0, 5.2481e0, + 5.4954e0, 5.7544e0, 6.0256e0, 6.3096e0, 6.6069e0, 6.9183e0, 7.2444e0, + 7.5858e0, 7.9433e0, 8.3176e0, 8.7096e0, 9.1201e0, 9.5499e0, 1.0000e1, + 1.0471e1, 1.0965e1, 1.1482e1, 1.2023e1, 1.2589e1, 1.3183e1, 1.3804e1, + 1.4454e1, 1.5136e1, 1.5849e1, 1.6596e1, 1.7378e1, 1.8197e1, 1.9055e1, + 1.9953e1, 2.0893e1, 2.1878e1, 2.2909e1, 2.3988e1, 2.5119e1, 2.6303e1, + 2.7542e1, 2.8840e1, 3.0200e1, 3.1623e1, 3.3113e1, 3.4674e1, 3.6308e1, + 3.8019e1, 3.9811e1, 4.1687e1, 4.3652e1, 4.5709e1, 4.7863e1, 5.0119e1, + 5.2481e1, 5.4954e1, 5.7544e1, 6.0256e1, 6.3096e1, 6.6069e1, 6.9183e1, + 7.2444e1, 7.5858e1, 7.9433e1, 8.3176e1, 8.7096e1, 9.1201e1, 9.5499e1, + 1.0000e2, 1.0471e2, 1.0965e2, 1.1482e2, 1.2023e2, 1.2589e2, 1.3183e2, + 1.3804e2, 1.4454e2, 1.5136e2, 1.5849e2, 1.6596e2, 1.7378e2, 1.8197e2, + 1.9055e2, 1.9953e2, 2.0893e2, 2.1878e2, 2.2909e2, 2.3988e2, 2.5119e2, + 2.6303e2, 2.7542e2, 2.8840e2, 3.0200e2, 3.1623e2, 3.3113e2, 3.4674e2, + 3.6308e2, 3.8019e2, 3.9811e2, 4.1687e2, 4.3652e2, 4.5709e2, 4.7863e2, + 5.0119e2, 5.2481e2, 5.4954e2, 5.7544e2, 6.0256e2, 6.3096e2, 6.6069e2, + 6.9183e2, 7.2444e2, 7.5858e2, 7.9433e2, 8.3176e2, 8.7096e2, 9.1201e2, + 9.5499e2, 1.0000e3, 1.0471e3, 1.0965e3, 1.1482e3, 1.2023e3, 1.2589e3, + 1.3183e3, 1.3804e3, 1.4454e3, 1.5136e3, 1.5849e3, 1.6596e3, 1.7378e3, + 1.8197e3, 1.9055e3, 1.9953e3, 2.0893e3, 2.1878e3, 2.2909e3, 2.3988e3, + 2.5119e3, 2.6303e3, 2.7542e3, 2.8840e3, 3.0200e3, 3.1623e3, 3.3113e3, + 3.4674e3, 3.6308e3, 3.8019e3, 3.9811e3, 4.1687e3, 4.3652e3, 4.5709e3, + 4.7863e3, 5.0119e3, 5.2481e3, 5.4954e3, 5.7544e3, 6.0256e3, 6.3096e3, + 6.6069e3, 6.9183e3, 7.2444e3, 7.5858e3, 7.9433e3, 8.3176e3, 8.7096e3, + 9.1201e3, 9.5499e3, 1.0000e4, 1.0471e4, 1.0965e4, 1.1482e4, 1.2023e4, + 1.2589e4, 1.3183e4, 1.3804e4, 1.4454e4, 1.5136e4, 1.5849e4, 1.6596e4, + 1.7378e4, 1.8197e4, 1.9055e4, 1.9953e4, 2.0893e4, 2.1878e4, 2.2909e4, + 2.3988e4, 2.5119e4, 2.6303e4, 2.7542e4, 2.8840e4, 3.0200e4, 3.1623e4, + 3.3113e4, 3.4674e4, 3.6308e4, 3.8019e4, 3.9811e4, 4.1687e4, 4.3652e4, + 4.5709e4, 4.7863e4, 5.0119e4, 5.2481e4, 5.4954e4, 5.7544e4, 6.0256e4, + 6.3096e4, 6.6069e4, 6.9183e4, 7.2444e4, 7.5858e4, 7.9433e4, 8.3176e4, + 8.7096e4, 9.1201e4, 9.5499e4, 1.0000e5, 1.0471e5, 1.0965e5, 1.1482e5, + 1.2023e5, 1.2589e5, 1.3183e5, 1.3804e5, 1.4454e5, 1.5136e5, 1.5849e5, + 1.6596e5, 1.7378e5, 1.8197e5, 1.9055e5, 1.9953e5, 2.0893e5, 2.1878e5, + 2.2909e5, 2.3988e5, 2.5119e5, 2.6303e5, 2.7542e5, 2.8840e5, 3.0200e5, + 3.1623e5, 3.3113e5, 3.4674e5, 3.6308e5, 3.8019e5, 3.9811e5, 4.1687e5, + 4.3652e5, 4.5709e5, 4.7863e5, 5.0119e5, 5.2481e5, 5.4954e5, 5.7544e5, + 6.0256e5, 6.3096e5, 6.6069e5, 6.9183e5, 7.2444e5, 7.5858e5, 7.9433e5, + 8.3176e5, 8.7096e5, 9.1201e5, 9.5499e5, 1.0000e6, 1.0471e6, 1.0965e6, + 1.1482e6, 1.2023e6, 1.2589e6, 1.3183e6, 1.3804e6, 1.4454e6, 1.5136e6, + 1.5849e6, 1.6596e6, 1.7378e6, 1.8197e6, 1.9055e6, 1.9953e6, 2.0893e6, + 2.1878e6, 2.2909e6, 2.3988e6, 2.5119e6, 2.6303e6, 2.7542e6, 2.8840e6, + 3.0200e6, 3.1623e6, 3.3113e6, 3.4674e6, 3.6308e6, 3.8019e6, 3.9811e6, + 4.1687e6, 4.3652e6, 4.5709e6, 4.7863e6, 5.0119e6, 5.2481e6, 5.4954e6, + 5.7544e6, 6.0256e6, 6.3096e6, 6.6069e6, 6.9183e6, 7.2444e6, 7.5858e6, + 7.9433e6, 8.3176e6, 8.7096e6, 9.1201e6, 9.5499e6, 1.0000e7, 1.0471e7, + 1.0965e7, 1.1482e7, 1.2023e7, 1.2589e7, 1.3183e7, 1.3804e7, 1.4454e7, + 1.5136e7, 1.5849e7, 1.6596e7, 1.7378e7, 1.8197e7, 1.9055e7, 1.9953e7, + 2.0000e7 +]) GROUP_STRUCTURES['CCFE-709'] = np.array([ 1.e-5, 1.0471e-5, 1.0965e-5, 1.1482e-5, 1.2023e-5, 1.2589e-5, 1.3183e-5, 1.3804e-5, 1.4454e-5, 1.5136e-5, @@ -630,3 +816,329 @@ GROUP_STRUCTURES['UKAEA-1102'] = np.array([ 5.7544e8, 6.0256e8, 6.3096e8, 6.6069e8, 6.9183e8, 7.2444e8, 7.5858e8, 7.9433e8, 8.3176e8, 8.7096e8, 9.1201e8, 9.5499e8, 1.e9]) +GROUP_STRUCTURES['ECCO-1968'] = np.array([ + 1.00001e-5, 3.00000e-3, 5.00000e-3, 6.90000e-3, 1.00000e-2, 1.50000e-2, + 2.00000e-2, 2.50000e-2, 3.00000e-2, 3.50000e-2, 4.20000e-2, 5.00000e-2, + 5.80000e-2, 6.70000e-2, 7.70000e-2, 8.00000e-2, 9.50000e-2, 1.00000e-1, + 1.15000e-1, 1.34000e-1, 1.40000e-1, 1.46370e-1, 1.53030e-1, 1.60000e-1, + 1.69710e-1, 1.80000e-1, 1.89000e-1, 1.98810e-1, 2.09140e-1, 2.20000e-1, + 2.33580e-1, 2.48000e-1, 2.63510e-1, 2.80000e-1, 3.00000e-1, 3.14500e-1, + 3.20000e-1, 3.34660e-1, 3.50000e-1, 3.69930e-1, 3.91000e-1, 4.00000e-1, + 4.13990e-1, 4.33000e-1, 4.49680e-1, 4.67010e-1, 4.85000e-1, 5.00000e-1, + 5.19620e-1, 5.31580e-1, 5.40000e-1, 5.66960e-1, 5.95280e-1, 6.25000e-1, + 6.53150e-1, 6.82560e-1, 7.05000e-1, 7.41550e-1, 7.80000e-1, 7.90000e-1, + 8.19450e-1, 8.50000e-1, 8.60000e-1, 8.76425e-1, 9.10000e-1, 9.30000e-1, + 9.50000e-1, 9.72000e-1, 9.86000e-1, 9.96000e-1, + 1.020000, 1.035000, 1.045000, 1.071000, 1.080000, 1.097000, 1.110000, + 1.123000, 1.150000, 1.170000, 1.202060, 1.235000, 1.267080, 1.300000, + 1.337500, 1.370000, 1.404560, 1.440000, 1.475000, 1.500000, 1.544340, + 1.590000, 1.629510, 1.670000, 1.711970, 1.755000, 1.797000, 1.840000, + 1.855390, 1.884460, 1.930000, 1.974490, 2.020000, 2.059610, 2.100000, + 2.130000, 2.185310, 2.242050, 2.300270, 2.360000, 2.382370, 2.421710, + 2.485030, 2.550000, 2.600000, 2.659320, 2.720000, 2.767920, 2.837990, + 2.909830, 2.983490, 3.059020, 3.137330, 3.217630, 3.300000, 3.380750, + 3.466330, 3.554080, 3.644050, 3.736300, 3.830880, 3.927860, 4.000000, + 4.129250, 4.233782, 4.340961, 4.450853, 4.563526, 4.679053, 4.797503, + 4.918953, 5.043477, 5.085681, 5.128239, 5.171153, 5.214426, 5.258061, + 5.302061, 5.346430, 5.391169, 5.436284, 5.481775, 5.527647, 5.573904, + 5.620547, 5.667581, 5.715008, 5.762832, 5.811056, 5.859684, 5.908719, + 5.958164, 6.008022, 6.058298, 6.108995, 6.160116, 6.211665, 6.263645, + 6.316060, 6.368914, 6.422210, 6.475952, 6.530144, 6.584789, 6.639892, + 6.695455, 6.751484, 6.807981, 6.864952, 6.922399, 6.980326, 7.038739, + 7.097640, 7.157034, 7.216925, 7.277317, 7.338215, 7.399622, 7.461544, + 7.523983, 7.586945, 7.650434, 7.714454, 7.779009, 7.844105, 7.909746, + 7.975936, 8.042680, 8.109982, 8.177848, 8.246281, 8.315287, 8.384871, + 8.455037, 8.525790, 8.597135, 8.669077, 8.741621, 8.814772, 8.888536, + 8.962916, 9.037919, 9.113550, 9.189814, 9.266715, 9.344261, 9.422455, + 9.501303, 9.580812, 9.660985, 9.741830, 9.823351, 9.905554, 9.988446, + 1.007203e1, 1.015631e1, 1.024130e1, 1.032701e1, 1.041342e1, 1.050056e1, + 1.058843e1, 1.067704e1, 1.076639e1, 1.085648e1, 1.094733e1, 1.103894e1, + 1.113132e1, 1.122446e1, 1.131839e1, 1.141311e1, 1.150861e1, 1.160492e1, + 1.170203e1, 1.179995e1, 1.189870e1, 1.199827e1, 1.209867e1, 1.219991e1, + 1.230201e1, 1.240495e1, 1.250876e1, 1.261343e1, 1.271898e1, 1.282542e1, + 1.293274e1, 1.304097e1, 1.315010e1, 1.326014e1, 1.337110e1, 1.348299e1, + 1.359582e1, 1.370959e1, 1.382431e1, 1.394000e1, 1.405665e1, 1.417428e1, + 1.429289e1, 1.441250e1, 1.453310e1, 1.465472e1, 1.477735e1, 1.490101e1, + 1.502570e1, 1.515144e1, 1.527823e1, 1.540608e1, 1.553500e1, 1.566500e1, + 1.579609e1, 1.592827e1, 1.606156e1, 1.619597e1, 1.633150e1, 1.646816e1, + 1.660597e1, 1.674493e1, 1.688506e1, 1.702635e1, 1.716883e1, 1.731250e1, + 1.745738e1, 1.760346e1, 1.775077e1, 1.789931e1, 1.804910e1, 1.820013e1, + 1.835244e1, 1.850601e1, 1.866087e1, 1.881703e1, 1.897449e1, 1.913328e1, + 1.929339e1, 1.945484e1, 1.961764e1, 1.978180e1, 1.994734e1, 2.011426e1, + 2.028258e1, 2.045231e1, 2.062345e1, 2.079603e1, 2.097006e1, 2.114554e1, + 2.132249e1, 2.150092e1, 2.168084e1, 2.186227e1, 2.204522e1, 2.222969e1, + 2.241572e1, 2.260329e1, 2.279244e1, 2.298317e1, 2.317550e1, 2.336944e1, + 2.356499e1, 2.376219e1, 2.396104e1, 2.416154e1, 2.436373e1, 2.456761e1, + 2.477320e1, 2.498050e1, 2.518954e1, 2.540033e1, 2.561289e1, 2.582722e1, + 2.604335e1, 2.626128e1, 2.648104e1, 2.670264e1, 2.692609e1, 2.715141e1, + 2.737862e1, 2.760773e1, 2.783875e1, 2.807171e1, 2.830662e1, 2.854349e1, + 2.878235e1, 2.902320e1, 2.926607e1, 2.951098e1, 2.975793e1, 3.000695e1, + 3.025805e1, 3.051126e1, 3.076658e1, 3.102404e1, 3.128365e1, 3.154544e1, + 3.180942e1, 3.207560e1, 3.234401e1, 3.261467e1, 3.288760e1, 3.316281e1, + 3.344032e1, 3.372015e1, 3.400233e1, 3.428686e1, 3.457378e1, 3.486310e1, + 3.515484e1, 3.544902e1, 3.574566e1, 3.604479e1, 3.634642e1, 3.665057e1, + 3.695727e1, 3.726653e1, 3.757838e1, 3.789285e1, 3.820994e1, 3.852969e1, + 3.885211e1, 3.917723e1, 3.950507e1, 3.983565e1, 4.016900e1, 4.050514e1, + 4.084410e1, 4.118589e1, 4.153054e1, 4.187807e1, 4.222851e1, 4.258189e1, + 4.293822e1, 4.329753e1, 4.365985e1, 4.402521e1, 4.439361e1, 4.476511e1, + 4.513971e1, 4.551744e1, 4.589834e1, 4.628243e1, 4.666972e1, 4.706026e1, + 4.745407e1, 4.785117e1, 4.825160e1, 4.865538e1, 4.906253e1, 4.947309e1, + 4.988709e1, 5.030456e1, 5.072551e1, 5.114999e1, 5.157802e1, 5.200963e1, + 5.244486e1, 5.288373e1, 5.332626e1, 5.377251e1, 5.422248e1, 5.467623e1, + 5.513376e1, 5.559513e1, 5.606036e1, 5.652948e1, 5.700253e1, 5.747954e1, + 5.796053e1, 5.844556e1, 5.893464e1, 5.942781e1, 5.992511e1, 6.042657e1, + 6.093223e1, 6.144212e1, 6.195628e1, 6.247474e1, 6.299754e1, 6.352471e1, + 6.405630e1, 6.459233e1, 6.513285e1, 6.567789e1, 6.622749e1, 6.678169e1, + 6.734053e1, 6.790405e1, 6.847228e1, 6.904527e1, 6.962305e1, 7.020566e1, + 7.079316e1, 7.138556e1, 7.198293e1, 7.258529e1, 7.319270e1, 7.380518e1, + 7.442280e1, 7.504558e1, 7.567357e1, 7.630682e1, 7.694537e1, 7.758926e1, + 7.823854e1, 7.889325e1, 7.955344e1, 8.021915e1, 8.089044e1, 8.156734e1, + 8.224991e1, 8.293819e1, 8.363223e1, 8.433208e1, 8.503778e1, 8.574939e1, + 8.646695e1, 8.719052e1, 8.792015e1, 8.865588e1, 8.939776e1, 9.014586e1, + 9.090021e1, 9.166088e1, 9.242791e1, 9.320136e1, 9.398128e1, 9.476773e1, + 9.556076e1, 9.636043e1, 9.716679e1, 9.797990e1, 9.879981e1, 9.962658e1, + 1.004603e2, 1.013009e2, 1.021486e2, 1.030034e2, 1.038654e2, 1.047345e2, + 1.056110e2, 1.064947e2, 1.073859e2, 1.082845e2, 1.091907e2, 1.101044e2, + 1.110258e2, 1.119548e2, 1.128917e2, 1.138364e2, 1.147890e2, 1.157496e2, + 1.167182e2, 1.176949e2, 1.186798e2, 1.196729e2, 1.206744e2, 1.216842e2, + 1.227024e2, 1.237292e2, 1.247646e2, 1.258087e2, 1.268615e2, 1.279231e2, + 1.289935e2, 1.300730e2, 1.311615e2, 1.322590e2, 1.333658e2, 1.344818e2, + 1.356072e2, 1.367420e2, 1.378862e2, 1.390401e2, 1.402036e2, 1.413768e2, + 1.425599e2, 1.437529e2, 1.449558e2, 1.461688e2, 1.473920e2, 1.486254e2, + 1.498691e2, 1.511232e2, 1.523879e2, 1.536631e2, 1.549489e2, 1.562456e2, + 1.575531e2, 1.588715e2, 1.602010e2, 1.615415e2, 1.628933e2, 1.642565e2, + 1.656310e2, 1.670170e2, 1.684146e2, 1.698239e2, 1.712451e2, 1.726781e2, + 1.741231e2, 1.755802e2, 1.770494e2, 1.785310e2, 1.800250e2, 1.815315e2, + 1.830505e2, 1.845823e2, 1.861269e2, 1.876845e2, 1.892551e2, 1.908388e2, + 1.924358e2, 1.940461e2, 1.956699e2, 1.973073e2, 1.989584e2, 2.006233e2, + 2.023021e2, 2.039950e2, 2.057021e2, 2.074234e2, 2.091592e2, 2.109095e2, + 2.126744e2, 2.144541e2, 2.162487e2, 2.180583e2, 2.198830e2, 2.217230e2, + 2.235784e2, 2.254494e2, 2.273360e2, 2.292384e2, 2.311567e2, 2.330910e2, + 2.350416e2, 2.370084e2, 2.389917e2, 2.409917e2, 2.430083e2, 2.450418e2, + 2.470924e2, 2.491601e2, 2.512451e2, 2.533476e2, 2.554676e2, 2.576054e2, + 2.597611e2, 2.619348e2, 2.641267e2, 2.663370e2, 2.685657e2, 2.708131e2, + 2.730793e2, 2.753645e2, 2.776688e2, 2.799924e2, 2.823354e2, 2.846980e2, + 2.870804e2, 2.894827e2, 2.919052e2, 2.943479e2, 2.968110e2, 2.992948e2, + 3.017993e2, 3.043248e2, 3.068715e2, 3.094394e2, 3.120288e2, 3.146399e2, + 3.172729e2, 3.199279e2, 3.226051e2, 3.253047e2, 3.280269e2, 3.307719e2, + 3.335398e2, 3.363309e2, 3.391454e2, 3.419834e2, 3.448452e2, 3.477309e2, + 3.506408e2, 3.535750e2, 3.565338e2, 3.595173e2, 3.625258e2, 3.655595e2, + 3.686185e2, 3.717032e2, 3.748137e2, 3.779502e2, 3.811129e2, 3.843021e2, + 3.875180e2, 3.907608e2, 3.940308e2, 3.973281e2, 4.006530e2, 4.040057e2, + 4.073865e2, 4.107955e2, 4.142332e2, 4.176995e2, 4.211949e2, 4.247195e2, + 4.282736e2, 4.318575e2, 4.354713e2, 4.391154e2, 4.427900e2, 4.464953e2, + 4.502317e2, 4.539993e2, 4.577984e2, 4.616294e2, 4.654923e2, 4.693877e2, + 4.733156e2, 4.772763e2, 4.812703e2, 4.852976e2, 4.893587e2, 4.934537e2, + 4.975830e2, 5.017468e2, 5.059455e2, 5.101793e2, 5.144486e2, 5.187536e2, + 5.230946e2, 5.274719e2, 5.318859e2, 5.363368e2, 5.408249e2, 5.453506e2, + 5.499142e2, 5.545160e2, 5.591563e2, 5.638354e2, 5.685536e2, 5.733114e2, + 5.781089e2, 5.829466e2, 5.878248e2, 5.927438e2, 5.977040e2, 6.027057e2, + 6.077492e2, 6.128350e2, 6.179633e2, 6.231345e2, 6.283489e2, 6.336071e2, + 6.389092e2, 6.442557e2, 6.496469e2, 6.550832e2, 6.605651e2, 6.660928e2, + 6.716668e2, 6.772874e2, 6.829550e2, 6.886701e2, 6.944330e2, 7.002441e2, + 7.061038e2, 7.120126e2, 7.179709e2, 7.239790e2, 7.300373e2, 7.361464e2, + 7.423066e2, 7.485183e2, 7.547820e2, 7.610981e2, 7.674671e2, 7.738894e2, + 7.803654e2, 7.868957e2, 7.934805e2, 8.001205e2, 8.068160e2, 8.135676e2, + 8.203756e2, 8.272407e2, 8.341631e2, 8.411435e2, 8.481824e2, 8.552801e2, + 8.624372e2, 8.696542e2, 8.769316e2, 8.842699e2, 8.916696e2, 8.991312e2, + 9.066553e2, 9.142423e2, 9.218928e2, 9.296074e2, 9.373865e2, 9.452307e2, + 9.531405e2, 9.611165e2, 9.691593e2, 9.772694e2, 9.854473e2, 9.936937e2, + 1.002009e3, 1.010394e3, 1.018849e3, 1.027375e3, 1.035972e3, 1.044641e3, + 1.053383e3, 1.062198e3, 1.071087e3, 1.080050e3, 1.089088e3, 1.098201e3, + 1.107391e3, 1.116658e3, 1.126002e3, 1.135425e3, 1.144926e3, 1.154507e3, + 1.164168e3, 1.173910e3, 1.183734e3, 1.193639e3, 1.203628e3, 1.213700e3, + 1.223857e3, 1.234098e3, 1.244425e3, 1.254839e3, 1.265339e3, 1.275928e3, + 1.286605e3, 1.297372e3, 1.308228e3, 1.319176e3, 1.330215e3, 1.341346e3, + 1.352571e3, 1.363889e3, 1.375303e3, 1.386811e3, 1.398416e3, 1.410118e3, + 1.421919e3, 1.433817e3, 1.445816e3, 1.457915e3, 1.470115e3, 1.482417e3, + 1.494822e3, 1.507331e3, 1.519944e3, 1.532663e3, 1.545489e3, 1.558422e3, + 1.571463e3, 1.584613e3, 1.597874e3, 1.611245e3, 1.624728e3, 1.638324e3, + 1.652034e3, 1.665858e3, 1.679798e3, 1.693855e3, 1.708030e3, 1.722323e3, + 1.736735e3, 1.751268e3, 1.765923e3, 1.780701e3, 1.795602e3, 1.810628e3, + 1.825780e3, 1.841058e3, 1.856464e3, 1.871999e3, 1.887665e3, 1.903461e3, + 1.919389e3, 1.935451e3, 1.951647e3, 1.967979e3, 1.984447e3, 2.001053e3, + 2.017798e3, 2.034684e3, 2.051710e3, 2.068879e3, 2.086192e3, 2.103650e3, + 2.121253e3, 2.139004e3, 2.156904e3, 2.174953e3, 2.193153e3, 2.211506e3, + 2.230012e3, 2.248673e3, 2.267490e3, 2.286465e3, 2.305599e3, 2.324892e3, + 2.344347e3, 2.363965e3, 2.383747e3, 2.403695e3, 2.423809e3, 2.444092e3, + 2.464545e3, 2.485168e3, 2.505965e3, 2.526935e3, 2.548081e3, 2.569403e3, + 2.590904e3, 2.612586e3, 2.634448e3, 2.656494e3, 2.678723e3, 2.701139e3, + 2.723743e3, 2.746536e3, 2.769519e3, 2.792695e3, 2.816065e3, 2.839630e3, + 2.863392e3, 2.887354e3, 2.911515e3, 2.935879e3, 2.960447e3, 2.985221e3, + 3.010202e3, 3.035391e3, 3.060792e3, 3.086405e3, 3.112233e3, 3.138276e3, + 3.164538e3, 3.191019e3, 3.217722e3, 3.244649e3, 3.271800e3, 3.299179e3, + 3.326787e3, 3.354626e3, 3.382698e3, 3.411005e3, 3.439549e3, 3.468332e3, + 3.497355e3, 3.526622e3, 3.556133e3, 3.585891e3, 3.615898e3, 3.646157e3, + 3.676668e3, 3.707435e3, 3.738460e3, 3.769744e3, 3.801290e3, 3.833099e3, + 3.865175e3, 3.897520e3, 3.930135e3, 3.963023e3, 3.996186e3, 4.029627e3, + 4.063347e3, 4.097350e3, 4.131637e3, 4.166211e3, 4.201075e3, 4.236230e3, + 4.271679e3, 4.307425e3, 4.343471e3, 4.379817e3, 4.416468e3, 4.453426e3, + 4.490693e3, 4.528272e3, 4.566165e3, 4.604375e3, 4.642906e3, 4.681758e3, + 4.720936e3, 4.760441e3, 4.800277e3, 4.840447e3, 4.880952e3, 4.921797e3, + 4.962983e3, 5.004514e3, 5.046393e3, 5.088622e3, 5.131204e3, 5.174143e3, + 5.217441e3, 5.261101e3, 5.305127e3, 5.349521e3, 5.394287e3, 5.439427e3, + 5.484945e3, 5.530844e3, 5.577127e3, 5.623797e3, 5.670858e3, 5.718312e3, + 5.766164e3, 5.814416e3, 5.863072e3, 5.912135e3, 5.961609e3, 6.011496e3, + 6.061802e3, 6.112528e3, 6.163678e3, 6.215257e3, 6.267267e3, 6.319712e3, + 6.372597e3, 6.425924e3, 6.479697e3, 6.533920e3, 6.588597e3, 6.643731e3, + 6.699327e3, 6.755388e3, 6.811918e3, 6.868921e3, 6.926401e3, 6.984362e3, + 7.042809e3, 7.101744e3, 7.161172e3, 7.221098e3, 7.281525e3, 7.342458e3, + 7.403901e3, 7.465858e3, 7.528334e3, 7.591332e3, 7.654857e3, 7.718914e3, + 7.783507e3, 7.848641e3, 7.914319e3, 7.980548e3, 8.047330e3, 8.114671e3, + 8.182576e3, 8.251049e3, 8.320095e3, 8.389719e3, 8.459926e3, 8.530719e3, + 8.602106e3, 8.674090e3, 8.746676e3, 8.819869e3, 8.893675e3, 8.968099e3, + 9.043145e3, 9.118820e3, 9.195127e3, 9.272074e3, 9.349664e3, 9.427903e3, + 9.506797e3, 9.586352e3, 9.666572e3, 9.747463e3, 9.829031e3, 9.911282e3, + 9.994221e3, 1.007785e4, 1.016219e4, 1.024723e4, 1.033298e4, 1.041944e4, + 1.050664e4, 1.059456e4, 1.068321e4, 1.077261e4, 1.086276e4, 1.095366e4, + 1.104532e4, 1.113775e4, 1.123095e4, 1.132494e4, 1.141970e4, 1.151527e4, + 1.161163e4, 1.170880e4, 1.180678e4, 1.190558e4, 1.200521e4, 1.210567e4, + 1.220697e4, 1.230912e4, 1.241212e4, 1.251599e4, 1.262073e4, 1.272634e4, + 1.283283e4, 1.294022e4, 1.304851e4, 1.315770e4, 1.326780e4, 1.337883e4, + 1.349079e4, 1.360368e4, 1.371752e4, 1.383231e4, 1.394806e4, 1.406478e4, + 1.418247e4, 1.430116e4, 1.442083e4, 1.454151e4, 1.466319e4, 1.478590e4, + 1.490963e4, 1.503439e4, 1.516020e4, 1.528706e4, 1.541499e4, 1.554398e4, + 1.567406e4, 1.580522e4, 1.593748e4, 1.607085e4, 1.620533e4, 1.634094e4, + 1.647768e4, 1.661557e4, 1.675461e4, 1.689482e4, 1.703620e4, 1.717876e4, + 1.732251e4, 1.746747e4, 1.761364e4, 1.776104e4, 1.790966e4, 1.805953e4, + 1.821066e4, 1.836305e4, 1.851671e4, 1.867166e4, 1.882791e4, 1.898547e4, + 1.914434e4, 1.930454e4, 1.946608e4, 1.962898e4, 1.979324e4, 1.995887e4, + 2.012589e4, 2.029431e4, 2.046413e4, 2.063538e4, 2.080806e4, 2.098218e4, + 2.115777e4, 2.133482e4, 2.151335e4, 2.169338e4, 2.187491e4, 2.205796e4, + 2.224255e4, 2.242868e4, 2.261636e4, 2.280562e4, 2.299646e4, 2.318890e4, + 2.338295e4, 2.357862e4, 2.377593e4, 2.397489e4, 2.417552e4, 2.437782e4, + 2.458182e4, 2.478752e4, 2.499495e4, 2.520411e4, 2.541502e4, 2.562770e4, + 2.584215e4, 2.605841e4, 2.627647e4, 2.649635e4, 2.671808e4, 2.694166e4, + 2.700000e4, 2.716711e4, 2.739445e4, 2.762369e4, 2.785485e4, 2.808794e4, + 2.832299e4, 2.850000e4, 2.856000e4, 2.879899e4, 2.903999e4, 2.928300e4, + 2.952804e4, 2.977514e4, 3.002430e4, 3.027555e4, 3.052890e4, 3.078437e4, + 3.104198e4, 3.130174e4, 3.156368e4, 3.182781e4, 3.209415e4, 3.236272e4, + 3.263353e4, 3.290662e4, 3.318198e4, 3.345965e4, 3.373965e4, 3.402199e4, + 3.430669e4, 3.459377e4, 3.488326e4, 3.517517e4, 3.546952e4, 3.576633e4, + 3.606563e4, 3.636743e4, 3.667176e4, 3.697864e4, 3.728808e4, 3.760011e4, + 3.791476e4, 3.823203e4, 3.855196e4, 3.887457e4, 3.919988e4, 3.952791e4, + 3.985869e4, 4.019223e4, 4.052857e4, 4.086771e4, 4.120970e4, 4.155455e4, + 4.190229e4, 4.225293e4, 4.260651e4, 4.296305e4, 4.332257e4, 4.368510e4, + 4.405066e4, 4.441928e4, 4.479099e4, 4.516581e4, 4.554376e4, 4.592488e4, + 4.630919e4, 4.669671e4, 4.708747e4, 4.748151e4, 4.787884e4, 4.827950e4, + 4.868351e4, 4.909090e4, 4.950170e4, 4.991594e4, 5.033364e4, 5.075484e4, + 5.117957e4, 5.160785e4, 5.203971e4, 5.247518e4, 5.291430e4, 5.335710e4, + 5.380360e4, 5.425384e4, 5.470784e4, 5.516564e4, 5.562728e4, 5.609278e4, + 5.656217e4, 5.703549e4, 5.751277e4, 5.799405e4, 5.847935e4, 5.896871e4, + 5.946217e4, 5.995976e4, 6.046151e4, 6.096747e4, 6.147765e4, 6.199211e4, + 6.251086e4, 6.303396e4, 6.356144e4, 6.409333e4, 6.462968e4, 6.517051e4, + 6.571586e4, 6.626579e4, 6.682031e4, 6.737947e4, 6.794331e4, 6.851187e4, + 6.908519e4, 6.966330e4, 7.024626e4, 7.083409e4, 7.142684e4, 7.202455e4, + 7.262726e4, 7.323502e4, 7.384786e4, 7.446583e4, 7.508897e4, 7.571733e4, + 7.635094e4, 7.698986e4, 7.763412e4, 7.828378e4, 7.893887e4, 7.950000e4, + 7.959944e4, 8.026554e4, 8.093721e4, 8.161451e4, 8.229747e4, 8.250000e4, + 8.298615e4, 8.368059e4, 8.438084e4, 8.508695e4, 8.579897e4, 8.651695e4, + 8.724094e4, 8.797098e4, 8.870714e4, 8.944945e4, 9.019798e4, 9.095277e4, + 9.171388e4, 9.248135e4, 9.325525e4, 9.403563e4, 9.482253e4, 9.561602e4, + 9.641615e4, 9.722297e4, 9.803655e4, 9.885694e4, 9.968419e4, 1.005184e5, + 1.013595e5, 1.022077e5, 1.030630e5, 1.039254e5, 1.047951e5, 1.056720e5, + 1.065563e5, 1.074480e5, 1.083471e5, 1.092538e5, 1.101681e5, 1.110900e5, + 1.120196e5, 1.129570e5, 1.139022e5, 1.148554e5, 1.158165e5, 1.167857e5, + 1.177629e5, 1.187484e5, 1.197421e5, 1.207441e5, 1.217545e5, 1.227734e5, + 1.238008e5, 1.248368e5, 1.258814e5, 1.269348e5, 1.279970e5, 1.290681e5, + 1.301482e5, 1.312373e5, 1.323355e5, 1.334429e5, 1.345596e5, 1.356856e5, + 1.368210e5, 1.379660e5, 1.391205e5, 1.402847e5, 1.414586e5, 1.426423e5, + 1.438360e5, 1.450396e5, 1.462533e5, 1.474772e5, 1.487113e5, 1.499558e5, + 1.512106e5, 1.524760e5, 1.537519e5, 1.550385e5, 1.563359e5, 1.576442e5, + 1.589634e5, 1.602936e5, 1.616349e5, 1.629875e5, 1.643514e5, 1.657268e5, + 1.671136e5, 1.685120e5, 1.699221e5, 1.713441e5, 1.727779e5, 1.742237e5, + 1.756817e5, 1.771518e5, 1.786342e5, 1.801291e5, 1.816364e5, 1.831564e5, + 1.846891e5, 1.862346e5, 1.877930e5, 1.893645e5, 1.909491e5, 1.925470e5, + 1.941583e5, 1.957830e5, 1.974214e5, 1.990734e5, 2.007393e5, 2.024191e5, + 2.041130e5, 2.058210e5, 2.075434e5, 2.092801e5, 2.110314e5, 2.127974e5, + 2.145781e5, 2.163737e5, 2.181844e5, 2.200102e5, 2.218512e5, 2.237077e5, + 2.255797e5, 2.274674e5, 2.293709e5, 2.312903e5, 2.332258e5, 2.351775e5, + 2.371455e5, 2.391299e5, 2.411310e5, 2.431488e5, 2.451835e5, 2.472353e5, + 2.493042e5, 2.513904e5, 2.534941e5, 2.556153e5, 2.577544e5, 2.599113e5, + 2.620863e5, 2.642794e5, 2.664910e5, 2.687210e5, 2.709697e5, 2.732372e5, + 2.755237e5, 2.778293e5, 2.801543e5, 2.824986e5, 2.848626e5, 2.872464e5, + 2.896501e5, 2.920740e5, 2.945181e5, 2.969826e5, 2.972000e5, 2.985000e5, + 2.994678e5, 3.019738e5, 3.045008e5, 3.070489e5, 3.096183e5, 3.122093e5, + 3.148219e5, 3.174564e5, 3.201129e5, 3.227916e5, 3.254928e5, 3.282166e5, + 3.309631e5, 3.337327e5, 3.365254e5, 3.393415e5, 3.421812e5, 3.450446e5, + 3.479320e5, 3.508435e5, 3.537795e5, 3.567399e5, 3.597252e5, 3.627354e5, + 3.657708e5, 3.688317e5, 3.719181e5, 3.750304e5, 3.781687e5, 3.813333e5, + 3.845243e5, 3.877421e5, 3.909868e5, 3.942586e5, 3.975578e5, 4.008846e5, + 4.042393e5, 4.076220e5, 4.110331e5, 4.144727e5, 4.179410e5, 4.214384e5, + 4.249651e5, 4.285213e5, 4.321072e5, 4.357231e5, 4.393693e5, 4.430460e5, + 4.467535e5, 4.504920e5, 4.542618e5, 4.580631e5, 4.618963e5, 4.657615e5, + 4.696591e5, 4.735892e5, 4.775523e5, 4.815485e5, 4.855782e5, 4.896416e5, + 4.937390e5, 4.978707e5, 5.020369e5, 5.062381e5, 5.104743e5, 5.147461e5, + 5.190535e5, 5.233971e5, 5.277769e5, 5.321934e5, 5.366469e5, 5.411377e5, + 5.456660e5, 5.502322e5, 5.548366e5, 5.594796e5, 5.641614e5, 5.688824e5, + 5.736429e5, 5.784432e5, 5.832837e5, 5.881647e5, 5.930866e5, 5.980496e5, + 6.030542e5, 6.081006e5, 6.131893e5, 6.183206e5, 6.234948e5, 6.287123e5, + 6.339734e5, 6.392786e5, 6.446282e5, 6.500225e5, 6.554620e5, 6.609470e5, + 6.664779e5, 6.720551e5, 6.776790e5, 6.833499e5, 6.890683e5, 6.948345e5, + 7.006490e5, 7.065121e5, 7.124243e5, 7.183860e5, 7.243976e5, 7.304594e5, + 7.365720e5, 7.427358e5, 7.489511e5, 7.552184e5, 7.615382e5, 7.679109e5, + 7.743369e5, 7.808167e5, 7.873507e5, 7.939393e5, 8.005831e5, 8.072825e5, + 8.140380e5, 8.208500e5, 8.277190e5, 8.346455e5, 8.416299e5, 8.486728e5, + 8.557746e5, 8.629359e5, 8.701570e5, 8.774387e5, 8.847812e5, 8.921852e5, + 8.996511e5, 9.071795e5, 9.147709e5, 9.224259e5, 9.301449e5, 9.379285e5, + 9.457772e5, 9.536916e5, 9.616723e5, 9.697197e5, 9.778344e5, 9.860171e5, + 9.942682e5, 1.002588e6, 1.010978e6, 1.019438e6, 1.027969e6, 1.036571e6, + 1.045245e6, 1.053992e6, 1.062812e6, 1.071706e6, 1.080674e6, 1.089717e6, + 1.098836e6, 1.108032e6, 1.117304e6, 1.126654e6, 1.136082e6, 1.145588e6, + 1.155175e6, 1.164842e6, 1.174589e6, 1.184418e6, 1.194330e6, 1.204324e6, + 1.214402e6, 1.224564e6, 1.234812e6, 1.245145e6, 1.255564e6, 1.266071e6, + 1.276666e6, 1.287349e6, 1.298122e6, 1.308985e6, 1.319938e6, 1.330984e6, + 1.342122e6, 1.353353e6, 1.364678e6, 1.376098e6, 1.387613e6, 1.399225e6, + 1.410934e6, 1.422741e6, 1.434646e6, 1.446652e6, 1.458758e6, 1.470965e6, + 1.483274e6, 1.495686e6, 1.508202e6, 1.520823e6, 1.533550e6, 1.546383e6, + 1.559323e6, 1.572372e6, 1.585530e6, 1.598797e6, 1.612176e6, 1.625667e6, + 1.639271e6, 1.652989e6, 1.666821e6, 1.680770e6, 1.694834e6, 1.709017e6, + 1.723318e6, 1.737739e6, 1.752281e6, 1.766944e6, 1.781731e6, 1.796640e6, + 1.811675e6, 1.826835e6, 1.842122e6, 1.857538e6, 1.873082e6, 1.888756e6, + 1.904561e6, 1.920499e6, 1.936570e6, 1.952776e6, 1.969117e6, 1.985595e6, + 2.002210e6, 2.018965e6, 2.035860e6, 2.052897e6, 2.070076e6, 2.087398e6, + 2.104866e6, 2.122480e6, 2.140241e6, 2.158151e6, 2.176211e6, 2.194421e6, + 2.212785e6, 2.231302e6, 2.249973e6, 2.268802e6, 2.287787e6, 2.306932e6, + 2.326237e6, 2.345703e6, 2.365332e6, 2.385126e6, 2.405085e6, 2.425211e6, + 2.445505e6, 2.465970e6, 2.486605e6, 2.507414e6, 2.528396e6, 2.549554e6, + 2.570889e6, 2.592403e6, 2.614096e6, 2.635971e6, 2.658030e6, 2.680272e6, + 2.702701e6, 2.725318e6, 2.748124e6, 2.771121e6, 2.794310e6, 2.817693e6, + 2.841272e6, 2.865048e6, 2.889023e6, 2.913199e6, 2.937577e6, 2.962159e6, + 2.986947e6, 3.011942e6, 3.037147e6, 3.062562e6, 3.088190e6, 3.114032e6, + 3.140091e6, 3.166368e6, 3.192864e6, 3.219583e6, 3.246525e6, 3.273692e6, + 3.301087e6, 3.328711e6, 3.356566e6, 3.384654e6, 3.412978e6, 3.441538e6, + 3.470337e6, 3.499377e6, 3.528661e6, 3.558189e6, 3.587965e6, 3.617989e6, + 3.648265e6, 3.678794e6, 3.709579e6, 3.740621e6, 3.771924e6, 3.803488e6, + 3.835316e6, 3.867410e6, 3.899773e6, 3.932407e6, 3.965314e6, 3.998497e6, + 4.031957e6, 4.065697e6, 4.099719e6, 4.134026e6, 4.168620e6, 4.203504e6, + 4.238679e6, 4.274149e6, 4.309916e6, 4.345982e6, 4.382350e6, 4.419022e6, + 4.456001e6, 4.493290e6, 4.530890e6, 4.568805e6, 4.607038e6, 4.645590e6, + 4.684465e6, 4.723666e6, 4.763194e6, 4.803053e6, 4.843246e6, 4.883775e6, + 4.924643e6, 4.965853e6, 5.007408e6, 5.049311e6, 5.091564e6, 5.134171e6, + 5.177135e6, 5.220458e6, 5.264143e6, 5.308195e6, 5.352614e6, 5.397406e6, + 5.442572e6, 5.488116e6, 5.534042e6, 5.580351e6, 5.627049e6, 5.674137e6, + 5.721619e6, 5.769498e6, 5.817778e6, 5.866462e6, 5.915554e6, 5.965056e6, + 6.014972e6, 6.065307e6, 6.116062e6, 6.167242e6, 6.218851e6, 6.270891e6, + 6.323367e6, 6.376282e6, 6.429639e6, 6.483443e6, 6.537698e6, 6.592406e6, + 6.647573e6, 6.703200e6, 6.759294e6, 6.815857e6, 6.872893e6, 6.930406e6, + 6.988401e6, 7.046881e6, 7.105850e6, 7.165313e6, 7.225274e6, 7.285736e6, + 7.346704e6, 7.408182e6, 7.470175e6, 7.532687e6, 7.595721e6, 7.659283e6, + 7.723377e6, 7.788008e6, 7.853179e6, 7.918896e6, 7.985162e6, 8.051983e6, + 8.119363e6, 8.187308e6, 8.255820e6, 8.324906e6, 8.394570e6, 8.464817e6, + 8.535652e6, 8.607080e6, 8.679105e6, 8.751733e6, 8.824969e6, 8.898818e6, + 8.973284e6, 9.048374e6, 9.124092e6, 9.200444e6, 9.277435e6, 9.355070e6, + 9.433354e6, 9.512294e6, 9.591895e6, 9.672161e6, 9.753099e6, 9.834715e6, + 9.917013e6, 1.000000e7, 1.008368e7, 1.016806e7, 1.025315e7, 1.033895e7, + 1.042547e7, 1.051271e7, 1.060068e7, 1.068939e7, 1.077884e7, 1.086904e7, + 1.095999e7, 1.105171e7, 1.114419e7, 1.123745e7, 1.133148e7, 1.142631e7, + 1.152193e7, 1.161834e7, 1.171557e7, 1.181360e7, 1.191246e7, 1.201215e7, + 1.211267e7, 1.221403e7, 1.231624e7, 1.241930e7, 1.252323e7, 1.262802e7, + 1.273370e7, 1.284025e7, 1.294770e7, 1.305605e7, 1.316531e7, 1.327548e7, + 1.338657e7, 1.349859e7, 1.361155e7, 1.372545e7, 1.384031e7, 1.395612e7, + 1.407291e7, 1.419068e7, 1.430943e7, 1.442917e7, 1.454991e7, 1.467167e7, + 1.479444e7, 1.491825e7, 1.504309e7, 1.516897e7, 1.529590e7, 1.542390e7, + 1.555297e7, 1.568312e7, 1.581436e7, 1.594670e7, 1.608014e7, 1.621470e7, + 1.635039e7, 1.648721e7, 1.662518e7, 1.676430e7, 1.690459e7, 1.704605e7, + 1.718869e7, 1.733253e7, 1.747757e7, 1.762383e7, 1.777131e7, 1.792002e7, + 1.806998e7, 1.822119e7, 1.837367e7, 1.852742e7, 1.868246e7, 1.883880e7, + 1.899644e7, 1.915541e7, 1.931570e7, 1.947734e7, 1.964033e7]) diff --git a/openmc/mgxs/groups.py b/openmc/mgxs/groups.py index f26218e031..182402ed7f 100644 --- a/openmc/mgxs/groups.py +++ b/openmc/mgxs/groups.py @@ -5,30 +5,36 @@ from numbers import Real import numpy as np import openmc.checkvalue as cv +import openmc.mgxs class EnergyGroups: - """An energy groups structure used for multi-group cross-sections. + """An energy group structure used for multigroup cross-sections. Parameters ---------- - group_edges : Iterable of Real - The energy group boundaries [eV] + group_edges : Iterable of float or str + The energy group boundaries in [eV] or the name of the group structure + (Must be a valid key in the openmc.mgxs.GROUP_STRUCTURES dictionary). + + .. versionchanged:: 0.14.0 + Changed to allow a string specifying the group structure name. Attributes ---------- - group_edges : Iterable of Real - The energy group boundaries [eV] + group_edges : np.ndarray + The energy group boundaries in [eV] num_groups : int The number of energy groups """ - def __init__(self, group_edges=None): - self._group_edges = None + def __init__(self, group_edges): + if isinstance(group_edges, str): + self._name = group_edges.upper() + group_edges = openmc.mgxs.GROUP_STRUCTURES[self._name] - if group_edges is not None: - self.group_edges = group_edges + self.group_edges = group_edges def __deepcopy__(self, memo): existing = memo.get(id(self)) @@ -59,20 +65,26 @@ class EnergyGroups: def __hash__(self): return hash(tuple(self.group_edges)) + def __repr__(self): + if hasattr(self, '_name'): + return f"" + else: + return f"" + @property def group_edges(self): return self._group_edges - @property - def num_groups(self): - return len(self.group_edges) - 1 - @group_edges.setter def group_edges(self, edges): cv.check_type('group edges', edges, Iterable, Real) cv.check_greater_than('number of group edges', len(edges), 1) self._group_edges = np.array(edges) + @property + def num_groups(self): + return len(self.group_edges) - 1 + def get_group(self, energy): """Returns the energy group in which the given energy resides. @@ -164,7 +176,7 @@ class EnergyGroups: if groups == 'all': return np.arange(self.num_groups) else: - indices = np.zeros(len(groups), dtype=np.int) + indices = np.zeros(len(groups), dtype=int) for i, group in enumerate(groups): cv.check_greater_than('group', group, 0) @@ -226,10 +238,7 @@ class EnergyGroups: group_edges = np.sort(group_edges) # Create a new condensed EnergyGroups object - condensed_groups = EnergyGroups() - condensed_groups.group_edges = group_edges - - return condensed_groups + return EnergyGroups(group_edges) def can_merge(self, other): """Determine if energy groups can be merged with another. diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 5c70720334..12a4630bdc 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -1,4 +1,3 @@ -from collections import OrderedDict from collections.abc import Iterable import copy from numbers import Integral @@ -72,13 +71,18 @@ class Library: Number of equi-width polar angle bins for angle discretization num_azimuthal : Integral Number of equi-width azimuthal angle bins for angle discretization + nuclides : Iterable of str or 'sum' + The optional user-specified nuclides for which to compute cross + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides + are not specified by the user, all nuclides in the domain + are included. estimator : str or None The tally estimator used to compute multi-group cross sections. If None, the default for each MGXS type is used. tally_trigger : openmc.Trigger An (optional) tally precision trigger given to each tally used to compute the cross section - all_mgxs : collections.OrderedDict + all_mgxs : dict MGXS objects keyed by domain ID and cross section type sp_filename : str The filename of the statepoint with tally data used to the @@ -107,13 +111,14 @@ class Library: self._energy_groups = None self._num_polar = 1 self._num_azimuthal = 1 + self._nuclides = None self._num_delayed_groups = 0 self._correction = 'P0' self._scatter_format = 'legendre' self._legendre_order = 0 self._histogram_bins = 16 self._tally_trigger = None - self._all_mgxs = OrderedDict() + self._all_mgxs = {} self._sp_filename = None self._keff = None self._sparse = False @@ -145,6 +150,7 @@ class Library: clone._energy_groups = copy.deepcopy(self.energy_groups, memo) clone._num_polar = self.num_polar clone._num_azimuthal = self.num_azimuthal + clone._nuclides = self._nuclides clone._num_delayed_groups = self.num_delayed_groups clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo) clone._all_mgxs = copy.deepcopy(self.all_mgxs) @@ -152,9 +158,9 @@ class Library: clone._keff = self._keff clone._sparse = self.sparse - clone._all_mgxs = OrderedDict() + clone._all_mgxs = {} for domain in self.domains: - clone.all_mgxs[domain.id] = OrderedDict() + clone.all_mgxs[domain.id] = {} for mgxs_type in self.mgxs_types: mgxs = copy.deepcopy(self.all_mgxs[domain.id][mgxs_type]) clone.all_mgxs[domain.id][mgxs_type] = mgxs @@ -171,22 +177,65 @@ class Library: def geometry(self): return self._geometry + @geometry.setter + def geometry(self, geometry): + cv.check_type('geometry', geometry, openmc.Geometry) + self._geometry = geometry + @property def name(self): return self._name + @name.setter + def name(self, name): + cv.check_type('name', name, str) + self._name = name + @property def mgxs_types(self): return self._mgxs_types + @mgxs_types.setter + def mgxs_types(self, mgxs_types): + all_mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES + \ + openmc.mgxs.ARBITRARY_VECTOR_TYPES + \ + openmc.mgxs.ARBITRARY_MATRIX_TYPES + if mgxs_types == 'all': + self._mgxs_types = all_mgxs_types + else: + cv.check_iterable_type('mgxs_types', mgxs_types, str) + for mgxs_type in mgxs_types: + cv.check_value('mgxs_type', mgxs_type, all_mgxs_types) + self._mgxs_types = mgxs_types + @property def by_nuclide(self): return self._by_nuclide + @by_nuclide.setter + def by_nuclide(self, by_nuclide): + cv.check_type('by_nuclide', by_nuclide, bool) + + if by_nuclide and self.domain_type == 'mesh': + raise ValueError('Unable to create MGXS library by nuclide with ' + 'mesh domain') + + self._by_nuclide = by_nuclide + @property def domain_type(self): return self._domain_type + @domain_type.setter + def domain_type(self, domain_type): + cv.check_value('domain type', domain_type, openmc.mgxs.DOMAIN_TYPES) + + if self.by_nuclide and domain_type == 'mesh': + raise ValueError('Unable to create MGXS library by nuclide with ' + 'mesh domain') + + self._domain_type = domain_type + @property def domains(self): if self._domains == 'all': @@ -205,109 +254,6 @@ class Library: else: return self._domains - @property - def energy_groups(self): - return self._energy_groups - - @property - def num_delayed_groups(self): - return self._num_delayed_groups - - @property - def num_polar(self): - return self._num_polar - - @property - def num_azimuthal(self): - return self._num_azimuthal - - @property - def correction(self): - return self._correction - - @property - def scatter_format(self): - return self._scatter_format - - @property - def legendre_order(self): - return self._legendre_order - - @property - def histogram_bins(self): - return self._histogram_bins - - @property - def tally_trigger(self): - return self._tally_trigger - - @property - def estimator(self): - return self._estimator - - @property - def num_groups(self): - return self.energy_groups.num_groups - - @property - def all_mgxs(self): - return self._all_mgxs - - @property - def sp_filename(self): - return self._sp_filename - - @property - def keff(self): - return self._keff - - @property - def sparse(self): - return self._sparse - - @geometry.setter - def geometry(self, geometry): - cv.check_type('geometry', geometry, openmc.Geometry) - self._geometry = geometry - - @name.setter - def name(self, name): - cv.check_type('name', name, str) - self._name = name - - @mgxs_types.setter - def mgxs_types(self, mgxs_types): - all_mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES + \ - openmc.mgxs.ARBITRARY_VECTOR_TYPES + \ - openmc.mgxs.ARBITRARY_MATRIX_TYPES - if mgxs_types == 'all': - self._mgxs_types = all_mgxs_types - else: - cv.check_iterable_type('mgxs_types', mgxs_types, str) - for mgxs_type in mgxs_types: - cv.check_value('mgxs_type', mgxs_type, all_mgxs_types) - self._mgxs_types = mgxs_types - - @by_nuclide.setter - def by_nuclide(self, by_nuclide): - cv.check_type('by_nuclide', by_nuclide, bool) - - if by_nuclide and self.domain_type == 'mesh': - raise ValueError('Unable to create MGXS library by nuclide with ' - 'mesh domain') - - self._by_nuclide = by_nuclide - - @domain_type.setter - def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, openmc.mgxs.DOMAIN_TYPES) - - if self.by_nuclide and domain_type == 'mesh': - raise ValueError('Unable to create MGXS library by nuclide with ' - 'mesh domain') - - self._domain_type = domain_type - @domains.setter def domains(self, domains): @@ -347,11 +293,28 @@ class Library: self._domains = list(domains) + @property + def nuclides(self): + return self._nuclides + + @nuclides.setter + def nuclides(self, nuclides): + cv.check_iterable_type('nuclides', nuclides, str) + self._nuclides = nuclides + + @property + def energy_groups(self): + return self._energy_groups + @energy_groups.setter def energy_groups(self, energy_groups): cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) self._energy_groups = energy_groups + @property + def num_delayed_groups(self): + return self._num_delayed_groups + @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): @@ -361,18 +324,30 @@ class Library: equality=True) self._num_delayed_groups = num_delayed_groups + @property + def num_polar(self): + return self._num_polar + @num_polar.setter def num_polar(self, num_polar): cv.check_type('num_polar', num_polar, Integral) cv.check_greater_than('num_polar', num_polar, 0) self._num_polar = num_polar + @property + def num_azimuthal(self): + return self._num_azimuthal + @num_azimuthal.setter def num_azimuthal(self, num_azimuthal): cv.check_type('num_azimuthal', num_azimuthal, Integral) cv.check_greater_than('num_azimuthal', num_azimuthal, 0) self._num_azimuthal = num_azimuthal + @property + def correction(self): + return self._correction + @correction.setter def correction(self, correction): cv.check_value('correction', correction, ('P0', None)) @@ -390,6 +365,10 @@ class Library: self._correction = correction + @property + def scatter_format(self): + return self._scatter_format + @scatter_format.setter def scatter_format(self, scatter_format): cv.check_value('scatter_format', scatter_format, @@ -403,6 +382,10 @@ class Library: self._scatter_format = scatter_format + @property + def legendre_order(self): + return self._legendre_order + @legendre_order.setter def legendre_order(self, legendre_order): cv.check_type('legendre_order', legendre_order, Integral) @@ -424,6 +407,10 @@ class Library: self._legendre_order = legendre_order + @property + def histogram_bins(self): + return self._histogram_bins + @histogram_bins.setter def histogram_bins(self, histogram_bins): cv.check_type('histogram_bins', histogram_bins, Integral) @@ -443,16 +430,44 @@ class Library: self._histogram_bins = histogram_bins + @property + def tally_trigger(self): + return self._tally_trigger + @tally_trigger.setter def tally_trigger(self, tally_trigger): cv.check_type('tally trigger', tally_trigger, openmc.Trigger) self._tally_trigger = tally_trigger + @property + def estimator(self): + return self._estimator + @estimator.setter def estimator(self, estimator): cv.check_value('estimator', estimator, ESTIMATOR_TYPES) self._estimator = estimator + @property + def num_groups(self): + return self.energy_groups.num_groups + + @property + def all_mgxs(self): + return self._all_mgxs + + @property + def sp_filename(self): + return self._sp_filename + + @property + def keff(self): + return self._keff + + @property + def sparse(self): + return self._sparse + @sparse.setter def sparse(self, sparse): """Convert tally data from NumPy arrays to SciPy list of lists (LIL) @@ -487,7 +502,7 @@ class Library: # Initialize MGXS for each domain and mgxs type and store in dictionary for domain in self.domains: - self.all_mgxs[domain.id] = OrderedDict() + self.all_mgxs[domain.id] = {} for mgxs_type in self.mgxs_types: if mgxs_type in openmc.mgxs.MDGXS_TYPES: mgxs = openmc.mgxs.MDGXS.get_mgxs( @@ -524,6 +539,20 @@ class Library: mgxs.legendre_order = self.legendre_order mgxs.histogram_bins = self.histogram_bins + if self.by_nuclide: + try: + domain_nuclides = domain.get_nuclides() + except AttributeError: + domain_nuclides = None + if self.nuclides: + if domain_nuclides: + mgxs.nuclides = [ + nuclide for nuclide in self.nuclides + if nuclide in domain_nuclides + ] + ["total"] + else: + mgxs.nuclides = self.nuclides + self.all_mgxs[domain.id][mgxs_type] = mgxs def add_to_tallies_file(self, tallies_file, merge=True): @@ -590,10 +619,10 @@ class Library: self._sp_filename = statepoint._f.filename self._geometry = statepoint.summary.geometry - self._nuclides = statepoint.summary.nuclides + self._atomic_weight_ratios = statepoint.summary.nuclides if statepoint.run_mode == 'eigenvalue': - self._keff = statepoint.k_combined.n + self._keff = statepoint.keff.n # Load tallies for each MGXS for each domain and mgxs type for domain in self.domains: @@ -656,7 +685,7 @@ class Library: # Check that requested domain is included in library if mgxs_type not in self.mgxs_types: - msg = 'Unable to find MGXS type "{0}"'.format(mgxs_type) + msg = f'Unable to find MGXS type "{mgxs_type}"' raise ValueError(msg) return self.all_mgxs[domain_id][mgxs_type] @@ -872,7 +901,7 @@ class Library: if not os.path.exists(directory): os.makedirs(directory) - full_filename = os.path.join(directory, '{}.pkl'.format(filename)) + full_filename = os.path.join(directory, f'{filename}.pkl') full_filename = full_filename.replace(' ', '-') # Load and return pickled Library object @@ -1005,7 +1034,7 @@ class Library: xsdata.num_azimuthal = self.num_azimuthal if nuclide != 'total': - xsdata.atomic_weight_ratio = self._nuclides[nuclide] + xsdata.atomic_weight_ratio = self._atomic_weight_ratios[nuclide] if subdomain is None: subdomain = 'all' diff --git a/openmc/mgxs/mdgxs.py b/openmc/mgxs/mdgxs.py index 7a139c7f87..b95a4fbc0e 100644 --- a/openmc/mgxs/mdgxs.py +++ b/openmc/mgxs/mdgxs.py @@ -1,4 +1,3 @@ -from collections import OrderedDict import copy import itertools from numbers import Integral @@ -21,7 +20,7 @@ MDGXS_TYPES = ( 'delayed-nu-fission matrix' ) -# Maximum number of delayed groups, from src/constants.F90 +# Maximum number of delayed groups, from include/openmc/constants.h MAX_DELAYED_GROUPS = 8 @@ -90,7 +89,7 @@ class MDGXS(MGXS): the multi-group cross section estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to @@ -110,7 +109,7 @@ class MDGXS(MGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -120,8 +119,11 @@ class MDGXS(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -158,7 +160,7 @@ class MDGXS(MGXS): clone._sparse = self.sparse clone._derived = self.derived - clone._tallies = OrderedDict() + clone._tallies = {} for tally_type, tally in self.tallies.items(): clone.tallies[tally_type] = copy.deepcopy(tally, memo) @@ -184,13 +186,6 @@ class MDGXS(MGXS): def delayed_groups(self): return self._delayed_groups - @property - def num_delayed_groups(self): - if self.delayed_groups is None: - return 1 - else: - return len(self.delayed_groups) - @delayed_groups.setter def delayed_groups(self, delayed_groups): @@ -207,6 +202,13 @@ class MDGXS(MGXS): self._delayed_groups = delayed_groups + @property + def num_delayed_groups(self): + if self.delayed_groups is None: + return 1 + else: + return len(self.delayed_groups) + @property def filters(self): @@ -214,7 +216,7 @@ class MDGXS(MGXS): group_edges = self.energy_groups.group_edges energy_filter = openmc.EnergyFilter(group_edges) - if self.delayed_groups != None: + if self.delayed_groups is not None: delayed_filter = openmc.DelayedGroupFilter(self.delayed_groups) filters = [[energy_filter], [delayed_filter, energy_filter]] else: @@ -303,7 +305,7 @@ class MDGXS(MGXS): subdomains : Iterable of Integral or 'all' Subdomain IDs of interest. Defaults to 'all'. nuclides : Iterable of str or 'all' or 'sum' - A list of nuclide name strings (e.g., ['U-235', 'U-238']). The + A list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will return the cross sections for all nuclides in the spatial domain. The special string 'sum' will return the cross section summed over all nuclides. Defaults to 'all'. @@ -453,7 +455,7 @@ class MDGXS(MGXS): ---------- nuclides : list of str A list of nuclide name strings - (e.g., ['U-235', 'U-238']; default is []) + (e.g., ['U235', 'U238']; default is []) groups : list of int A list of energy group indices starting at 1 for the high energies (e.g., [1, 2, 3]; default is []) @@ -565,7 +567,7 @@ class MDGXS(MGXS): Defaults to 'all'. nuclides : Iterable of str or 'all' or 'sum' The nuclides of the cross-sections to include in the report. This - may be a list of nuclide name strings (e.g., ['U-235', 'U-238']). + may be a list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will report the cross sections for all nuclides in the spatial domain. The special string 'sum' will report the cross sections summed over all nuclides. Defaults to 'all'. @@ -583,7 +585,7 @@ class MDGXS(MGXS): if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'mesh': xyz = [range(1, x + 1) for x in self.domain.dimension] subdomains = list(itertools.product(*xyz)) @@ -605,12 +607,12 @@ class MDGXS(MGXS): # Build header for string with type and domain info string = 'Multi-Delayed-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type) + string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) # Generate the header for an individual XS - xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type)) + xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' # If cross section data has not been computed, only print string header if self.tallies is None: @@ -639,7 +641,7 @@ class MDGXS(MGXS): string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) # Add the cross section header - string += '{0: <16}\n'.format(xs_header) + string += f'{xs_header: <16}\n' for delayed_group in self.delayed_groups: @@ -741,9 +743,9 @@ class MDGXS(MGXS): df.to_csv(filename + '.csv', index=False) elif format == 'excel': if self.domain_type == 'mesh': - df.to_excel(filename + '.xls') + df.to_excel(filename + '.xlsx') else: - df.to_excel(filename + '.xls', index=False) + df.to_excel(filename + '.xlsx', index=False) elif format == 'pickle': df.to_pickle(filename + '.pkl') elif format == 'latex': @@ -782,7 +784,7 @@ class MDGXS(MGXS): Energy groups of interest. Defaults to 'all'. nuclides : Iterable of str or 'all' or 'sum' The nuclides of the cross-sections to include in the dataframe. This - may be a list of nuclide name strings (e.g., ['U-235', 'U-238']). + may be a list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will include the cross sections for all nuclides in the spatial domain. The special string 'sum' will include the cross sections summed over all nuclides. Defaults @@ -873,7 +875,7 @@ class MDGXS(MGXS): # Sort the dataframe by domain type id (e.g., distribcell id) and # energy groups such that data is from fast to thermal if self.domain_type == 'mesh': - mesh_str = 'mesh {0}'.format(self.domain.id) + mesh_str = f'mesh {self.domain.id}' df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), (mesh_str, 'z')] + columns, inplace=True) else: @@ -969,7 +971,7 @@ class ChiDelayed(MDGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`ChiDelayed.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -990,7 +992,7 @@ class ChiDelayed(MDGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -1000,14 +1002,17 @@ class ChiDelayed(MDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ # Store whether or not the number density should be removed for microscopic - # values of this data; since this chi data is normalized to 1.0, the - # data should not be divided by the number density + # values of this data; since this chi data is normalized to 1.0, the + # data should not be divided by the number density _divide_by_density = False def __init__(self, domain=None, domain_type=None, energy_groups=None, @@ -1107,7 +1112,7 @@ class ChiDelayed(MDGXS): ---------- nuclides : list of str A list of nuclide name strings - (e.g., ['U-235', 'U-238']; default is []) + (e.g., ['U235', 'U238']; default is []) groups : list of Integral A list of energy group indices starting at 1 for the high energies (e.g., [1, 2, 3]; default is []) @@ -1241,7 +1246,7 @@ class ChiDelayed(MDGXS): subdomains : Iterable of Integral or 'all' Subdomain IDs of interest. Defaults to 'all'. nuclides : Iterable of str or 'all' or 'sum' - A list of nuclide name strings (e.g., ['U-235', 'U-238']). The + A list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will return the cross sections for all nuclides in the spatial domain. The special string 'sum' will return the cross section summed over all nuclides. Defaults to 'all'. @@ -1485,7 +1490,7 @@ class DelayedNuFissionXS(MDGXS): the multi-group cross section estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`DelayedNuFissionXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -1506,7 +1511,7 @@ class DelayedNuFissionXS(MDGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -1516,8 +1521,11 @@ class DelayedNuFissionXS(MDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -1621,7 +1629,7 @@ class Beta(MDGXS): the multi-group cross section estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`Beta.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -1642,7 +1650,7 @@ class Beta(MDGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -1652,8 +1660,11 @@ class Beta(MDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -1811,7 +1822,7 @@ class DecayRate(MDGXS): the multi-group cross section estimator : {'tracklength', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`DecayRate.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -1832,7 +1843,7 @@ class DecayRate(MDGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -1842,8 +1853,11 @@ class DecayRate(MDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -1870,9 +1884,6 @@ class DecayRate(MDGXS): @property def filters(self): - # Create the non-domain specific Filters for the Tallies - group_edges = self.energy_groups.group_edges - if self.delayed_groups is not None: delayed_filter = openmc.DelayedGroupFilter(self.delayed_groups) filters = [[delayed_filter], [delayed_filter]] @@ -1935,7 +1946,7 @@ class DecayRate(MDGXS): subdomains : Iterable of Integral or 'all' Subdomain IDs of interest. Defaults to 'all'. nuclides : Iterable of str or 'all' or 'sum' - A list of nuclide name strings (e.g., ['U-235', 'U-238']). The + A list of nuclide name strings (e.g., ['U235', 'U238']). The special string 'all' will return the cross sections for all nuclides in the spatial domain. The special string 'sum' will return the cross section summed over all nuclides. Defaults to 'all'. @@ -2122,7 +2133,7 @@ class MatrixMDGXS(MDGXS): the multi-group cross section estimator : {'tracklength', 'collision', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to @@ -2152,8 +2163,11 @@ class MatrixMDGXS(MDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -2455,7 +2469,7 @@ class MatrixMDGXS(MDGXS): if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'mesh': xyz = [range(1, x + 1) for x in self.domain.dimension] subdomains = list(itertools.product(*xyz)) @@ -2477,12 +2491,12 @@ class MatrixMDGXS(MDGXS): # Build header for string with type and domain info string = 'Multi-Delayed-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type) + string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) # Generate the header for an individual XS - xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type)) + xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' # If cross section data has not been computed, only print string header if self.tallies is None: @@ -2518,7 +2532,7 @@ class MatrixMDGXS(MDGXS): string += '{: <16}=\t{}\n'.format('\tNuclide', nuclide) # Build header for cross section type - string += '{: <16}\n'.format(xs_header) + string += f'{xs_header: <16}\n' if self.delayed_groups is not None: @@ -2717,7 +2731,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`DelayedNuFissionXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -2738,7 +2752,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): being tracked. This is unity if the by_nuclide attribute is False. nuclides : Iterable of str or 'sum' The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides + sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides are not specified by the user, all nuclides in the spatial domain are included. This attribute is 'sum' if by_nuclide is false. sparse : bool @@ -2748,8 +2762,11 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -2759,6 +2776,6 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): super().__init__(domain, domain_type, energy_groups, delayed_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'delayed-nu-fission' - self._hdf5_key = 'delayed-nu-fission matrix' + self._mgxs_type = 'delayed-nu-fission matrix' self._estimator = 'analog' self._valid_estimators = ['analog'] diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index ff739fe851..b8f2b8d3fb 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -1,8 +1,8 @@ -from collections import OrderedDict import copy from numbers import Integral import os import warnings +from textwrap import dedent import h5py import numpy as np @@ -20,6 +20,7 @@ MGXS_TYPES = ( 'transport', 'nu-transport', 'absorption', + 'reduced absorption', 'capture', 'fission', 'nu-fission', @@ -147,6 +148,11 @@ def _df_column_convert_to_bin(df, current_name, new_name, values_to_bin, df.rename(columns={current_name: new_name}, inplace=True) +def add_params(cls): + cls.__doc__ += cls._params + return cls + +@add_params class MGXS: """An abstract multi-group cross section for some energy group structure within some spatial domain. @@ -157,6 +163,9 @@ class MGXS: .. note:: Users should instantiate the subclasses of this abstract class. + """ + + _params = dedent(""" Parameters ---------- domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh @@ -207,7 +216,7 @@ class MGXS: the multi-group cross section estimator : {'tracklength', 'collision', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to @@ -237,10 +246,13 @@ class MGXS: Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store - """ + .. versionadded:: 0.13.1 + + """) # Store whether or not the number density should be removed for microscopic # values of this data @@ -266,7 +278,7 @@ class MGXS: self._sparse = False self._loaded_sp = False self._derived = False - self._hdf5_key = None + self._mgxs_type = None self._valid_estimators = ESTIMATOR_TYPES self.name = name @@ -305,9 +317,9 @@ class MGXS: clone._sparse = self.sparse clone._loaded_sp = self._loaded_sp clone._derived = self.derived - clone._hdf5_key = self._hdf5_key + clone._mgxs_type = self._mgxs_type - clone._tallies = OrderedDict() + clone._tallies = {} for tally_type, tally in self.tallies.items(): clone.tallies[tally_type] = copy.deepcopy(tally, memo) @@ -441,6 +453,11 @@ class MGXS: def name(self): return self._name + @name.setter + def name(self, name): + cv.check_type('name', name, str) + self._name = name + @property def rxn_type(self): return self._rxn_type @@ -449,30 +466,78 @@ class MGXS: def by_nuclide(self): return self._by_nuclide + @by_nuclide.setter + def by_nuclide(self, by_nuclide): + cv.check_type('by_nuclide', by_nuclide, bool) + self._by_nuclide = by_nuclide + @property def domain(self): return self._domain + @domain.setter + def domain(self, domain): + cv.check_type('domain', domain, _DOMAINS) + self._domain = domain + + # Assign a domain type + if self.domain_type is None: + if isinstance(domain, openmc.Material): + self._domain_type = 'material' + elif isinstance(domain, openmc.Cell): + self._domain_type = 'cell' + elif isinstance(domain, openmc.Universe): + self._domain_type = 'universe' + elif isinstance(domain, openmc.RegularMesh): + self._domain_type = 'mesh' + @property def domain_type(self): return self._domain_type + @domain_type.setter + def domain_type(self, domain_type): + cv.check_value('domain type', domain_type, DOMAIN_TYPES) + self._domain_type = domain_type + @property def energy_groups(self): return self._energy_groups + @energy_groups.setter + def energy_groups(self, energy_groups): + cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) + self._energy_groups = energy_groups + @property def num_polar(self): return self._num_polar + @num_polar.setter + def num_polar(self, num_polar): + cv.check_type('num_polar', num_polar, Integral) + cv.check_greater_than('num_polar', num_polar, 0) + self._num_polar = num_polar + @property def num_azimuthal(self): return self._num_azimuthal + @num_azimuthal.setter + def num_azimuthal(self, num_azimuthal): + cv.check_type('num_azimuthal', num_azimuthal, Integral) + cv.check_greater_than('num_azimuthal', num_azimuthal, 0) + self._num_azimuthal = num_azimuthal + @property def tally_trigger(self): return self._tally_trigger + @tally_trigger.setter + def tally_trigger(self, tally_trigger): + cv.check_type('tally trigger', tally_trigger, openmc.Trigger) + self._tally_trigger = tally_trigger + @property def num_groups(self): return self.energy_groups.num_groups @@ -499,6 +564,11 @@ class MGXS: def estimator(self): return self._estimator + @estimator.setter + def estimator(self, estimator): + cv.check_value('estimator', estimator, self._valid_estimators) + self._estimator = estimator + @property def tallies(self): @@ -506,7 +576,7 @@ class MGXS: if self._tallies is None: # Initialize a collection of Tallies - self._tallies = OrderedDict() + self._tallies ={} # Create a domain Filter object filter_type = _DOMAIN_TO_FILTER[self.domain_type] @@ -573,6 +643,31 @@ class MGXS: def sparse(self): return self._sparse + @sparse.setter + def sparse(self, sparse): + """Convert tally data from NumPy arrays to SciPy list of lists (LIL) + sparse matrices, and vice versa. + + This property may be used to reduce the amount of data in memory during + tally data processing. The tally data will be stored as SciPy LIL + matrices internally within the Tally object. All tally data access + properties and methods will return data as a dense NumPy array. + + """ + + cv.check_type('sparse', sparse, bool) + + # Sparsify or densify the derived MGXS tallies and the base tallies + if self._xs_tally: + self.xs_tally.sparse = sparse + if self._rxn_rate_tally: + self.rxn_rate_tally.sparse = sparse + + for tally_name in self.tallies: + self.tallies[tally_name].sparse = sparse + + self._sparse = sparse + @property def num_subdomains(self): if self.domain_type.startswith('sum('): @@ -600,6 +695,11 @@ class MGXS: else: return ['sum'] + @nuclides.setter + def nuclides(self, nuclides): + cv.check_iterable_type('nuclides', nuclides, str) + self._nuclides = nuclides + @property def loaded_sp(self): return self._loaded_sp @@ -609,100 +709,12 @@ class MGXS: return self._derived @property - def hdf5_key(self): - if self._hdf5_key is not None: - return self._hdf5_key + def mgxs_type(self): + if self._mgxs_type is not None: + return self._mgxs_type else: return self._rxn_type - @name.setter - def name(self, name): - cv.check_type('name', name, str) - self._name = name - - @by_nuclide.setter - def by_nuclide(self, by_nuclide): - cv.check_type('by_nuclide', by_nuclide, bool) - self._by_nuclide = by_nuclide - - @nuclides.setter - def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, str) - self._nuclides = nuclides - - @estimator.setter - def estimator(self, estimator): - cv.check_value('estimator', estimator, self._valid_estimators) - self._estimator = estimator - - @domain.setter - def domain(self, domain): - cv.check_type('domain', domain, _DOMAINS) - self._domain = domain - - # Assign a domain type - if self.domain_type is None: - if isinstance(domain, openmc.Material): - self._domain_type = 'material' - elif isinstance(domain, openmc.Cell): - self._domain_type = 'cell' - elif isinstance(domain, openmc.Universe): - self._domain_type = 'universe' - elif isinstance(domain, openmc.RegularMesh): - self._domain_type = 'mesh' - - @domain_type.setter - def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, DOMAIN_TYPES) - self._domain_type = domain_type - - @energy_groups.setter - def energy_groups(self, energy_groups): - cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) - self._energy_groups = energy_groups - - @num_polar.setter - def num_polar(self, num_polar): - cv.check_type('num_polar', num_polar, Integral) - cv.check_greater_than('num_polar', num_polar, 0) - self._num_polar = num_polar - - @num_azimuthal.setter - def num_azimuthal(self, num_azimuthal): - cv.check_type('num_azimuthal', num_azimuthal, Integral) - cv.check_greater_than('num_azimuthal', num_azimuthal, 0) - self._num_azimuthal = num_azimuthal - - @tally_trigger.setter - def tally_trigger(self, tally_trigger): - cv.check_type('tally trigger', tally_trigger, openmc.Trigger) - self._tally_trigger = tally_trigger - - @sparse.setter - def sparse(self, sparse): - """Convert tally data from NumPy arrays to SciPy list of lists (LIL) - sparse matrices, and vice versa. - - This property may be used to reduce the amount of data in memory during - tally data processing. The tally data will be stored as SciPy LIL - matrices internally within the Tally object. All tally data access - properties and methods will return data as a dense NumPy array. - - """ - - cv.check_type('sparse', sparse, bool) - - # Sparsify or densify the derived MGXS tallies and the base tallies - if self._xs_tally: - self.xs_tally.sparse = sparse - if self._rxn_rate_tally: - self.rxn_rate_tally.sparse = sparse - - for tally_name in self.tallies: - self.tallies[tally_name].sparse = sparse - - self._sparse = sparse - @staticmethod def get_mgxs(mgxs_type, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, @@ -761,6 +773,8 @@ class MGXS: mgxs = TransportXS(domain, domain_type, energy_groups, nu=True) elif mgxs_type == 'absorption': mgxs = AbsorptionXS(domain, domain_type, energy_groups) + elif mgxs_type == 'reduced absorption': + mgxs = ReducedAbsorptionXS(domain, domain_type, energy_groups) elif mgxs_type == 'capture': mgxs = CaptureXS(domain, domain_type, energy_groups) elif mgxs_type == 'fission': @@ -813,6 +827,8 @@ class MGXS: elif mgxs_type in ARBITRARY_MATRIX_TYPES: mgxs = ArbitraryMatrixXS(mgxs_type, domain, domain_type, energy_groups) + else: + raise ValueError(f"Unknown MGXS type: {mgxs_type}") mgxs.by_nuclide = by_nuclide mgxs.name = name @@ -902,20 +918,20 @@ class MGXS: # Sum the atomic number densities for all nuclides if nuclides == 'sum': nuclides = self.get_nuclides() - densities = np.zeros(1, dtype=np.float) + densities = np.zeros(1, dtype=float) for nuclide in nuclides: densities[0] += self.get_nuclide_density(nuclide) # Tabulate the atomic number densities for all nuclides elif nuclides == 'all': nuclides = self.get_nuclides() - densities = np.zeros(self.num_nuclides, dtype=np.float) + densities = np.zeros(self.num_nuclides, dtype=float) for i, nuclide in enumerate(nuclides): densities[i] += self.get_nuclide_density(nuclide) # Tabulate the atomic number densities for each specified nuclide else: - densities = np.zeros(len(nuclides), dtype=np.float) + densities = np.zeros(len(nuclides), dtype=float) for i, nuclide in enumerate(nuclides): densities[i] = self.get_nuclide_density(nuclide) @@ -941,7 +957,7 @@ class MGXS: self.xs_tally._nuclides = [] nuclides = self.get_nuclides() for nuclide in nuclides: - self.xs_tally.nuclides.append(openmc.Nuclide(nuclide)) + self.xs_tally.nuclides.append(nuclide) # Remove NaNs which may have resulted from divide-by-zero operations self.xs_tally._mean = np.nan_to_num(self.xs_tally.mean) @@ -1412,7 +1428,7 @@ class MGXS: filter_bins=subdomains) avg_xs.tallies[tally_type] = tally_avg - avg_xs._domain_type = 'sum({0})'.format(self.domain_type) + avg_xs._domain_type = f'sum({self.domain_type})' avg_xs.sparse = self.sparse return avg_xs @@ -1463,7 +1479,7 @@ class MGXS: # Clone this MGXS to initialize the homogenized version homogenized_mgxs = copy.deepcopy(self) homogenized_mgxs._derived = True - name = 'hom({}, '.format(self.domain.name) + name = f'hom({self.domain.name}, ' # Get the domain filter filter_type = _DOMAIN_TO_FILTER[self.domain_type] @@ -1490,7 +1506,7 @@ class MGXS: denom_tally += other_denom_tally # Update the name for the homogenzied MGXS - name += '{}, '.format(mgxs.domain.name) + name += f'{mgxs.domain.name}, ' # Set the properties of the homogenized MGXS homogenized_mgxs._rxn_rate_tally = rxn_rate_tally @@ -1674,7 +1690,7 @@ class MGXS: merged_mgxs.nuclides = self.nuclides + other.nuclides # Null base tallies but merge reaction rate and cross section tallies - merged_mgxs._tallies = OrderedDict() + merged_mgxs._tallies ={} merged_mgxs._rxn_rate_tally = self.rxn_rate_tally.merge(other.rxn_rate_tally) merged_mgxs._xs_tally = self.xs_tally.merge(other.xs_tally) @@ -1704,7 +1720,7 @@ class MGXS: if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'mesh': subdomains = list(self.domain.indices) else: @@ -1725,12 +1741,12 @@ class MGXS: # Build header for string with type and domain info string = 'Multi-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type) + string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) # Generate the header for an individual XS - xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type)) + xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' # If cross section data has not been computed, only print string header if self.tallies is None: @@ -1758,7 +1774,7 @@ class MGXS: string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) # Build header for cross section type - string += '{0: <16}\n'.format(xs_header) + string += f'{xs_header: <16}\n' template = '{0: <12}Group {1} [{2: <10} - {3: <10}eV]:\t' average_xs = self.get_xs(nuclides=[nuclide], @@ -1871,7 +1887,7 @@ class MGXS: if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'sum(distribcell)': domain_filter = self.xs_tally.find_filter('sum(distribcell)') subdomains = domain_filter.bins @@ -1884,7 +1900,7 @@ class MGXS: if self.by_nuclide: if nuclides == 'all': nuclides = self.get_nuclides() - densities = np.zeros(len(nuclides), dtype=np.float) + densities = np.zeros(len(nuclides), dtype=float) elif nuclides == 'sum': nuclides = ['sum'] else: @@ -1912,7 +1928,7 @@ class MGXS: subdomain_group = domain_group # Create a separate HDF5 group for this cross section - rxn_group = subdomain_group.require_group(self.hdf5_key) + rxn_group = subdomain_group.require_group(self.mgxs_type) # Create a separate HDF5 group for each nuclide for j, nuclide in enumerate(nuclides): @@ -1986,9 +2002,9 @@ class MGXS: df.to_csv(filename + '.csv', index=False) elif format == 'excel': if self.domain_type == 'mesh': - df.to_excel(filename + '.xls') + df.to_excel(filename + '.xlsx') else: - df.to_excel(filename + '.xls', index=False) + df.to_excel(filename + '.xlsx', index=False) elif format == 'pickle': df.to_pickle(filename + '.pkl') elif format == 'latex': @@ -2116,7 +2132,7 @@ class MGXS: # Sort the dataframe by domain type id (e.g., distribcell id) and # energy groups such that data is from fast to thermal if self.domain_type == 'mesh': - mesh_str = 'mesh {0}'.format(self.domain.id) + mesh_str = f'mesh {self.domain.id}' df.sort_values(by=[(mesh_str, 'x'), (mesh_str, 'y'), (mesh_str, 'z')] + columns, inplace=True) else: @@ -2145,6 +2161,7 @@ class MGXS: return 'cm^-1' if xs_type == 'macro' else 'barns' +@add_params class MatrixMGXS(MGXS): """An abstract multi-group cross section for some energy group structure within some spatial domain. This class is specifically intended for @@ -2158,89 +2175,6 @@ class MatrixMGXS(MGXS): .. note:: Users should instantiate the subclasses of this abstract class. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file) and the number of mesh cells for - 'mesh' domain types. - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ @property def _dont_squeeze(self): @@ -2513,7 +2447,7 @@ class MatrixMGXS(MGXS): if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'mesh': subdomains = list(self.domain.indices) else: @@ -2534,12 +2468,12 @@ class MatrixMGXS(MGXS): # Build header for string with type and domain info string = 'Multi-Group XS\n' - string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type) + string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.mgxs_type) string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type) string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) # Generate the header for an individual XS - xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type)) + xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' # If cross section data has not been computed, only print string header if self.tallies is None: @@ -2575,7 +2509,7 @@ class MatrixMGXS(MGXS): string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) # Build header for cross section type - string += '{0: <16}\n'.format(xs_header) + string += f'{xs_header: <16}\n' template = '{0: <12}Group {1} -> Group {2}:\t\t' average_xs = self.get_xs(nuclides=[nuclide], @@ -2625,6 +2559,7 @@ class MatrixMGXS(MGXS): print(string) +@add_params class TotalXS(MGXS): r"""A total multi-group cross section. @@ -2651,95 +2586,11 @@ class TotalXS(MGXS): \sigma_t (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`TotalXS.tally_keys` property and values - are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'total' @@ -2839,7 +2690,7 @@ class TransportXS(MGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`TransportXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -2870,14 +2721,17 @@ class TransportXS(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, nu=False, + def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) # Use tracklength estimators for the total MGXS term, and @@ -3077,7 +2931,7 @@ class DiffusionCoefficient(TransportXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`TransportXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -3108,14 +2962,17 @@ class DiffusionCoefficient(TransportXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, nu=False, + def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super(DiffusionCoefficient, self).__init__(domain, domain_type, groups, + super(DiffusionCoefficient, self).__init__(domain, domain_type, energy_groups, nu, by_nuclide, name, num_polar, num_azimuthal) if not nu: @@ -3154,31 +3011,139 @@ class DiffusionCoefficient(TransportXS): raise ValueError(msg) # Switch EnergyoutFilter to EnergyFilter + # If 'scatter-1' is not in tallies, it is because the transport correction has + # already occurred on this MGXS in another function or in a previous call to this function. + if 'scatter-1' in self.tallies: + p1_tally = self.tallies['scatter-1'] + old_filt = p1_tally.filters[-2] + new_filt = openmc.EnergyFilter(old_filt.values) + p1_tally.filters[-2] = new_filt + + p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], + filter_bins=[('P1',)],squeeze=True) + p1_tally._scores = ['scatter-1'] + total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] + trans_corr = p1_tally / self.tallies['flux (analog)'] + transport = total_xs - trans_corr + diff_coef = transport**(-1) / 3.0 + self._xs_tally = diff_coef + self._compute_xs() + + else: + self._xs_tally = self.tallies[self._rxn_type] / self.tallies['flux (tracklength)'] + self._compute_xs() + + return self._xs_tally + + def get_condensed_xs(self, coarse_groups): + """Construct an energy-condensed version of this cross section. + + Parameters + ---------- + coarse_groups : openmc.mgxs.EnergyGroups + The coarse energy group structure of interest + + Returns + ------- + MGXS + A new MGXS condensed to the group structure of interest + + """ + + cv.check_type('coarse_groups', coarse_groups, EnergyGroups) + cv.check_less_than('coarse groups', coarse_groups.num_groups, + self.num_groups, equality=True) + cv.check_value('upper coarse energy', coarse_groups.group_edges[-1], + [self.energy_groups.group_edges[-1]]) + cv.check_value('lower coarse energy', coarse_groups.group_edges[0], + [self.energy_groups.group_edges[0]]) + + # Clone this MGXS to initialize the condensed version + condensed_xs = copy.deepcopy(self) + + # If 'scatter-1' is not in tallies, it is because the transport correction has + # already occurred on this MGXS in another function or in a previous call to this function. + if 'scatter-1' in self.tallies: p1_tally = self.tallies['scatter-1'] old_filt = p1_tally.filters[-2] new_filt = openmc.EnergyFilter(old_filt.values) p1_tally.filters[-2] = new_filt - - # Slice Legendre expansion filter and change name of score p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter], filter_bins=[('P1',)], squeeze=True) p1_tally._scores = ['scatter-1'] - - # Compute total cross section total_xs = self.tallies['total'] / self.tallies['flux (tracklength)'] - - # Compute transport correction term trans_corr = p1_tally / self.tallies['flux (analog)'] - - # Compute the diffusion coefficient transport = total_xs - trans_corr diff_coef = transport**(-1) / 3.0 - self._xs_tally = diff_coef - self._compute_xs() + diff_coef *= self.tallies['flux (tracklength)'] + flux_tally = condensed_xs.tallies['flux (tracklength)'] + condensed_xs._tallies = {} + condensed_xs._tallies[self._rxn_type] = diff_coef + condensed_xs._tallies['flux (tracklength)'] = flux_tally + condensed_xs._rxn_rate_tally = diff_coef + condensed_xs._xs_tally = None + condensed_xs._sparse = False + condensed_xs._energy_groups = coarse_groups - return self._xs_tally + else: + condensed_xs._rxn_rate_tally = None + condensed_xs._xs_tally = None + condensed_xs._sparse = False + condensed_xs._energy_groups = coarse_groups + # Build energy indices to sum across + energy_indices = [] + for group in range(coarse_groups.num_groups, 0, -1): + low, high = coarse_groups.get_group_bounds(group) + low_index = np.where(self.energy_groups.group_edges == low)[0][0] + energy_indices.append(low_index) + + fine_edges = self.energy_groups.group_edges + + # Condense each of the tallies to the coarse group structure + for tally in condensed_xs.tallies.values(): + + # Make condensed tally derived and null out sum, sum_sq + tally._derived = True + tally._sum = None + tally._sum_sq = None + + # Get tally data arrays reshaped with one dimension per filter + mean = tally.get_reshaped_data(value='mean') + std_dev = tally.get_reshaped_data(value='std_dev') + + # Sum across all applicable fine energy group filters + for i, tally_filter in enumerate(tally.filters): + if not isinstance(tally_filter, (openmc.EnergyFilter, + openmc.EnergyoutFilter)): + continue + elif len(tally_filter.bins) != len(fine_edges) - 1: + continue + elif not np.allclose(tally_filter.bins[:, 0], fine_edges[:-1]): + continue + else: + cedge = coarse_groups.group_edges + tally_filter.values = cedge + tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T + mean = np.add.reduceat(mean, energy_indices, axis=i) + std_dev = np.add.reduceat(std_dev**2, energy_indices, + axis=i) + std_dev = np.sqrt(std_dev) + + # Reshape condensed data arrays with one dimension for all filters + mean = np.reshape(mean, tally.shape) + std_dev = np.reshape(std_dev, tally.shape) + + # Override tally's data with the new condensed data + tally._mean = mean + tally._std_dev = std_dev + + # Compute the energy condensed multi-group cross section + condensed_xs.sparse = self.sparse + return condensed_xs + +@add_params class AbsorptionXS(MGXS): r"""An absorption multi-group cross section. @@ -3209,100 +3174,74 @@ class AbsorptionXS(MGXS): \sigma_a (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`AbsorptionXS.tally_keys` property and - values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file) and the number of mesh cells for - 'mesh' domain types. - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'absorption' +@add_params +class ReducedAbsorptionXS(MGXS): + r"""A reduced absorption multi-group cross section. + + The reduced absorption reaction rate is defined as the difference between + absorption and the production of neutrons due to (n,xn) reactions. + + This class can be used for both OpenMC input generation and tally data + post-processing to compute spatially-homogenized and energy-integrated + multi-group capture cross sections for multi-group neutronics calculations. + At a minimum, one needs to set the :attr:`CaptureXS.energy_groups` and + :attr:`CaptureXS.domain` properties. Tallies for the flux and appropriate + reaction rates over the specified domain are generated automatically via the + :attr:`CaptureXS.tallies` property, which can then be appended to a + :class:`openmc.Tallies` instance. + + For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the + necessary data to compute multi-group cross sections from a + :class:`openmc.StatePoint` instance. The derived multi-group cross section + can then be obtained from the :attr:`CaptureXS.xs_tally` property. + + For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the + reduced absorption cross section is calculated as: + + .. math:: + + \frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; + \left(\sigma_a (r, E) - \sigma_{n,2n}(r,E) - 2\sigma_{n,3n}(r,E) - + 3\sigma_{n,4n}(r,E) \right) \psi (r, E, \Omega)}{\int_{r \in V} dr + \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. + + .. versionadded:: 0.13.1 + + """ + + def __init__(self, domain=None, domain_type=None, energy_groups=None, + by_nuclide=False, name='', num_polar=1, num_azimuthal=1): + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, + num_polar, num_azimuthal) + self._rxn_type = 'reduced absorption' + + @property + def scores(self): + return ['flux', 'absorption', '(n,2n)', '(n,3n)', '(n,4n)'] + + @property + def rxn_rate_tally(self): + if self._rxn_rate_tally is None: + self._rxn_rate_tally = ( + self.tallies['absorption'] + - self.tallies['(n,2n)'] + - 2*self.tallies['(n,3n)'] + - 3*self.tallies['(n,4n)'] + ) + self._rxn_rate_tally.sparse = self.sparse + return self._rxn_rate_tally + + +@add_params class CaptureXS(MGXS): r"""A capture multi-group cross section. @@ -3336,95 +3275,11 @@ class CaptureXS(MGXS): \Omega) \right ]}{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`CaptureXS.tally_keys` property and - values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'capture' @@ -3539,7 +3394,7 @@ class FissionXS(MGXS): the multi-group cross section estimator : {'tracklength', 'collision', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`FissionXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -3570,15 +3425,18 @@ class FissionXS(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, nu=False, + def __init__(self, domain=None, domain_type=None, energy_groups=None, nu=False, prompt=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._nu = False self._prompt = False @@ -3595,10 +3453,6 @@ class FissionXS(MGXS): def nu(self): return self._nu - @property - def prompt(self): - return self._prompt - @nu.setter def nu(self, nu): cv.check_type('nu', nu, bool) @@ -3611,6 +3465,10 @@ class FissionXS(MGXS): else: self._rxn_type = 'prompt-nu-fission' + @property + def prompt(self): + return self._prompt + @prompt.setter def prompt(self, prompt): cv.check_type('prompt', prompt, bool) @@ -3624,6 +3482,7 @@ class FissionXS(MGXS): self._rxn_type = 'prompt-nu-fission' +@add_params class KappaFissionXS(MGXS): r"""A recoverable fission energy production rate multi-group cross section. @@ -3657,95 +3516,11 @@ class KappaFissionXS(MGXS): \kappa\sigma_f (r, E) \psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)}. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`KappaFissionXS.tally_keys` property and - values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'kappa-fission' @@ -3838,7 +3613,7 @@ class ScatterXS(MGXS): the multi-group cross section estimator : {'tracklength', 'collision', 'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`ScatterXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -3869,15 +3644,18 @@ class ScatterXS(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, nu=False): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self.nu = nu @@ -3902,6 +3680,7 @@ class ScatterXS(MGXS): self._valid_estimators = ['analog'] +@add_params class ArbitraryXS(MGXS): r"""A multi-group cross section for an arbitrary reaction type. @@ -3930,102 +3709,17 @@ class ArbitraryXS(MGXS): where :math:`\sigma_X` is the requested reaction type of interest. - Parameters - ---------- - rxn_type : str - Reaction type (e.g., '(n,2n)', '(n,Xt)', etc.) - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., '(n,2n)', '(n,Xt)', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`TotalXS.tally_keys` property and values - are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, rxn_type, domain=None, domain_type=None, groups=None, + def __init__(self, rxn_type, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): cv.check_value("rxn_type", rxn_type, ARBITRARY_VECTOR_TYPES) - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = rxn_type +@add_params class ArbitraryMatrixXS(MatrixMGXS): r"""A multi-group matrix cross section for an arbitrary reaction type. @@ -4061,100 +3755,13 @@ class ArbitraryMatrixXS(MatrixMGXS): where :math:`\sigma_X` is the requested reaction type of interest. - Parameters - ---------- - rxn_type : str - Reaction type (e.g., '(n,2n)', '(n,nta)', etc.). Valid names have - neutrons as a product. - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : 'analog' - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`NuFissionMatrixXS.tally_keys` - property and values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ - def __init__(self, rxn_type, domain=None, domain_type=None, groups=None, + def __init__(self, rxn_type, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): cv.check_value("rxn_type", rxn_type, ARBITRARY_MATRIX_TYPES) - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = rxn_type.split(" ")[0] self._estimator = 'analog' @@ -4316,7 +3923,7 @@ class ScatterMatrixXS(MatrixMGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`ScatterMatrixXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -4347,15 +3954,18 @@ class ScatterMatrixXS(MatrixMGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, nu=False): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._formulation = 'simple' self._correction = 'P0' @@ -4396,26 +4006,115 @@ class ScatterMatrixXS(MatrixMGXS): def formulation(self): return self._formulation + @formulation.setter + def formulation(self, formulation): + cv.check_value('formulation', formulation, ('simple', 'consistent')) + self._formulation = formulation + + if self.formulation == 'simple': + self._valid_estimators = ['analog'] + if not self.nu: + self._mgxs_type = 'scatter matrix' + else: + self._mgxs_type = 'nu-scatter matrix' + else: + self._valid_estimators = ['tracklength'] + if not self.nu: + self._mgxs_type = 'consistent scatter matrix' + else: + self._mgxs_type = 'consistent nu-scatter matrix' + @property def correction(self): return self._correction + @correction.setter + def correction(self, correction): + cv.check_value('correction', correction, ('P0', None)) + + if self.scatter_format == SCATTER_LEGENDRE: + if correction == 'P0' and self.legendre_order > 0: + msg = 'The P0 correction will be ignored since the ' \ + 'scattering order {} is greater than '\ + 'zero'.format(self.legendre_order) + warnings.warn(msg) + elif self.scatter_format == SCATTER_HISTOGRAM: + msg = 'The P0 correction will be ignored since the ' \ + 'scatter format is set to histogram' + warnings.warn(msg) + + self._correction = correction + @property def scatter_format(self): return self._scatter_format + @scatter_format.setter + def scatter_format(self, scatter_format): + cv.check_value('scatter_format', scatter_format, MU_TREATMENTS) + self._scatter_format = scatter_format + @property def legendre_order(self): return self._legendre_order + @legendre_order.setter + def legendre_order(self, legendre_order): + cv.check_type('legendre_order', legendre_order, Integral) + cv.check_greater_than('legendre_order', legendre_order, 0, + equality=True) + cv.check_less_than('legendre_order', legendre_order, _MAX_LEGENDRE, + equality=True) + + if self.scatter_format == SCATTER_LEGENDRE: + if self.correction == 'P0' and legendre_order > 0: + msg = 'The P0 correction will be ignored since the ' \ + 'scattering order {} is greater than '\ + 'zero'.format(legendre_order) + warnings.warn(msg, RuntimeWarning) + self.correction = None + elif self.scatter_format == SCATTER_HISTOGRAM: + msg = 'The legendre order will be ignored since the ' \ + 'scatter format is set to histogram' + warnings.warn(msg) + + self._legendre_order = legendre_order + @property def histogram_bins(self): return self._histogram_bins + @histogram_bins.setter + def histogram_bins(self, histogram_bins): + cv.check_type('histogram_bins', histogram_bins, Integral) + cv.check_greater_than('histogram_bins', histogram_bins, 0) + + self._histogram_bins = histogram_bins + @property def nu(self): return self._nu + @nu.setter + def nu(self, nu): + cv.check_type('nu', nu, bool) + self._nu = nu + + if self.formulation == 'simple': + if not nu: + self._rxn_type = 'scatter' + self._mgxs_type = 'scatter matrix' + else: + self._rxn_type = 'nu-scatter' + self._mgxs_type = 'nu-scatter matrix' + else: + if not nu: + self._rxn_type = 'scatter' + self._mgxs_type = 'consistent scatter matrix' + else: + self._rxn_type = 'nu-scatter' + self._mgxs_type = 'consistent nu-scatter matrix' + @property def scores(self): @@ -4697,95 +4396,6 @@ class ScatterMatrixXS(MatrixMGXS): return self._xs_tally - @nu.setter - def nu(self, nu): - cv.check_type('nu', nu, bool) - self._nu = nu - - if self.formulation == 'simple': - if not nu: - self._rxn_type = 'scatter' - self._hdf5_key = 'scatter matrix' - else: - self._rxn_type = 'nu-scatter' - self._hdf5_key = 'nu-scatter matrix' - else: - if not nu: - self._rxn_type = 'scatter' - self._hdf5_key = 'consistent scatter matrix' - else: - self._rxn_type = 'nu-scatter' - self._hdf5_key = 'consistent nu-scatter matrix' - - @formulation.setter - def formulation(self, formulation): - cv.check_value('formulation', formulation, ('simple', 'consistent')) - self._formulation = formulation - - if self.formulation == 'simple': - self._valid_estimators = ['analog'] - if not self.nu: - self._hdf5_key = 'scatter matrix' - else: - self._hdf5_key = 'nu-scatter matrix' - else: - self._valid_estimators = ['tracklength'] - if not self.nu: - self._hdf5_key = 'consistent scatter matrix' - else: - self._hdf5_key = 'consistent nu-scatter matrix' - - @correction.setter - def correction(self, correction): - cv.check_value('correction', correction, ('P0', None)) - - if self.scatter_format == SCATTER_LEGENDRE: - if correction == 'P0' and self.legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(self.legendre_order) - warnings.warn(msg) - elif self.scatter_format == SCATTER_HISTOGRAM: - msg = 'The P0 correction will be ignored since the ' \ - 'scatter format is set to histogram' - warnings.warn(msg) - - self._correction = correction - - @scatter_format.setter - def scatter_format(self, scatter_format): - cv.check_value('scatter_format', scatter_format, MU_TREATMENTS) - self._scatter_format = scatter_format - - @legendre_order.setter - def legendre_order(self, legendre_order): - cv.check_type('legendre_order', legendre_order, Integral) - cv.check_greater_than('legendre_order', legendre_order, 0, - equality=True) - cv.check_less_than('legendre_order', legendre_order, _MAX_LEGENDRE, - equality=True) - - if self.scatter_format == SCATTER_LEGENDRE: - if self.correction == 'P0' and legendre_order > 0: - msg = 'The P0 correction will be ignored since the ' \ - 'scattering order {} is greater than '\ - 'zero'.format(legendre_order) - warnings.warn(msg, RuntimeWarning) - self.correction = None - elif self.scatter_format == SCATTER_HISTOGRAM: - msg = 'The legendre order will be ignored since the ' \ - 'scatter format is set to histogram' - warnings.warn(msg) - - self._legendre_order = legendre_order - - @histogram_bins.setter - def histogram_bins(self, histogram_bins): - cv.check_type('histogram_bins', histogram_bins, Integral) - cv.check_greater_than('histogram_bins', histogram_bins, 0) - - self._histogram_bins = histogram_bins - def load_from_statepoint(self, statepoint): """Extracts tallies in an OpenMC StatePoint with the data needed to compute multi-group cross sections. @@ -4867,7 +4477,7 @@ class ScatterMatrixXS(MatrixMGXS): slice_xs.legendre_order = legendre_order # Slice the scattering tally - filter_bins = [tuple(['P{}'.format(i) + filter_bins = [tuple([f'P{i}' for i in range(self.legendre_order + 1)])] slice_xs.tallies[self.rxn_type] = \ slice_xs.tallies[self.rxn_type].get_slice( @@ -5004,7 +4614,7 @@ class ScatterMatrixXS(MatrixMGXS): cv.check_less_than( 'moment', moment, self.legendre_order, equality=True) filters.append(openmc.LegendreFilter) - filter_bins.append(('P{}'.format(moment),)) + filter_bins.append((f'P{moment}',)) num_angle_bins = 1 else: num_angle_bins = self.legendre_order + 1 @@ -5175,7 +4785,7 @@ class ScatterMatrixXS(MatrixMGXS): if not isinstance(subdomains, str): cv.check_iterable_type('subdomains', subdomains, Integral) elif self.domain_type == 'distribcell': - subdomains = np.arange(self.num_subdomains, dtype=np.int) + subdomains = np.arange(self.num_subdomains, dtype=int) elif self.domain_type == 'mesh': subdomains = list(self.domain.indices) else: @@ -5195,9 +4805,9 @@ class ScatterMatrixXS(MatrixMGXS): cv.check_value('xs_type', xs_type, ['macro', 'micro']) if self.correction != 'P0' and self.scatter_format == SCATTER_LEGENDRE: - rxn_type = '{0} (P{1})'.format(self.rxn_type, moment) + rxn_type = f'{self.mgxs_type} (P{moment})' else: - rxn_type = self.rxn_type + rxn_type = self.mgxs_type # Build header for string with type and domain info string = 'Multi-Group XS\n' @@ -5206,7 +4816,7 @@ class ScatterMatrixXS(MatrixMGXS): string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id) # Generate the header for an individual XS - xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type)) + xs_header = f'\tCross Sections [{self.get_units(xs_type)}]:' # If cross section data has not been computed, only print string header if self.tallies is None: @@ -5242,7 +4852,7 @@ class ScatterMatrixXS(MatrixMGXS): string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) # Build header for cross section type - string += '{0: <16}\n'.format(xs_header) + string += f'{xs_header: <16}\n' average_xs = self.get_xs(nuclides=[nuclide], subdomains=[subdomain], @@ -5294,8 +4904,7 @@ class ScatterMatrixXS(MatrixMGXS): for azi in range(len(azi_bins) - 1): azi_low, azi_high = azi_bins[azi: azi + 2] string += \ - '\t\tPolar Angle: [{0:5f} - {1:5f}]'.format( - pol_low, pol_high) + \ + f'\t\tPolar Angle: [{pol_low:5f} - {pol_high:5f}]' + \ '\tAzimuthal Angle: [{0:5f} - {1:5f}]'.format( azi_low, azi_high) + '\n' string += print_groups_and_histogram( @@ -5313,6 +4922,7 @@ class ScatterMatrixXS(MatrixMGXS): print(string) +@add_params class MultiplicityMatrixXS(MatrixMGXS): r"""The scattering multiplicity matrix. @@ -5353,90 +4963,6 @@ class MultiplicityMatrixXS(MatrixMGXS): where :math:`\upsilon_i` is the multiplicity for the :math:`i`-th reaction. - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : 'analog' - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`MultiplicityMatrixXS.tally_keys` - property and values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ # Store whether or not the number density should be removed for microscopic @@ -5445,9 +4971,9 @@ class MultiplicityMatrixXS(MatrixMGXS): # for microscopic data _divide_by_density = False - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'multiplicity matrix' self._estimator = 'analog' @@ -5488,6 +5014,7 @@ class MultiplicityMatrixXS(MatrixMGXS): return self._xs_tally +@add_params class ScatterProbabilityMatrix(MatrixMGXS): r"""The group-to-group scattering probability matrix. @@ -5526,90 +5053,6 @@ class ScatterProbabilityMatrix(MatrixMGXS): \sigma_{s,g'} \phi \rangle} \end{aligned} - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : 'analog' - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`ScatterProbabilityMatrix.tally_keys` - property and values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ # Store whether or not the number density should be removed for microscopic @@ -5617,12 +5060,12 @@ class ScatterProbabilityMatrix(MatrixMGXS): # to 1.0, this density division is not necessary _divide_by_density = False - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'scatter' - self._hdf5_key = 'scatter probability matrix' + self._mgxs_type = 'scatter probability matrix' self._estimator = 'analog' self._valid_estimators = ['analog'] @@ -5749,7 +5192,7 @@ class NuFissionMatrixXS(MatrixMGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`NuFissionMatrixXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -5780,22 +5223,25 @@ class NuFissionMatrixXS(MatrixMGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1, prompt=False): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) if not prompt: self._rxn_type = 'nu-fission' - self._hdf5_key = 'nu-fission matrix' + self._mgxs_type = 'nu-fission matrix' else: self._rxn_type = 'prompt-nu-fission' - self._hdf5_key = 'prompt-nu-fission matrix' + self._mgxs_type = 'prompt-nu-fission matrix' self._estimator = 'analog' self._valid_estimators = ['analog'] self.prompt = prompt @@ -5907,7 +5353,7 @@ class Chi(MGXS): the multi-group cross section estimator : 'analog' The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`Chi.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -5938,8 +5384,11 @@ class Chi(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ @@ -5948,15 +5397,11 @@ class Chi(MGXS): # data should not be divided by the number density _divide_by_density = False - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, prompt=False, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) - if not prompt: - self._rxn_type = 'chi' - else: - self._rxn_type = 'chi-prompt' self._estimator = 'analog' self._valid_estimators = ['analog'] self.prompt = prompt @@ -5970,6 +5415,17 @@ class Chi(MGXS): def prompt(self): return self._prompt + @prompt.setter + def prompt(self, prompt): + cv.check_type('prompt', prompt, bool) + self._prompt = prompt + if not self.prompt: + self._rxn_type = 'chi' + self._mgxs_type = 'chi' + else: + self._rxn_type = 'chi-prompt' + self._mgxs_type = 'chi-prompt' + @property def _dont_squeeze(self): """Create a tuple of axes which should not be removed during the get_xs @@ -6026,17 +5482,6 @@ class Chi(MGXS): return self._xs_tally - @prompt.setter - def prompt(self, prompt): - cv.check_type('prompt', prompt, bool) - self._prompt = prompt - if not self.prompt: - self._rxn_type = 'nu-fission' - self._hdf5_key = 'chi' - else: - self._rxn_type = 'prompt-nu-fission' - self._hdf5_key = 'chi-prompt' - def get_homogenized_mgxs(self, other_mgxs): """Construct a homogenized mgxs with other MGXS objects. @@ -6354,6 +5799,7 @@ class Chi(MGXS): return '%' +@add_params class InverseVelocity(MGXS): r"""An inverse velocity multi-group cross section. @@ -6383,91 +5829,6 @@ class InverseVelocity(MGXS): \frac{\psi (r, E, \Omega)}{v (r, E)}}{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega)} - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`InverseVelocity.tally_keys` property - and values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file) and the number of mesh cells for - 'mesh' domain types. - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - """ # Store whether or not the number density should be removed for microscopic @@ -6476,9 +5837,9 @@ class InverseVelocity(MGXS): # values _divide_by_density = False - def __init__(self, domain=None, domain_type=None, groups=None, + def __init__(self, domain=None, domain_type=None, energy_groups=None, by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - super().__init__(domain, domain_type, groups, by_nuclide, name, + super().__init__(domain, domain_type, energy_groups, by_nuclide, name, num_polar, num_azimuthal) self._rxn_type = 'inverse-velocity' @@ -6560,7 +5921,7 @@ class MeshSurfaceMGXS(MGXS): the multi-group cross section estimator : {'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section rxn_rate_tally : openmc.Tally Derived tally for the reaction rate tally used in the numerator to @@ -6584,8 +5945,11 @@ class MeshSurfaceMGXS(MGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ def __init__(self, domain=None, domain_type=None, energy_groups=None, @@ -6603,10 +5967,6 @@ class MeshSurfaceMGXS(MGXS): def domain(self): return self._domain - @property - def domain_type(self): - return self._domain_type - @domain.setter def domain(self, domain): cv.check_type('domain', domain, openmc.RegularMesh) @@ -6616,6 +5976,10 @@ class MeshSurfaceMGXS(MGXS): if self.domain_type is None: self._domain_type = 'mesh' + @property + def domain_type(self): + return self._domain_type + @domain_type.setter def domain_type(self, domain_type): cv.check_value('domain type', domain_type, 'mesh') @@ -6862,7 +6226,7 @@ class MeshSurfaceMGXS(MGXS): if 'group out' in df: df = df[df['group out'].isin(groups)] - mesh_str = 'mesh {0}'.format(self.domain.id) + mesh_str = f'mesh {self.domain.id}' col_key = (mesh_str, 'surf') surfaces = df.pop(col_key) df.insert(len(self.domain.dimension), col_key, surfaces) @@ -6944,7 +6308,7 @@ class Current(MeshSurfaceMGXS): the multi-group cross section estimator : {'analog'} The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict + tallies : dict OpenMC tallies needed to compute the multi-group cross section. The keys are strings listed in the :attr:`TotalXS.tally_keys` property and values are instances of :class:`openmc.Tally`. @@ -6970,12 +6334,15 @@ class Current(MeshSurfaceMGXS): Whether or not a statepoint file has been loaded with tally data derived : bool Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store + mgxs_type : str + The name of this MGXS type, to be used when printing and + indexing in an HDF5 data store + + .. versionadded:: 0.13.1 """ def __init__(self, domain=None, domain_type=None, - groups=None, by_nuclide=False, name=''): + energy_groups=None, by_nuclide=False, name=''): super(Current, self).__init__(domain, domain_type, - groups, by_nuclide, name) + energy_groups, by_nuclide, name) self._rxn_type = 'current' diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 67ad151cc6..b840563cff 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1,10 +1,9 @@ import copy from numbers import Real, Integral -import os import h5py import numpy as np -from scipy.integrate import simps +import scipy.integrate from scipy.interpolate import interp1d from scipy.special import eval_legendre @@ -19,21 +18,17 @@ ROOM_TEMPERATURE_KELVIN = 294.0 # Supported incoming particle MGXS angular treatment representations REPRESENTATION_ISOTROPIC = 'isotropic' REPRESENTATION_ANGLE = 'angle' -_REPRESENTATIONS = [ +_REPRESENTATIONS = { REPRESENTATION_ISOTROPIC, REPRESENTATION_ANGLE -] +} # Supported scattering angular distribution representations -_SCATTER_TYPES = [ +_SCATTER_TYPES = { SCATTER_TABULAR, SCATTER_LEGENDRE, SCATTER_HISTOGRAM -] - -# List of MGXS indexing schemes -_XS_SHAPES = ["[G][G'][Order]", "[G]", "[G']", "[G][G']", "[DG]", "[DG][G]", - "[DG][G']", "[DG][G][G']"] +} # Number of mu points for conversion between scattering formats _NMU = 257 @@ -261,22 +256,62 @@ class XSdata: def name(self): return self._name + @name.setter + def name(self, name): + + check_type('name for XSdata', name, str) + self._name = name + @property def energy_groups(self): return self._energy_groups + @energy_groups.setter + def energy_groups(self, energy_groups): + + check_type('energy_groups', energy_groups, openmc.mgxs.EnergyGroups) + if energy_groups.group_edges is None: + msg = 'Unable to assign an EnergyGroups object ' \ + 'with uninitialized group edges' + raise ValueError(msg) + + self._energy_groups = energy_groups + @property def num_delayed_groups(self): return self._num_delayed_groups + @num_delayed_groups.setter + def num_delayed_groups(self, num_delayed_groups): + + check_type('num_delayed_groups', num_delayed_groups, Integral) + check_less_than('num_delayed_groups', num_delayed_groups, + openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) + check_greater_than('num_delayed_groups', num_delayed_groups, 0, + equality=True) + self._num_delayed_groups = num_delayed_groups + @property def representation(self): return self._representation + @representation.setter + def representation(self, representation): + + check_value('representation', representation, _REPRESENTATIONS) + self._representation = representation + @property def atomic_weight_ratio(self): return self._atomic_weight_ratio + @atomic_weight_ratio.setter + def atomic_weight_ratio(self, atomic_weight_ratio): + + check_type('atomic_weight_ratio', atomic_weight_ratio, Real) + check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) + self._atomic_weight_ratio = atomic_weight_ratio + @property def fissionable(self): return self._fissionable @@ -285,22 +320,55 @@ class XSdata: def temperatures(self): return self._temperatures + @temperatures.setter + def temperatures(self, temperatures): + + check_iterable_type('temperatures', temperatures, Real) + self._temperatures = np.array(temperatures) + @property def scatter_format(self): return self._scatter_format + @scatter_format.setter + def scatter_format(self, scatter_format): + + check_value('scatter_format', scatter_format, _SCATTER_TYPES) + self._scatter_format = scatter_format + @property def order(self): return self._order + @order.setter + def order(self, order): + + check_type('order', order, Integral) + check_greater_than('order', order, 0, equality=True) + self._order = order + @property def num_polar(self): return self._num_polar + @num_polar.setter + def num_polar(self, num_polar): + + check_type('num_polar', num_polar, Integral) + check_greater_than('num_polar', num_polar, 0) + self._num_polar = num_polar + @property def num_azimuthal(self): return self._num_azimuthal + @num_azimuthal.setter + def num_azimuthal(self, num_azimuthal): + + check_type('num_azimuthal', num_azimuthal, Integral) + check_greater_than('num_azimuthal', num_azimuthal, 0) + self._num_azimuthal = num_azimuthal + @property def total(self): return self._total @@ -402,79 +470,6 @@ class XSdata: return self._xs_shapes - @name.setter - def name(self, name): - - check_type('name for XSdata', name, str) - self._name = name - - @energy_groups.setter - def energy_groups(self, energy_groups): - - check_type('energy_groups', energy_groups, openmc.mgxs.EnergyGroups) - if energy_groups.group_edges is None: - msg = 'Unable to assign an EnergyGroups object ' \ - 'with uninitialized group edges' - raise ValueError(msg) - - self._energy_groups = energy_groups - - @num_delayed_groups.setter - def num_delayed_groups(self, num_delayed_groups): - - check_type('num_delayed_groups', num_delayed_groups, Integral) - check_less_than('num_delayed_groups', num_delayed_groups, - openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) - check_greater_than('num_delayed_groups', num_delayed_groups, 0, - equality=True) - self._num_delayed_groups = num_delayed_groups - - @representation.setter - def representation(self, representation): - - check_value('representation', representation, _REPRESENTATIONS) - self._representation = representation - - @atomic_weight_ratio.setter - def atomic_weight_ratio(self, atomic_weight_ratio): - - check_type('atomic_weight_ratio', atomic_weight_ratio, Real) - check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) - self._atomic_weight_ratio = atomic_weight_ratio - - @temperatures.setter - def temperatures(self, temperatures): - - check_iterable_type('temperatures', temperatures, Real) - self._temperatures = np.array(temperatures) - - @scatter_format.setter - def scatter_format(self, scatter_format): - - check_value('scatter_format', scatter_format, _SCATTER_TYPES) - self._scatter_format = scatter_format - - @order.setter - def order(self, order): - - check_type('order', order, Integral) - check_greater_than('order', order, 0, equality=True) - self._order = order - - @num_polar.setter - def num_polar(self, num_polar): - - check_type('num_polar', num_polar, Integral) - check_greater_than('num_polar', num_polar, 0) - self._num_polar = num_polar - - @num_azimuthal.setter - def num_azimuthal(self, num_azimuthal): - - check_type('num_azimuthal', num_azimuthal, Integral) - check_greater_than('num_azimuthal', num_azimuthal, 0) - self._num_azimuthal = num_azimuthal - def add_temperature(self, temperature): """This method re-sizes the attributes of this XSdata object so that it can accommodate an additional temperature. Note that the set_* methods @@ -1824,6 +1819,12 @@ class XSdata: # Reset and re-generate XSdata.xs_shapes with the new scattering format xsdata._xs_shapes = None + # scipy 1.11+ prefers 'simpson', whereas older versions use 'simps' + if hasattr(scipy.integrate, 'simpson'): + integrate = scipy.integrate.simpson + else: + integrate = scipy.integrate.simps + for i, temp in enumerate(xsdata.temperatures): orig_data = self._scatter_matrix[i] new_shape = orig_data.shape[:-1] + (xsdata.num_orders,) @@ -1861,7 +1862,7 @@ class XSdata: table_fine[..., imu] += ((l + 0.5) * eval_legendre(l, mu_fine[imu]) * orig_data[..., l]) - new_data[..., h_bin] = simps(table_fine, mu_fine) + new_data[..., h_bin] = integrate(table_fine, x=mu_fine) elif self.scatter_format == SCATTER_TABULAR: # Calculate the mu points of the current data @@ -1875,7 +1876,7 @@ class XSdata: for l in range(xsdata.num_orders): y = (interp1d(mu_self, orig_data)(mu_fine) * eval_legendre(l, mu_fine)) - new_data[..., l] = simps(y, mu_fine) + new_data[..., l] = integrate(y, x=mu_fine) elif target_format == SCATTER_TABULAR: # Simply use an interpolating function to get the new data @@ -1894,7 +1895,7 @@ class XSdata: interp = interp1d(mu_self, orig_data) for h_bin in range(xsdata.num_orders): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) - new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) + new_data[..., h_bin] = integrate(interp(mu_fine), x=mu_fine) elif self.scatter_format == SCATTER_HISTOGRAM: # The histogram format does not have enough information to @@ -1920,7 +1921,7 @@ class XSdata: mu_fine = np.linspace(-1, 1, _NMU) for l in range(xsdata.num_orders): y = interp(mu_fine) * norm * eval_legendre(l, mu_fine) - new_data[..., l] = simps(y, mu_fine) + new_data[..., l] = integrate(y, x=mu_fine) elif target_format == SCATTER_TABULAR: # Simply use an interpolating function to get the new data @@ -1939,7 +1940,7 @@ class XSdata: for h_bin in range(xsdata.num_orders): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) new_data[..., h_bin] = \ - norm * simps(interp(mu_fine), mu_fine) + norm * integrate(interp(mu_fine), x=mu_fine) # Remove small values resulting from numerical precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. @@ -1965,7 +1966,7 @@ class XSdata: grp.attrs['fissionable'] = self.fissionable if self.representation is not None: - grp.attrs['representation'] = np.string_(self.representation) + grp.attrs['representation'] = np.bytes_(self.representation) if self.representation == REPRESENTATION_ANGLE: if self.num_azimuthal is not None: grp.attrs['num_azimuthal'] = self.num_azimuthal @@ -1973,9 +1974,9 @@ class XSdata: if self.num_polar is not None: grp.attrs['num_polar'] = self.num_polar - grp.attrs['scatter_shape'] = np.string_("[G][G'][Order]") + grp.attrs['scatter_shape'] = np.bytes_("[G][G'][Order]") if self.scatter_format is not None: - grp.attrs['scatter_format'] = np.string_(self.scatter_format) + grp.attrs['scatter_format'] = np.bytes_(self.scatter_format) if self.order is not None: grp.attrs['order'] = self.order @@ -2331,23 +2332,15 @@ class MGXSLibrary: def energy_groups(self): return self._energy_groups - @property - def num_delayed_groups(self): - return self._num_delayed_groups - - @property - def xsdatas(self): - return self._xsdatas - - @property - def names(self): - return [xsdata.name for xsdata in self.xsdatas] - @energy_groups.setter def energy_groups(self, energy_groups): check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) self._energy_groups = energy_groups + @property + def num_delayed_groups(self): + return self._num_delayed_groups + @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): check_type('num_delayed_groups', num_delayed_groups, Integral) @@ -2357,6 +2350,14 @@ class MGXSLibrary: openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) self._num_delayed_groups = num_delayed_groups + @property + def xsdatas(self): + return self._xsdatas + + @property + def names(self): + return [xsdata.name for xsdata in self.xsdatas] + def add_xsdata(self, xsdata): """Add an XSdata entry to the file. @@ -2517,7 +2518,7 @@ class MGXSLibrary: # Create and write to the HDF5 file file = h5py.File(filename, "w", libver=libver) - file.attrs['filetype'] = np.string_(_FILETYPE_MGXS_LIBRARY) + file.attrs['filetype'] = np.bytes_(_FILETYPE_MGXS_LIBRARY) file.attrs['version'] = [_VERSION_MGXS_LIBRARY, 0] file.attrs['energy_groups'] = self.energy_groups.num_groups file.attrs['delayed_groups'] = self.num_delayed_groups @@ -2536,8 +2537,7 @@ class MGXSLibrary: ---------- filename : str, optional Name of HDF5 file containing MGXS data. Default is None. - If not provided, the value of the OPENMC_MG_CROSS_SECTIONS - environmental variable will be used + If not provided, openmc.config['mg_cross_sections'] will be used. Returns ------- @@ -2545,15 +2545,14 @@ class MGXSLibrary: Multi-group cross section data object. """ - # If filename is None, get the cross sections from the - # OPENMC_CROSS_SECTIONS environment variable + # If filename is None, get the cross sections from openmc.config if filename is None: - filename = os.environ.get('OPENMC_MG_CROSS_SECTIONS') + filename = openmc.config.get('mg_cross_sections') # Check to make sure there was an environmental variable. if filename is None: - raise ValueError("Either path or OPENMC_MG_CROSS_SECTIONS " - "environmental variable must be set") + raise ValueError("Either path or openmc.config['mg_cross_sections']" + "must be set") check_type('filename', filename, str) file = h5py.File(filename, 'r') diff --git a/openmc/mixin.py b/openmc/mixin.py index 516162464d..0bc4128b0b 100644 --- a/openmc/mixin.py +++ b/openmc/mixin.py @@ -14,8 +14,11 @@ class EqualityMixin: def __eq__(self, other): if isinstance(other, type(self)): for key, value in self.__dict__.items(): - if not np.array_equal(value, other.__dict__.get(key)): - return False + if isinstance(value, np.ndarray): + if not np.array_equal(value, other.__dict__.get(key)): + return False + else: + return value == other.__dict__.get(key) else: return False @@ -69,12 +72,17 @@ class IDManagerMixin: cls.used_ids.add(uid) self._id = uid + @classmethod + def reset_ids(cls): + """Reset counters""" + cls.used_ids.clear() + cls.next_id = 1 + def reset_auto_ids(): """Reset counters for all auto-generated IDs""" for cls in IDManagerMixin.__subclasses__(): - cls.used_ids.clear() - cls.next_id = 1 + cls.reset_ids() def reserve_ids(ids, cls=None): diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index 41cede08e0..41aa920eae 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -1,16 +1,12 @@ from collections.abc import Iterable -from functools import partial from math import sqrt -from numbers import Real from operator import attrgetter from warnings import warn -from openmc import ( - XPlane, YPlane, Plane, ZCylinder, Cylinder, XCylinder, - YCylinder, Universe, Cell) -from ..checkvalue import ( - check_type, check_value, check_length, check_less_than, - check_iterable_type) +from openmc import Cylinder, Universe, Cell +from .surface_composite import RectangularPrism, HexagonalPrism +from ..checkvalue import (check_type, check_value, check_length, + check_less_than, check_iterable_type) import openmc.data @@ -111,271 +107,26 @@ def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', return out + + def rectangular_prism(width, height, axis='z', origin=(0., 0.), boundary_type='transmission', corner_radius=0.): - """Get an infinite rectangular prism from four planar surfaces. - - .. versionchanged:: 0.11 - This function was renamed from `get_rectangular_prism` to - `rectangular_prism`. - - Parameters - ---------- - width: float - Prism width in units of cm. The width is aligned with the y, x, - or x axes for prisms parallel to the x, y, or z axis, respectively. - height: float - Prism height in units of cm. The height is aligned with the z, z, - or y axes for prisms parallel to the x, y, or z axis, respectively. - axis : {'x', 'y', 'z'} - Axis with which the infinite length of the prism should be aligned. - Defaults to 'z'. - origin: Iterable of two floats - Origin of the prism. The two floats correspond to (y,z), (x,z) or - (x,y) for prisms parallel to the x, y or z axis, respectively. - Defaults to (0., 0.). - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} - Boundary condition that defines the behavior for particles hitting the - surfaces comprising the rectangular prism (default is 'transmission'). - corner_radius: float - Prism corner radius in units of cm. Defaults to 0. - - Returns - ------- - openmc.Region - The inside of a rectangular prism - - """ - - check_type('width', width, Real) - check_type('height', height, Real) - check_type('corner_radius', corner_radius, Real) - check_value('axis', axis, ['x', 'y', 'z']) - check_type('origin', origin, Iterable, Real) - - # Define function to create a plane on given axis - def plane(axis, name, value): - cls = globals()['{}Plane'.format(axis.upper())] - return cls(name='{} {}'.format(name, axis), - boundary_type=boundary_type, - **{axis + '0': value}) - - if axis == 'x': - x1, x2 = 'y', 'z' - elif axis == 'y': - x1, x2 = 'x', 'z' - else: - x1, x2 = 'x', 'y' - - # Get cylinder class corresponding to given axis - cyl = globals()['{}Cylinder'.format(axis.upper())] - - # Create rectangular region - min_x1 = plane(x1, 'minimum', -width/2 + origin[0]) - max_x1 = plane(x1, 'maximum', width/2 + origin[0]) - min_x2 = plane(x2, 'minimum', -height/2 + origin[1]) - max_x2 = plane(x2, 'maximum', height/2 + origin[1]) - if boundary_type == 'periodic': - min_x1.periodic_surface = max_x1 - min_x2.periodic_surface = max_x2 - prism = +min_x1 & -max_x1 & +min_x2 & -max_x2 - - # Handle rounded corners if given - if corner_radius > 0.: - if boundary_type == 'periodic': - raise ValueError('Periodic boundary conditions not permitted when ' - 'rounded corners are used.') - - args = {'r': corner_radius, 'boundary_type': boundary_type} - - args[x1 + '0'] = origin[0] - width/2 + corner_radius - args[x2 + '0'] = origin[1] - height/2 + corner_radius - x1_min_x2_min = cyl(name='{} min {} min'.format(x1, x2), **args) - - args[x1 + '0'] = origin[0] - width/2 + corner_radius - args[x2 + '0'] = origin[1] - height/2 + corner_radius - x1_min_x2_min = cyl(name='{} min {} min'.format(x1, x2), **args) - - args[x1 + '0'] = origin[0] - width/2 + corner_radius - args[x2 + '0'] = origin[1] + height/2 - corner_radius - x1_min_x2_max = cyl(name='{} min {} max'.format(x1, x2), **args) - - args[x1 + '0'] = origin[0] + width/2 - corner_radius - args[x2 + '0'] = origin[1] - height/2 + corner_radius - x1_max_x2_min = cyl(name='{} max {} min'.format(x1, x2), **args) - - args[x1 + '0'] = origin[0] + width/2 - corner_radius - args[x2 + '0'] = origin[1] + height/2 - corner_radius - x1_max_x2_max = cyl(name='{} max {} max'.format(x1, x2), **args) - - x1_min = plane(x1, 'min', -width/2 + origin[0] + corner_radius) - x1_max = plane(x1, 'max', width/2 + origin[0] - corner_radius) - x2_min = plane(x2, 'min', -height/2 + origin[1] + corner_radius) - x2_max = plane(x2, 'max', height/2 + origin[1] - corner_radius) - - corners = (+x1_min_x2_min & -x1_min & -x2_min) | \ - (+x1_min_x2_max & -x1_min & +x2_max) | \ - (+x1_max_x2_min & +x1_max & -x2_min) | \ - (+x1_max_x2_max & +x1_max & +x2_max) - - prism = prism & ~corners - - return prism - - -def get_rectangular_prism(*args, **kwargs): - warn("get_rectangular_prism(...) has been renamed rectangular_prism(...). " - "Future versions of OpenMC will not accept get_rectangular_prism.", - FutureWarning) - return rectangular_prism(*args, **kwargs) + warn("The rectangular_prism(...) function has been replaced by the " + "RectangularPrism(...) class. Future versions of OpenMC will not " + "accept rectangular_prism.", FutureWarning) + return -RectangularPrism( + width=width, height=height, axis=axis, origin=origin, + boundary_type=boundary_type, corner_radius=corner_radius) def hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), boundary_type='transmission', corner_radius=0.): - """Create a hexagon region from six surface planes. - - .. versionchanged:: 0.11 - This function was renamed from `get_hexagonal_prism` to - `hexagonal_prism`. - - Parameters - ---------- - edge_length : float - Length of a side of the hexagon in cm - orientation : {'x', 'y'} - An 'x' orientation means that two sides of the hexagon are parallel to - the x-axis and a 'y' orientation means that two sides of the hexagon are - parallel to the y-axis. - origin: Iterable of two floats - Origin of the prism. Defaults to (0., 0.). - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} - Boundary condition that defines the behavior for particles hitting the - surfaces comprising the hexagonal prism (default is 'transmission'). - corner_radius: float - Prism corner radius in units of cm. Defaults to 0. - - Returns - ------- - openmc.Region - The inside of a hexagonal prism - - """ - - l = edge_length - x, y = origin - - if orientation == 'y': - right = XPlane(x + sqrt(3.)/2*l, boundary_type=boundary_type) - left = XPlane(x - sqrt(3.)/2*l, boundary_type=boundary_type) - c = sqrt(3.)/3. - - # y = -x/sqrt(3) + a - upper_right = Plane(a=c, b=1., d=l+x*c+y, boundary_type=boundary_type) - - # y = x/sqrt(3) + a - upper_left = Plane(a=-c, b=1., d=l-x*c+y, boundary_type=boundary_type) - - # y = x/sqrt(3) - a - lower_right = Plane(a=-c, b=1., d=-l-x*c+y, boundary_type=boundary_type) - - # y = -x/sqrt(3) - a - lower_left = Plane(a=c, b=1., d=-l+x*c+y, boundary_type=boundary_type) - - prism = -right & +left & -upper_right & -upper_left & \ - +lower_right & +lower_left - - if boundary_type == 'periodic': - right.periodic_surface = left - upper_right.periodic_surface = lower_left - lower_right.periodic_surface = upper_left - - elif orientation == 'x': - top = YPlane(y0=y + sqrt(3.)/2*l, boundary_type=boundary_type) - bottom = YPlane(y0=y - sqrt(3.)/2*l, boundary_type=boundary_type) - c = sqrt(3.) - - # y = -sqrt(3)*(x - a) - upper_right = Plane(a=c, b=1., d=c*l+x*c+y, boundary_type=boundary_type) - - # y = sqrt(3)*(x + a) - lower_right = Plane(a=-c, b=1., d=-c*l-x*c+y, - boundary_type=boundary_type) - - # y = -sqrt(3)*(x + a) - lower_left = Plane(a=c, b=1., d=-c*l+x*c+y, boundary_type=boundary_type) - - # y = sqrt(3)*(x + a) - upper_left = Plane(a=-c, b=1., d=c*l-x*c+y, boundary_type=boundary_type) - - prism = -top & +bottom & -upper_right & +lower_right & \ - +lower_left & -upper_left - - if boundary_type == 'periodic': - top.periodic_surface = bottom - upper_right.periodic_surface = lower_left - lower_right.periodic_surface = upper_left - - # Handle rounded corners if given - if corner_radius > 0.: - if boundary_type == 'periodic': - raise ValueError('Periodic boundary conditions not permitted when ' - 'rounded corners are used.') - - c = sqrt(3.)/2 - t = l - corner_radius/c - - # Cylinder with corner radius and boundary type pre-applied - cyl1 = partial(ZCylinder, r=corner_radius, boundary_type=boundary_type) - cyl2 = partial(ZCylinder, r=corner_radius/(2*c), - boundary_type=boundary_type) - - if orientation == 'x': - x_min_y_min_in = cyl1(name='x min y min in', x0=x-t/2, y0=y-c*t) - x_min_y_max_in = cyl1(name='x min y max in', x0=x+t/2, y0=y-c*t) - x_max_y_min_in = cyl1(name='x max y min in', x0=x-t/2, y0=y+c*t) - x_max_y_max_in = cyl1(name='x max y max in', x0=x+t/2, y0=y+c*t) - x_min_in = cyl1(name='x min in', x0=x-t, y0=y) - x_max_in = cyl1(name='x max in', x0=x+t, y0=y) - - x_min_y_min_out = cyl2(name='x min y min out', x0=x-l/2, y0=y-c*l) - x_min_y_max_out = cyl2(name='x min y max out', x0=x+l/2, y0=y-c*l) - x_max_y_min_out = cyl2(name='x max y min out', x0=x-l/2, y0=y+c*l) - x_max_y_max_out = cyl2(name='x max y max out', x0=x+l/2, y0=y+c*l) - x_min_out = cyl2(name='x min out', x0=x-l, y0=y) - x_max_out = cyl2(name='x max out', x0=x+l, y0=y) - - corners = (+x_min_y_min_in & -x_min_y_min_out | - +x_min_y_max_in & -x_min_y_max_out | - +x_max_y_min_in & -x_max_y_min_out | - +x_max_y_max_in & -x_max_y_max_out | - +x_min_in & -x_min_out | - +x_max_in & -x_max_out) - - elif orientation == 'y': - x_min_y_min_in = cyl1(name='x min y min in', x0=x-c*t, y0=y-t/2) - x_min_y_max_in = cyl1(name='x min y max in', x0=x-c*t, y0=y+t/2) - x_max_y_min_in = cyl1(name='x max y min in', x0=x+c*t, y0=y-t/2) - x_max_y_max_in = cyl1(name='x max y max in', x0=x+c*t, y0=y+t/2) - y_min_in = cyl1(name='y min in', x0=x, y0=y-t) - y_max_in = cyl1(name='y max in', x0=x, y0=y+t) - - x_min_y_min_out = cyl2(name='x min y min out', x0=x-c*l, y0=y-l/2) - x_min_y_max_out = cyl2(name='x min y max out', x0=x-c*l, y0=y+l/2) - x_max_y_min_out = cyl2(name='x max y min out', x0=x+c*l, y0=y-l/2) - x_max_y_max_out = cyl2(name='x max y max out', x0=x+c*l, y0=y+l/2) - y_min_out = cyl2(name='y min out', x0=x, y0=y-l) - y_max_out = cyl2(name='y max out', x0=x, y0=y+l) - - corners = (+x_min_y_min_in & -x_min_y_min_out | - +x_min_y_max_in & -x_min_y_max_out | - +x_max_y_min_in & -x_max_y_min_out | - +x_max_y_max_in & -x_max_y_max_out | - +y_min_in & -y_min_out | - +y_max_in & -y_max_out) - - prism = prism & ~corners - - return prism + warn("The hexagonal_prism(...) function has been replaced by the " + "HexagonalPrism(...) class. Future versions of OpenMC will not " + "accept hexagonal_prism.", FutureWarning) + return -HexagonalPrism( + edge_length=edge_length, orientation=orientation, origin=origin, + boundary_type=boundary_type, corner_radius=corner_radius) def get_hexagonal_prism(*args, **kwargs): @@ -462,16 +213,15 @@ def pin(surfaces, items, subdivisions=None, divide_vols=True, check_iterable_type("surfaces", surfaces[1:], surf_type) # Check for increasing radii and equal centers - if surf_type is ZCylinder: + if surf_type is openmc.ZCylinder: center_getter = attrgetter("x0", "y0") - elif surf_type is YCylinder: + elif surf_type is openmc.YCylinder: center_getter = attrgetter("x0", "z0") - elif surf_type is XCylinder: + elif surf_type is openmc.XCylinder: center_getter = attrgetter("z0", "y0") else: raise TypeError( - "Not configured to interpret {} surfaces".format( - surf_type.__name__)) + f"Not configured to interpret {surf_type.__name__} surfaces") centers = set() prev_rad = 0 diff --git a/openmc/model/model.py b/openmc/model/model.py index 072c5ae73b..c1ffafafd3 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,30 +1,27 @@ -from collections.abc import Iterable -from contextlib import contextmanager -from functools import lru_cache -import os +from __future__ import annotations +from collections.abc import Iterable, Sequence +import copy +from functools import cache from pathlib import Path -from numbers import Integral -from tempfile import NamedTemporaryFile +import math +from numbers import Integral, Real +import random +import re +from tempfile import NamedTemporaryFile, TemporaryDirectory +import warnings import h5py +import lxml.etree as ET +import numpy as np import openmc +import openmc._xml as xml from openmc.dummy_comm import DummyCommunicator from openmc.executor import _process_CLI_arguments -from openmc.checkvalue import check_type, check_value +from openmc.checkvalue import check_type, check_value, PathLike from openmc.exceptions import InvalidIDError - - -@contextmanager -def _change_directory(working_dir): - """A context manager for executing in a provided working directory""" - start_dir = Path.cwd() - Path.mkdir(working_dir, exist_ok=True) - os.chdir(working_dir) - try: - yield - finally: - os.chdir(start_dir) +from openmc.plots import add_plot_params, _BASIS_INDICES +from openmc.utility_funcs import change_directory class Model: @@ -70,47 +67,92 @@ class Model: """ - def __init__(self, geometry=None, materials=None, settings=None, - tallies=None, plots=None): - self.geometry = openmc.Geometry() - self.materials = openmc.Materials() - self.settings = openmc.Settings() - self.tallies = openmc.Tallies() - self.plots = openmc.Plots() - - if geometry is not None: - self.geometry = geometry - if materials is not None: - self.materials = materials - if settings is not None: - self.settings = settings - if tallies is not None: - self.tallies = tallies - if plots is not None: - self.plots = plots + def __init__( + self, + geometry: openmc.Geometry | None = None, + materials: openmc.Materials | None = None, + settings: openmc.Settings | None = None, + tallies: openmc.Tallies | None = None, + plots: openmc.Plots | None = None, + ): + self.geometry = openmc.Geometry() if geometry is None else geometry + self.materials = openmc.Materials() if materials is None else materials + self.settings = openmc.Settings() if settings is None else settings + self.tallies = openmc.Tallies() if tallies is None else tallies + self.plots = openmc.Plots() if plots is None else plots @property - def geometry(self): + def geometry(self) -> openmc.Geometry: return self._geometry + @geometry.setter + def geometry(self, geometry): + check_type('geometry', geometry, openmc.Geometry) + self._geometry = geometry + @property - def materials(self): + def materials(self) -> openmc.Materials: return self._materials + @materials.setter + def materials(self, materials): + check_type('materials', materials, Iterable, openmc.Material) + if isinstance(materials, openmc.Materials): + self._materials = materials + else: + if not hasattr(self, '_materials'): + self._materials = openmc.Materials() + del self._materials[:] + for mat in materials: + self._materials.append(mat) + @property - def settings(self): + def settings(self) -> openmc.Settings: return self._settings + @settings.setter + def settings(self, settings): + check_type('settings', settings, openmc.Settings) + self._settings = settings + @property - def tallies(self): + def tallies(self) -> openmc.Tallies: return self._tallies - @property - def plots(self): - return self._plots + @tallies.setter + def tallies(self, tallies): + check_type('tallies', tallies, Iterable, openmc.Tally) + if isinstance(tallies, openmc.Tallies): + self._tallies = tallies + else: + if not hasattr(self, '_tallies'): + self._tallies = openmc.Tallies() + del self._tallies[:] + for tally in tallies: + self._tallies.append(tally) @property - def is_initialized(self): + def plots(self) -> openmc.Plots: + return self._plots + + @plots.setter + def plots(self, plots): + check_type('plots', plots, Iterable, openmc.PlotBase) + if isinstance(plots, openmc.Plots): + self._plots = plots + else: + if not hasattr(self, '_plots'): + self._plots = openmc.Plots() + del self._plots[:] + for plot in plots: + self._plots.append(plot) + + @property + def bounding_box(self) -> openmc.BoundingBox: + return self.geometry.bounding_box + + @property + def is_initialized(self) -> bool: try: import openmc.lib return openmc.lib.is_initialized @@ -118,25 +160,25 @@ class Model: return False @property - @lru_cache(maxsize=None) - def _materials_by_id(self): + @cache + def _materials_by_id(self) -> dict: """Dictionary mapping material ID --> material""" - if self.materials is None: - mats = self.geometry.get_all_materials().values() - else: + if self.materials: mats = self.materials + else: + mats = self.geometry.get_all_materials().values() return {mat.id: mat for mat in mats} @property - @lru_cache(maxsize=None) - def _cells_by_id(self): + @cache + def _cells_by_id(self) -> dict: """Dictionary mapping cell ID --> cell""" cells = self.geometry.get_all_cells() return {cell.id: cell for cell in cells.values()} @property - @lru_cache(maxsize=None) - def _cells_by_name(self): + @cache + def _cells_by_name(self) -> dict[int, openmc.Cell]: # Get the names maps, but since names are not unique, store a set for # each name key. In this way when the user requests a change by a name, # the change will be applied to all of the same name. @@ -148,8 +190,8 @@ class Model: return result @property - @lru_cache(maxsize=None) - def _materials_by_name(self): + @cache + def _materials_by_name(self) -> dict[int, openmc.Material]: if self.materials is None: mats = self.geometry.get_all_materials().values() else: @@ -161,65 +203,61 @@ class Model: result[mat.name].add(mat) return result - @geometry.setter - def geometry(self, geometry): - check_type('geometry', geometry, openmc.Geometry) - self._geometry = geometry + def add_kinetics_parameters_tallies(self, num_groups: int | None = None): + """Add tallies for calculating kinetics parameters using the IFP method. - @materials.setter - def materials(self, materials): - check_type('materials', materials, Iterable, openmc.Material) - if isinstance(materials, openmc.Materials): - self._materials = materials - else: - del self._materials[:] - for mat in materials: - self._materials.append(mat) + This method adds tallies to the model for calculating two kinetics + parameters, the generation time and the effective delayed neutron + fraction (beta effective). After a model is run, these parameters can be + determined through the :meth:`openmc.StatePoint.ifp_results` method. - @settings.setter - def settings(self, settings): - check_type('settings', settings, openmc.Settings) - self._settings = settings + Parameters + ---------- + num_groups : int, optional + Number of precursor groups to filter the delayed neutron fraction. + If None, only the total effective delayed neutron fraction is + tallied. - @tallies.setter - def tallies(self, tallies): - check_type('tallies', tallies, Iterable, openmc.Tally) - if isinstance(tallies, openmc.Tallies): - self._tallies = tallies - else: - del self._tallies[:] - for tally in tallies: - self._tallies.append(tally) - - @plots.setter - def plots(self, plots): - check_type('plots', plots, Iterable, openmc.Plot) - if isinstance(plots, openmc.Plots): - self._plots = plots - else: - del self._plots[:] - for plot in plots: - self._plots.append(plot) + """ + if not any('ifp-time-numerator' in t.scores for t in self.tallies): + gen_time_tally = openmc.Tally(name='IFP time numerator') + gen_time_tally.scores = ['ifp-time-numerator'] + self.tallies.append(gen_time_tally) + if not any('ifp-beta-numerator' in t.scores for t in self.tallies): + beta_tally = openmc.Tally(name='IFP beta numerator') + beta_tally.scores = ['ifp-beta-numerator'] + if num_groups is not None: + beta_tally.filters = [openmc.DelayedGroupFilter(list(range(1, num_groups + 1)))] + self.tallies.append(beta_tally) + if not any('ifp-denominator' in t.scores for t in self.tallies): + denom_tally = openmc.Tally(name='IFP denominator') + denom_tally.scores = ['ifp-denominator'] + self.tallies.append(denom_tally) @classmethod - def from_xml(cls, geometry='geometry.xml', materials='materials.xml', - settings='settings.xml', tallies='tallies.xml', - plots='plots.xml'): + def from_xml( + cls, + geometry: PathLike = "geometry.xml", + materials: PathLike = "materials.xml", + settings: PathLike = "settings.xml", + tallies: PathLike = "tallies.xml", + plots: PathLike = "plots.xml", + ) -> Model: """Create model from existing XML files Parameters ---------- - geometry : str + geometry : PathLike Path to geometry.xml file - materials : str + materials : PathLike Path to materials.xml file - settings : str + settings : PathLike Path to settings.xml file - tallies : str + tallies : PathLike Path to tallies.xml file .. versionadded:: 0.13.0 - plots : str + plots : PathLike Path to plots.xml file .. versionadded:: 0.13.0 @@ -233,12 +271,56 @@ class Model: materials = openmc.Materials.from_xml(materials) geometry = openmc.Geometry.from_xml(geometry, materials) settings = openmc.Settings.from_xml(settings) - tallies = openmc.Tallies.from_xml(tallies) if Path(tallies).exists() else None + tallies = openmc.Tallies.from_xml( + tallies) if Path(tallies).exists() else None plots = openmc.Plots.from_xml(plots) if Path(plots).exists() else None return cls(geometry, materials, settings, tallies, plots) - def init_lib(self, threads=None, geometry_debug=False, restart_file=None, - tracks=False, output=True, event_based=None, intracomm=None): + @classmethod + def from_model_xml(cls, path: PathLike = "model.xml") -> Model: + """Create model from single XML file + + .. versionadded:: 0.13.3 + + Parameters + ---------- + path : PathLike + Path to model.xml file + """ + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) + root = tree.getroot() + + model = cls() + + meshes = {} + model.settings = openmc.Settings.from_xml_element( + root.find('settings'), meshes) + model.materials = openmc.Materials.from_xml_element( + root.find('materials')) + model.geometry = openmc.Geometry.from_xml_element( + root.find('geometry'), model.materials) + + if root.find('tallies') is not None: + model.tallies = openmc.Tallies.from_xml_element( + root.find('tallies'), meshes) + + if root.find('plots') is not None: + model.plots = openmc.Plots.from_xml_element(root.find('plots')) + + return model + + def init_lib( + self, + threads: int | None = None, + geometry_debug: bool = False, + restart_file: PathLike | None = None, + tracks: bool = False, + output: bool = True, + event_based: bool | None = None, + intracomm=None, + directory: PathLike | None = None, + ): """Initializes the model in memory via the C API .. versionadded:: 0.13.0 @@ -253,10 +335,12 @@ class Model: variable). geometry_debug : bool, optional Turn on geometry debugging during simulation. Defaults to False. - restart_file : str, optional + restart_file : PathLike, optional Path to restart file to use tracks : bool, optional - Write tracks for all particles. Defaults to False. + Enables the writing of particles tracks. The number of particle + tracks written to tracks.h5 is limited to 1000 unless + Settings.max_tracks is set. Defaults to False. output : bool Capture OpenMC output from standard out event_based : None or bool, optional @@ -264,6 +348,8 @@ class Model: the Settings will be used. intracomm : mpi4py.MPI.Intracomm or None, optional MPI intracommunicator + directory : PathLike or None, optional + Directory to write XML files to. Defaults to None. """ import openmc.lib @@ -277,7 +363,8 @@ class Model: args = _process_CLI_arguments( volume=False, geometry_debug=geometry_debug, restart_file=restart_file, threads=threads, tracks=tracks, - event_based=event_based) + event_based=event_based, path_input=directory) + # Args adds the openmc_exec command in the first entry; remove it args = args[1:] @@ -291,7 +378,10 @@ class Model: self._intracomm = DummyCommunicator() if self._intracomm.rank == 0: - self.export_to_xml() + if directory is not None: + self.export_to_xml(directory=directory) + else: + self.export_to_xml() self._intracomm.barrier() # We cannot pass DummyCommunicator to openmc.lib.init so pass instead @@ -299,6 +389,28 @@ class Model: # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) + def sync_dagmc_universes(self): + """Synchronize all DAGMC universes in the current geometry. + + This method iterates over all DAGMC universes in the geometry and + synchronizes their cells with the current material assignments. Requires + that the model has been initialized via :meth:`Model.init_lib`. + + .. versionadded:: 0.15.1 + + """ + if self.is_initialized: + if self.materials: + materials = self.materials + else: + materials = list(self.geometry.get_all_materials().values()) + for univ in self.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + univ.sync_dagmc_cells(materials) + else: + raise ValueError("The model must be initialized before calling " + "this method") + def finalize_lib(self): """Finalize simulation and free memory allocated for the C API @@ -310,9 +422,15 @@ class Model: openmc.lib.finalize() - def deplete(self, timesteps, method='cecm', final_step=True, - operator_kwargs=None, directory='.', output=True, - **integrator_kwargs): + def deplete( + self, + method: str = "cecm", + final_step: bool = True, + operator_kwargs: dict | None = None, + directory: PathLike = ".", + output: bool = True, + **integrator_kwargs, + ): """Deplete model using specified timesteps/power .. versionchanged:: 0.13.0 @@ -321,26 +439,28 @@ class Model: Parameters ---------- - timesteps : iterable of float - Array of timesteps in units of [s]. Note that values are not - cumulative. - method : str, optional + timesteps : iterable of float or iterable of tuple + Array of timesteps. Note that values are not cumulative. The units are + specified by the `timestep_units` argument when `timesteps` is an + iterable of float. Alternatively, units can be specified for each step + by passing an iterable of (value, unit) tuples. + method : str Integration method used for depletion (e.g., 'cecm', 'predictor'). Defaults to 'cecm'. final_step : bool, optional Indicate whether or not a transport solve should be run at the end of the last timestep. Defaults to running this transport solve. operator_kwargs : dict - Keyword arguments passed to the depletion Operator initializer + Keyword arguments passed to the depletion operator initializer (e.g., :func:`openmc.deplete.Operator`) - directory : str, optional + directory : PathLike, optional Directory to write XML files to. If it doesn't exist already, it will be created. Defaults to the current working directory output : bool Capture OpenMC output from standard out integrator_kwargs : dict - Remaining keyword arguments passed to the depletion Integrator - initializer (e.g., :func:`openmc.deplete.integrator.cecm`). + Remaining keyword arguments passed to the depletion integrator + (e.g., :class:`openmc.deplete.CECMIntegrator`). """ @@ -358,10 +478,10 @@ class Model: # Store whether or not the library was initialized when we started started_initialized = self.is_initialized - with _change_directory(Path(directory)): + with change_directory(directory): with openmc.lib.quiet_dll(output): - depletion_operator = \ - dep.Operator(self, **op_kwargs) + # TODO: Support use of IndependentOperator too + depletion_operator = dep.CoupledOperator(self, **op_kwargs) # Tell depletion_operator.finalize NOT to clear C API memory when # it is done @@ -371,8 +491,7 @@ class Model: check_value('method', method, dep.integrators.integrator_by_name.keys()) integrator_class = dep.integrators.integrator_by_name[method] - integrator = integrator_class(depletion_operator, timesteps, - **integrator_kwargs) + integrator = integrator_class(depletion_operator, **integrator_kwargs) # Now perform the depletion with openmc.lib.quiet_dll(output): @@ -395,40 +514,127 @@ class Model: depletion_operator.cleanup_when_done = True depletion_operator.finalize() - def export_to_xml(self, directory='.'): - """Export model to XML files. + def export_to_xml(self, directory: PathLike = '.', remove_surfs: bool = False, + nuclides_to_ignore: Iterable[str] | None = None): + """Export model to separate XML files. Parameters ---------- - directory : str + directory : PathLike Directory to write XML files to. If it doesn't exist already, it will be created. + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting. + + .. versionadded:: 0.13.1 + nuclides_to_ignore : list of str + Nuclides to ignore when exporting to XML. """ # Create directory if required d = Path(directory) if not d.is_dir(): - d.mkdir(parents=True) + d.mkdir(parents=True, exist_ok=True) self.settings.export_to_xml(d) - self.geometry.export_to_xml(d) + self.geometry.export_to_xml(d, remove_surfs=remove_surfs) # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build # a collection. if self.materials: - self.materials.export_to_xml(d) + self.materials.export_to_xml(d, nuclides_to_ignore=nuclides_to_ignore) else: materials = openmc.Materials(self.geometry.get_all_materials() .values()) - materials.export_to_xml(d) + materials.export_to_xml(d, nuclides_to_ignore=nuclides_to_ignore) if self.tallies: self.tallies.export_to_xml(d) if self.plots: self.plots.export_to_xml(d) - def import_properties(self, filename): + def export_to_model_xml(self, path: PathLike = 'model.xml', remove_surfs: bool = False, + nuclides_to_ignore: Iterable[str] | None = None): + """Export model to a single XML file. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + path : str or PathLike + Location of the XML file to write (default is 'model.xml'). Can be a + directory or file path. + remove_surfs : bool + Whether or not to remove redundant surfaces from the geometry when + exporting. + nuclides_to_ignore : list of str + Nuclides to ignore when exporting to XML. + + """ + xml_path = Path(path) + # if the provided path doesn't end with the XML extension, assume the + # input path is meant to be a directory. If the directory does not + # exist, create it and place a 'model.xml' file there. + if not str(xml_path).endswith('.xml'): + if not xml_path.exists(): + xml_path.mkdir(parents=True, exist_ok=True) + elif not xml_path.is_dir(): + raise FileExistsError(f"File exists and is not a directory: '{xml_path}'") + xml_path /= 'model.xml' + # if this is an XML file location and the file's parent directory does + # not exist, create it before continuing + elif not xml_path.parent.exists(): + xml_path.parent.mkdir(parents=True, exist_ok=True) + + if remove_surfs: + warnings.warn("remove_surfs kwarg will be deprecated soon, please " + "set the Geometry.merge_surfaces attribute instead.") + self.geometry.merge_surfaces = True + + # provide a memo to track which meshes have been written + mesh_memo = set() + settings_element = self.settings.to_xml_element(mesh_memo) + geometry_element = self.geometry.to_xml_element() + + xml.clean_indentation(geometry_element, level=1) + xml.clean_indentation(settings_element, level=1) + + # If a materials collection was specified, export it. Otherwise, look + # for all materials in the geometry and use that to automatically build + # a collection. + if self.materials: + materials = self.materials + else: + materials = openmc.Materials(self.geometry.get_all_materials() + .values()) + + with open(xml_path, 'w', encoding='utf-8', errors='xmlcharrefreplace') as fh: + # write the XML header + fh.write("\n") + fh.write("\n") + # Write the materials collection to the open XML file first. + # This will write the XML header also + materials._write_xml(fh, False, level=1, + nuclides_to_ignore=nuclides_to_ignore) + # Write remaining elements as a tree + fh.write(ET.tostring(geometry_element, encoding="unicode")) + fh.write(ET.tostring(settings_element, encoding="unicode")) + + if self.tallies: + tallies_element = self.tallies.to_xml_element(mesh_memo) + xml.clean_indentation( + tallies_element, level=1, trailing_indent=self.plots) + fh.write(ET.tostring(tallies_element, encoding="unicode")) + if self.plots: + plots_element = self.plots.to_xml_element() + xml.clean_indentation( + plots_element, level=1, trailing_indent=False) + fh.write(ET.tostring(plots_element, encoding="unicode")) + fh.write("\n") + + def import_properties(self, filename: PathLike): """Import physical properties .. versionchanged:: 0.13.0 @@ -436,7 +642,7 @@ class Model: Parameters ---------- - filename : str + filename : PathLike Path to properties HDF5 file See Also @@ -458,15 +664,34 @@ class Model: raise ValueError("Number of cells in properties file doesn't " "match current model.") - # Update temperatures for cells filled with materials + # Update temperatures and densities for cells filled with materials for name, group in cells_group.items(): cell_id = int(name.split()[1]) cell = cells[cell_id] if cell.fill_type in ('material', 'distribmat'): - cell.temperature = group['temperature'][()] + temperature = group['temperature'][()] + cell.temperature = temperature if self.is_initialized: lib_cell = openmc.lib.cells[cell_id] - lib_cell.set_temperature(group['temperature'][()]) + if temperature.size > 1: + for i, T in enumerate(temperature): + lib_cell.set_temperature(T, i) + else: + lib_cell.set_temperature(temperature[0]) + + if group['density']: + density = group['density'][()] + if density.size > 1: + cell.density = [rho for rho in density] + else: + cell.density = density + if self.is_initialized: + lib_cell = openmc.lib.cells[cell_id] + if density.size > 1: + for i, rho in enumerate(density): + lib_cell.set_density(rho, i) + else: + lib_cell.set_density(density[0]) # Make sure number of materials matches mats_group = fh['materials'] @@ -484,13 +709,28 @@ class Model: C_mat = openmc.lib.materials[mat_id] C_mat.set_density(atom_density, 'atom/b-cm') - def run(self, particles=None, threads=None, geometry_debug=False, - restart_file=None, tracks=False, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, event_based=None): - """Runs OpenMC. If the C API has been initialized, then the C API is - used, otherwise, this method creates the XML files and runs OpenMC via - a system call. In both cases this method returns the path to the last - statepoint file generated. + def run( + self, + particles: int | None = None, + threads: int | None = None, + geometry_debug: bool = False, + restart_file: PathLike | None = None, + tracks: bool = False, + output: bool = True, + cwd: PathLike = ".", + openmc_exec: PathLike = "openmc", + mpi_args: Iterable[str] = None, + event_based: bool | None = None, + export_model_xml: bool = True, + apply_tally_results: bool = False, + **export_kwargs, + ) -> Path: + """Run OpenMC + + If the C API has been initialized, then the C API is used, otherwise, + this method creates the XML files and runs OpenMC via a system call. In + both cases this method returns the path to the last statepoint file + generated. .. versionchanged:: 0.12 Instead of returning the final k-effective value, this function now @@ -510,29 +750,44 @@ class Model: value set by the :envvar:`OMP_NUM_THREADS` environment variable). geometry_debug : bool, optional Turn on geometry debugging during simulation. Defaults to False. - restart_file : str, optional + restart_file : str or PathLike Path to restart file to use tracks : bool, optional - Write tracks for all particles. Defaults to False. + Enables the writing of particles tracks. The number of particle + tracks written to tracks.h5 is limited to 1000 unless + Settings.max_tracks is set. Defaults to False. output : bool, optional Capture OpenMC output from standard out - cwd : str, optional - Path to working directory to run in. Defaults to the current - working directory. + cwd : PathLike, optional + Path to working directory to run in. Defaults to the current working + directory. openmc_exec : str, optional Path to OpenMC executable. Defaults to 'openmc'. mpi_args : list of str, optional - MPI execute command and any additional MPI arguments to pass, - e.g. ['mpiexec', '-n', '8']. + MPI execute command and any additional MPI arguments to pass, e.g. + ['mpiexec', '-n', '8']. event_based : None or bool, optional - Turns on event-based parallelism if True. If None, the value in - the Settings will be used. + Turns on event-based parallelism if True. If None, the value in the + Settings will be used. + export_model_xml : bool, optional + Exports a single model.xml file rather than separate files. Defaults + to True. + + .. versionadded:: 0.13.3 + apply_tally_results : bool + Whether to apply results of the final statepoint file to the + model's tally objects. + + .. versionadded:: 0.15.1 + **export_kwargs + Keyword arguments passed to either :meth:`Model.export_to_model_xml` + or :meth:`Model.export_to_xml`. Returns ------- Path - Path to the last statepoint written by this run - (None if no statepoint was written) + Path to the last statepoint written by this run (None if no + statepoint was written) """ @@ -545,7 +800,7 @@ class Model: last_statepoint = None # Operate in the provided working directory - with _change_directory(Path(cwd)): + with change_directory(cwd): if self.is_initialized: # Handle the run options as applicable # First dont allow ones that must be set via init @@ -576,10 +831,14 @@ class Model: else: # Then run via the command line - self.export_to_xml() + if export_model_xml: + self.export_to_model_xml(**export_kwargs) + else: + self.export_to_xml(**export_kwargs) + path_input = export_kwargs.get("path", None) openmc.run(particles, threads, geometry_debug, restart_file, tracks, output, Path('.'), openmc_exec, mpi_args, - event_based) + event_based, path_input) # Get output directory and return the last statepoint written if self.settings.output and 'path' in self.settings.output: @@ -591,11 +850,23 @@ class Model: if mtime >= tstart: # >= allows for poor clock resolution tstart = mtime last_statepoint = sp + + if apply_tally_results: + self.apply_tally_results(last_statepoint) + return last_statepoint - def calculate_volumes(self, threads=None, output=True, cwd='.', - openmc_exec='openmc', mpi_args=None, - apply_volumes=True): + def calculate_volumes( + self, + threads: int | None = None, + output: bool = True, + cwd: PathLike = ".", + openmc_exec: PathLike = "openmc", + mpi_args: list[str] | None = None, + apply_volumes: bool = True, + export_model_xml: bool = True, + **export_kwargs, + ): """Runs an OpenMC stochastic volume calculation and, if requested, applies volumes to the model @@ -624,14 +895,21 @@ class Model: apply_volumes : bool, optional Whether apply the volume calculation results from this calculation to the model. Defaults to applying the volumes. + export_model_xml : bool, optional + Exports a single model.xml file rather than separate files. Defaults + to True. + **export_kwargs + Keyword arguments passed to either :meth:`Model.export_to_model_xml` + or :meth:`Model.export_to_xml`. + """ if len(self.settings.volume_calculations) == 0: # Then there is no volume calculation specified - raise ValueError("The Settings.volume_calculation attribute must" + raise ValueError("The Settings.volume_calculations attribute must" " be specified before executing this method!") - with _change_directory(Path(cwd)): + with change_directory(cwd): if self.is_initialized: if threads is not None: msg = "Threads must be set via Model.is_initialized(...)" @@ -645,10 +923,15 @@ class Model: openmc.lib.calculate_volumes(output) else: - self.export_to_xml() - openmc.calculate_volumes(threads=threads, output=output, - openmc_exec=openmc_exec, - mpi_args=mpi_args) + if export_model_xml: + self.export_to_model_xml(**export_kwargs) + else: + self.export_to_xml(**export_kwargs) + path_input = export_kwargs.get("path", None) + openmc.calculate_volumes( + threads=threads, output=output, openmc_exec=openmc_exec, + mpi_args=mpi_args, path_input=path_input + ) # Now we apply the volumes if apply_volumes: @@ -656,7 +939,12 @@ class Model: for i, vol_calc in enumerate(self.settings.volume_calculations): vol_calc.load_results(f"volume_{i + 1}.h5") # First add them to the Python side - self.geometry.add_volume_information(vol_calc) + if vol_calc.domain_type == "material" and self.materials: + for material in self.materials: + if material.id in vol_calc.volumes: + material.add_volume_information(vol_calc) + else: + self.geometry.add_volume_information(vol_calc) # And now repeat for the C API if self.is_initialized and vol_calc.domain_type == 'material': @@ -665,7 +953,358 @@ class Model: openmc.lib.materials[domain_id].volume = \ vol_calc.volumes[domain_id].n - def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc'): + + def _set_plot_defaults( + self, + origin: Sequence[float] | None, + width: Sequence[float] | None, + pixels: int | Sequence[int], + basis: str + ): + x, y, _ = _BASIS_INDICES[basis] + + bb = self.bounding_box + # checks to see if bounding box contains -inf or inf values + if np.isinf(bb.extent[basis]).any(): + if origin is None: + origin = (0, 0, 0) + if width is None: + width = (10, 10) + else: + if origin is None: + # if nan values in the bb.center they get replaced with 0.0 + # this happens when the bounding_box contains inf values + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RuntimeWarning) + origin = np.nan_to_num(bb.center) + if width is None: + bb_width = bb.width + width = (bb_width[x], bb_width[y]) + + if isinstance(pixels, int): + aspect_ratio = width[0] / width[1] + pixels_y = math.sqrt(pixels / aspect_ratio) + pixels = (int(pixels / pixels_y), int(pixels_y)) + + return origin, width, pixels + + def id_map( + self, + origin: Sequence[float] | None = None, + width: Sequence[float] | None = None, + pixels: int | Sequence[int] = 40000, + basis: str = 'xy', + **init_kwargs + ) -> np.ndarray: + """Generate an ID map for domains based on the plot parameters + + If the model is not yet initialized, it will be initialized with + openmc.lib. If the model is initialized, the model will remain + initialized after this method call exits. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + origin : Sequence[float], optional + Origin of the plot. If unspecified, this argument defaults to the + center of the bounding box if the bounding box does not contain inf + values for the provided basis, otherwise (0.0, 0.0, 0.0). + width : Sequence[float], optional + Width of the plot. If unspecified, this argument defaults to the + width of the bounding box if the bounding box does not contain inf + values for the provided basis, otherwise (10.0, 10.0). + pixels : int | Sequence[int], optional + If an iterable of ints is provided then this directly sets the + number of pixels to use in each basis direction. If a single int is + provided then this sets the total number of pixels in the plot and + the number of pixels in each basis direction is calculated from this + total and the image aspect ratio based on the width argument. + basis : {'xy', 'yz', 'xz'}, optional + Basis of the plot. + **init_kwargs + Keyword arguments passed to :meth:`Model.init_lib`. + + Returns + ------- + id_map : numpy.ndarray + A NumPy array with shape (vertical pixels, horizontal pixels, 3) of + OpenMC property IDs with dtype int32. The last dimension of the + array contains cell IDs, cell instances, and material IDs (in that + order). + """ + import openmc.lib + + origin, width, pixels = self._set_plot_defaults( + origin, width, pixels, basis) + + # initialize the openmc.lib.plot._PlotBase object + plot_obj = openmc.lib.plot._PlotBase() + plot_obj.origin = origin + plot_obj.width = width[0] + plot_obj.height = width[1] + plot_obj.h_res = pixels[0] + plot_obj.v_res = pixels[1] + plot_obj.basis = basis + + # Silence output by default. Also set arguments to start in volume + # calculation mode to avoid loading cross sections + init_kwargs.setdefault('output', False) + init_kwargs.setdefault('args', ['-c']) + + with openmc.lib.TemporarySession(self, **init_kwargs): + return openmc.lib.id_map(plot_obj) + + @add_plot_params + def plot( + self, + origin: Sequence[float] | None = None, + width: Sequence[float] | None = None, + pixels: int | Sequence[int] = 40000, + basis: str = 'xy', + color_by: str = 'cell', + colors: dict | None = None, + seed: int | None = None, + openmc_exec: PathLike = 'openmc', + axes=None, + legend: bool = False, + axis_units: str = 'cm', + outline: bool | str = False, + show_overlaps: bool = False, + overlap_color: Sequence[int] | str | None = None, + n_samples: int | None = None, + plane_tolerance: float = 1., + legend_kwargs: dict | None = None, + source_kwargs: dict | None = None, + contour_kwargs: dict | None = None, + **kwargs, + ): + """Display a slice plot of the model. + + .. versionadded:: 0.15.1 + """ + import matplotlib.image as mpimg + import matplotlib.patches as mpatches + import matplotlib.pyplot as plt + + check_type('n_samples', n_samples, int | None) + check_type('plane_tolerance', plane_tolerance, Real) + if legend_kwargs is None: + legend_kwargs = {} + legend_kwargs.setdefault('bbox_to_anchor', (1.05, 1)) + legend_kwargs.setdefault('loc', 2) + legend_kwargs.setdefault('borderaxespad', 0.0) + if source_kwargs is None: + source_kwargs = {} + source_kwargs.setdefault('marker', 'x') + + # Set indices using basis and create axis labels + x, y, z = _BASIS_INDICES[basis] + xlabel, ylabel = f'{basis[0]} [{axis_units}]', f'{basis[1]} [{axis_units}]' + + # Determine extents of plot + origin, width, pixels = self._set_plot_defaults( + origin, width, pixels, basis) + + axis_scaling_factor = {'km': 0.00001, 'm': 0.01, 'cm': 1, 'mm': 10} + + x_min = (origin[x] - 0.5*width[0]) * axis_scaling_factor[axis_units] + x_max = (origin[x] + 0.5*width[0]) * axis_scaling_factor[axis_units] + y_min = (origin[y] - 0.5*width[1]) * axis_scaling_factor[axis_units] + y_max = (origin[y] + 0.5*width[1]) * axis_scaling_factor[axis_units] + + # Determine whether any materials contains macroscopic data and if so, + # set energy mode accordingly + _energy_mode = self.settings._energy_mode + for mat in self.geometry.get_all_materials().values(): + if mat._macroscopic is not None: + self.settings.energy_mode = 'multi-group' + break + + with TemporaryDirectory() as tmpdir: + _plot_seed = self.settings.plot_seed + if seed is not None: + self.settings.plot_seed = seed + + # Create plot object matching passed arguments + plot = openmc.Plot() + plot.origin = origin + plot.width = width + plot.pixels = pixels + plot.basis = basis + plot.color_by = color_by + plot.show_overlaps = show_overlaps + if overlap_color is not None: + plot.overlap_color = overlap_color + if colors is not None: + plot.colors = colors + self.plots.append(plot) + + # Run OpenMC in geometry plotting mode + self.plot_geometry(False, cwd=tmpdir, openmc_exec=openmc_exec) + + # Undo changes to model + self.plots.pop() + self.settings._plot_seed = _plot_seed + self.settings._energy_mode = _energy_mode + + # Read image from file + img_path = Path(tmpdir) / f'plot_{plot.id}.png' + if not img_path.is_file(): + img_path = img_path.with_suffix('.ppm') + img = mpimg.imread(str(img_path)) + + # Create a figure sized such that the size of the axes within + # exactly matches the number of pixels specified + if axes is None: + px = 1/plt.rcParams['figure.dpi'] + fig, axes = plt.subplots() + axes.set_xlabel(xlabel) + axes.set_ylabel(ylabel) + params = fig.subplotpars + width = pixels[0]*px/(params.right - params.left) + height = pixels[1]*px/(params.top - params.bottom) + fig.set_size_inches(width, height) + + if outline: + # Combine R, G, B values into a single int + rgb = (img * 256).astype(int) + image_value = (rgb[..., 0] << 16) + \ + (rgb[..., 1] << 8) + (rgb[..., 2]) + + # Set default arguments for contour() + if contour_kwargs is None: + contour_kwargs = {} + contour_kwargs.setdefault('colors', 'k') + contour_kwargs.setdefault('linestyles', 'solid') + contour_kwargs.setdefault('algorithm', 'serial') + + axes.contour( + image_value, + origin="upper", + levels=np.unique(image_value), + extent=(x_min, x_max, y_min, y_max), + **contour_kwargs + ) + + # add legend showing which colors represent which material + # or cell if that was requested + if legend: + if plot.colors == {}: + raise ValueError("Must pass 'colors' dictionary if you " + "are adding a legend via legend=True.") + + if color_by == "cell": + expected_key_type = openmc.Cell + else: + expected_key_type = openmc.Material + + patches = [] + for key, color in plot.colors.items(): + + if isinstance(key, int): + raise TypeError( + "Cannot use IDs in colors dict for auto legend.") + elif not isinstance(key, expected_key_type): + raise TypeError( + "Color dict key type does not match color_by") + + # this works whether we're doing cells or materials + label = key.name if key.name != '' else key.id + + # matplotlib takes RGB on 0-1 scale rather than 0-255. at + # this point PlotBase has already checked that 3-tuple + # based colors are already valid, so if the length is three + # then we know it just needs to be converted to the 0-1 + # format. + if len(color) == 3 and not isinstance(color, str): + scaled_color = ( + color[0]/255, color[1]/255, color[2]/255) + else: + scaled_color = color + + key_patch = mpatches.Patch(color=scaled_color, label=label) + patches.append(key_patch) + + axes.legend(handles=patches, **legend_kwargs) + + # Plot image and return the axes + if outline != 'only': + axes.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) + + + if n_samples: + # Sample external source particles + particles = self.sample_external_source(n_samples) + + # Get points within tolerance of the slice plane + slice_value = origin[z] + xs = [] + ys = [] + tol = plane_tolerance + for particle in particles: + if (slice_value - tol < particle.r[z] < slice_value + tol): + xs.append(particle.r[x]) + ys.append(particle.r[y]) + axes.scatter(xs, ys, **source_kwargs) + + return axes + + def sample_external_source( + self, + n_samples: int = 1000, + prn_seed: int | None = None, + **init_kwargs + ) -> openmc.ParticleList: + """Sample external source and return source particles. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + n_samples : int + Number of samples + prn_seed : int + Pseudorandom number generator (PRNG) seed; if None, one will be + generated randomly. + **init_kwargs + Keyword arguments passed to :func:`openmc.lib.init` + + Returns + ------- + openmc.ParticleList + List of samples source particles + """ + import openmc.lib + + # Silence output by default. Also set arguments to start in volume + # calculation mode to avoid loading cross sections + init_kwargs.setdefault('output', False) + init_kwargs.setdefault('args', ['-c']) + + with openmc.lib.TemporarySession(self, **init_kwargs): + return openmc.lib.sample_external_source( + n_samples=n_samples, prn_seed=prn_seed + ) + + def apply_tally_results(self, statepoint: PathLike | openmc.StatePoint): + """Apply results from a statepoint to tally objects on the Model + + Parameters + ---------- + statepoint : PathLike or openmc.StatePoint + Statepoint file used to update tally results + """ + self.tallies.add_results(statepoint) + + def plot_geometry( + self, + output: bool = True, + cwd: PathLike = ".", + openmc_exec: PathLike = "openmc", + export_model_xml: bool = True, + **export_kwargs, + ): """Creates plot images as specified by the Model.plots attribute .. versionadded:: 0.13.0 @@ -674,12 +1313,18 @@ class Model: ---------- output : bool, optional Capture OpenMC output from standard out - cwd : str, optional + cwd : PathLike, optional Path to working directory to run in. Defaults to the current working directory. - openmc_exec : str, optional + openmc_exec : PathLike, optional Path to OpenMC executable. Defaults to 'openmc'. This only applies to the case when not using the C API. + export_model_xml : bool, optional + Exports a single model.xml file rather than separate files. Defaults + to True. + **export_kwargs + Keyword arguments passed to either :meth:`Model.export_to_model_xml` + or :meth:`Model.export_to_xml`. """ @@ -688,16 +1333,27 @@ class Model: raise ValueError("The Model.plots attribute must be specified " "before executing this method!") - with _change_directory(Path(cwd)): + with change_directory(cwd): if self.is_initialized: # Compute the volumes openmc.lib.plot_geometry(output) else: - self.export_to_xml() - openmc.plot_geometry(output=output, openmc_exec=openmc_exec) + if export_model_xml: + self.export_to_model_xml(**export_kwargs) + else: + self.export_to_xml(**export_kwargs) + path_input = export_kwargs.get("path", None) + openmc.plot_geometry(output=output, openmc_exec=openmc_exec, + path_input=path_input) - def _change_py_lib_attribs(self, names_or_ids, value, obj_type, - attrib_name, density_units='atom/b-cm'): + def _change_py_lib_attribs( + self, + names_or_ids: Iterable[str] | Iterable[int], + value: float | Iterable[float], + obj_type: str, + attrib_name: str, + density_units: str = "atom/b-cm", + ): # Method to do the same work whether it is a cell or material and # a temperature or volume check_type('names_or_ids', names_or_ids, Iterable, (Integral, str)) @@ -776,7 +1432,9 @@ class Model: else: setattr(lib_obj, attrib_name, value) - def rotate_cells(self, names_or_ids, vector): + def rotate_cells( + self, names_or_ids: Iterable[str] | Iterable[int], vector: Iterable[float] + ): """Rotate the identified cell(s) by the specified rotation vector. The rotation is only applied to cells filled with a universe. @@ -798,7 +1456,9 @@ class Model: self._change_py_lib_attribs(names_or_ids, vector, 'cell', 'rotation') - def translate_cells(self, names_or_ids, vector): + def translate_cells( + self, names_or_ids: Iterable[str] | Iterable[int], vector: Iterable[float] + ): """Translate the identified cell(s) by the specified translation vector. The translation is only applied to cells filled with a universe. @@ -821,7 +1481,12 @@ class Model: self._change_py_lib_attribs(names_or_ids, vector, 'cell', 'translation') - def update_densities(self, names_or_ids, density, density_units='atom/b-cm'): + def update_densities( + self, + names_or_ids: Iterable[str] | Iterable[int], + density: float, + density_units: str = "atom/b-cm", + ): """Update the density of a given set of materials to a new value .. note:: If applying this change to a name that is not unique, then @@ -844,7 +1509,9 @@ class Model: self._change_py_lib_attribs(names_or_ids, density, 'material', 'density', density_units) - def update_cell_temperatures(self, names_or_ids, temperature): + def update_cell_temperatures( + self, names_or_ids: Iterable[str] | Iterable[int], temperature: float + ): """Update the temperature of a set of cells to the given value .. note:: If applying this change to a name that is not unique, then @@ -865,7 +1532,9 @@ class Model: self._change_py_lib_attribs(names_or_ids, temperature, 'cell', 'temperature') - def update_material_volumes(self, names_or_ids, volume): + def update_material_volumes( + self, names_or_ids: Iterable[str] | Iterable[int], volume: float + ): """Update the volume of a set of materials to the given value .. note:: If applying this change to a name that is not unique, then @@ -884,3 +1553,646 @@ class Model: """ self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume') + + def differentiate_depletable_mats(self, diff_volume_method: str = None): + """Assign distribmats for each depletable material + + .. versionadded:: 0.14.0 + + .. versionchanged:: 0.15.1 + diff_volume_method default is None, do not set volumes on the new + material ovjects. Is now a convenience method for + differentiate_mats(diff_volume_method, depletable_only=True) + + Parameters + ---------- + diff_volume_method : str + Specifies how the volumes of the new materials should be found. + - None: Do not assign volumes to the new materials (Default) + - 'divide equally': Divide the original material volume equally between the new materials + - 'match cell': Set the volume of the material to the volume of the cell they fill + """ + self.differentiate_mats(diff_volume_method, depletable_only=True) + + def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bool = True): + """Assign distribmats for each material + + .. versionadded:: 0.15.1 + + Parameters + ---------- + diff_volume_method : str + Specifies how the volumes of the new materials should be found. + - None: Do not assign volumes to the new materials (Default) + - 'divide equally': Divide the original material volume equally between the new materials + - 'match cell': Set the volume of the material to the volume of the cell they fill + depletable_only : bool + Default is True, only depletable materials will be differentiated. If False, all materials will be + differentiated. + """ + check_value('volume differentiation method', diff_volume_method, ("divide equally", "match cell", None)) + + # Count the number of instances for each cell and material + self.geometry.determine_paths(instances_only=True) + + # Get list of materials + if self.materials: + materials = self.materials + else: + materials = list(self.geometry.get_all_materials().values()) + + # Find all or depletable_only materials which have multiple instance + distribmats = set() + for mat in materials: + # Differentiate all materials with multiple instances + diff_mat = mat.num_instances > 1 + # If depletable_only is True, differentiate only depletable materials + if depletable_only: + diff_mat = diff_mat and mat.depletable + if diff_mat: + # Assign volumes to the materials according to requirements + if diff_volume_method == "divide equally": + if mat.volume is None: + raise RuntimeError( + "Volume not specified for " + f"material with ID={mat.id}.") + else: + mat.volume /= mat.num_instances + elif diff_volume_method == "match cell": + for cell in self.geometry.get_all_material_cells().values(): + if cell.fill == mat: + if not cell.volume: + raise ValueError( + f"Volume of cell ID={cell.id} not specified. " + "Set volumes of cells prior to using " + "diff_volume_method='match cell'.") + distribmats.add(mat) + + if not distribmats: + return + + # Assign distribmats to cells + for cell in self.geometry.get_all_material_cells().values(): + if cell.fill in distribmats: + mat = cell.fill + + # Clone materials + if cell.num_instances > 1: + cell.fill = [mat.clone() for _ in range(cell.num_instances)] + else: + cell.fill = mat.clone() + + # For 'match cell', assign volumes based on the cells + if diff_volume_method == 'match cell': + if cell.fill_type == 'distribmat': + for clone_mat in cell.fill: + clone_mat.volume = cell.volume + else: + cell.fill.volume = cell.volume + + if self.materials is not None: + self.materials = openmc.Materials( + self.geometry.get_all_materials().values() + ) + + def _generate_infinite_medium_mgxs( + self, + groups: openmc.mgxs.EnergyGroups, + nparticles: int, + mgxs_path: PathLike, + correction: str | None, + directory: PathLike, + ): + """Generate a MGXS library by running multiple OpenMC simulations, each + representing an infinite medium simulation of a single isolated + material. A discrete source is used to sample particles, with an equal + strength spread across each of the energy groups. This is a highly naive + method that ignores all spatial self shielding effects and all resonance + shielding effects between materials. + + Parameters + ---------- + groups : openmc.mgxs.EnergyGroups + Energy group structure for the MGXS. + nparticles : int + Number of particles to simulate per batch when generating MGXS. + mgxs_path : str + Filename for the MGXS HDF5 file. + correction : str + Transport correction to apply to the MGXS. Options are None and + "P0". + directory : str + Directory to run the simulation in, so as to contain XML files. + """ + warnings.warn("The infinite medium method of generating MGXS may hang " + "if a material has a k-infinity > 1.0.") + mgxs_sets = [] + for material in self.materials: + model = openmc.Model() + + # Set materials on the model + model.materials = [material] + + # Settings + model.settings.batches = 100 + model.settings.particles = nparticles + model.settings.run_mode = 'fixed source' + + # Make a discrete source that is uniform over the bins of the group structure + n_groups = groups.num_groups + midpoints = [] + strengths = [] + for i in range(n_groups): + bounds = groups.get_group_bounds(i+1) + midpoints.append((bounds[0] + bounds[1]) / 2.0) + strengths.append(1.0) + + energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), energy=energy_distribution) + model.settings.output = {'summary': True, 'tallies': False} + + # Geometry + box = openmc.model.RectangularPrism( + 100000.0, 100000.0, boundary_type='reflective') + name = material.name + infinite_cell = openmc.Cell(name=name, fill=material, region=-box) + infinite_universe = openmc.Universe(name=name, cells=[infinite_cell]) + model.geometry.root_universe = infinite_universe + + # Add MGXS Tallies + + # Initialize MGXS library with a finished OpenMC geometry object + mgxs_lib = openmc.mgxs.Library(model.geometry) + + # Pick energy group structure + mgxs_lib.energy_groups = groups + + # Disable transport correction + mgxs_lib.correction = correction + + # Specify needed cross sections for random ray + if correction == 'P0': + mgxs_lib.mgxs_types = [ + 'nu-transport', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi' + ] + elif correction is None: + mgxs_lib.mgxs_types = [ + 'total', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi' + ] + + # Specify a "cell" domain type for the cross section tally filters + mgxs_lib.domain_type = "material" + + # Specify the cell domains over which to compute multi-group cross sections + mgxs_lib.domains = model.geometry.get_all_materials().values() + + # Do not compute cross sections on a nuclide-by-nuclide basis + mgxs_lib.by_nuclide = False + + # Check the library - if no errors are raised, then the library is satisfactory. + mgxs_lib.check_library_for_openmc_mgxs() + + # Construct all tallies needed for the multi-group cross section library + mgxs_lib.build_library() + + # Create a "tallies.xml" file for the MGXS Library + mgxs_lib.add_to_tallies_file(model.tallies, merge=True) + + # Run + statepoint_filename = model.run(cwd=directory) + + # Load MGXS + with openmc.StatePoint(statepoint_filename) as sp: + mgxs_lib.load_from_statepoint(sp) + + # Create a MGXS File which can then be written to disk + mgxs_set = mgxs_lib.get_xsdata(domain=material, xsdata_name=name) + mgxs_sets.append(mgxs_set) + + # Write the file to disk + mgxs_file = openmc.MGXSLibrary(energy_groups=groups) + for mgxs_set in mgxs_sets: + mgxs_file.add_xsdata(mgxs_set) + mgxs_file.export_to_hdf5(mgxs_path) + + @staticmethod + def _create_stochastic_slab_geometry( + materials: Sequence[openmc.Material], + cell_thickness: float = 1.0, + num_repeats: int = 100, + ) -> tuple[openmc.Geometry, openmc.stats.Box]: + """Create a geometry representing a stochastic "sandwich" of materials in a + layered slab geometry. To reduce the impact of the order of materials in + the slab, the materials are applied to 'num_repeats' different randomly + positioned layers of 'cell_thickness' each. + + Parameters + ---------- + materials : list of openmc.Material + List of materials to assign. Each material will appear exactly num_repeats times, + then the ordering is randomly shuffled. + cell_thickness : float, optional + Thickness of each lattice cell in x (default 1.0 cm). + num_repeats : int, optional + Number of repeats for each material (default 100). + + Returns + ------- + geometry : openmc.Geometry + The constructed geometry. + box : openmc.stats.Box + A spatial sampling distribution covering the full slab domain. + """ + if not materials: + raise ValueError("At least one material must be provided.") + + num_materials = len(materials) + total_cells = num_materials * num_repeats + total_width = total_cells * cell_thickness + + # Generate an infinite cell/universe for each material + universes = [] + for i in range(num_materials): + cell = openmc.Cell(fill=materials[i]) + universes.append(openmc.Universe(cells=[cell])) + + # Make a list of randomized material idx assignments for the stochastic slab + assignments = list(range(num_materials)) * num_repeats + random.seed(42) + random.shuffle(assignments) + + # Create a list of the (randomized) universe assignments to be used + # when defining the problem lattice. + lattice_entries = [universes[m] for m in assignments] + + # Create the RectLattice for the 1D material variation in x. + lattice = openmc.RectLattice() + lattice.pitch = (cell_thickness, total_width, total_width) + lattice.lower_left = (0.0, 0.0, 0.0) + lattice.universes = [[lattice_entries]] + lattice.outer = universes[0] + + # Define the six outer surfaces with reflective boundary conditions + rpp = openmc.model.RectangularParallelepiped( + 0.0, total_width, 0.0, total_width, 0.0, total_width, + boundary_type='reflective' + ) + + # Create an outer cell that fills with the lattice. + outer_cell = openmc.Cell(fill=lattice, region=-rpp) + + # Build the geometry + geometry = openmc.Geometry([outer_cell]) + + # Define the spatial distribution that covers the full cubic domain + box = openmc.stats.Box(*outer_cell.bounding_box) + + return geometry, box + + def _generate_stochastic_slab_mgxs( + self, + groups: openmc.mgxs.EnergyGroups, + nparticles: int, + mgxs_path: PathLike, + correction: str | None, + directory: PathLike, + ) -> None: + """Generate MGXS assuming a stochastic "sandwich" of materials in a layered + slab geometry. While geometry-specific spatial shielding effects are not + captured, this method can be useful when the geometry has materials only + found far from the source region that the "material_wise" method would + not be capable of generating cross sections for. Conversely, this method + will generate cross sections for all materials in the problem regardless + of type. If this is a fixed source problem, a discrete source is used to + sample particles, with an equal strength spread across each of the + energy groups. + + Parameters + ---------- + groups : openmc.mgxs.EnergyGroups + Energy group structure for the MGXS. + nparticles : int + Number of particles to simulate per batch when generating MGXS. + mgxs_path : str + Filename for the MGXS HDF5 file. + correction : str + Transport correction to apply to the MGXS. Options are None and + "P0". + directory : str + Directory to run the simulation in, so as to contain XML files. + """ + model = openmc.Model() + model.materials = self.materials + + # Settings + model.settings.batches = 200 + model.settings.inactive = 100 + model.settings.particles = nparticles + model.settings.output = {'summary': True, 'tallies': False} + model.settings.run_mode = self.settings.run_mode + + # Stochastic slab geometry + model.geometry, spatial_distribution = Model._create_stochastic_slab_geometry( + model.materials) + + # Make a discrete source that is uniform over the bins of the group structure + n_groups = groups.num_groups + midpoints = [] + strengths = [] + for i in range(n_groups): + bounds = groups.get_group_bounds(i+1) + midpoints.append((bounds[0] + bounds[1]) / 2.0) + strengths.append(1.0) + + energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) + model.settings.source = [openmc.IndependentSource( + space=spatial_distribution, energy=energy_distribution, strength=1.0)] + + model.settings.output = {'summary': True, 'tallies': False} + + # Add MGXS Tallies + + # Initialize MGXS library with a finished OpenMC geometry object + mgxs_lib = openmc.mgxs.Library(model.geometry) + + # Pick energy group structure + mgxs_lib.energy_groups = groups + + # Disable transport correction + mgxs_lib.correction = correction + + # Specify needed cross sections for random ray + if correction == 'P0': + mgxs_lib.mgxs_types = ['nu-transport', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi'] + elif correction is None: + mgxs_lib.mgxs_types = ['total', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi'] + + # Specify a "cell" domain type for the cross section tally filters + mgxs_lib.domain_type = "material" + + # Specify the cell domains over which to compute multi-group cross sections + mgxs_lib.domains = model.geometry.get_all_materials().values() + + # Do not compute cross sections on a nuclide-by-nuclide basis + mgxs_lib.by_nuclide = False + + # Check the library - if no errors are raised, then the library is satisfactory. + mgxs_lib.check_library_for_openmc_mgxs() + + # Construct all tallies needed for the multi-group cross section library + mgxs_lib.build_library() + + # Create a "tallies.xml" file for the MGXS Library + mgxs_lib.add_to_tallies_file(model.tallies, merge=True) + + # Run + statepoint_filename = model.run(cwd=directory) + + # Load MGXS + with openmc.StatePoint(statepoint_filename) as sp: + mgxs_lib.load_from_statepoint(sp) + + names = [mat.name for mat in mgxs_lib.domains] + + # Create a MGXS File which can then be written to disk + mgxs_file = mgxs_lib.create_mg_library(xs_type='macro', xsdata_names=names) + mgxs_file.export_to_hdf5(mgxs_path) + + def _generate_material_wise_mgxs( + self, + groups: openmc.mgxs.EnergyGroups, + nparticles: int, + mgxs_path: PathLike, + correction: str | None, + directory: PathLike, + ) -> None: + """Generate a material-wise MGXS library for the model by running the + original continuous energy OpenMC simulation of the full material + geometry and source, and tally MGXS data for each material. This method + accurately conserves reaction rates totaled over the entire simulation + domain. However, when the geometry has materials only found far from the + source region, it is possible the Monte Carlo solver may not be able to + score any tallies to these material types, thus resulting in zero cross + section values for these materials. For such cases, the "stochastic + slab" method may be more appropriate. + + Parameters + ---------- + groups : openmc.mgxs.EnergyGroups + Energy group structure for the MGXS. + nparticles : int + Number of particles to simulate per batch when generating MGXS. + mgxs_path : PathLike + Filename for the MGXS HDF5 file. + correction : str + Transport correction to apply to the MGXS. Options are None and + "P0". + directory : PathLike + Directory to run the simulation in, so as to contain XML files. + """ + model = copy.deepcopy(self) + model.tallies = openmc.Tallies() + + # Settings + model.settings.batches = 200 + model.settings.inactive = 100 + model.settings.particles = nparticles + model.settings.output = {'summary': True, 'tallies': False} + + # Add MGXS Tallies + + # Initialize MGXS library with a finished OpenMC geometry object + mgxs_lib = openmc.mgxs.Library(model.geometry) + + # Pick energy group structure + mgxs_lib.energy_groups = groups + + # Disable transport correction + mgxs_lib.correction = correction + + # Specify needed cross sections for random ray + if correction == 'P0': + mgxs_lib.mgxs_types = [ + 'nu-transport', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi' + ] + elif correction is None: + mgxs_lib.mgxs_types = [ + 'total', 'absorption', 'nu-fission', 'fission', + 'consistent nu-scatter matrix', 'multiplicity matrix', 'chi' + ] + + # Specify a "cell" domain type for the cross section tally filters + mgxs_lib.domain_type = "material" + + # Specify the cell domains over which to compute multi-group cross sections + mgxs_lib.domains = model.geometry.get_all_materials().values() + + # Do not compute cross sections on a nuclide-by-nuclide basis + mgxs_lib.by_nuclide = False + + # Check the library - if no errors are raised, then the library is satisfactory. + mgxs_lib.check_library_for_openmc_mgxs() + + # Construct all tallies needed for the multi-group cross section library + mgxs_lib.build_library() + + # Create a "tallies.xml" file for the MGXS Library + mgxs_lib.add_to_tallies_file(model.tallies, merge=True) + + # Run + statepoint_filename = model.run(cwd=directory) + + # Load MGXS + with openmc.StatePoint(statepoint_filename) as sp: + mgxs_lib.load_from_statepoint(sp) + + names = [mat.name for mat in mgxs_lib.domains] + + # Create a MGXS File which can then be written to disk + mgxs_file = mgxs_lib.create_mg_library( + xs_type='macro', xsdata_names=names) + mgxs_file.export_to_hdf5(mgxs_path) + + def convert_to_multigroup( + self, + method: str = "material_wise", + groups: str = "CASMO-2", + nparticles: int = 2000, + overwrite_mgxs_library: bool = False, + mgxs_path: PathLike = "mgxs.h5", + correction: str | None = None, + ): + """Convert all materials from continuous energy to multigroup. + + If no MGXS data library file is found, generate one using one or more + continuous energy Monte Carlo simulations. + + Parameters + ---------- + method : {"material_wise", "stochastic_slab", "infinite_medium"}, optional + Method to generate the MGXS. + groups : openmc.mgxs.EnergyGroups or str, optional + Energy group structure for the MGXS or the name of the group + structure (based on keys from openmc.mgxs.GROUP_STRUCTURES). + mgxs_path : str, optional + Filename of the mgxs.h5 library file. + correction : str, optional + Transport correction to apply to the MGXS. Options are None and + "P0". + """ + if isinstance(groups, str): + groups = openmc.mgxs.EnergyGroups(groups) + + # Do all work (including MGXS generation) in a temporary directory + # to avoid polluting the working directory with residual XML files + with TemporaryDirectory() as tmpdir: + + # Determine if there are DAGMC universes in the model. If so, we need to synchronize + # the dagmc materials with cells. + # TODO: Can this be done without having to init/finalize? + for univ in self.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + self.init_lib(directory=tmpdir) + self.sync_dagmc_universes() + self.finalize_lib() + break + + # Make sure all materials have a name, and that the name is a valid HDF5 + # dataset name + for material in self.materials: + if not material.name or not material.name.strip(): + material.name = f"material {material.id}" + material.name = re.sub(r'[^a-zA-Z0-9]', '_', material.name) + + # If needed, generate the needed MGXS data library file + if not Path(mgxs_path).is_file() or overwrite_mgxs_library: + if method == "infinite_medium": + self._generate_infinite_medium_mgxs( + groups, nparticles, mgxs_path, correction, tmpdir) + elif method == "material_wise": + self._generate_material_wise_mgxs( + groups, nparticles, mgxs_path, correction, tmpdir) + elif method == "stochastic_slab": + self._generate_stochastic_slab_mgxs( + groups, nparticles, mgxs_path, correction, tmpdir) + else: + raise ValueError( + f'MGXS generation method "{method}" not recognized') + else: + print(f'Existing MGXS library file "{mgxs_path}" will be used') + + # Convert all continuous energy materials to multigroup + self.materials.cross_sections = mgxs_path + for material in self.materials: + material.set_density('macro', 1.0) + material._nuclides = [] + material._sab = [] + material.add_macroscopic(material.name) + + self.settings.energy_mode = 'multi-group' + + def convert_to_random_ray(self): + """Convert a multigroup model to use random ray. + + This method determines values for the needed settings and adds them to + the settings.random_ray dictionary so as to enable random ray mode. The + settings that are populated are: + + - 'ray_source' (openmc.IndependentSource): Where random ray starting + points are sampled from. + - 'distance_inactive' (float): The "dead zone" distance at the beginning + of the ray. + - 'distance_active' (float): The "active" distance of the ray + - 'particles' (int): Number of rays to simulate + + The method will determine reasonable defaults for each of the above + variables based on analysis of the model's geometry. The function will + have no effect if the random ray dictionary is already defined in the + model settings. + """ + # If the random ray dictionary is already set, don't overwrite it + if self.settings.random_ray: + warnings.warn("Random ray conversion skipped as " + "settings.random_ray dictionary is already set.") + return + + if self.settings.energy_mode != 'multi-group': + raise ValueError( + "Random ray conversion failed: energy mode must be " + "'multi-group'. Use convert_to_multigroup() first." + ) + + # Helper function for detecting infinity + def _replace_infinity(value): + if np.isinf(value): + return 1.0 if value > 0 else -1.0 + return value + + # Get a bounding box for sampling rays. We can utilize the geometry's bounding box + # though for 2D problems we need to detect the infinities and replace them with an + # arbitrary finite value. + bounding_box = self.geometry.bounding_box + lower_left = [_replace_infinity(v) for v in bounding_box.lower_left] + upper_right = [_replace_infinity(v) for v in bounding_box.upper_right] + uniform_dist_ray = openmc.stats.Box(lower_left, upper_right) + rr_source = openmc.IndependentSource(space=uniform_dist_ray) + self.settings.random_ray['ray_source'] = rr_source + + # For the dead zone and active length, a reasonable guess is the larger of either: + # 1) The maximum chord length through the geometry (as defined by its bounding box) + # 2) 30 cm + # Then, set the active length to be 5x longer than the dead zone length, for the sake of efficiency. + chord_length = np.array(upper_right) - np.array(lower_left) + max_length = max(np.linalg.norm(chord_length), 30.0) + + self.settings.random_ray['distance_inactive'] = max_length + self.settings.random_ray['distance_active'] = 5 * max_length + + # Take a wild guess as to how many rays are needed + self.settings.particles = 2 * int(max_length) diff --git a/openmc/model/surface_composite.py b/openmc/model/surface_composite.py index 09a3c0170f..5962897de5 100644 --- a/openmc/model/surface_composite.py +++ b/openmc/model/surface_composite.py @@ -1,8 +1,19 @@ +from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Iterable, Sequence from copy import copy +from functools import partial +from math import sqrt, pi, sin, cos, isclose +from numbers import Real +import warnings +import operator + +import numpy as np +from scipy.spatial import ConvexHull, Delaunay import openmc -from openmc.checkvalue import check_greater_than, check_value +from openmc.checkvalue import (check_greater_than, check_value, check_less_than, + check_iterable_type, check_length, check_type) class CompositeSurface(ABC): @@ -16,12 +27,16 @@ class CompositeSurface(ABC): return surf def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False): - surf = copy(self) + surf = self if inplace else copy(self) for name in self._surface_names: s = getattr(surf, name) setattr(surf, name, s.rotate(rotation, pivot, order, inplace)) return surf + @property + def component_surfaces(self): + return [getattr(self, name) for name in self._surface_names] + @property def boundary_type(self): return getattr(self, self._surface_names[0]).boundary_type @@ -30,12 +45,14 @@ class CompositeSurface(ABC): def boundary_type(self, boundary_type): # Set boundary type on underlying surfaces, but not for ambiguity plane # on one-sided cones + classes = (XConeOneSided, YConeOneSided, ZConeOneSided, Vessel) for name in self._surface_names: - if name != 'plane': - getattr(self, name).boundary_type = boundary_type + if isinstance(self, classes) and name.startswith('plane'): + continue + getattr(self, name).boundary_type = boundary_type def __repr__(self): - return "<{} at 0x{:x}>".format(type(self).__name__, id(self)) + return f"<{type(self).__name__} at 0x{id(self):x}>" @property @abstractmethod @@ -43,13 +60,340 @@ class CompositeSurface(ABC): """Iterable of attribute names corresponding to underlying surfaces.""" @abstractmethod - def __pos__(self): - """Return the positive half-space of the composite surface.""" - - @abstractmethod - def __neg__(self): + def __neg__(self) -> openmc.Region: """Return the negative half-space of the composite surface.""" + def __pos__(self) -> openmc.Region: + """Return the positive half-space of the composite surface.""" + return ~(-self) + + +class CylinderSector(CompositeSurface): + """Infinite cylindrical sector composite surface. + + A cylinder sector is composed of two cylindrical and two planar surfaces. + The cylindrical surfaces are concentric, and the planar surfaces intersect + the central axis of the cylindrical surfaces. + + This class acts as a proper surface, meaning that unary `+` and `-` + operators applied to it will produce a half-space. The negative + side is defined to be the region inside of the cylinder sector. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + r1 : float + Inner radius of sector. Must be less than r2. + r2 : float + Outer radius of sector. Must be greater than r1. + theta1 : float + Clockwise-most bound of sector in degrees. Assumed to be in the + counterclockwise direction with respect to the first basis axis + (+y, +z, or +x). Must be less than :attr:`theta2`. + theta2 : float + Counterclockwise-most bound of sector in degrees. Assumed to be in the + counterclockwise direction with respect to the first basis axis + (+y, +z, or +x). Must be greater than :attr:`theta1`. + center : iterable of float + Coordinate for central axes of cylinders in the (y, z), (x, z), or (x, y) + basis. Defaults to (0,0). + axis : {'x', 'y', 'z'} + Central axis of the cylinders defining the inner and outer surfaces of + the sector. Defaults to 'z'. + **kwargs : dict + Keyword arguments passed to the :class:`Cylinder` and + :class:`Plane` constructors. + + Attributes + ---------- + outer_cyl : openmc.ZCylinder, openmc.YCylinder, or openmc.XCylinder + Outer cylinder surface. + inner_cyl : openmc.ZCylinder, openmc.YCylinder, or openmc.XCylinder + Inner cylinder surface. + plane1 : openmc.Plane + Plane at angle :math:`\\theta_1` relative to the first basis axis. + plane2 : openmc.Plane + Plane at angle :math:`\\theta_2` relative to the first basis axis. + + """ + + _surface_names = ('outer_cyl', 'inner_cyl', 'plane1', 'plane2') + + def __init__(self, + r1, + r2, + theta1, + theta2, + center=(0., 0.), + axis='z', + **kwargs): + + if r2 <= r1: + raise ValueError('r2 must be greater than r1.') + + if theta2 <= theta1: + raise ValueError('theta2 must be greater than theta1.') + + # Determine whether the angle between theta1 and theta2 is a reflex + # angle, in which case we need to use a union between the planar + # half-spaces + self._reflex = (theta2 - theta1 > 180.0) + + phi1 = pi / 180 * theta1 + phi2 = pi / 180 * theta2 + + # Coords for axis-perpendicular planes + p1 = np.array([center[0], center[1], 1.]) + + p2_plane1 = np.array([r1 * cos(phi1) + center[0], r1 * sin(phi1) + center[1], 0.]) + p3_plane1 = np.array([r2 * cos(phi1) + center[0], r2 * sin(phi1) + center[1], 0.]) + + p2_plane2 = np.array([r1 * cos(phi2) + center[0], r1 * sin(phi2)+ center[1], 0.]) + p3_plane2 = np.array([r2 * cos(phi2) + center[0], r2 * sin(phi2)+ center[1], 0.]) + + points = [p1, p2_plane1, p3_plane1, p2_plane2, p3_plane2] + if axis == 'z': + coord_map = [0, 1, 2] + self.inner_cyl = openmc.ZCylinder(*center, r1, **kwargs) + self.outer_cyl = openmc.ZCylinder(*center, r2, **kwargs) + elif axis == 'y': + coord_map = [0, 2, 1] + self.inner_cyl = openmc.YCylinder(*center, r1, **kwargs) + self.outer_cyl = openmc.YCylinder(*center, r2, **kwargs) + elif axis == 'x': + coord_map = [2, 0, 1] + self.inner_cyl = openmc.XCylinder(*center, r1, **kwargs) + self.outer_cyl = openmc.XCylinder(*center, r2, **kwargs) + + # Reorder the points to correspond to the correct central axis + for p in points: + p[:] = p[coord_map] + + self.plane1 = openmc.Plane.from_points(p1, p2_plane1, p3_plane1, + **kwargs) + self.plane2 = openmc.Plane.from_points(p1, p2_plane2, p3_plane2, + **kwargs) + if axis == 'y': + self.plane1.flip_normal() + self.plane2.flip_normal() + + @classmethod + def from_theta_alpha(cls, + r1, + r2, + theta, + alpha, + center = (0.,0.), + axis='z', + **kwargs): + r"""Alternate constructor for :class:`CylinderSector`. Returns a + :class:`CylinderSector` object based on a central angle :math:`\theta` + and an angular offset :math:`\alpha`. Note that + :math:`\theta_1 = \alpha` and :math:`\theta_2 = \alpha + \theta`. + + Parameters + ---------- + r1 : float + Inner radius of sector. Must be less than r2. + r2 : float + Outer radius of sector. Must be greater than r1. + theta : float + Central angle, :math:`\theta`, of the sector in degrees. Must be + greater that 0 and less than 360. + alpha : float + Angular offset, :math:`\alpha`, of sector in degrees. + The offset is in the counter-clockwise direction + with respect to the first basis axis (+y, +z, or +x). Note that + negative values translate to an offset in the clockwise direction. + center : iterable of float + Coordinate for central axes of cylinders in the (y, z), (x, z), or + (x, y) basis. Defaults to (0,0). + axis : {'x', 'y', 'z'} + Central axis of the cylinders defining the inner and outer surfaces + of the sector. Defaults to 'z'. + **kwargs : dict + Keyword arguments passed to the :class:`Cylinder` and + :class:`Plane` constructors. + + Returns + ------- + CylinderSector + CylinderSector with the given central angle at the given + offset. + """ + if theta >= 360. or theta <= 0: + raise ValueError('theta must be less than 360 and greater than 0.') + + theta1 = alpha + theta2 = alpha + theta + + return cls(r1, r2, theta1, theta2, center=center, axis=axis, **kwargs) + + def __neg__(self): + if self._reflex: + return -self.outer_cyl & +self.inner_cyl & (-self.plane1 | +self.plane2) + else: + return -self.outer_cyl & +self.inner_cyl & -self.plane1 & +self.plane2 + + +class IsogonalOctagon(CompositeSurface): + r"""Infinite isogonal octagon composite surface + + An isogonal octagon is composed of eight planar surfaces. The prism is + parallel to the x, y, or z axis. The remaining two axes (y and z, x and z, + or x and y) serve as a basis for constructing the surfaces. Two surfaces + are parallel to the first basis axis, two surfaces are parallel + to the second basis axis, and the remaining four surfaces intersect both + basis axes at 45 degree angles. + + This class acts as a proper surface, meaning that unary `+` and `-` + operators applied to it will produce a half-space. The negative side is + defined to be the region inside of the octagonal prism. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + center : iterable of float + Coordinate for the central axis of the octagon in the + (y, z), (x, z), or (x, y) basis depending on the axis parameter. + r1 : float + Half-width of octagon across its basis axis-parallel sides in units + of cm. Must be less than :math:`r_2\sqrt{2}`. + r2 : float + Half-width of octagon across its basis axis intersecting sides in + units of cm. Must be less than than :math:`r_1\sqrt{2}`. + axis : {'x', 'y', 'z'} + Central axis of octagon. Defaults to 'z' + **kwargs + Keyword arguments passed to underlying plane classes + + Attributes + ---------- + top : openmc.ZPlane, openmc.XPlane, or openmc.YPlane + Top planar surface of octagon + bottom : openmc.ZPlane, openmc.XPlane, or openmc.YPlane + Bottom planar surface of octagon + right : openmc.YPlane, openmc.ZPlane, or openmc.XPlane + Right planar surface of octagon + left : openmc.YPlane, openmc.ZPlane, or openmc.XPlane + Left planar surface of octagon + upper_right : openmc.Plane + Upper right planar surface of octagon + lower_right : openmc.Plane + Lower right planar surface of octagon + lower_left : openmc.Plane + Lower left planar surface of octagon + upper_left : openmc.Plane + Upper left planar surface of octagon + + """ + + _surface_names = ('top', 'bottom', + 'upper_right', 'lower_left', + 'right', 'left', + 'lower_right', 'upper_left') + + def __init__(self, center, r1, r2, axis='z', **kwargs): + c1, c2 = center + + # Coordinates for axis-perpendicular planes + cright = c1 + r1 + cleft = c1 - r1 + + ctop = c2 + r1 + cbottom = c2 - r1 + + # Side lengths + if r2 > r1 * sqrt(2): + raise ValueError('r2 is greater than sqrt(2) * r1. Octagon' + + ' may be erroneous.') + if r1 > r2 * sqrt(2): + raise ValueError('r1 is greater than sqrt(2) * r2. Octagon' + + ' may be erroneous.') + + L_basis_ax = (r2 * sqrt(2) - r1) + + # Coordinates for quadrant planes + p1_ur = np.array([L_basis_ax, r1, 0.]) + p2_ur = np.array([r1, L_basis_ax, 0.]) + p3_ur = np.array([r1, L_basis_ax, 1.]) + + p1_lr = np.array([r1, -L_basis_ax, 0.]) + p2_lr = np.array([L_basis_ax, -r1, 0.]) + p3_lr = np.array([L_basis_ax, -r1, 1.]) + + p1_ll = -p1_ur + p2_ll = -p2_ur + p3_ll = -p3_ur + + p1_ul = -p1_lr + p2_ul = -p2_lr + p3_ul = -p3_lr + + points = [p1_ur, p2_ur, p3_ur, p1_lr, p2_lr, p3_lr, + p1_ll, p2_ll, p3_ll, p1_ul, p2_ul, p3_ul] + + # Orientation specific variables + if axis == 'z': + coord_map = [0, 1, 2] + self.top = openmc.YPlane(ctop, **kwargs) + self.bottom = openmc.YPlane(cbottom, **kwargs) + self.right = openmc.XPlane(cright, **kwargs) + self.left = openmc.XPlane(cleft, **kwargs) + elif axis == 'y': + coord_map = [0, 2, 1] + self.top = openmc.ZPlane(ctop, **kwargs) + self.bottom = openmc.ZPlane(cbottom, **kwargs) + self.right = openmc.XPlane(cright, **kwargs) + self.left = openmc.XPlane(cleft, **kwargs) + elif axis == 'x': + coord_map = [2, 0, 1] + self.top = openmc.ZPlane(ctop, **kwargs) + self.bottom = openmc.ZPlane(cbottom, **kwargs) + self.right = openmc.YPlane(cright, **kwargs) + self.left = openmc.YPlane(cleft, **kwargs) + self.axis = axis + + # Put our coordinates in (x,y,z) order and add the offset + for p in points: + p[0] += c1 + p[1] += c2 + p[:] = p[coord_map] + + self.upper_right = openmc.Plane.from_points(p1_ur, p2_ur, p3_ur, + **kwargs) + self.lower_right = openmc.Plane.from_points(p1_lr, p2_lr, p3_lr, + **kwargs) + self.lower_left = openmc.Plane.from_points(p1_ll, p2_ll, p3_ll, + **kwargs) + self.upper_left = openmc.Plane.from_points(p1_ul, p2_ul, p3_ul, + **kwargs) + + def __neg__(self): + if self.axis == 'y': + region = -self.top & +self.bottom & -self.right & +self.left & \ + -self.upper_right & -self.lower_right & +self.lower_left & \ + +self.upper_left + else: + region = -self.top & +self.bottom & -self.right & +self.left & \ + +self.upper_right & +self.lower_right & -self.lower_left & \ + -self.upper_left + + return region + + def __pos__(self): + if self.axis == 'y': + region = +self.top | -self.bottom | +self.right | -self.left | \ + +self.upper_right | +self.lower_right | -self.lower_left | \ + -self.upper_left + else: + region = +self.top | -self.bottom | +self.right | -self.left | \ + -self.upper_right | -self.lower_right | +self.lower_left | \ + +self.upper_left + return region + class RightCircularCylinder(CompositeSurface): """Right circular cylinder composite surface @@ -72,6 +416,10 @@ class RightCircularCylinder(CompositeSurface): Radius of the cylinder axis : {'x', 'y', 'z'} Axis of the cylinder + upper_fillet_radius : float + Upper edge fillet radius in [cm]. + lower_fillet_radius : float + Lower edge fillet radius in [cm]. **kwargs Keyword arguments passed to underlying cylinder and plane classes @@ -83,33 +431,173 @@ class RightCircularCylinder(CompositeSurface): Bottom planar surface of the cylinder top : openmc.Plane Top planar surface of the cylinder + upper_fillet_torus : openmc.Torus + Surface that creates the filleted edge for the upper end of the + cylinder. Only present if :attr:`upper_fillet_radius` is set. + upper_fillet_cylinder : openmc.Cylinder + Surface that bounds :attr:`upper_fillet_torus` radially. Only present + if :attr:`upper_fillet_radius` is set. + upper_fillet_plane : openmc.Plane + Surface that bounds :attr:`upper_fillet_torus` axially. Only present if + :attr:`upper_fillet_radius` is set. + lower_fillet_torus : openmc.Torus + Surface that creates the filleted edge for the lower end of the + cylinder. Only present if :attr:`lower_fillet_radius` is set. + lower_fillet_cylinder : openmc.Cylinder + Surface that bounds :attr:`lower_fillet_torus` radially. Only present + if :attr:`lower_fillet_radius` is set. + lower_fillet_plane : openmc.Plane + Surface that bounds :attr:`lower_fillet_torus` axially. Only present if + :attr:`lower_fillet_radius` is set. """ _surface_names = ('cyl', 'bottom', 'top') - def __init__(self, center_base, height, radius, axis='z', **kwargs): + def __init__(self, center_base, height, radius, axis='z', + upper_fillet_radius=0., lower_fillet_radius=0., **kwargs): cx, cy, cz = center_base check_greater_than('cylinder height', height, 0.0) check_greater_than('cylinder radius', radius, 0.0) check_value('cylinder axis', axis, ('x', 'y', 'z')) + check_type('upper_fillet_radius', upper_fillet_radius, float) + check_less_than('upper_fillet_radius', upper_fillet_radius, + radius, equality=True) + check_type('lower_fillet_radius', lower_fillet_radius, float) + check_less_than('lower_fillet_radius', lower_fillet_radius, + radius, equality=True) + if axis == 'x': self.cyl = openmc.XCylinder(y0=cy, z0=cz, r=radius, **kwargs) self.bottom = openmc.XPlane(x0=cx, **kwargs) self.top = openmc.XPlane(x0=cx + height, **kwargs) + x1, x2 = 'y', 'z' + axcoord, axcoord1, axcoord2 = 0, 1, 2 elif axis == 'y': self.cyl = openmc.YCylinder(x0=cx, z0=cz, r=radius, **kwargs) self.bottom = openmc.YPlane(y0=cy, **kwargs) self.top = openmc.YPlane(y0=cy + height, **kwargs) + x1, x2 = 'x', 'z' + axcoord, axcoord1, axcoord2 = 1, 0, 2 elif axis == 'z': self.cyl = openmc.ZCylinder(x0=cx, y0=cy, r=radius, **kwargs) self.bottom = openmc.ZPlane(z0=cz, **kwargs) self.top = openmc.ZPlane(z0=cz + height, **kwargs) + x1, x2 = 'x', 'y' + axcoord, axcoord1, axcoord2 = 2, 0, 1 + + def _create_fillet_objects(axis_args, height, center_base, radius, fillet_radius, pos='upper'): + axis, x1, x2, axcoord, axcoord1, axcoord2 = axis_args + fillet_ext = height / 2 - fillet_radius + sign = 1 + if pos == 'lower': + sign = -1 + coord = center_base[axcoord] + (height / 2) + sign * fillet_ext + + # cylinder + cyl_name = f'{pos}_min' + cylinder_args = { + x1 + '0': center_base[axcoord1], + x2 + '0': center_base[axcoord2], + 'r': radius - fillet_radius + } + cls = getattr(openmc, f'{axis.upper()}Cylinder') + cyl = cls(name=f'{cyl_name} {axis}', **cylinder_args) + + #torus + tor_name = f'{axis} {pos}' + tor_args = { + 'a': radius - fillet_radius, + 'b': fillet_radius, + 'c': fillet_radius, + x1 + '0': center_base[axcoord1], + x2 + '0': center_base[axcoord2], + axis + '0': coord + } + cls = getattr(openmc, f'{axis.upper()}Torus') + torus = cls(name=tor_name, **tor_args) + + # plane + p_name = f'{pos} ext' + p_args = {axis + '0': coord} + cls = getattr(openmc, f'{axis.upper()}Plane') + plane = cls(name=p_name, **p_args) + + return cyl, torus, plane + + if upper_fillet_radius > 0. or lower_fillet_radius > 0.: + if 'boundary_type' in kwargs: + if kwargs['boundary_type'] == 'periodic': + raise ValueError('Periodic boundary conditions not permitted when ' + 'rounded corners are used.') + + axis_args = (axis, x1, x2, axcoord, axcoord1, axcoord2) + if upper_fillet_radius > 0.: + cylinder, torus, plane = _create_fillet_objects( + axis_args, height, center_base, radius, upper_fillet_radius) + self.upper_fillet_cylinder = cylinder + self.upper_fillet_torus = torus + self.upper_fillet_plane = plane + self._surface_names += ('upper_fillet_cylinder', + 'upper_fillet_torus', + 'upper_fillet_plane') + + if lower_fillet_radius > 0.: + cylinder, torus, plane = _create_fillet_objects( + axis_args, height, center_base, radius, lower_fillet_radius, + pos='lower' + ) + self.lower_fillet_cylinder = cylinder + self.lower_fillet_torus = torus + self.lower_fillet_plane = plane + + self._surface_names += ('lower_fillet_cylinder', + 'lower_fillet_torus', + 'lower_fillet_plane') + + def _get_fillet(self): + upper_fillet = self._get_upper_fillet() + lower_fillet = self._get_lower_fillet() + has_upper_fillet = upper_fillet is not None + has_lower_fillet = lower_fillet is not None + if has_lower_fillet and has_upper_fillet: + fillet = lower_fillet | upper_fillet + elif has_upper_fillet and not has_lower_fillet: + fillet = upper_fillet + elif not has_upper_fillet and has_lower_fillet: + fillet = lower_fillet + else: + fillet = None + return fillet + + def _get_upper_fillet(self): + has_upper_fillet = hasattr(self, 'upper_fillet_plane') + if has_upper_fillet: + upper_fillet = +self.upper_fillet_cylinder & +self.upper_fillet_torus & +self.upper_fillet_plane + else: + upper_fillet = None + return upper_fillet + + def _get_lower_fillet(self): + has_lower_fillet = hasattr(self, 'lower_fillet_plane') + if has_lower_fillet: + lower_fillet = +self.lower_fillet_cylinder & +self.lower_fillet_torus & -self.lower_fillet_plane + else: + lower_fillet = None + return lower_fillet def __neg__(self): - return -self.cyl & +self.bottom & -self.top + prism = -self.cyl & +self.bottom & -self.top + fillet = self._get_fillet() + if fillet is not None: + prism = prism & ~fillet + return prism def __pos__(self): - return +self.cyl | -self.bottom | +self.top + prism = +self.cyl | -self.bottom | +self.top + fillet = self._get_fillet() + if fillet is not None: + prism = prism | fillet + return prism class RectangularParallelepiped(CompositeSurface): @@ -160,16 +648,96 @@ class RectangularParallelepiped(CompositeSurface): self.zmax = openmc.ZPlane(z0=zmax, **kwargs) def __neg__(self): - return +self.xmin & -self.xmax & +self.ymin & -self.ymax & +self.zmin & -self.zmax + return -self.xmax & +self.xmin & -self.ymax & +self.ymin & -self.zmax & +self.zmin def __pos__(self): - return -self.xmin | +self.xmax | -self.ymin | +self.ymax | -self.zmin | +self.zmax + return +self.xmax | -self.xmin | +self.ymax | -self.ymin | +self.zmax | -self.zmin + + +class OrthogonalBox(CompositeSurface): + """Arbitrarily oriented orthogonal box + + This composite surface is composed of four or six planar surfaces that form + an arbitrarily oriented orthogonal box when combined. + + Parameters + ---------- + v : iterable of float + (x,y,z) coordinates of a corner of the box + a1 : iterable of float + Vector of first side starting from ``v`` + a2 : iterable of float + Vector of second side starting from ``v`` + a3 : iterable of float, optional + Vector of third side starting from ``v``. When not specified, it is + assumed that the box will be infinite along the vector normal to the + plane specified by ``a1`` and ``a2``. + **kwargs + Keyword arguments passed to underlying plane classes + + Attributes + ---------- + ax1_min, ax1_max : openmc.Plane + Planes representing minimum and maximum along first axis + ax2_min, ax2_max : openmc.Plane + Planes representing minimum and maximum along second axis + ax3_min, ax3_max : openmc.Plane + Planes representing minimum and maximum along third axis + + """ + _surface_names = ('ax1_min', 'ax1_max', 'ax2_min', 'ax2_max', 'ax3_min', 'ax3_max') + + def __init__(self, v, a1, a2, a3=None, **kwargs): + v = np.array(v) + a1 = np.array(a1) + a2 = np.array(a2) + if has_a3 := a3 is not None: + a3 = np.array(a3) + else: + a3 = np.cross(a1, a2) # normal to plane specified by a1 and a2 + + # Generate corners of box + p1 = v + p2 = v + a1 + p3 = v + a2 + p4 = v + a3 + p5 = v + a1 + a2 + p6 = v + a2 + a3 + p7 = v + a1 + a3 + + # Generate 6 planes of box + self.ax1_min = openmc.Plane.from_points(p1, p3, p4, **kwargs) + self.ax1_max = openmc.Plane.from_points(p2, p5, p7, **kwargs) + self.ax2_min = openmc.Plane.from_points(p1, p4, p2, **kwargs) + self.ax2_max = openmc.Plane.from_points(p3, p6, p5, **kwargs) + if has_a3: + self.ax3_min = openmc.Plane.from_points(p1, p2, p3, **kwargs) + self.ax3_max = openmc.Plane.from_points(p4, p7, p6, **kwargs) + + # Make sure a point inside the box produces the correct senses. If not, + # flip the plane coefficients so it does. + mid_point = v + (a1 + a2 + a3)/2 + nums = (1, 2, 3) if has_a3 else (1, 2) + for num in nums: + min_surf = getattr(self, f'ax{num}_min') + max_surf = getattr(self, f'ax{num}_max') + if mid_point in -min_surf: + min_surf.flip_normal() + if mid_point in +max_surf: + max_surf.flip_normal() + + def __neg__(self): + region = (+self.ax1_min & -self.ax1_max & + +self.ax2_min & -self.ax2_max) + if hasattr(self, 'ax3_min'): + region &= (+self.ax3_min & -self.ax3_max) + return region class XConeOneSided(CompositeSurface): - """One-sided cone parallel the x-axis + r"""One-sided cone parallel the x-axis - A one-sided cone is composed of a normal cone surface and an "ambiguity" + A one-sided cone is composed of a normal cone surface and a "disambiguation" surface that eliminates the ambiguity as to which region of space is included. This class acts as a proper surface, meaning that unary `+` and `-` operators applied to it will produce a half-space. The negative side is @@ -180,13 +748,16 @@ class XConeOneSided(CompositeSurface): Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -199,7 +770,7 @@ class XConeOneSided(CompositeSurface): cone : openmc.XCone Regular two-sided cone plane : openmc.XPlane - Ambiguity surface + Disambiguation surface up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -217,17 +788,11 @@ class XConeOneSided(CompositeSurface): def __neg__(self): return -self.cone & (+self.plane if self.up else -self.plane) - def __pos__(self): - if self.up: - return (+self.cone & +self.plane) | -self.plane - else: - return (+self.cone & -self.plane) | +self.plane - class YConeOneSided(CompositeSurface): - """One-sided cone parallel the y-axis + r"""One-sided cone parallel the y-axis - A one-sided cone is composed of a normal cone surface and an "ambiguity" + A one-sided cone is composed of a normal cone surface and a "disambiguation" surface that eliminates the ambiguity as to which region of space is included. This class acts as a proper surface, meaning that unary `+` and `-` operators applied to it will produce a half-space. The negative side is @@ -238,13 +803,16 @@ class YConeOneSided(CompositeSurface): Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -257,7 +825,7 @@ class YConeOneSided(CompositeSurface): cone : openmc.YCone Regular two-sided cone plane : openmc.YPlane - Ambiguity surface + Disambiguation surface up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -273,13 +841,12 @@ class YConeOneSided(CompositeSurface): self.up = up __neg__ = XConeOneSided.__neg__ - __pos__ = XConeOneSided.__pos__ class ZConeOneSided(CompositeSurface): - """One-sided cone parallel the z-axis + r"""One-sided cone parallel the z-axis - A one-sided cone is composed of a normal cone surface and an "ambiguity" + A one-sided cone is composed of a normal cone surface and a "disambiguation" surface that eliminates the ambiguity as to which region of space is included. This class acts as a proper surface, meaning that unary `+` and `-` operators applied to it will produce a half-space. The negative side is @@ -290,13 +857,16 @@ class ZConeOneSided(CompositeSurface): Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -309,7 +879,7 @@ class ZConeOneSided(CompositeSurface): cone : openmc.ZCone Regular two-sided cone plane : openmc.ZPlane - Ambiguity surface + Disambiguation surface up : bool Whether to select the side of the cone that extends to infinity in the positive direction of the coordinate axis (the positive half-space of @@ -325,4 +895,1068 @@ class ZConeOneSided(CompositeSurface): self.up = up __neg__ = XConeOneSided.__neg__ - __pos__ = XConeOneSided.__pos__ + + +class Polygon(CompositeSurface): + """Polygon formed from a path of closed points. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + points : np.ndarray + An Nx2 array of points defining the vertices of the polygon. + basis : {'rz', 'xy', 'yz', 'xz'}, optional + 2D basis set for the polygon. The polygon is two dimensional and has + infinite extent in the third (unspecified) dimension. For example, the + 'xy' basis produces a polygon with infinite extent in the +/- z + direction. For the 'rz' basis the phi extent is infinite, thus forming + an axisymmetric surface. + + Attributes + ---------- + points : np.ndarray + An Nx2 array of points defining the vertices of the polygon. + basis : {'rz', 'xy', 'yz', 'xz'} + 2D basis set for the polygon. + regions : list of openmc.Region + A list of :class:`openmc.Region` objects, one for each of the convex polygons + formed during the decomposition of the input polygon. + region : openmc.Union + The union of all the regions comprising the polygon. + """ + + def __init__(self, points, basis='rz'): + check_value('basis', basis, ('xy', 'yz', 'xz', 'rz')) + self._basis = basis + + # Create a constrained triangulation of the validated points. + # The constrained triangulation is set to the _tri attribute + self._constrain_triangulation(self._validate_points(points)) + + # Decompose the polygon into groups of simplices forming convex subsets + # and get the sets of (surface, operator) pairs defining the polygon + self._surfsets = self._decompose_polygon_into_convex_sets() + + # Set surface names as required by CompositeSurface protocol + surfnames = [] + i = 0 + for surfset in self._surfsets: + for surf, op, on_boundary in surfset: + if on_boundary: + setattr(self, f'surface_{i}', surf) + surfnames.append(f'surface_{i}') + i += 1 + self._surfnames = tuple(surfnames) + + # Generate a list of regions whose union represents the polygon. + regions = [] + for surfs_ops in self._surfsets: + regions.append([op(surf) for surf, op, _ in surfs_ops]) + self._regions = [openmc.Intersection(regs) for regs in regions] + + # Create the union of all the convex subsets + self._region = openmc.Union(self._regions) + + def __neg__(self): + return self._region + + @property + def _surface_names(self): + return self._surfnames + + @CompositeSurface.boundary_type.setter + def boundary_type(self, boundary_type): + if boundary_type != 'transmission': + warnings.warn("Setting boundary_type to a value other than " + "'transmission' on Polygon composite surfaces can " + "result in unintended behavior. Please use the " + "regions property of the Polygon to generate " + "individual openmc.Cell objects to avoid unwanted " + "behavior.") + for name in self._surface_names: + getattr(self, name).boundary_type = boundary_type + + @property + def points(self): + return self._tri.points + + @property + def basis(self): + return self._basis + + @property + def _normals(self): + """Generate the outward normal unit vectors for the polygon.""" + # Rotation matrix for 90 degree clockwise rotation (-90 degrees about z + # axis for an 'xy' basis). + rotation = np.array([[0., 1.], [-1., 0.]]) + # Get the unit vectors that point from one point in the polygon to the + # next given that they are ordered counterclockwise and that the final + # point is connected to the first point + tangents = np.diff(self.points, axis=0, append=[self.points[0, :]]) + tangents /= np.linalg.norm(tangents, axis=-1, keepdims=True) + # Rotate the tangent vectors clockwise by 90 degrees, which for a + # counter-clockwise ordered polygon will produce the outward normal + # vectors. + return rotation.dot(tangents.T).T + + @property + def _equations(self): + normals = self._normals + equations = np.empty((normals.shape[0], 3)) + equations[:, :2] = normals + equations[:, 2] = -np.sum(normals*self.points, axis=-1) + return equations + + @property + def regions(self): + return self._regions + + @property + def region(self): + return self._region + + def _validate_points(self, points): + """Ensure the closed path defined by points does not intersect and is + oriented counter-clockwise. + + Parameters + ---------- + points : np.ndarray (Nx2) + An Nx2 array of coordinate pairs describing the vertices. + + Returns + ------- + ordered_points : the input points ordered counter-clockwise + """ + points = np.asarray(points, dtype=float) + check_iterable_type('points', points, float, min_depth=2, max_depth=2) + check_length('points', points[0, :], 2, 2) + + # If the last point is the same as the first, remove it and make sure + # there are still at least 3 points for a valid polygon. + if np.allclose(points[0, :], points[-1, :]): + points = points[:-1, :] + check_length('points', points, 3) + + if len(points) != len(np.unique(points, axis=0)): + raise ValueError('Duplicate points were detected in the Polygon input') + + # Order the points counter-clockwise (necessary for offset method) + # Calculates twice the signed area of the polygon using the "Shoelace + # Formula" https://en.wikipedia.org/wiki/Shoelace_formula + # If signed area is positive the curve is oriented counter-clockwise. + # If the signed area is negative the curve is oriented clockwise. + xpts, ypts = points.T + if np.sum(ypts*(np.roll(xpts, 1) - np.roll(xpts, -1))) < 0: + points = points[::-1, :] + + # Check if polygon is self-intersecting by comparing edges pairwise + n = len(points) + for i in range(n): + p0 = np.append(points[i, :], 0) + p1 = np.append(points[(i + 1) % n, :], 0) + for j in range(i + 1, n): + p2 = np.append(points[j, :], 0) + p3 = np.append(points[(j + 1) % n, :], 0) + # Compute orientation of p0 wrt p2->p3 line segment + cp0 = np.cross(p3-p0, p2-p0)[-1] + # Compute orientation of p1 wrt p2->p3 line segment + cp1 = np.cross(p3-p1, p2-p1)[-1] + # Compute orientation of p2 wrt p0->p1 line segment + cp2 = np.cross(p1-p2, p0-p2)[-1] + # Compute orientation of p3 wrt p0->p1 line segment + cp3 = np.cross(p1-p3, p0-p3)[-1] + + # Group cross products in an array and find out how many are 0 + cross_products = np.array([[cp0, cp1], [cp2, cp3]]) + cps_near_zero = np.isclose(cross_products, 0).astype(int) + num_zeros = np.sum(cps_near_zero) + + # Topologies of 2 finite line segments categorized by the number + # of zero-valued cross products: + # + # 0: No 3 points lie on the same line + # 1: 1 point lies on the same line defined by the other line + # segment, but is not coincident with either of the points + # 2: 2 points are coincident, but the line segments are not + # collinear which guarantees no intersection + # 3: not possible, except maybe floating point issues? + # 4: Both line segments are collinear, simply need to check if + # they overlap or not + # adapted from algorithm linked below and modified to only + # consider intersections on the interior of line segments as + # proper intersections: i.e. segments sharing end points do not + # count as intersections. + # https://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/ + + if num_zeros == 0: + # If the orientations of p0 and p1 have opposite signs + # and the orientations of p2 and p3 have opposite signs + # then there is an intersection. + if all(np.prod(cross_products, axis=-1) < 0): + raise ValueError('Polygon cannot be self-intersecting') + continue + + elif num_zeros == 1: + # determine which line segment has 2 out of the 3 collinear + # points + idx = np.argwhere(np.sum(cps_near_zero, axis=-1) == 0) + if np.prod(cross_products[idx, :]) < 0: + raise ValueError('Polygon cannot be self-intersecting') + continue + + elif num_zeros == 2: + continue + + elif num_zeros == 3: + warnings.warn('Unclear if Polygon is self-intersecting') + continue + + else: + # All 4 cross products are zero + # Determine number of unique points, x span and y span for + # both line segments + xmin1, xmax1 = min(p0[0], p1[0]), max(p0[0], p1[0]) + ymin1, ymax1 = min(p0[1], p1[1]), max(p0[1], p1[1]) + xmin2, xmax2 = min(p2[0], p3[0]), max(p2[0], p3[0]) + ymin2, ymax2 = min(p2[1], p3[1]), max(p2[1], p3[1]) + xlap = xmin1 < xmax2 and xmin2 < xmax1 + ylap = ymin1 < ymax2 and ymin2 < ymax1 + if xlap or ylap: + raise ValueError('Polygon cannot be self-intersecting') + continue + + return points + + def _constrain_triangulation(self, points, depth=0): + """Generate a constrained triangulation by ensuring all edges of the + Polygon are contained within the simplices. + + Parameters + ---------- + points : np.ndarray (Nx2) + An Nx2 array of coordinate pairs describing the vertices. These + points represent a planar straight line graph. + + Returns + ------- + None + """ + # Only attempt the triangulation up to 5 times. + if depth > 4: + raise RuntimeError('Could not create a valid triangulation after 5' + ' attempts') + + tri = Delaunay(points, qhull_options='QJ') + # Loop through the boundary edges of the polygon. If an edge is not + # included in the triangulation, break it into two line segments. + n = len(points) + new_pts = [] + for i, j in zip(range(n), range(1, n + 1)): + # If both vertices of any edge are not found in any simplex, insert + # a new point between them. + if not any([i in s and j % n in s for s in tri.simplices]): + newpt = (points[i, :] + points[j % n, :]) / 2 + new_pts.append((j, newpt)) + + # If all the edges are included in the triangulation set it, otherwise + # try again with additional points inserted on offending edges. + if not new_pts: + self._tri = tri + else: + for i, pt in new_pts[::-1]: + points = np.insert(points, i, pt, axis=0) + self._constrain_triangulation(points, depth=depth + 1) + + def _group_simplices(self, neighbor_map, group=None): + """Generate a convex grouping of simplices. + + Parameters + ---------- + neighbor_map : dict + A map whose keys are simplex indices for simplices inside the polygon + and whose values are a list of simplex indices that neighbor this + simplex and are also inside the polygon. + group : list + A list of simplex indices that comprise the current convex group. + + Returns + ------- + group : list + The list of simplex indices that comprise the complete convex group. + """ + # If neighbor_map is empty there's nothing left to do + if not neighbor_map: + return group + # If group is empty, grab the next simplex in the dictionary and recurse + if group is None: + # Start with smallest neighbor lists + sidx = sorted(neighbor_map.items(), key=lambda item: len(item[1]))[0][0] + return self._group_simplices(neighbor_map, group=[sidx]) + # Otherwise use the last simplex in the group + else: + sidx = group[-1] + # Remove current simplex from dictionary since it is in a group + neighbors = neighbor_map.pop(sidx, []) + # For each neighbor check if it is part of the same convex + # hull as the rest of the group. If yes, recurse. If no, continue on. + for n in neighbors: + if n in group or neighbor_map.get(n) is None: + continue + test_group = group + [n] + test_point_idx = np.unique(self._tri.simplices[test_group, :]) + test_points = self.points[test_point_idx] + test_hull = ConvexHull(test_points, qhull_options='Qc') + pts_on_hull = len(test_hull.vertices) + len(test_hull.coplanar) + # If test_points are convex (including coplanar) keep adding to + # this group + if len(test_points) == pts_on_hull: + group = self._group_simplices(neighbor_map, group=test_group) + return group + + def _get_convex_hull_surfs(self, qhull): + """Generate a list of surfaces given by a set of linear equations + + Parameters + ---------- + qhull : scipy.spatial.ConvexHull + A ConvexHull object representing the sub-region of the polygon. + + Returns + ------- + surfs_ops : list of (surface, operator) tuples + + """ + basis = self.basis + boundary_eqns = self._equations + # Collect surface/operator pairs such that the intersection of the + # regions defined by these pairs is the inside of the polygon. + surfs_ops = [] + # hull facet equation: dx*x + dy*y + c = 0 + for dx, dy, c in qhull.equations: + # check if this facet is on the boundary of the polygon + facet_eq = np.array([dx, dy, c]) + on_boundary = any([np.allclose(facet_eq, eq) for eq in boundary_eqns]) + # Check if the facet is horizontal + if isclose(dx, 0, abs_tol=1e-8): + if basis in ('xz', 'yz', 'rz'): + surf = openmc.ZPlane(z0=-c/dy) + else: + surf = openmc.YPlane(y0=-c/dy) + # if (0, 1).(dx, dy) < 0 we want positive halfspace instead + op = operator.pos if dy < 0 else operator.neg + # Check if the facet is vertical + elif isclose(dy, 0, abs_tol=1e-8): + if basis in ('xy', 'xz'): + surf = openmc.XPlane(x0=-c/dx) + elif basis == 'yz': + surf = openmc.YPlane(y0=-c/dx) + else: + surf = openmc.ZCylinder(r=-c/dx) + # if (1, 0).(dx, dy) < 0 we want positive halfspace instead + op = operator.pos if dx < 0 else operator.neg + # Otherwise the facet is at an angle + else: + op = operator.neg + if basis == 'xy': + surf = openmc.Plane(a=dx, b=dy, c=0.0, d=-c) + elif basis == 'yz': + surf = openmc.Plane(a=0.0, b=dx, c=dy, d=-c) + elif basis == 'xz': + surf = openmc.Plane(a=dx, b=0.0, c=dy, d=-c) + else: + y0 = -c/dy + r2 = dy**2 / dx**2 + # Check if the *slope* of the facet is positive. If dy/dx < 0 + # then we want up to be True for the one-sided cones. + up = dy / dx < 0 + surf = openmc.model.ZConeOneSided(z0=y0, r2=r2, up=up) + # if (1, -1).(dx, dy) < 0 for up cones we want positive halfspace + # if (1, 1).(dx, dy) < 0 for down cones we want positive halfspace + # otherwise we keep the negative halfspace operator + if (up and dx - dy < 0) or (not up and dx + dy < 0): + op = operator.pos + + surfs_ops.append((surf, op, on_boundary)) + + return surfs_ops + + def _decompose_polygon_into_convex_sets(self): + """Decompose the Polygon into a set of convex polygons. + + Returns + ------- + surfsets : a list of lists of surface, operator pairs + """ + from matplotlib.path import Path + + # Get centroids of all the simplices and determine if they are inside + # the polygon defined by input vertices or not. + centroids = np.mean(self.points[self._tri.simplices], axis=1) + in_polygon = Path(self.points).contains_points(centroids) + self._in_polygon = in_polygon + + # Build a map with keys of simplex indices inside the polygon whose + # values are lists of that simplex's neighbors also inside the + # polygon + neighbor_map = {} + for i, nlist in enumerate(self._tri.neighbors): + if not in_polygon[i]: + continue + neighbor_map[i] = [n for n in nlist if in_polygon[n] and n >=0] + + # Get the groups of simplices forming convex polygons whose union + # comprises the full input polygon. While there are still simplices + # left in the neighbor map, group them together into convex sets. + groups = [] + while neighbor_map: + groups.append(self._group_simplices(neighbor_map)) + self._groups = groups + + # Generate lists of (surface, operator) pairs for each convex + # sub-region. + surfsets = [] + for group in groups: + # Find all the unique points in the convex group of simplices, + # generate the convex hull and find the resulting surfaces and + # unary operators that represent this convex subset of the polygon. + idx = np.unique(self._tri.simplices[group, :]) + qhull = ConvexHull(self.points[idx, :]) + surf_ops = self._get_convex_hull_surfs(qhull) + surfsets.append(surf_ops) + return surfsets + + def offset(self, distance: float | Sequence[float] | np.ndarray) -> Polygon: + """Offset this polygon by a set distance + + Parameters + ---------- + distance : float or sequence of float or np.ndarray + The distance to offset the polygon by. Positive is outward + (expanding) and negative is inward (shrinking). If a float is + provided, the same offset is applied to all vertices. If a list or + tuple is provided, each vertex gets a different offset. If an + iterable or numpy array is provided, each vertex gets a different + offset. + + Returns + ------- + offset_polygon : openmc.model.Polygon + """ + + if isinstance(distance, float): + distance = np.full(len(self.points), distance) + elif isinstance(distance, Sequence): + distance = np.array(distance) + elif not isinstance(distance, np.ndarray): + raise TypeError("Distance must be a float or sequence of float.") + + if len(distance) != len(self.points): + raise ValueError( + f"Length of distance {len(distance)} array must " + f"match number of polygon points {len(self.points)}" + ) + + normals = np.insert(self._normals, 0, self._normals[-1, :], axis=0) + cos2theta = np.sum(normals[1:, :]*normals[:-1, :], axis=-1, keepdims=True) + costheta = np.cos(np.arccos(cos2theta) / 2) + nvec = (normals[1:, :] + normals[:-1, :]) + unit_nvec = nvec / np.linalg.norm(nvec, axis=-1, keepdims=True) + disp_vec = distance[:, np.newaxis] / costheta * unit_nvec + + return type(self)(self.points + disp_vec, basis=self.basis) + + +class CruciformPrism(CompositeSurface): + """Generalized cruciform prism + + This surface represents a prism parallel to an axis formed by planes at + multiple distances from the center. Equivalent to the 'gcross' derived + surface in Serpent. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + distances : iterable of float + A monotonically increasing (or decreasing) iterable of distances in [cm] + that form the planes of the generalized cruciform. + center : iterable of float + The center of the prism in the two non-parallel axes (e.g., (x, y) when + axis is 'z') in [cm] + axis : {'x', 'y', 'z'} + Axis to which the prism is parallel + **kwargs + Keyword arguments passed to underlying plane classes + + """ + + def __init__(self, distances, center=(0., 0.), axis='z', **kwargs): + x0, y0 = center + self.distances = distances + + if axis == 'x': + cls_horizontal = openmc.YPlane + cls_vertical = openmc.ZPlane + elif axis == 'y': + cls_horizontal = openmc.XPlane + cls_vertical = openmc.ZPlane + elif axis == 'z': + cls_horizontal = openmc.XPlane + cls_vertical = openmc.YPlane + else: + raise ValueError("axis must be 'x', 'y', or 'z'") + + # Create each planar surface + surfnames = [] + for i, d in enumerate(distances): + setattr(self, f'hmin{i}', cls_horizontal(x0 - d, **kwargs)) + setattr(self, f'hmax{i}', cls_horizontal(x0 + d, **kwargs)) + setattr(self, f'vmin{i}', cls_vertical(y0 - d, **kwargs)) + setattr(self, f'vmax{i}', cls_vertical(y0 + d, **kwargs)) + surfnames.extend([f'hmin{i}', f'hmax{i}', f'vmin{i}', f'vmax{i}']) + + # Set _surfnames to satisfy CompositeSurface protocol + self._surfnames = tuple(surfnames) + + @property + def _surface_names(self): + return self._surfnames + + @property + def distances(self): + return self._distances + + @distances.setter + def distances(self, values): + values = np.array(values, dtype=float) + # check for positive values + if not (values > 0).all(): + raise ValueError("distances must be positive") + # Check for monotonicity + if (values[1:] > values[:-1]).all() or (values[1:] < values[:-1]).all(): + self._distances = values + else: + raise ValueError("distances must be monotonic") + + def __neg__(self): + n = len(self.distances) + regions = [] + for i in range(n): + regions.append( + +getattr(self, f'hmin{i}') & + -getattr(self, f'hmax{i}') & + +getattr(self, f'vmin{n-1-i}') & + -getattr(self, f'vmax{n-1-i}') + ) + return openmc.Union(regions) + + +# Define function to create a plane on given axis +def _plane(axis, name, value, boundary_type='transmission', albedo=1.0): + cls = getattr(openmc, f'{axis.upper()}Plane') + return cls(value, name=f'{name} {axis}', + boundary_type=boundary_type, albedo=albedo) + + +class RectangularPrism(CompositeSurface): + """Infinite rectangular prism bounded by four planar surfaces. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + width : float + Prism width in units of [cm]. The width is aligned with the x, x, or z + axes for prisms parallel to the x, y, or z axis, respectively. + height : float + Prism height in units of [cm]. The height is aligned with the x, y, or z + axes for prisms parallel to the x, y, or z axis, respectively. + axis : {'x', 'y', 'z'} + Axis with which the infinite length of the prism should be aligned. + origin : Iterable of two floats + Origin of the prism. The two floats correspond to (y,z), (x,z) or (x,y) + for prisms parallel to the x, y or z axis, respectively. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Boundary condition that defines the behavior for particles hitting the + surfaces comprising the rectangular prism. + albedo : float, optional + Albedo of the prism's surfaces as a ratio of particle weight after + interaction with the surface to the initial weight. Values must be + positive. Only applicable if the boundary type is 'reflective', + 'periodic', or 'white'. + corner_radius : float + Prism corner radius in units of [cm]. + + """ + _surface_names = ('min_x1', 'max_x1', 'min_x2', 'max_x2') + + def __init__( + self, + width: float, + height: float, + axis: str = 'z', + origin: Sequence[float] = (0., 0.), + boundary_type: str = 'transmission', + albedo: float = 1., + corner_radius: float = 0. + ): + check_type('width', width, Real) + check_type('height', height, Real) + check_type('albedo', albedo, Real) + check_type('corner_radius', corner_radius, Real) + check_value('axis', axis, ('x', 'y', 'z')) + check_type('origin', origin, Iterable, Real) + + if axis == 'x': + x1, x2 = 'y', 'z' + elif axis == 'y': + x1, x2 = 'x', 'z' + else: + x1, x2 = 'x', 'y' + + # Get cylinder class corresponding to given axis + cyl = getattr(openmc, f'{axis.upper()}Cylinder') + + # Create container for boundary arguments + bc_args = {'boundary_type': boundary_type, 'albedo': albedo} + + # Create rectangular region + self.min_x1 = _plane(x1, 'minimum', -width/2 + origin[0], **bc_args) + self.max_x1 = _plane(x1, 'maximum', width/2 + origin[0], **bc_args) + self.min_x2 = _plane(x2, 'minimum', -height/2 + origin[1], **bc_args) + self.max_x2 = _plane(x2, 'maximum', height/2 + origin[1], **bc_args) + if boundary_type == 'periodic': + self.min_x1.periodic_surface = self.max_x1 + self.min_x2.periodic_surface = self.max_x2 + + # Handle rounded corners if given + if corner_radius > 0.: + if boundary_type == 'periodic': + raise ValueError('Periodic boundary conditions not permitted when ' + 'rounded corners are used.') + + args = {'r': corner_radius, 'boundary_type': boundary_type, 'albedo': albedo} + + args[x1 + '0'] = origin[0] - width/2 + corner_radius + args[x2 + '0'] = origin[1] - height/2 + corner_radius + self.x1_min_x2_min = cyl(name=f'{x1} min {x2} min', **args) + + args[x1 + '0'] = origin[0] - width/2 + corner_radius + args[x2 + '0'] = origin[1] + height/2 - corner_radius + self.x1_min_x2_max = cyl(name=f'{x1} min {x2} max', **args) + + args[x1 + '0'] = origin[0] + width/2 - corner_radius + args[x2 + '0'] = origin[1] - height/2 + corner_radius + self.x1_max_x2_min = cyl(name=f'{x1} max {x2} min', **args) + + args[x1 + '0'] = origin[0] + width/2 - corner_radius + args[x2 + '0'] = origin[1] + height/2 - corner_radius + self.x1_max_x2_max = cyl(name=f'{x1} max {x2} max', **args) + + self.x1_min = _plane(x1, 'min', -width/2 + origin[0] + corner_radius, + **bc_args) + self.x1_max = _plane(x1, 'max', width/2 + origin[0] - corner_radius, + **bc_args) + self.x2_min = _plane(x2, 'min', -height/2 + origin[1] + corner_radius, + **bc_args) + self.x2_max = _plane(x2, 'max', height/2 + origin[1] - corner_radius, + **bc_args) + self._surface_names += ( + 'x1_min_x2_min', 'x1_min_x2_max', 'x1_max_x2_min', + 'x1_max_x2_max', 'x1_min', 'x1_max', 'x2_min', 'x2_max' + ) + + def __neg__(self): + prism = +self.min_x1 & -self.max_x1 & +self.min_x2 & -self.max_x2 + + # Cut out corners if a corner radius was given + if hasattr(self, 'x1_min'): + corners = ( + (+self.x1_min_x2_min & -self.x1_min & -self.x2_min) | + (+self.x1_min_x2_max & -self.x1_min & +self.x2_max) | + (+self.x1_max_x2_min & +self.x1_max & -self.x2_min) | + (+self.x1_max_x2_max & +self.x1_max & +self.x2_max) + ) + prism &= ~corners + + return prism + + +class HexagonalPrism(CompositeSurface): + """Hexagonal prism comoposed of six planar surfaces + + .. versionadded:: 0.14.0 + + Parameters + ---------- + edge_length : float + Length of a side of the hexagon in [cm] + orientation : {'x', 'y'} + An 'x' orientation means that two sides of the hexagon are parallel to + the x-axis and a 'y' orientation means that two sides of the hexagon are + parallel to the y-axis. + origin : Iterable of two floats + Origin of the prism. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Boundary condition that defines the behavior for particles hitting the + surfaces comprising the hexagonal prism. + albedo : float, optional + Albedo of the prism's surfaces as a ratio of particle weight after + interaction with the surface to the initial weight. Values must be + positive. Only applicable if the boundary type is 'reflective', + 'periodic', or 'white'. + corner_radius : float + Prism corner radius in units of [cm]. + + """ + _surface_names = ('plane_max', 'plane_min', 'upper_right', 'upper_left', + 'lower_right', 'lower_left') + + def __init__( + self, + edge_length: float = 1., + orientation: str = 'y', + origin: Sequence[float] = (0., 0.), + boundary_type: str = 'transmission', + albedo: float = 1., + corner_radius: float = 0. + ): + check_type('edge_length', edge_length, Real) + check_type('albedo', albedo, Real) + check_type('corner_radius', corner_radius, Real) + check_value('orientation', orientation, ('x', 'y')) + check_type('origin', origin, Iterable, Real) + + l = edge_length + x, y = origin + + # Create container for boundary arguments + bc_args = {'boundary_type': boundary_type, 'albedo': albedo} + + if orientation == 'y': + # Left and right planes + self.plane_max = openmc.XPlane(x + sqrt(3.)/2*l, **bc_args) + self.plane_min = openmc.XPlane(x - sqrt(3.)/2*l, **bc_args) + c = sqrt(3.)/3. + + # y = -x/sqrt(3) + a + self.upper_right = openmc.Plane(a=c, b=1., d=l+x*c+y, **bc_args) + + # y = x/sqrt(3) + a + self.upper_left = openmc.Plane(a=-c, b=1., d=l-x*c+y, **bc_args) + + # y = x/sqrt(3) - a + self.lower_right = openmc.Plane(a=-c, b=1., d=-l-x*c+y, **bc_args) + + # y = -x/sqrt(3) - a + self.lower_left = openmc.Plane(a=c, b=1., d=-l+x*c+y, **bc_args) + + elif orientation == 'x': + self.plane_max = openmc.YPlane(y + sqrt(3.)/2*l, **bc_args) + self.plane_min = openmc.YPlane(y - sqrt(3.)/2*l, **bc_args) + c = sqrt(3.) + + # Upper-right surface: y = -sqrt(3)*(x - a) + self.upper_right = openmc.Plane(a=c, b=1., d=c*l+x*c+y, **bc_args) + + # Lower-right surface: y = sqrt(3)*(x + a) + self.lower_right = openmc.Plane(a=-c, b=1., d=-c*l-x*c+y, **bc_args) + + # Lower-left surface: y = -sqrt(3)*(x + a) + self.lower_left = openmc.Plane(a=c, b=1., d=-c*l+x*c+y, **bc_args) + + # Upper-left surface: y = sqrt(3)*(x + a) + self.upper_left = openmc.Plane(a=-c, b=1., d=c*l-x*c+y, **bc_args) + + # Handle periodic boundary conditions + if boundary_type == 'periodic': + self.plane_min.periodic_surface = self.plane_max + self.upper_right.periodic_surface = self.lower_left + self.lower_right.periodic_surface = self.upper_left + + # Handle rounded corners if given + if corner_radius > 0.: + if boundary_type == 'periodic': + raise ValueError('Periodic boundary conditions not permitted ' + 'when rounded corners are used.') + + c = sqrt(3.)/2 + t = l - corner_radius/c + + # Cylinder with corner radius and boundary type pre-applied + cyl1 = partial(openmc.ZCylinder, r=corner_radius, **bc_args) + cyl2 = partial(openmc.ZCylinder, r=corner_radius/(2*c), **bc_args) + + if orientation == 'x': + self.x_min_y_min_in = cyl1(name='x min y min in', x0=x-t/2, y0=y-c*t) + self.x_min_y_max_in = cyl1(name='x min y max in', x0=x+t/2, y0=y-c*t) + self.x_max_y_min_in = cyl1(name='x max y min in', x0=x-t/2, y0=y+c*t) + self.x_max_y_max_in = cyl1(name='x max y max in', x0=x+t/2, y0=y+c*t) + self.min_in = cyl1(name='x min in', x0=x-t, y0=y) + self.max_in = cyl1(name='x max in', x0=x+t, y0=y) + + self.x_min_y_min_out = cyl2(name='x min y min out', x0=x-l/2, y0=y-c*l) + self.x_min_y_max_out = cyl2(name='x min y max out', x0=x+l/2, y0=y-c*l) + self.x_max_y_min_out = cyl2(name='x max y min out', x0=x-l/2, y0=y+c*l) + self.x_max_y_max_out = cyl2(name='x max y max out', x0=x+l/2, y0=y+c*l) + self.min_out = cyl2(name='x min out', x0=x-l, y0=y) + self.max_out = cyl2(name='x max out', x0=x+l, y0=y) + + elif orientation == 'y': + self.x_min_y_min_in = cyl1(name='x min y min in', x0=x-c*t, y0=y-t/2) + self.x_min_y_max_in = cyl1(name='x min y max in', x0=x-c*t, y0=y+t/2) + self.x_max_y_min_in = cyl1(name='x max y min in', x0=x+c*t, y0=y-t/2) + self.x_max_y_max_in = cyl1(name='x max y max in', x0=x+c*t, y0=y+t/2) + self.min_in = cyl1(name='y min in', x0=x, y0=y-t) + self.max_in = cyl1(name='y max in', x0=x, y0=y+t) + + self.x_min_y_min_out = cyl2(name='x min y min out', x0=x-c*l, y0=y-l/2) + self.x_min_y_max_out = cyl2(name='x min y max out', x0=x-c*l, y0=y+l/2) + self.x_max_y_min_out = cyl2(name='x max y min out', x0=x+c*l, y0=y-l/2) + self.x_max_y_max_out = cyl2(name='x max y max out', x0=x+c*l, y0=y+l/2) + self.min_out = cyl2(name='y min out', x0=x, y0=y-l) + self.max_out = cyl2(name='y max out', x0=x, y0=y+l) + + # Add to tuple of surface names + for s in ('in', 'out'): + self._surface_names += ( + f'x_min_y_min_{s}', f'x_min_y_max_{s}', + f'x_max_y_min_{s}', f'x_max_y_max_{s}', + f'min_{s}', f'max_{s}') + + def __neg__(self) -> openmc.Region: + prism = ( + -self.plane_max & +self.plane_min & + -self.upper_right & -self.upper_left & + +self.lower_right & +self.lower_left + ) + + # Cut out corners if a corner radius was given + if hasattr(self, 'min_in'): + corners = ( + +self.x_min_y_min_in & -self.x_min_y_min_out | + +self.x_min_y_max_in & -self.x_min_y_max_out | + +self.x_max_y_min_in & -self.x_max_y_min_out | + +self.x_max_y_max_in & -self.x_max_y_max_out | + +self.min_in & -self.min_out | + +self.max_in & -self.max_out + ) + prism &= ~corners + + return prism + + +def _rotation_matrix(v1, v2): + """Compute rotation matrix that would rotate v1 into v2. + + Parameters + ---------- + v1 : numpy.ndarray + Unrotated vector + v2 : numpy.ndarray + Rotated vector + + Returns + ------- + 3x3 rotation matrix + + """ + # Normalize vectors and compute cosine + u1 = v1 / np.linalg.norm(v1) + u2 = v2 / np.linalg.norm(v2) + cos_angle = np.dot(u1, u2) + + I = np.identity(3) + + # Handle special case where vectors are parallel or anti-parallel + if isclose(abs(cos_angle), 1.0, rel_tol=1e-8): + return np.sign(cos_angle)*I + else: + # Calculate rotation angle + sin_angle = np.sqrt(1 - cos_angle*cos_angle) + + # Calculate axis of rotation + axis = np.cross(u1, u2) + axis /= np.linalg.norm(axis) + + # Create cross-product matrix K + kx, ky, kz = axis + K = np.array([ + [0.0, -kz, ky], + [kz, 0.0, -kx], + [-ky, kx, 0.0] + ]) + + # Create rotation matrix using Rodrigues' rotation formula + return I + K * sin_angle + (K @ K) * (1 - cos_angle) + + +class ConicalFrustum(CompositeSurface): + """Conical frustum. + + A conical frustum, also known as a right truncated cone, is a cone that is + truncated by two parallel planes that are perpendicular to the axis of the + cone. The lower and upper base of the conical frustum are circular faces. + This surface is equivalent to the TRC macrobody in MCNP. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + center_base : iterable of float + Cartesian coordinates of the center of the bottom planar face. + axis : iterable of float + Vector from the center of the bottom planar face to the center of the + top planar face that defines the axis of the cone. The length of this + vector is the height of the conical frustum. + r1 : float + Radius of the lower cone base + r2 : float + Radius of the upper cone base + **kwargs + Keyword arguments passed to underlying plane classes + + Attributes + ---------- + cone : openmc.Cone + Cone surface + plane_bottom : openmc.Plane + Plane surface defining the bottom of the frustum + plane_top : openmc.Plane + Plane surface defining the top of the frustum + + """ + _surface_names = ('cone', 'plane_bottom', 'plane_top') + + def __init__(self, center_base: Sequence[float], axis: Sequence[float], + r1: float, r2: float, **kwargs): + center_base = np.array(center_base) + axis = np.array(axis) + + # Determine length of axis height vector + h = np.linalg.norm(axis) + + # To create the frustum oriented with the correct axis, first we will + # create a cone along the z axis and then rotate it according to the + # given axis. Thus, we first need to determine the apex using the z axis + # as a reference. + x0, y0, z0 = center_base + if r1 != r2: + apex = z0 + r1*h/(r1 - r2) + r_sq = ((r1 - r2)/h)**2 + cone = openmc.ZCone(x0, y0, apex, r2=r_sq, **kwargs) + else: + # In the degenerate case r1 == r2, the cone becomes a cylinder + cone = openmc.ZCylinder(x0, y0, r1, **kwargs) + + # Create the parallel planes + plane_bottom = openmc.ZPlane(z0, **kwargs) + plane_top = openmc.ZPlane(z0 + h, **kwargs) + + # Determine rotation matrix corresponding to specified axis + u = np.array([0., 0., 1.]) + rotation = _rotation_matrix(u, axis) + + # Rotate the surfaces + self.cone = cone.rotate(rotation, pivot=center_base) + self.plane_bottom = plane_bottom.rotate(rotation, pivot=center_base) + self.plane_top = plane_top.rotate(rotation, pivot=center_base) + + def __neg__(self) -> openmc.Region: + return +self.plane_bottom & -self.plane_top & -self.cone + + +class Vessel(CompositeSurface): + """Vessel composed of cylinder with semi-ellipsoid top and bottom. + + This composite surface is represented by a finite cylinder with ellipsoidal + top and bottom surfaces. This surface is equivalent to the 'vesesl' surface + in Serpent. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + r : float + Radius of vessel. + p1 : float + Minimum coordinate for cylindrical part of vessel. + p2 : float + Maximum coordinate for cylindrical part of vessel. + h1 : float + Height of bottom ellipsoidal part of vessel. + h2 : float + Height of top ellipsoidal part of vessel. + center : 2-tuple of float + Coordinate for central axis of the cylinder in the (y, z), (x, z), or + (x, y) basis. Defaults to (0,0). + axis : {'x', 'y', 'z'} + Central axis of the cylinder. + + """ + + _surface_names = ('cyl', 'plane_bottom', 'plane_top', 'bottom', 'top') + + def __init__(self, r: float, p1: float, p2: float, h1: float, h2: float, + center: Sequence[float] = (0., 0.), axis: str = 'z', **kwargs): + if p1 >= p2: + raise ValueError('p1 must be less than p2') + check_value('axis', axis, {'x', 'y', 'z'}) + + c1, c2 = center + cyl_class = getattr(openmc, f'{axis.upper()}Cylinder') + plane_class = getattr(openmc, f'{axis.upper()}Plane') + self.cyl = cyl_class(c1, c2, r, **kwargs) + self.plane_bottom = plane_class(p1) + self.plane_top = plane_class(p2) + + # General equation for an ellipsoid: + # (x-x₀)²/r² + (y-y₀)²/r² + (z-z₀)²/h² = 1 + # (x-x₀)² + (y-y₀)² + (z-z₀)²s² = r² + # Let s = r/h: + # (x² - 2x₀x + x₀²) + (y² - 2y₀y + y₀²) + (z² - 2z₀z + z₀²)s² = r² + # x² + y² + s²z² - 2x₀x - 2y₀y - 2s²z₀z + (x₀² + y₀² + z₀²s² - r²) = 0 + + sb = (r/h1) + st = (r/h2) + kwargs['a'] = kwargs['b'] = kwargs['c'] = 1.0 + kwargs_bottom = kwargs + kwargs_top = kwargs.copy() + + sb2 = sb*sb + st2 = st*st + kwargs_bottom['k'] = c1*c1 + c2*c2 + p1*p1*sb2 - r*r + kwargs_top['k'] = c1*c1 + c2*c2 + p2*p2*st2 - r*r + + if axis == 'x': + kwargs_bottom['a'] *= sb2 + kwargs_top['a'] *= st2 + kwargs_bottom['g'] = -2*p1*sb2 + kwargs_top['g'] = -2*p2*st2 + kwargs_top['h'] = kwargs_bottom['h'] = -2*c1 + kwargs_top['j'] = kwargs_bottom['j'] = -2*c2 + elif axis == 'y': + kwargs_bottom['b'] *= sb2 + kwargs_top['b'] *= st2 + kwargs_top['g'] = kwargs_bottom['g'] = -2*c1 + kwargs_bottom['h'] = -2*p1*sb2 + kwargs_top['h'] = -2*p2*st2 + kwargs_top['j'] = kwargs_bottom['j'] = -2*c2 + elif axis == 'z': + kwargs_bottom['c'] *= sb2 + kwargs_top['c'] *= st2 + kwargs_top['g'] = kwargs_bottom['g'] = -2*c1 + kwargs_top['h'] = kwargs_bottom['h'] = -2*c2 + kwargs_bottom['j'] = -2*p1*sb2 + kwargs_top['j'] = -2*p2*st2 + + self.bottom = openmc.Quadric(**kwargs_bottom) + self.top = openmc.Quadric(**kwargs_top) + + def __neg__(self): + return ((-self.cyl & +self.plane_bottom & -self.plane_top) | + (-self.bottom & -self.plane_bottom) | + (-self.top & +self.plane_top)) diff --git a/openmc/model/triso.py b/openmc/model/triso.py index 76437d1061..ac70044491 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -139,10 +139,20 @@ class _Container(ABC): def sphere_radius(self): return self._sphere_radius + @sphere_radius.setter + def sphere_radius(self, sphere_radius): + self._sphere_radius = float(sphere_radius) + self._limits = None + self._cell_length = None + @property def center(self): return self._center + @center.setter + def center(self, center): + self._center = center + @abstractproperty def limits(self): pass @@ -155,15 +165,6 @@ class _Container(ABC): def volume(self): pass - @sphere_radius.setter - def sphere_radius(self, sphere_radius): - self._sphere_radius = float(sphere_radius) - self._limits = None - self._cell_length = None - - @center.setter - def center(self, center): - self._center = center def mesh_cell(self, p): """Calculate the index of the cell in a mesh overlaid on the domain in @@ -301,14 +302,32 @@ class _RectangularPrism(_Container): def width(self): return self._width + @width.setter + def width(self, width): + self._width = float(width) + self._limits = None + self._cell_length = None + @property def depth(self): return self._depth + @depth.setter + def depth(self, depth): + self._depth = float(depth) + self._limits = None + self._cell_length = None + @property def height(self): return self._height + @height.setter + def height(self, height): + self._height = float(height) + self._limits = None + self._cell_length = None + @property def limits(self): if self._limits is None: @@ -317,8 +336,13 @@ class _RectangularPrism(_Container): x, y, z = self.width/2, self.depth/2, self.height/2 self._limits = [[c[0] - x + r, c[1] - y + r, c[2] - z + r], [c[0] + x - r, c[1] + y - r, c[2] + z - r]] + return self._limits + @limits.setter + def limits(self, limits): + self._limits = limits + @property def cell_length(self): if self._cell_length is None: @@ -331,28 +355,6 @@ class _RectangularPrism(_Container): def volume(self): return self.width*self.depth*self.height - @width.setter - def width(self, width): - self._width = float(width) - self._limits = None - self._cell_length = None - - @depth.setter - def depth(self, depth): - self._depth = float(depth) - self._limits = None - self._cell_length = None - - @height.setter - def height(self, height): - self._height = float(height) - self._limits = None - self._cell_length = None - - @limits.setter - def limits(self, limits): - self._limits = limits - @classmethod def from_region(self, region, sphere_radius): check_type('region', region, openmc.Region) @@ -471,14 +473,31 @@ class _Cylinder(_Container): def length(self): return self._length + @length.setter + def length(self, length): + self._length = float(length) + self._limits = None + self._cell_length = None + @property def radius(self): return self._radius + @radius.setter + def radius(self, radius): + self._radius = float(radius) + self._limits = None + self._cell_length = None + @property def axis(self): return self._axis + @axis.setter + def axis(self, axis): + self._axis = axis + self._shift = None + @property def shift(self): if self._shift is None: @@ -499,6 +518,10 @@ class _Cylinder(_Container): self._limits = [[z0 - z + r], [z0 + z - r, self.radius - r]] return self._limits + @limits.setter + def limits(self, limits): + self._limits = limits + @property def cell_length(self): if self._cell_length is None: @@ -514,27 +537,6 @@ class _Cylinder(_Container): def volume(self): return self.length*pi*self.radius**2 - @length.setter - def length(self, length): - self._length = float(length) - self._limits = None - self._cell_length = None - - @radius.setter - def radius(self, radius): - self._radius = float(radius) - self._limits = None - self._cell_length = None - - @axis.setter - def axis(self, axis): - self._axis = axis - self._shift = None - - @limits.setter - def limits(self, limits): - self._limits = limits - @classmethod def from_region(self, region, sphere_radius): check_type('region', region, openmc.Region) @@ -677,10 +679,21 @@ class _SphericalShell(_Container): def radius(self): return self._radius + @radius.setter + def radius(self, radius): + self._radius = float(radius) + self._limits = None + self._cell_length = None + @property def inner_radius(self): return self._inner_radius + @inner_radius.setter + def inner_radius(self, inner_radius): + self._inner_radius = float(inner_radius) + self._limits = None + @property def limits(self): if self._limits is None: @@ -692,6 +705,10 @@ class _SphericalShell(_Container): self._limits = [[r_min], [r_max]] return self._limits + @limits.setter + def limits(self, limits): + self._limits = limits + @property def cell_length(self): if self._cell_length is None: @@ -704,21 +721,6 @@ class _SphericalShell(_Container): def volume(self): return _volume_sphere(self.radius) - _volume_sphere(self.inner_radius) - @radius.setter - def radius(self, radius): - self._radius = float(radius) - self._limits = None - self._cell_length = None - - @inner_radius.setter - def inner_radius(self, inner_radius): - self._inner_radius = float(inner_radius) - self._limits = None - - @limits.setter - def limits(self, limits): - self._limits = limits - @classmethod def from_region(self, region, sphere_radius): check_type('region', region, openmc.Region) @@ -817,6 +819,11 @@ def create_triso_lattice(trisos, lower_left, pitch, shape, background, virtual=F background : openmc.Material A background material that is used anywhere within the lattice but outside a TRISO particle + virtual : bool + If True, create a virtual lattice where each cell is repeated + according to the pitch and shape. This is useful for creating a + lattice with a very large number of elements. + Default is False. Returns ------- @@ -1221,7 +1228,7 @@ def _close_random_pack(domain, spheres, contraction_rate): def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, - contraction_rate=1.e-3, seed=1): + contraction_rate=1.e-3, seed=None): """Generate a random, non-overlapping configuration of spheres within a container. @@ -1250,7 +1257,7 @@ def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, reached using a smaller contraction rate, but the algorithm will take longer to converge. seed : int, optional - RNG seed. + Pseudorandom number generator seed passed to :func:`random.seed` Returns ------ @@ -1293,7 +1300,8 @@ def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, """ # Seed RNG - random.seed(seed) + if seed is not None: + random.seed(seed) # Create container with the correct shape based on the supplied region domain = None @@ -1325,14 +1333,13 @@ def pack_spheres(radius, region, pf=None, num_spheres=None, initial_pf=0.3, # Check packing fraction for close random packing if pf > MAX_PF_CRP: - raise ValueError('Packing fraction {0} is greater than the limit for ' - 'close random packing, {1}'.format(pf, MAX_PF_CRP)) + raise ValueError(f'Packing fraction {pf} is greater than the limit for ' + f'close random packing, {MAX_PF_CRP}') # Check packing fraction for random sequential packing if initial_pf > MAX_PF_RSP: - raise ValueError('Initial packing fraction {0} is greater than the ' - 'limit for random sequential packing, ' - '{1}'.format(initial_pf, MAX_PF_RSP)) + raise ValueError(f'Initial packing fraction {initial_pf} is greater than' + f'the limit for random sequential packing, {MAX_PF_RSP}') # Calculate the sphere radius used in the initial random sequential # packing from the initial packing fraction diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 196c8d7701..d5ae4bddbb 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -26,7 +26,7 @@ class Nuclide(str): if name.endswith('m'): name = name[:-1] + '_m1' - msg = ('OpenMC nuclides follow the GND naming convention. ' + msg = ('OpenMC nuclides follow the GNDS naming convention. ' f'Nuclide "{orig_name}" is being renamed as "{name}".') warnings.warn(msg) diff --git a/openmc/plots.py b/openmc/plots.py index 3599a32742..e9130f0bea 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -1,17 +1,22 @@ from collections.abc import Iterable, Mapping -from numbers import Real, Integral +from numbers import Integral, Real from pathlib import Path -from xml.etree import ElementTree as ET +from textwrap import dedent +import h5py +import lxml.etree as ET import numpy as np import openmc import openmc.checkvalue as cv -from ._xml import clean_indentation, reorder_attributes +from openmc.checkvalue import PathLike + +from ._xml import clean_indentation, get_elem_list, get_text from .mixin import IDManagerMixin +_BASES = {'xy', 'xz', 'yz'} -_BASES = ['xy', 'xz', 'yz'] +_BASIS_INDICES = {'xy': (0, 1, 2), 'xz': (0, 2, 1), 'yz': (1, 2, 0)} _SVG_COLORS = { 'aliceblue': (240, 248, 255), @@ -163,24 +168,469 @@ _SVG_COLORS = { 'yellowgreen': (154, 205, 50) } +_PLOT_PARAMS = dedent("""\ -def _get_plot_image(plot): + Parameters + ---------- + origin : iterable of float + Coordinates at the origin of the plot. If left as None, + the center of the bounding box will be used to attempt to ascertain + the origin with infinite values being replaced by 0. + width : iterable of float + Width of the plot in each basis direction. If left as none then the + width of the bounding box will be used to attempt to + ascertain the plot width. Defaults to (10, 10) if the bounding box + contains inf values. + pixels : Iterable of int or int + If an iterable of ints is provided then this directly sets the + number of pixels to use in each basis direction. If a single int + is provided then this sets the total number of pixels in the plot + and the number of pixels in each basis direction is calculated + from this total and the image aspect ratio based on the width + argument. + basis : {'xy', 'xz', 'yz'} + The basis directions for the plot + color_by : {'cell', 'material'} + Indicate whether the plot should be colored by cell or by material + colors : dict + Assigns colors to specific materials or cells. Keys are instances of + :class:`Cell` or :class:`Material` and values are RGB 3-tuples, RGBA + 4-tuples, or strings indicating SVG color names. Red, green, blue, + and alpha should all be floats in the range [0.0, 1.0], for example: + + .. code-block:: python + + # Make water blue + water = openmc.Cell(fill=h2o) + universe.plot(..., colors={water: (0., 0., 1.)) + seed : int + Seed for the random number generator + openmc_exec : str + Path to OpenMC executable. + axes : matplotlib.Axes + Axes to draw to + + .. versionadded:: 0.13.1 + legend : bool + Whether a legend showing material or cell names should be drawn + + .. versionadded:: 0.14.0 + axis_units : {'km', 'm', 'cm', 'mm'} + Units used on the plot axis + + .. versionadded:: 0.14.0 + outline : bool or str + Whether outlines between color boundaries should be drawn. If set to + 'only', only outlines will be drawn. + + .. versionadded:: 0.14.0 + show_overlaps: bool + Indicate whether or not overlapping regions are shown. + Default is False. + overlap_color: Iterable of int or str + Color to apply to overlapping regions. Default is red. + n_samples : int, optional + The number of source particles to sample and add to plot. Defaults + to None which doesn't plot any particles on the plot. + plane_tolerance: float + When plotting a plane the source locations within the plane +/- + the plane_tolerance will be included and those outside of the + plane_tolerance will not be shown + legend_kwargs : dict + Keyword arguments passed to :func:`matplotlib.pyplot.legend`. + + .. versionadded:: 0.14.0 + source_kwargs : dict, optional + Keyword arguments passed to :func:`matplotlib.pyplot.scatter`. + contour_kwargs : dict, optional + Keyword arguments passed to :func:`matplotlib.pyplot.contour`. + **kwargs + Keyword arguments passed to :func:`matplotlib.pyplot.imshow`. + + Returns + ------- + matplotlib.axes.Axes + Axes containing resulting image +""") + + +# Decorator for consistently adding plot parameters to docstrings (Model.plot, +# Geometry.plot, Universe.plot, etc.) +def add_plot_params(func): + func.__doc__ += _PLOT_PARAMS + return func + + +def _get_plot_image(plot, cwd): from IPython.display import Image # Make sure .png file was created - stem = plot.filename if plot.filename is not None else f'plot_{plot.id}' - png_file = f'{stem}.png' - if not Path(png_file).exists(): - raise FileNotFoundError(f"Could not find .png image for plot {plot.id}") + png_filename = plot.filename if plot.filename is not None else f'plot_{plot.id}' - return Image(png_file) + # Add file extension if not already present. The C++ code added it + # automatically if it wasn't present. + if Path(png_filename).suffix != ".png": + png_filename += ".png" + + png_file = Path(cwd) / png_filename + if not png_file.exists(): + raise FileNotFoundError( + f"Could not find .png image for plot {plot.id}. Your version of " + "OpenMC may not be built against libpng.") + + return Image(str(png_file)) -class Plot(IDManagerMixin): +def voxel_to_vtk(voxel_file: PathLike, output: PathLike = 'plot.vti'): + """Converts a voxel HDF5 file to a VTK file + + .. versionadded:: 0.14.0 + + Parameters + ---------- + voxel_file : path-like + Path of the input h5 to convert + output : path-like + Path of the output vti file produced + + Returns + ------- + Path + Path of the .vti file produced + """ + + # imported vtk only if used as vtk is an option dependency + import vtk + + _min_version = (2, 0) + + # Read data from voxel file + with h5py.File(voxel_file, "r") as fh: + # check version + version = tuple(fh.attrs["version"]) + if version < _min_version: + old_version = ".".join(map(str, version)) + min_version = ".".join(map(str, _min_version)) + err_msg = ( + f"This voxel file's version is {old_version}. This function only " + f" supports voxel files with version {min_version} or higher. " + "Please generate a new voxel file using a newer version of OpenMC." + ) + raise ValueError(err_msg) + + dimension = fh.attrs["num_voxels"] + width = fh.attrs["voxel_width"] + lower_left = fh.attrs["lower_left"] + + nx, ny, nz = dimension + + grid = vtk.vtkImageData() + grid.SetDimensions(nx + 1, ny + 1, nz + 1) + grid.SetOrigin(*lower_left) + grid.SetSpacing(*width) + + # transpose data from OpenMC ordering (zyx) to VTK ordering (xyz) + # and flatten to 1-D array + h5data = fh["data"][...] + + data = vtk.vtkIntArray() + data.SetName("id") + # set the array using the h5data array + data.SetArray(h5data, h5data.size, True) + # add data to image grid + grid.GetCellData().AddArray(data) + + writer = vtk.vtkXMLImageDataWriter() + if vtk.vtkVersion.GetVTKMajorVersion() > 5: + writer.SetInputData(grid) + else: + writer.SetInput(grid) + output = str(output) + if not output.endswith(".vti"): + output += ".vti" + writer.SetFileName(output) + writer.Write() + + return output + + +class PlotBase(IDManagerMixin): + """ + Parameters + ---------- + plot_id : int + Unique identifier for the plot + name : str + Name of the plot + + Attributes + ---------- + id : int + Unique identifier + name : str + Name of the plot + pixels : Iterable of int + Number of pixels to use in each direction + filename : str + Path to write the plot to + color_by : {'cell', 'material'} + Indicate whether the plot should be colored by cell or by material + background : Iterable of int or str + Color of the background + mask_components : Iterable of openmc.Cell or openmc.Material or int + The cells or materials (or corresponding IDs) to mask + mask_background : Iterable of int or str + Color to apply to all cells/materials listed in mask_components + show_overlaps : bool + Indicate whether or not overlapping regions are shown + overlap_color : Iterable of int or str + Color to apply to overlapping regions + colors : dict + Dictionary indicating that certain cells/materials should be + displayed with a particular color. The keys can be of type + :class:`~openmc.Cell`, :class:`~openmc.Material`, or int (ID for a + cell/material). + level : int + Universe depth to plot at + """ + + next_id = 1 + used_ids = set() + + def __init__(self, plot_id=None, name=''): + # Initialize Plot class attributes + self.id = plot_id + self.name = name + self._pixels = [400, 400] + self._filename = None + self._color_by = 'cell' + self._background = None + self._mask_components = None + self._mask_background = None + self._show_overlaps = False + self._overlap_color = None + self._colors = {} + self._level = None + + @property + def name(self): + return self._name + + @name.setter + def name(self, name): + cv.check_type('plot name', name, str) + self._name = name + + @property + def pixels(self): + return self._pixels + + @pixels.setter + def pixels(self, pixels): + cv.check_type('plot pixels', pixels, Iterable, Integral) + cv.check_length('plot pixels', pixels, 2, 3) + for dim in pixels: + cv.check_greater_than('plot pixels', dim, 0) + self._pixels = pixels + + @property + def filename(self): + return self._filename + + @filename.setter + def filename(self, filename): + cv.check_type('filename', filename, str) + self._filename = filename + + @property + def color_by(self): + return self._color_by + + @color_by.setter + def color_by(self, color_by): + cv.check_value('plot color_by', color_by, ['cell', 'material']) + self._color_by = color_by + + @property + def background(self): + return self._background + + @background.setter + def background(self, background): + self._check_color('plot background', background) + self._background = background + + @property + def mask_components(self): + return self._mask_components + + @mask_components.setter + def mask_components(self, mask_components): + cv.check_type('plot mask components', mask_components, Iterable, + (openmc.Cell, openmc.Material, Integral)) + self._mask_components = mask_components + + @property + def mask_background(self): + return self._mask_background + + @mask_background.setter + def mask_background(self, mask_background): + self._check_color('plot mask background', mask_background) + self._mask_background = mask_background + + @property + def show_overlaps(self): + return self._show_overlaps + + @show_overlaps.setter + def show_overlaps(self, show_overlaps): + cv.check_type(f'Show overlaps flag for Plot ID="{self.id}"', + show_overlaps, bool) + self._show_overlaps = show_overlaps + + @property + def overlap_color(self): + return self._overlap_color + + @overlap_color.setter + def overlap_color(self, overlap_color): + self._check_color('plot overlap color', overlap_color) + self._overlap_color = overlap_color + + @property + def colors(self): + return self._colors + + @colors.setter + def colors(self, colors): + cv.check_type('plot colors', colors, Mapping) + for key, value in colors.items(): + cv.check_type('plot color key', key, + (openmc.Cell, openmc.Material, Integral)) + self._check_color('plot color value', value) + self._colors = colors + + @property + def level(self): + return self._level + + @level.setter + def level(self, plot_level): + cv.check_type('plot level', plot_level, Integral) + cv.check_greater_than('plot level', plot_level, 0, equality=True) + self._level = plot_level + + @staticmethod + def _check_color(err_string, color): + cv.check_type(err_string, color, Iterable) + if isinstance(color, str): + if color.lower() not in _SVG_COLORS: + raise ValueError(f"'{color}' is not a valid color.") + else: + cv.check_length(err_string, color, 3) + for rgb in color: + cv.check_type(err_string, rgb, Real) + cv.check_greater_than('RGB component', rgb, 0, True) + cv.check_less_than('RGB component', rgb, 256) + + # Helper function that returns the domain ID given either a + # Cell/Material object or the domain ID itself + @staticmethod + def _get_id(domain): + return domain if isinstance(domain, Integral) else domain.id + + def colorize(self, geometry, seed=1): + """Generate a color scheme for each domain in the plot. + + This routine may be used to generate random, reproducible color schemes. + The colors generated are based upon cell/material IDs in the geometry. + + Parameters + ---------- + geometry : openmc.Geometry + The geometry for which the plot is defined + seed : Integral + The random number seed used to generate the color scheme + + """ + + cv.check_type('geometry', geometry, openmc.Geometry) + cv.check_type('seed', seed, Integral) + cv.check_greater_than('seed', seed, 1, equality=True) + + # Get collections of the domains which will be plotted + if self.color_by == 'material': + domains = geometry.get_all_materials().values() + else: + domains = geometry.get_all_cells().values() + + rng = np.random.RandomState(seed) + + # Generate random colors for each feature + for domain in domains: + self.colors[domain] = rng.randint(0, 256, (3,)) + + def _colors_to_xml(self, element): + for domain, color in sorted(self._colors.items(), + key=lambda x: self._get_id(x[0])): + subelement = ET.SubElement(element, "color") + subelement.set("id", str(self._get_id(domain))) + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement.set("rgb", ' '.join(str(x) for x in color)) + + def to_xml_element(self): + """Save common plot attributes to XML element + + Returns + ------- + element : lxml.etree._Element + XML element containing plot data + + """ + + element = ET.Element("plot") + element.set("id", str(self._id)) + if len(self._name) > 0: + element.set("name", str(self.name)) + if self._filename is not None: + element.set("filename", self._filename) + element.set("color_by", self._color_by) + + subelement = ET.SubElement(element, "pixels") + subelement.text = ' '.join(map(str, self._pixels)) + + if self._background is not None: + subelement = ET.SubElement(element, "background") + color = self._background + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement.text = ' '.join(str(x) for x in color) + + if self._mask_components is not None: + subelement = ET.SubElement(element, "mask") + subelement.set("components", ' '.join( + str(PlotBase._get_id(d)) for d in self._mask_components)) + color = self._mask_background + if color is not None: + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement.set("background", ' '.join( + str(x) for x in color)) + + if self._level is not None: + subelement = ET.SubElement(element, "level") + subelement.text = str(self._level) + + return element + + +class Plot(PlotBase): """Definition of a finite region of space to be plotted. - OpenMC is capable of generating two-dimensional slice plots and - three-dimensional voxel plots. Colors that are used in plots can be given as + OpenMC is capable of generating two-dimensional slice plots, or + three-dimensional voxel or projection plots. Colors that are used in plots can be given as RGB tuples, e.g. (255, 255, 255) would be white, or by a string indicating a valid `SVG color `_. @@ -197,225 +647,103 @@ class Plot(IDManagerMixin): Unique identifier name : str Name of the plot - width : Iterable of float - Width of the plot in each basis direction pixels : Iterable of int - Number of pixels to use in each basis direction - origin : tuple or list of ndarray - Origin (center) of the plot - filename : + Number of pixels to use in each direction + filename : str Path to write the plot to color_by : {'cell', 'material'} Indicate whether the plot should be colored by cell or by material - type : {'slice', 'voxel'} - The type of the plot - basis : {'xy', 'xz', 'yz'} - The basis directions for the plot background : Iterable of int or str Color of the background - mask_components : Iterable of openmc.Cell or openmc.Material - The cells or materials to plot + mask_components : Iterable of openmc.Cell or openmc.Material or int + The cells or materials (or corresponding IDs) to mask mask_background : Iterable of int or str - Color to apply to all cells/materials not listed in mask_components + Color to apply to all cells/materials listed in mask_components show_overlaps : bool Indicate whether or not overlapping regions are shown overlap_color : Iterable of int or str Color to apply to overlapping regions colors : dict - Dictionary indicating that certain cells/materials (keys) should be - displayed with a particular color. + Dictionary indicating that certain cells/materials should be + displayed with a particular color. The keys can be of type + :class:`~openmc.Cell`, :class:`~openmc.Material`, or int (ID for a + cell/material). level : int Universe depth to plot at + width : Iterable of float + Width of the plot in each basis direction + origin : tuple or list of ndarray + Origin (center) of the plot + type : {'slice', 'voxel'} + The type of the plot + basis : {'xy', 'xz', 'yz'} + The basis directions for the plot meshlines : dict Dictionary defining type, id, linewidth and color of a mesh to be plotted on top of a plot """ - next_id = 1 - used_ids = set() - def __init__(self, plot_id=None, name=''): - # Initialize Plot class attributes - self.id = plot_id - self.name = name + super().__init__(plot_id, name) self._width = [4.0, 4.0] - self._pixels = [400, 400] self._origin = [0., 0., 0.] - self._filename = None - self._color_by = 'cell' self._type = 'slice' self._basis = 'xy' - self._background = None - self._mask_components = None - self._mask_background = None - self._show_overlaps = False - self._overlap_color = None - self._colors = {} - self._level = None self._meshlines = None - @property - def name(self): - return self._name - @property def width(self): return self._width - @property - def pixels(self): - return self._pixels - - @property - def origin(self): - return self._origin - - @property - def filename(self): - return self._filename - - @property - def color_by(self): - return self._color_by - - @property - def type(self): - return self._type - - @property - def basis(self): - return self._basis - - @property - def background(self): - return self._background - - @property - def mask_components(self): - return self._mask_components - - @property - def mask_background(self): - return self._mask_background - - @property - def show_overlaps(self): - return self._show_overlaps - - @property - def overlap_color(self): - return self._overlap_color - - @property - def colors(self): - return self._colors - - @property - def level(self): - return self._level - - @property - def meshlines(self): - return self._meshlines - - @name.setter - def name(self, name): - cv.check_type('plot name', name, str) - self._name = name - @width.setter def width(self, width): cv.check_type('plot width', width, Iterable, Real) cv.check_length('plot width', width, 2, 3) self._width = width + @property + def origin(self): + return self._origin + @origin.setter def origin(self, origin): cv.check_type('plot origin', origin, Iterable, Real) cv.check_length('plot origin', origin, 3) self._origin = origin - @pixels.setter - def pixels(self, pixels): - cv.check_type('plot pixels', pixels, Iterable, Integral) - cv.check_length('plot pixels', pixels, 2, 3) - for dim in pixels: - cv.check_greater_than('plot pixels', dim, 0) - self._pixels = pixels - - @filename.setter - def filename(self, filename): - cv.check_type('filename', filename, str) - self._filename = filename - - @color_by.setter - def color_by(self, color_by): - cv.check_value('plot color_by', color_by, ['cell', 'material']) - self._color_by = color_by + @property + def type(self): + return self._type @type.setter def type(self, plottype): cv.check_value('plot type', plottype, ['slice', 'voxel']) self._type = plottype + @property + def basis(self): + return self._basis + @basis.setter def basis(self, basis): cv.check_value('plot basis', basis, _BASES) self._basis = basis - @background.setter - def background(self, background): - self._check_color('plot background', background) - self._background = background - - @colors.setter - def colors(self, colors): - cv.check_type('plot colors', colors, Mapping) - for key, value in colors.items(): - cv.check_type('plot color key', key, (openmc.Cell, openmc.Material)) - self._check_color('plot color value', value) - self._colors = colors - - @mask_components.setter - def mask_components(self, mask_components): - cv.check_type('plot mask components', mask_components, Iterable, - (openmc.Cell, openmc.Material)) - self._mask_components = mask_components - - @mask_background.setter - def mask_background(self, mask_background): - self._check_color('plot mask background', mask_background) - self._mask_background = mask_background - - @show_overlaps.setter - def show_overlaps(self, show_overlaps): - cv.check_type(f'Show overlaps flag for Plot ID="{self.id}"', - show_overlaps, bool) - self._show_overlaps = show_overlaps - - @overlap_color.setter - def overlap_color(self, overlap_color): - self._check_color('plot overlap color', overlap_color) - self._overlap_color = overlap_color - - @level.setter - def level(self, plot_level): - cv.check_type('plot level', plot_level, Integral) - cv.check_greater_than('plot level', plot_level, 0, equality=True) - self._level = plot_level + @property + def meshlines(self): + return self._meshlines @meshlines.setter def meshlines(self, meshlines): cv.check_type('plot meshlines', meshlines, dict) if 'type' not in meshlines: msg = f'Unable to set the meshlines to "{meshlines}" which ' \ - 'does not have a "type" key' + 'does not have a "type" key' raise ValueError(msg) elif meshlines['type'] not in ['tally', 'entropy', 'ufs', 'cmfd']: - msg = 'Unable to set the meshlines with ' \ - 'type "{}"'.format(meshlines['type']) + msg = f"Unable to set the meshlines with type \"{meshlines['type']}\"" raise ValueError(msg) if 'id' in meshlines: @@ -424,7 +752,8 @@ class Plot(IDManagerMixin): equality=True) if 'linewidth' in meshlines: - cv.check_type('plot mesh linewidth', meshlines['linewidth'], Integral) + cv.check_type('plot mesh linewidth', + meshlines['linewidth'], Integral) cv.check_greater_than('plot mesh linewidth', meshlines['linewidth'], 0, equality=True) @@ -433,19 +762,6 @@ class Plot(IDManagerMixin): self._meshlines = meshlines - @staticmethod - def _check_color(err_string, color): - cv.check_type(err_string, color, Iterable) - if isinstance(color, str): - if color.lower() not in _SVG_COLORS: - raise ValueError(f"'{color}' is not a valid color.") - else: - cv.check_length(err_string, color, 3) - for rgb in color: - cv.check_type(err_string, rgb, Real) - cv.check_greater_than('RGB component', rgb, 0, True) - cv.check_less_than('RGB component', rgb, 256) - def __repr__(self): string = 'Plot\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) @@ -512,40 +828,9 @@ class Plot(IDManagerMixin): plot.origin = np.insert((lower_left + upper_right)/2, slice_index, slice_coord) plot.width = upper_right - lower_left + plot.basis = basis return plot - def colorize(self, geometry, seed=1): - """Generate a color scheme for each domain in the plot. - - This routine may be used to generate random, reproducible color schemes. - The colors generated are based upon cell/material IDs in the geometry. - - Parameters - ---------- - geometry : openmc.Geometry - The geometry for which the plot is defined - seed : Integral - The random number seed used to generate the color scheme - - """ - - cv.check_type('geometry', geometry, openmc.Geometry) - cv.check_type('seed', seed, Integral) - cv.check_greater_than('seed', seed, 1, equality=True) - - # Get collections of the domains which will be plotted - if self.color_by == 'material': - domains = geometry.get_all_materials().values() - else: - domains = geometry.get_all_cells().values() - - # Set the seed for the random number generator - np.random.seed(seed) - - # Generate random colors for each feature - for domain in domains: - self.colors[domain] = np.random.randint(0, 256, (3,)) - def highlight_domains(self, geometry, domains, seed=1, alpha=0.5, background='gray'): """Use alpha compositing to highlight one or more domains in the plot. @@ -598,20 +883,16 @@ class Plot(IDManagerMixin): self._colors[domain] = (r, g, b) def to_xml_element(self): - """Return XML representation of the plot + """Return XML representation of the slice/voxel plot Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing plot data """ - element = ET.Element("plot") - element.set("id", str(self._id)) - if self._filename is not None: - element.set("filename", self._filename) - element.set("color_by", self._color_by) + element = super().to_xml_element() element.set("type", self._type) if self._type == 'slice': @@ -623,35 +904,8 @@ class Plot(IDManagerMixin): subelement = ET.SubElement(element, "width") subelement.text = ' '.join(map(str, self._width)) - subelement = ET.SubElement(element, "pixels") - subelement.text = ' '.join(map(str, self._pixels)) - - if self._background is not None: - subelement = ET.SubElement(element, "background") - color = self._background - if isinstance(color, str): - color = _SVG_COLORS[color.lower()] - subelement.text = ' '.join(str(x) for x in color) - if self._colors: - for domain, color in sorted(self._colors.items(), - key=lambda x: x[0].id): - subelement = ET.SubElement(element, "color") - subelement.set("id", str(domain.id)) - if isinstance(color, str): - color = _SVG_COLORS[color.lower()] - subelement.set("rgb", ' '.join(str(x) for x in color)) - - if self._mask_components is not None: - subelement = ET.SubElement(element, "mask") - subelement.set("components", ' '.join( - str(d.id) for d in self._mask_components)) - color = self._mask_background - if color is not None: - if isinstance(color, str): - color = _SVG_COLORS[color.lower()] - subelement.set("background", ' '.join( - str(x) for x in color)) + self._colors_to_xml(element) if self._show_overlaps: subelement = ET.SubElement(element, "show_overlaps") @@ -664,19 +918,14 @@ class Plot(IDManagerMixin): subelement = ET.SubElement(element, "overlap_color") subelement.text = ' '.join(str(x) for x in color) - - if self._level is not None: - subelement = ET.SubElement(element, "level") - subelement.text = str(self._level) - if self._meshlines is not None: subelement = ET.SubElement(element, "meshlines") subelement.set("meshtype", self._meshlines['type']) - if self._meshlines['id'] is not None: + if 'id' in self._meshlines: subelement.set("id", str(self._meshlines['id'])) - if self._meshlines['linewidth'] is not None: + if 'linewidth' in self._meshlines: subelement.set("linewidth", str(self._meshlines['linewidth'])) - if self._meshlines['color'] is not None: + if 'color' in self._meshlines: subelement.set("color", ' '.join(map( str, self._meshlines['color']))) @@ -688,7 +937,7 @@ class Plot(IDManagerMixin): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -697,66 +946,61 @@ class Plot(IDManagerMixin): Plot object """ - plot_id = int(elem.get("id")) - plot = cls(plot_id) + plot_id = int(get_text(elem, "id")) + name = get_text(elem, 'name', '') + plot = cls(plot_id, name) if "filename" in elem.keys(): - plot.filename = elem.get("filename") - plot.color_by = elem.get("color_by") - plot.type = elem.get("type") - plot.basis = elem.get("basis") + plot.filename = get_text(elem, "filename") + plot.color_by = get_text(elem, "color_by") + plot.type = get_text(elem, "type") + if plot.type == 'slice': + plot.basis = get_text(elem, "basis") - # Helper function to get a tuple of values - def get_tuple(elem, name, dtype=int): - subelem = elem.find(name) - if subelem is not None: - return tuple([dtype(x) for x in subelem.text.split()]) - - plot.origin = get_tuple(elem, "origin", float) - plot.width = get_tuple(elem, "width", float) - plot.pixels = get_tuple(elem, "pixels") - plot._background = get_tuple(elem, "background") + plot.origin = tuple(get_elem_list(elem, "origin", float)) + plot.width = tuple(get_elem_list(elem, "width", float)) + plot.pixels = tuple(get_elem_list(elem, "pixels")) + background = get_elem_list(elem, "background") + if background is not None: + plot._background = tuple(background) # Set plot colors colors = {} for color_elem in elem.findall("color"): - uid = color_elem.get("id") - colors[uid] = tuple([int(x) for x in color_elem.get("rgb").split()]) - # TODO: set colors (needs geometry information) + uid = int(get_text(color_elem, "id")) + colors[uid] = tuple(get_elem_list(color_elem, "rgb", int)) + plot.colors = colors # Set masking information mask_elem = elem.find("mask") if mask_elem is not None: - mask_components = [int(x) for x in mask_elem.get("components").split()] - # TODO: set mask components (needs geometry information) - background = mask_elem.get("background") + plot.mask_components = get_elem_list(mask_elem, "components", int) + background = get_elem_list(mask_elem, "background", int) if background is not None: - plot.mask_background = tuple([int(x) for x in background.split()]) + plot.mask_background = tuple(background) # show overlaps - overlap_elem = elem.find("show_overlaps") - if overlap_elem is not None: - plot.show_overlaps = (overlap_elem.text in ('true', '1')) - overlap_color = get_tuple(elem, "overlap_color") + overlap = get_text(elem, "show_overlaps") + if overlap is not None: + plot.show_overlaps = (overlap in ('true', '1')) + overlap_color = get_elem_list(elem, "overlap_color", int) if overlap_color is not None: - plot.overlap_color = overlap_color + plot.overlap_color = tuple(overlap_color) # Set universe level - level = elem.find("level") + level = get_text(elem, "level") if level is not None: - plot.level = int(level.text) + plot.level = int(level) # Set meshlines mesh_elem = elem.find("meshlines") if mesh_elem is not None: - meshlines = {'type': mesh_elem.get('meshtype')} + meshlines = {'type': get_text(mesh_elem, "meshtype")} if 'id' in mesh_elem.keys(): - meshlines['id'] = int(mesh_elem.get('id')) + meshlines['id'] = int(get_text(mesh_elem, "id")) if 'linewidth' in mesh_elem.keys(): - meshlines['linewidth'] = int(mesh_elem.get('linewidth')) + meshlines['linewidth'] = int(get_text(mesh_elem, "linewidth")) if 'color' in mesh_elem.keys(): - meshlines['color'] = tuple( - [int(x) for x in mesh_elem.get('color').split()] - ) + meshlines['color'] = tuple(get_elem_list(mesh_elem, "color", int)) plot.meshlines = meshlines return plot @@ -784,21 +1028,721 @@ class Plot(IDManagerMixin): """ # Create plots.xml - Plots([self]).export_to_xml() + Plots([self]).export_to_xml(cwd) # Run OpenMC in geometry plotting mode openmc.plot_geometry(False, openmc_exec, cwd) # Return produced image - return _get_plot_image(self) + return _get_plot_image(self, cwd) + + def to_vtk(self, output: PathLike | None = None, + openmc_exec: str = 'openmc', cwd: str = '.'): + """Render plot as an voxel image + + This method runs OpenMC in plotting mode to produce a .vti file. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + output : path-like + Path of the output .vti file produced + openmc_exec : str + Path to OpenMC executable + cwd : str, optional + Path to working directory to run in + + Returns + ------- + Path + Path of the .vti file produced + + """ + if self.type != 'voxel': + raise ValueError( + 'Generating a VTK file only works for voxel plots') + + # Create plots.xml + Plots([self]).export_to_xml(cwd) + + # Run OpenMC in geometry plotting mode and produces a h5 file + openmc.plot_geometry(False, openmc_exec, cwd) + + h5_voxel_filename = self.filename if self.filename is not None else f'plot_{self.id}' + + # Add file extension if not already present + if Path(h5_voxel_filename).suffix != ".h5": + h5_voxel_filename += ".h5" + + h5_voxel_file = Path(cwd) / h5_voxel_filename + if output is None: + output = h5_voxel_file.with_suffix('.vti') + + return voxel_to_vtk(h5_voxel_file, output) + + +class RayTracePlot(PlotBase): + """Definition of a camera's view of OpenMC geometry + + The camera projection may either by orthographic or perspective. Perspective + projections are more similar to a pinhole camera, and orthographic + projections preserve parallel lines and distances. + + This is an abstract base class that :class:`WireframeRayTracePlot` and + :class:`SolidRayTracePlot` finish the implementation of. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + plot_id : int + Unique identifier for the plot + name : str + Name of the plot + + Attributes + ---------- + horizontal_field_of_view : float + Field of view horizontally, in units of degrees, defaults to 70. + camera_position : tuple or list of ndarray + Position of the camera in 3D space. Defaults to (1, 0, 0). + look_at : tuple or list of ndarray + The center of the camera's image points to this place in 3D space. + Set to (0, 0, 0) by default. + up : tuple or list of ndarray + Which way is up for the camera. Must not be parallel to the + line between look_at and camera_position. Set to (0, 0, 1) by default. + orthographic_width : float + If set to a nonzero value, an orthographic projection is used. + All rays traced from the orthographic pixel array travel in the + same direction. The width of the starting array must be specified, + unlike with the default perspective projection. The height of the + array is deduced from the ratio of pixel dimensions for the image. + Defaults to zero, i.e. using perspective projection. + """ + + def __init__(self, plot_id=None, name=''): + # Initialize Plot class attributes + super().__init__(plot_id, name) + self._horizontal_field_of_view = 70.0 + self._camera_position = (1.0, 0.0, 0.0) + self._look_at = (0.0, 0.0, 0.0) + self._up = (0.0, 0.0, 1.0) + self._orthographic_width = 0.0 + + @property + def horizontal_field_of_view(self): + return self._horizontal_field_of_view + + @horizontal_field_of_view.setter + def horizontal_field_of_view(self, horizontal_field_of_view): + cv.check_type('plot horizontal field of view', horizontal_field_of_view, + Real) + assert horizontal_field_of_view > 0.0 + assert horizontal_field_of_view < 180.0 + self._horizontal_field_of_view = horizontal_field_of_view + + @property + def camera_position(self): + return self._camera_position + + @camera_position.setter + def camera_position(self, camera_position): + cv.check_type('plot camera position', camera_position, Iterable, Real) + cv.check_length('plot camera position', camera_position, 3) + self._camera_position = camera_position + + @property + def look_at(self): + return self._look_at + + @look_at.setter + def look_at(self, look_at): + cv.check_type('plot look at', look_at, Iterable, Real) + cv.check_length('plot look at', look_at, 3) + self._look_at = look_at + + @property + def up(self): + return self._up + + @up.setter + def up(self, up): + cv.check_type('plot up', up, Iterable, Real) + cv.check_length('plot up', up, 3) + self._up = up + + @property + def orthographic_width(self): + return self._orthographic_width + + @orthographic_width.setter + def orthographic_width(self, orthographic_width): + cv.check_type('plot orthographic width', orthographic_width, Real) + assert orthographic_width >= 0.0 + self._orthographic_width = orthographic_width + + def _check_domains_consistent_with_color_by(self, domains): + """Check domains are the same as the type we are coloring by""" + for region in domains: + # if an integer is passed, we have to assume it was a valid ID + if isinstance(region, int): + continue + + if self._color_by == 'material': + if not isinstance(region, openmc.Material): + raise Exception('Domain list must be materials if ' + 'color_by=material') + else: + if not isinstance(region, openmc.Cell): + raise Exception('Domain list must be cells if ' + 'color_by=cell') + + def to_xml_element(self): + """Return XML representation of the ray trace plot + + Returns + ------- + element : lxml.etree._Element + XML element containing plot data + + """ + + element = super().to_xml_element() + element.set("id", str(self._id)) + + subelement = ET.SubElement(element, "camera_position") + subelement.text = ' '.join(map(str, self._camera_position)) + + subelement = ET.SubElement(element, "look_at") + subelement.text = ' '.join(map(str, self._look_at)) + + subelement = ET.SubElement(element, "horizontal_field_of_view") + subelement.text = str(self._horizontal_field_of_view) + + # do not need to write if orthographic_width == 0.0 + if self._orthographic_width > 0.0: + subelement = ET.SubElement(element, "orthographic_width") + subelement.text = str(self._orthographic_width) + + return element + + def __repr__(self): + string = '' + string += '{: <16}=\t{}\n'.format('\tID', self._id) + string += '{: <16}=\t{}\n'.format('\tName', self._name) + string += '{: <16}=\t{}\n'.format('\tFilename', self._filename) + string += '{: <16}=\t{}\n'.format('\tHorizontal FOV', + self._horizontal_field_of_view) + string += '{: <16}=\t{}\n'.format('\tOrthographic width', + self._orthographic_width) + string += '{: <16}=\t{}\n'.format('\tCamera position', + self._camera_position) + string += '{: <16}=\t{}\n'.format('\tLook at', self._look_at) + string += '{: <16}=\t{}\n'.format('\tUp', self._up) + string += '{: <16}=\t{}\n'.format('\tPixels', self._pixels) + string += '{: <16}=\t{}\n'.format('\tColor by', self._color_by) + string += '{: <16}=\t{}\n'.format('\tBackground', self._background) + string += '{: <16}=\t{}\n'.format('\tColors', self._colors) + string += '{: <16}=\t{}\n'.format('\tLevel', self._level) + return string + + def _read_xml_attributes(self, elem): + """Helper function called by from_xml_element + of child classes. These are common vaues to be + read by any ray traced plot. + + Returns + ------- + None + """ + + filename = get_text(elem, "filename") + if filename is not None: + self.filename = filename + self.color_by = get_text(elem, "color_by") + + horizontal_fov = get_text(elem, "horizontal_field_of_view") + if horizontal_fov is not None: + self.horizontal_field_of_view = float(horizontal_fov) + + orthographic_width = get_text(elem, "orthographic_width") + if orthographic_width is not None: + self.orthographic_width = float(orthographic_width) + + self.pixels = tuple(get_elem_list(elem, "pixels", int)) + self.camera_position = tuple(get_elem_list(elem, "camera_position", float)) + self.look_at = tuple(get_elem_list(elem, "look_at", float)) + + background = get_elem_list(elem, "background", int) + if background is not None: + self.background = tuple(background) + + # Set masking information + if (mask_elem := elem.find("mask")) is not None: + mask_components = get_elem_list(mask_elem, "components", int) + # TODO: set mask components(needs geometry information) + background = get_elem_list(mask_elem, "background", int) + if background is not None: + self.mask_background = tuple(background) + + # Set universe level + level = get_text(elem, "level") + if level is not None: + self.level = int(level) + + +class WireframeRayTracePlot(RayTracePlot): + """Plots wireframes of geometry with volume rendered colors + + Colors are defined in the same manner as the Plot class, but with the + addition of a coloring parameter resembling a macroscopic cross section in + units of inverse centimeters. The volume rendering technique is used to + color regions of the model. An infinite cross section denotes a fully opaque + region, and zero represents a transparent region which will expose the color + of the regions behind it. + + .. versionchanged:: 0.15.1 + Renamed from ProjectionPlot to WireframeRayTracePlot + + Parameters + ---------- + plot_id : int + Unique identifier for the plot + name : str + Name of the plot + + Attributes + ---------- + id : int + Unique identifier + name : str + Name of the plot + pixels : Iterable of int + Number of pixels to use in each direction + filename : str + Path to write the plot to + color_by : {'cell', 'material'} + Indicate whether the plot should be colored by cell or by material + background : Iterable of int or str + Color of the background + mask_components : Iterable of openmc.Cell or openmc.Material or int + The cells or materials (or corresponding IDs) to mask + mask_background : Iterable of int or str + Color to apply to all cells/materials listed in mask_components + show_overlaps : bool + Indicate whether or not overlapping regions are shown + overlap_color : Iterable of int or str + Color to apply to overlapping regions + colors : dict + Dictionary indicating that certain cells/materials should be + displayed with a particular color. The keys can be of type + :class:`~openmc.Cell`, :class:`~openmc.Material`, or int (ID for a + cell/material). + level : int + Universe depth to plot at + horizontal_field_of_view : float + Field of view horizontally, in units of degrees, defaults to 70. + camera_position : tuple or list of ndarray + Position of the camera in 3D space. Defaults to (1, 0, 0). + look_at : tuple or list of ndarray + The center of the camera's image points to this place in 3D space. + Set to (0, 0, 0) by default. + up : tuple or list of ndarray + Which way is up for the camera. Must not be parallel to the + line between look_at and camera_position. Set to (0, 0, 1) by default. + orthographic_width : float + If set to a nonzero value, an orthographic projection is used. + All rays traced from the orthographic pixel array travel in the + same direction. The width of the starting array must be specified, + unlike with the default perspective projection. The height of the + array is deduced from the ratio of pixel dimensions for the image. + Defaults to zero, i.e. using perspective projection. + wireframe_thickness : int + Line thickness employed for drawing wireframes around cells or material + regions. Can be set to zero for no wireframes at all. Defaults to one + pixel. + wireframe_color : tuple of ints + RGB color of the wireframe lines. Defaults to black. + wireframe_domains : iterable of either Material or Cells + If provided, the wireframe is only drawn around these. If color_by is by + material, it must be a list of materials, else cells. + xs : dict + A mapping from cell/material IDs to floats. The floating point values + are macroscopic cross sections influencing the volume rendering opacity + of each geometric region. Zero corresponds to perfect transparency, and + infinity equivalent to opaque. These must be set by the user, but + default values can be obtained using the :meth:`set_transparent` method. + """ + + def __init__(self, plot_id=None, name=''): + super().__init__(plot_id, name) + self._wireframe_thickness = 1 + self._wireframe_color = _SVG_COLORS['black'] + self._wireframe_domains = [] + self._xs = {} + + @property + def wireframe_thickness(self): + return self._wireframe_thickness + + @wireframe_thickness.setter + def wireframe_thickness(self, wireframe_thickness): + cv.check_type('plot wireframe thickness', + wireframe_thickness, Integral) + assert wireframe_thickness >= 0 + self._wireframe_thickness = wireframe_thickness + + @property + def wireframe_color(self): + return self._wireframe_color + + @wireframe_color.setter + def wireframe_color(self, wireframe_color): + self._check_color('plot wireframe color', wireframe_color) + self._wireframe_color = wireframe_color + + @property + def wireframe_domains(self): + return self._wireframe_domains + + @wireframe_domains.setter + def wireframe_domains(self, wireframe_domains): + self._wireframe_domains = wireframe_domains + + @property + def xs(self): + return self._xs + + @xs.setter + def xs(self, xs): + cv.check_type('plot xs', xs, Mapping) + for key, value in xs.items(): + cv.check_type('plot xs key', key, (openmc.Cell, openmc.Material)) + cv.check_type('plot xs value', value, Real) + assert value >= 0.0 + self._xs = xs + + def set_transparent(self, geometry): + """Sets all volume rendering XS to zero for the model + + Parameters + ---------- + geometry : openmc.Geometry + The geometry for which the plot is defined + """ + + cv.check_type('geometry', geometry, openmc.Geometry) + + # Get collections of the domains which will be plotted + if self.color_by == 'material': + domains = geometry.get_all_materials().values() + else: + domains = geometry.get_all_cells().values() + + # Generate random colors for each feature + for domain in domains: + self.xs[domain] = 0.0 + + def __repr__(self): + string = 'Wireframe Ray-traced Plot\n' + string += super().__repr__() + string += '{: <16}=\t{}\n'.format('\tWireframe thickness', + self._wireframe_thickness) + string += '{: <16}=\t{}\n'.format('\tWireframe color', + self._wireframe_color) + string += '{: <16}=\t{}\n'.format('\tWireframe domains', + self._wireframe_domains) + string += '{: <16}=\t{}\n'.format('\tTransparencies', self._xs) + return string + + def to_xml_element(self): + """Return XML representation of the projection plot + + Returns + ------- + element : lxml.etree._Element + XML element containing plot data + + """ + element = super().to_xml_element() + element.set("type", "wireframe_raytrace") + + subelement = ET.SubElement(element, "wireframe_thickness") + subelement.text = str(self._wireframe_thickness) + + subelement = ET.SubElement(element, "wireframe_color") + color = self._wireframe_color + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement.text = ' '.join(str(x) for x in color) + + self._check_domains_consistent_with_color_by(self.wireframe_domains) + + if self._wireframe_domains: + id_list = [x.id for x in self._wireframe_domains] + subelement = ET.SubElement(element, "wireframe_ids") + subelement.text = ' '.join([str(x) for x in id_list]) + + # note that this differs from the slice plot colors + # in that "xs" must also be specified + if self._colors: + for domain, color in sorted(self._colors.items(), + key=lambda x: x[0].id): + subelement = ET.SubElement(element, "color") + subelement.set("id", str(domain.id)) + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement.set("rgb", ' '.join(str(x) for x in color)) + subelement.set("xs", str(self._xs[domain])) + + return element + + @classmethod + def from_xml_element(cls, elem): + """Generate plot object from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.WireframeRayTracePlot + WireframeRayTracePlot object + + """ + + plot_id = int(get_text(elem, "id")) + plot_name = get_text(elem, 'name', '') + plot = cls(plot_id, plot_name) + plot.type = "wireframe_raytrace" + + plot._read_xml_attributes(elem) + + # Attempt to get wireframe thickness.May not be present + wireframe_thickness = get_text(elem, "wireframe_thickness") + if wireframe_thickness is not None: + plot.wireframe_thickness = int(wireframe_thickness) + wireframe_color = get_elem_list(elem, "wireframe_color", int) + if wireframe_color: + plot.wireframe_color = wireframe_color + + # Set plot colors + for color_elem in elem.findall("color"): + uid = int(get_text(color_elem, "id")) + plot.colors[uid] = tuple(get_elem_list(color_elem, "rgb", int)) + plot.xs[uid] = float(get_text(color_elem, "xs")) + + return plot + + +class SolidRayTracePlot(RayTracePlot): + """Phong shading-based rendering of an OpenMC geometry + + This class defines a plot that uses Phong shading to enhance the + visualization of an OpenMC geometry by incorporating diffuse lighting and + configurable opacity for certain regions. It extends :class:`RayTracePlot` + by adding parameters related to lighting and transparency. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + plot_id : int, optional + Unique identifier for the plot + name : str, optional + Name of the plot + + Attributes + ---------- + id : int + Unique identifier + name : str + Name of the plot + pixels : Iterable of int + Number of pixels to use in each direction + filename : str + Path to write the plot to + color_by : {'cell', 'material'} + Indicate whether the plot should be colored by cell or by material + overlap_color : Iterable of int or str + Color to apply to overlapping regions + colors : dict + Dictionary indicating that certain cells/materials should be + displayed with a particular color. The keys can be of type + :class:`~openmc.Cell`, :class:`~openmc.Material`, or int (ID for a + cell/material). + horizontal_field_of_view : float + Field of view horizontally, in units of degrees, defaults to 70. + camera_position : tuple or list of ndarray + Position of the camera in 3D space. Defaults to (1, 0, 0). + look_at : tuple or list of ndarray + The center of the camera's image points to this place in 3D space. + Set to (0, 0, 0) by default. + up : tuple or list of ndarray + Which way is up for the camera. Must not be parallel to the + line between look_at and camera_position. Set to (0, 0, 1) by default. + orthographic_width : float + If set to a nonzero value, an orthographic projection is used. + All rays traced from the orthographic pixel array travel in the + same direction. The width of the starting array must be specified, + unlike with the default perspective projection. The height of the + array is deduced from the ratio of pixel dimensions for the image. + Defaults to zero, i.e. using perspective projection. + light_position : tuple or list of float + Position of the light source in 3D space. Defaults to None, which places + the light at the camera position. + diffuse_fraction : float + Fraction of lighting that is diffuse (non-directional). Defaults to 0.1. + Must be between 0 and 1. + opaque_domains : list + List of domains (e.g., cells or materials) that should be rendered as + opaque rather than allowing transparency. + """ + + def __init__(self, plot_id=None, name=''): + super().__init__(plot_id, name) + self._light_position = None + self._diffuse_fraction = 0.1 + self._opaque_domains = [] + + @property + def light_position(self): + return self._light_position + + @light_position.setter + def light_position(self, x): + cv.check_type('plot light position', x, Iterable, Real) + cv.check_length('plot light position', x, 3) + self._light_position = x + + @property + def diffuse_fraction(self): + return self._diffuse_fraction + + @diffuse_fraction.setter + def diffuse_fraction(self, x): + cv.check_type('diffuse fraction', x, Real) + cv.check_greater_than('diffuse fraction', x, 0.0, equality=True) + cv.check_less_than('diffuse fraction', x, 1.0, equality=True) + self._diffuse_fraction = x + + @property + def opaque_domains(self): + return self._opaque_domains + + @opaque_domains.setter + def opaque_domains(self, x): + # Note that _check_domains_consistent_with_color_by checks + # the types within later. This is because we don't necessarily + # know what types are acceptable until the user has set the + # color_by attribute, too. + cv.check_type('opaque domains', x, Iterable) + self._opaque_domains = x + + def __repr__(self): + string = 'Solid Ray-traced Plot\n' + string += super().__repr__() + string += '{: <16}=\t{}\n'.format('\tDiffuse Fraction', + self._diffuse_fraction) + string += '{: <16}=\t{}\n'.format('\tLight position', + self._light_position) + string += '{: <16}=\t{}\n'.format('\tOpaque domains', + self._opaque_domains) + return string + + def to_xml_element(self): + """Return XML representation of the solid ray-traced plot + + Returns + ------- + element : lxml.etree._Element + XML element containing plot data + + """ + element = super().to_xml_element() + element.set("type", "solid_raytrace") + + # no light position means put it at the camera + if self._light_position: + subelement = ET.SubElement(element, "light_position") + subelement.text = ' '.join(map(str, self._light_position)) + + # no diffuse fraction defaults to 0.1 + if self._diffuse_fraction: + subelement = ET.SubElement(element, "diffuse_fraction") + subelement.text = str(self._diffuse_fraction) + + self._check_domains_consistent_with_color_by(self.opaque_domains) + subelement = ET.SubElement(element, "opaque_ids") + + # Extract all IDs, or use the integer value passed in + # explicitly if that was given + subelement.text = ' '.join( + [str(domain) if isinstance(domain, int) else + str(domain.id) for domain in self._opaque_domains]) + + if self._colors: + self._colors_to_xml(element) + + return element + + def _read_phong_attributes(self, elem): + """Read attributes specific to the Phong plot from an XML element""" + light_position = get_elem_list(elem, 'light_position', float) + if light_position is not None: + self.light_position = tuple(light_position) + + diffuse_fraction = get_text(elem, "diffuse_fraction") + if diffuse_fraction is not None: + self.diffuse_fraction = float(diffuse_fraction) + + opaque_domains = get_elem_list(elem, 'opaque_ids', int) + if opaque_domains is not None: + self.opaque_domains = opaque_domains + + @classmethod + def from_xml_element(cls, elem): + """Generate plot object from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.WireframeRayTracePlot + WireframeRayTracePlot object + + """ + + plot_id = int(get_text(elem, "id")) + plot_name = get_text(elem, 'name', '') + plot = cls(plot_id, plot_name) + plot.type = "solid_raytrace" + + plot._read_xml_attributes(elem) + plot._read_phong_attributes(elem) + + # Set plot colors + for color_elem in elem.findall("color"): + uid = get_text(color_elem, "id") + plot.colors[uid] = tuple(get_elem_list(color_elem, "rgb", int)) + + return plot class Plots(cv.CheckedList): """Collection of Plots used for an OpenMC simulation. This class corresponds directly to the plots.xml input file. It can be - thought of as a normal Python list where each member is a :class:`Plot`. It - behaves like a list as the following example demonstrates: + thought of as a normal Python list where each member is inherits from + :class:`PlotBase`. It behaves like a list as the following example + demonstrates: >>> xz_plot = openmc.Plot() >>> big_plot = openmc.Plot() @@ -809,13 +1753,13 @@ class Plots(cv.CheckedList): Parameters ---------- - plots : Iterable of openmc.Plot - Plots to add to the collection + plots : Iterable of openmc.PlotBase + plots to add to the collection """ def __init__(self, plots=None): - super().__init__(Plot, 'plots collection') + super().__init__(PlotBase, 'plots collection') self._plots_file = ET.Element("plots") if plots is not None: self += plots @@ -825,7 +1769,7 @@ class Plots(cv.CheckedList): Parameters ---------- - plot : openmc.Plot + plot : openmc.PlotBase Plot to append """ @@ -863,7 +1807,6 @@ class Plots(cv.CheckedList): for plot in self: plot.colorize(geometry, seed) - def highlight_domains(self, geometry, domains, seed=1, alpha=0.5, background='gray'): """Use alpha compositing to highlight one or more domains in the plot. @@ -899,13 +1842,13 @@ class Plots(cv.CheckedList): self._plots_file.append(xml_element) - def export_to_xml(self, path='plots.xml'): - """Export plot specifications to an XML file. + def to_xml_element(self): + """Create a 'plots' element to be written to an XML file. - Parameters - ---------- - path : str - Path to file to write. Defaults to 'plots.xml'. + Returns + ------- + element : lxml.etree._Element + XML element containing all plot elements """ # Reset xml element tree @@ -916,16 +1859,56 @@ class Plots(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(self._plots_file) + return self._plots_file + + def export_to_xml(self, path='plots.xml'): + """Export plot specifications to an XML file. + + Parameters + ---------- + path : str + Path to file to write. Defaults to 'plots.xml'. + + """ # Check if path is a directory p = Path(path) if p.is_dir(): p /= 'plots.xml' + self.to_xml_element() # Write the XML Tree to the plots.xml file - reorder_attributes(self._plots_file) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(self._plots_file) tree.write(str(p), xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml_element(cls, elem): + """Generate plots collection from XML file + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.Plots + Plots collection + + """ + # Generate each plot + plots = cls() + for e in elem.findall('plot'): + plot_type = get_text(e, "type") + if plot_type == 'wireframe_raytrace': + plots.append(WireframeRayTracePlot.from_xml_element(e)) + elif plot_type == 'solid_raytrace': + plots.append(SolidRayTracePlot.from_xml_element(e)) + elif plot_type in ('slice', 'voxel'): + plots.append(Plot.from_xml_element(e)) + else: + raise ValueError("Unknown plot type: {}".format(plot_type)) + return plots + @classmethod def from_xml(cls, path='plots.xml'): """Generate plots collection from XML file @@ -941,11 +1924,7 @@ class Plots(cv.CheckedList): Plots collection """ - tree = ET.parse(path) + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) root = tree.getroot() - - # Generate each plot - plots = cls() - for elem in root.findall('plot'): - plots.append(Plot.from_xml_element(elem)) - return plots + return cls.from_xml_element(root) diff --git a/openmc/plotter.py b/openmc/plotter.py index 92acaf5bb5..abd8ab6dd4 100644 --- a/openmc/plotter.py +++ b/openmc/plotter.py @@ -1,6 +1,7 @@ +from __future__ import annotations from itertools import chain from numbers import Integral, Real -import string +from typing import Dict, Iterable, List import numpy as np @@ -8,15 +9,15 @@ import openmc.checkvalue as cv import openmc.data # Supported keywords for continuous-energy cross section plotting -PLOT_TYPES = ['total', 'scatter', 'elastic', 'inelastic', 'fission', +PLOT_TYPES = {'total', 'scatter', 'elastic', 'inelastic', 'fission', 'absorption', 'capture', 'nu-fission', 'nu-scatter', 'unity', - 'slowing-down power', 'damage'] + 'slowing-down power', 'damage'} # Supported keywords for multi-group cross section plotting -PLOT_TYPES_MGXS = ['total', 'absorption', 'scatter', 'fission', +PLOT_TYPES_MGXS = {'total', 'absorption', 'scatter', 'fission', 'kappa-fission', 'nu-fission', 'prompt-nu-fission', 'deleyed-nu-fission', 'chi', 'chi-prompt', 'chi-delayed', - 'inverse-velocity', 'beta', 'decay-rate', 'unity'] + 'inverse-velocity', 'beta', 'decay-rate', 'unity'} # Create a dictionary which can be used to convert PLOT_TYPES_MGXS to the # openmc.XSdata attribute name needed to access the data _PLOT_MGXS_ATTR = {line: line.replace(' ', '_').replace('-', '_') @@ -53,18 +54,97 @@ _MIN_E = 1.e-5 _MAX_E = 20.e6 -def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, - axis=None, sab_name=None, ce_cross_sections=None, - mg_cross_sections=None, enrichment=None, plot_CE=True, orders=None, - divisor_orders=None, **kwargs): +ELEMENT_NAMES = list(openmc.data.ELEMENT_SYMBOL.values())[1:] + + +def _get_legend_label(this, type): + """Gets a label for the element or nuclide or material and reaction plotted""" + if isinstance(this, str): + if type in openmc.data.DADZ: + if this in ELEMENT_NAMES: + return f'{this} {type}' + else: # this is a nuclide so the legend can contain more information + z, a, m = openmc.data.zam(this) + da, dz = openmc.data.DADZ[type] + gnds_name = openmc.data.gnds_name(z + dz, a + da, m) + # makes a string with nuclide reaction and new nuclide + # For example "Be9 (n,2n) Be8" + return f'{this} {type} {gnds_name}' + return f'{this} {type}' + elif this.name == '': + return f'Material {this.id} {type}' + else: + return f'{this.name} {type}' + + +def _get_yaxis_label(reactions, divisor_types): + """Gets a y axis label for the type of data plotted""" + + heat_values = {"heating", "heating-local", "damage-energy"} + + # if all the types are heating a different stem and unit is needed + if all(set(value).issubset(heat_values) for value in reactions.values()): + stem = "Heating" + elif all(isinstance(item, str) for item in reactions.keys()): + for nuc_reactions in reactions.values(): + for reaction in nuc_reactions: + if reaction in heat_values: + raise TypeError( + "Mixture of heating and Microscopic reactions. " + "Invalid type for plotting" + ) + stem = "Microscopic" + elif all(isinstance(item, openmc.Material) for item in reactions.keys()): + stem = 'Macroscopic' + else: + msg = "Mixture of openmc.Material and elements/nuclides. Invalid type for plotting" + raise TypeError(msg) + + if divisor_types: + mid, units = "Data", "" + else: + mid = "Cross Section" + units = { + "Macroscopic": "[1/cm]", + "Microscopic": "[b]", + "Heating": "[eV-barn]", + }[stem] + + return f'{stem} {mid} {units}' + +def _get_title(reactions): + """Gets a title for the type of data plotted""" + if len(reactions) == 1: + this, = reactions + name = this.name if isinstance(this, openmc.Material) else this + return f'Cross Section Plot For {name}' + else: + return 'Cross Section Plot' + + +def plot_xs( + reactions: Dict[str | openmc.Material, List[str]], + divisor_types: Iterable[str] | None = None, + temperature: float = 294.0, + axis: "plt.Axes" | None = None, + sab_name: str | None = None, + ce_cross_sections: str | None = None, + mg_cross_sections: str | None = None, + enrichment: float | None = None, + plot_CE: bool = True, + orders: Iterable[int] | None = None, + divisor_orders: Iterable[int] | None = None, + energy_axis_units: str = "eV", + **kwargs, +) -> "plt.Figure" | None: """Creates a figure of continuous-energy cross sections for this item. Parameters ---------- - this : str or openmc.Material - Object to source data from - types : Iterable of values of PLOT_TYPES - The type of cross sections to include in the plot. + reactions : dict + keys can be either a nuclide or element in string form or an + openmc.Material object. Values are a list of the types of + cross sections to include in the plot. divisor_types : Iterable of values of PLOT_TYPES, optional Cross section types which will divide those produced by types before plotting. A type of 'unity' can be used to effectively not @@ -74,23 +154,18 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, temperature of 294K will be plotted. Note that the nearest temperature in the library for each nuclide will be used as opposed to using any interpolation. - data_type : {'nuclide', 'element', 'material', 'macroscopic'}, optional - Type of object to plot. If not specified, a guess is made based on the - `this` argument. axis : matplotlib.axes, optional A previously generated axis to use for plotting. If not specified, a new axis and figure will be generated. sab_name : str, optional - Name of S(a,b) library to apply to MT=2 data when applicable; only used - for items which are instances of openmc.Element or openmc.Nuclide + Name of S(a,b) library to apply to MT=2 data when applicable. ce_cross_sections : str, optional Location of cross_sections.xml file. Default is None. mg_cross_sections : str, optional Location of MGXS HDF5 Library file. Default is None. enrichment : float, optional Enrichment for U235 in weight percent. For example, input 4.95 for - 4.95 weight percent enriched U. Default is None. This is only used for - items which are instances of openmc.Element + 4.95 weight percent enriched U. Default is None. plot_CE : bool, optional Denotes whether or not continuous-energy will be plotted. Defaults to plotting the continuous-energy data. @@ -101,9 +176,13 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, multi-group data. divisor_orders : Iterable of Integral, optional Same as orders, but for divisor_types - **kwargs + **kwargs : All keyword arguments are passed to :func:`matplotlib.pyplot.figure`. + energy_axis_units : {'eV', 'keV', 'MeV'} + Units used on the plot energy axis + + .. versionadded:: 0.15.0 Returns ------- @@ -117,125 +196,107 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None, import matplotlib.pyplot as plt cv.check_type("plot_CE", plot_CE, bool) + cv.check_value("energy_axis_units", energy_axis_units, {"eV", "keV", "MeV"}) - if data_type is None: - if isinstance(this, openmc.Nuclide): - data_type = 'nuclide' - elif isinstance(this, openmc.Element): - data_type = 'element' - elif isinstance(this, openmc.Material): - data_type = 'material' - elif isinstance(this, openmc.Macroscopic): - data_type = 'macroscopic' - elif isinstance(this, str): - if this[-1] in string.digits: - data_type = 'nuclide' - else: - data_type = 'element' - else: - raise TypeError("Invalid type for plotting") - - if plot_CE: - # Calculate for the CE cross sections - E, data = calculate_cexs(this, data_type, types, temperature, sab_name, - ce_cross_sections, enrichment) - if divisor_types: - cv.check_length('divisor types', divisor_types, len(types)) - Ediv, data_div = calculate_cexs(this, divisor_types, temperature, - sab_name, ce_cross_sections, - enrichment) - - # Create a new union grid, interpolate data and data_div on to that - # grid, and then do the actual division - Enum = E[:] - E = np.union1d(Enum, Ediv) - data_new = np.zeros((len(types), len(E))) - - for line in range(len(types)): - data_new[line, :] = \ - np.divide(np.interp(E, Enum, data[line, :]), - np.interp(E, Ediv, data_div[line, :])) - if divisor_types[line] != 'unity': - types[line] = types[line] + ' / ' + divisor_types[line] - data = data_new - else: - # Calculate for MG cross sections - E, data = calculate_mgxs(this, data_type, types, orders, temperature, - mg_cross_sections, ce_cross_sections, - enrichment) - - if divisor_types: - cv.check_length('divisor types', divisor_types, len(types)) - Ediv, data_div = calculate_mgxs(this, data_type, divisor_types, - divisor_orders, temperature, - mg_cross_sections, - ce_cross_sections, enrichment) - - # Perform the division - for line in range(len(types)): - data[line, :] /= data_div[line, :] - if divisor_types[line] != 'unity': - types[line] += ' / ' + divisor_types[line] + axis_scaling_factor = {"eV": 1.0, "keV": 1e-3, "MeV": 1e-6} # Generate the plot if axis is None: - fig, ax = plt.subplots() + fig, ax = plt.subplots(**kwargs) else: fig = None ax = axis + + all_types = [] + + for this, types in reactions.items(): + all_types = all_types + types + + if plot_CE: + cv.check_type("this", this, (str, openmc.Material)) + # Calculate for the CE cross sections + E, data = calculate_cexs(this, types, temperature, sab_name, + ce_cross_sections, enrichment) + if divisor_types: + cv.check_length('divisor types', divisor_types, len(types)) + Ediv, data_div = calculate_cexs(this, divisor_types, temperature, + sab_name, ce_cross_sections, + enrichment) + + # Create a new union grid, interpolate data and data_div on to that + # grid, and then do the actual division + Enum = E[:] + E = np.union1d(Enum, Ediv) + data_new = np.zeros((len(types), len(E))) + + for line in range(len(types)): + data_new[line, :] = \ + np.divide(np.interp(E, Enum, data[line, :]), + np.interp(E, Ediv, data_div[line, :])) + if divisor_types[line] != 'unity': + types[line] = types[line] + ' / ' + divisor_types[line] + data = data_new + else: + # Calculate for MG cross sections + E, data = calculate_mgxs(this, types, orders, temperature, + mg_cross_sections, ce_cross_sections, + enrichment) + + if divisor_types: + cv.check_length('divisor types', divisor_types, len(types)) + Ediv, data_div = calculate_mgxs(this, divisor_types, + divisor_orders, temperature, + mg_cross_sections, + ce_cross_sections, enrichment) + + # Perform the division + for line in range(len(types)): + data[line, :] /= data_div[line, :] + if divisor_types[line] != 'unity': + types[line] += ' / ' + divisor_types[line] + + E *= axis_scaling_factor[energy_axis_units] + + # Plot the data + for i in range(len(data)): + data[i, :] = np.nan_to_num(data[i, :]) + if np.sum(data[i, :]) > 0.: + ax.plot(E, data[i, :], label=_get_legend_label(this, types[i])) + # Set to loglog or semilogx depending on if we are plotting a data # type which we expect to vary linearly - if set(types).issubset(PLOT_TYPES_LINEAR): - plot_func = ax.semilogx + if set(all_types).issubset(PLOT_TYPES_LINEAR): + ax.set_xscale('log') + ax.set_yscale('linear') else: - plot_func = ax.loglog + ax.set_xscale('log') + ax.set_yscale('log') - # Plot the data - for i in range(len(data)): - data[i, :] = np.nan_to_num(data[i, :]) - if np.sum(data[i, :]) > 0.: - plot_func(E, data[i, :], label=types[i]) - - ax.set_xlabel('Energy [eV]') + ax.set_xlabel(f"Energy [{energy_axis_units}]") if plot_CE: - ax.set_xlim(_MIN_E, _MAX_E) + ax.set_xlim( + _MIN_E * axis_scaling_factor[energy_axis_units], + _MAX_E * axis_scaling_factor[energy_axis_units], + ) else: ax.set_xlim(E[-1], E[0]) - if divisor_types: - if data_type == 'nuclide': - ylabel = 'Nuclidic Microscopic Data' - elif data_type == 'element': - ylabel = 'Elemental Microscopic Data' - elif data_type == 'material' or data_type == 'macroscopic': - ylabel = 'Macroscopic Data' - else: - if data_type == 'nuclide': - ylabel = 'Microscopic Cross Section [b]' - elif data_type == 'element': - ylabel = 'Elemental Cross Section [b]' - elif data_type == 'material' or data_type == 'macroscopic': - ylabel = 'Macroscopic Cross Section [1/cm]' - ax.set_ylabel(ylabel) + + ax.set_ylabel(_get_yaxis_label(reactions, divisor_types)) ax.legend(loc='best') - name = this.name if data_type == 'material' else this - if len(types) > 1: - ax.set_title('Cross Sections for ' + name) - else: - ax.set_title('Cross Section for ' + name) + ax.set_title(_get_title(reactions)) return fig -def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, - cross_sections=None, enrichment=None): +def calculate_cexs(this, types, temperature=294., sab_name=None, + cross_sections=None, enrichment=None, ncrystal_cfg=None): """Calculates continuous-energy cross sections of a requested type. Parameters ---------- - this : {str, openmc.Nuclide, openmc.Element, openmc.Material} - Object to source data from - data_type : {'nuclide', 'element', 'material'} - Type of object to plot + this : str or openmc.Material + Object to source data from. Nuclides and elements should be input as a + str types : Iterable of values of PLOT_TYPES The type of cross sections to calculate temperature : float, optional @@ -251,6 +312,8 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, Enrichment for U235 in weight percent. For example, input 4.95 for 4.95 weight percent enriched U. Default is None (natural composition). + ncrystal_cfg : str, optional + Configuration string for NCrystal material. Returns ------- @@ -262,50 +325,44 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None, """ # Check types + cv.check_type('this', this, (str, openmc.Material)) cv.check_type('temperature', temperature, Real) if sab_name: cv.check_type('sab_name', sab_name, str) if enrichment: cv.check_type('enrichment', enrichment, Real) - if data_type == 'nuclide': - if isinstance(this, str): - nuc = openmc.Nuclide(this) + if isinstance(this, str): + if this in ELEMENT_NAMES: + energy_grid, data = _calculate_cexs_elem_mat( + this, types, temperature, cross_sections, sab_name, enrichment + ) else: - nuc = this - energy_grid, xs = _calculate_cexs_nuclide(nuc, types, temperature, - sab_name, cross_sections) - # Convert xs (Iterable of Callable) to a grid of cross section values - # calculated on the points in energy_grid for consistency with the - # element and material functions. - data = np.zeros((len(types), len(energy_grid))) - for line in range(len(types)): - data[line, :] = xs[line](energy_grid) - elif data_type == 'element': - if isinstance(this, str): - elem = openmc.Element(this) - else: - elem = this - energy_grid, data = _calculate_cexs_elem_mat(elem, types, temperature, - cross_sections, sab_name, - enrichment) - elif data_type == 'material': - cv.check_type('this', this, openmc.Material) + energy_grid, xs = _calculate_cexs_nuclide( + this, types, temperature, sab_name, cross_sections, + ncrystal_cfg + ) + + # Convert xs (Iterable of Callable) to a grid of cross section values + # calculated on the points in energy_grid for consistency with the + # element and material functions. + data = np.zeros((len(types), len(energy_grid))) + for line in range(len(types)): + data[line, :] = xs[line](energy_grid) + else: energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature, cross_sections) - else: - raise TypeError("Invalid type") return energy_grid, data def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, - cross_sections=None): + cross_sections=None, ncrystal_cfg=None): """Calculates continuous-energy cross sections of a requested type. Parameters ---------- - this : openmc.Nuclide + this : str Nuclide object to source data from types : Iterable of str or Integral The type of cross sections to calculate; values can either be those @@ -321,6 +378,8 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, Name of S(a,b) library to apply to MT=2 data when applicable. cross_sections : str, optional Location of cross_sections.xml file. Default is None. + ncrystal_cfg : str, optional + Configuration string for NCrystal material. Returns ------- @@ -354,7 +413,8 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, # Prep S(a,b) data if needed if sab_name: - sab = openmc.data.ThermalScattering.from_hdf5(sab_name) + sab = openmc.data.ThermalScattering.from_hdf5( + library.get_by_material(sab_name, data_type='thermal')['path']) # Obtain the nearest temperature if strT in sab.temperatures: sabT = strT @@ -438,6 +498,19 @@ def _calculate_cexs_nuclide(this, types, temperature=294., sab_name=None, [sab_sum, nuc[mt].xs[nucT]], [sab_Emax]) funcs.append(pw_funcs) + elif ncrystal_cfg: + import NCrystal + nc_scatter = NCrystal.createScatter(ncrystal_cfg) + nc_func = nc_scatter.xsect + nc_emax = 5 # eV # this should be obtained from NCRYSTAL_MAX_ENERGY + energy_grid = np.union1d(np.geomspace(min(energy_grid), + 1.1*nc_emax, + 1000),energy_grid) # NCrystal does not have + # an intrinsic energy grid + pw_funcs = openmc.data.Regions1D( + [nc_func, nuc[mt].xs[nucT]], + [nc_emax]) + funcs.append(pw_funcs) else: funcs.append(nuc[mt].xs[nucT]) elif mt in nuc: @@ -502,8 +575,8 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., Parameters ---------- - this : openmc.Material or openmc.Element - Object to source data from + this : openmc.Material or str + Object to source data from. Element can be input as str types : Iterable of values of PLOT_TYPES The type of cross sections to calculate temperature : float, optional @@ -540,26 +613,23 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., # Load the library library = openmc.data.DataLibrary.from_xml(cross_sections) + ncrystal_cfg = None if isinstance(this, openmc.Material): # Expand elements in to nuclides with atomic densities - nuclides = this.get_nuclide_atom_densities() - # For ease of processing split out the nuclide and its fraction - nuc_fractions = {nuclide[1][0]: nuclide[1][1] - for nuclide in nuclides.items()} + nuc_fractions = this.get_nuclide_atom_densities() # Create a dict of [nuclide name] = nuclide object to carry forward - # with a common nuclides format between openmc.Material and - # openmc.Element objects - nuclides = {nuclide[1][0]: nuclide[1][0] - for nuclide in nuclides.items()} + # with a common nuclides format between openmc.Material and Elements + nuclides = {nuclide: nuclide for nuclide in nuc_fractions} + # Add NCrystal cfg string if it exists + ncrystal_cfg = this.ncrystal_cfg else: # Expand elements in to nuclides with atomic densities - nuclides = this.expand(1., 'ao', enrichment=enrichment, + nuclides = openmc.Element(this).expand(1., 'ao', enrichment=enrichment, cross_sections=cross_sections) # For ease of processing split out the nuclide and its fraction nuc_fractions = {nuclide[0]: nuclide[1] for nuclide in nuclides} # Create a dict of [nuclide name] = nuclide object to carry forward - # with a common nuclides format between openmc.Material and - # openmc.Element objects + # with a common nuclides format between openmc.Material and Elements nuclides = {nuclide[0]: nuclide[0] for nuclide in nuclides} # Identify the nuclides which have S(a,b) data @@ -567,18 +637,17 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., for nuclide in nuclides.items(): sabs[nuclide[0]] = None if isinstance(this, openmc.Material): - for sab_name in this._sab: + for sab_name, _ in this._sab: sab = openmc.data.ThermalScattering.from_hdf5( library.get_by_material(sab_name, data_type='thermal')['path']) for nuc in sab.nuclides: - sabs[nuc] = library.get_by_material(sab_name, - data_type='thermal')['path'] + sabs[nuc] = sab_name else: if sab_name: - sab = openmc.data.ThermalScattering.from_hdf5(sab_name) + sab = openmc.data.ThermalScattering.from_hdf5( + library.get_by_material(sab_name, data_type='thermal')['path']) for nuc in sab.nuclides: - sabs[nuc] = library.get_by_material(sab_name, - data_type='thermal')['path'] + sabs[nuc] = sab_name # Now we can create the data sets to be plotted xs = {} @@ -586,9 +655,10 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., for nuclide in nuclides.items(): name = nuclide[0] nuc = nuclide[1] - sab_tab = sabs[name] - temp_E, temp_xs = calculate_cexs(nuc, 'nuclide', types, T, sab_tab, - cross_sections) + sab_name = sabs[name] + temp_E, temp_xs = calculate_cexs(nuc, types, T, sab_name, cross_sections, + ncrystal_cfg=ncrystal_cfg + ) E.append(temp_E) # Since the energy grids are different, store the cross sections as # a tabulated function so they can be calculated on any grid needed. @@ -615,7 +685,7 @@ def _calculate_cexs_elem_mat(this, types, temperature=294., return energy_grid, data -def calculate_mgxs(this, data_type, types, orders=None, temperature=294., +def calculate_mgxs(this, types, orders=None, temperature=294., cross_sections=None, ce_cross_sections=None, enrichment=None): """Calculates multi-group cross sections of a requested type. @@ -627,9 +697,7 @@ def calculate_mgxs(this, data_type, types, orders=None, temperature=294., Parameters ---------- this : str or openmc.Material - Object to source data from - data_type : {'nuclide', 'element', 'material', 'macroscopic'} - Type of object to plot + Object to source data from. Nuclides and elements can be input as a str types : Iterable of values of PLOT_TYPES_MGXS The type of cross sections to calculate orders : Iterable of Integral, optional @@ -645,7 +713,6 @@ def calculate_mgxs(this, data_type, types, orders=None, temperature=294., Location of MGXS HDF5 Library file. Default is None. ce_cross_sections : str, optional Location of continuous-energy cross_sections.xml file. Default is None. - This is used only for expanding an openmc.Element object passed as this enrichment : float, optional Enrichment for U235 in weight percent. For example, input 4.95 for 4.95 weight percent enriched U. Default is None @@ -669,13 +736,13 @@ def calculate_mgxs(this, data_type, types, orders=None, temperature=294., cv.check_type("cross_sections", cross_sections, str) library = openmc.MGXSLibrary.from_hdf5(cross_sections) - if data_type in ('nuclide', 'macroscopic'): - mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, - temperature) - elif data_type in ('element', 'material'): + if this in ELEMENT_NAMES or isinstance(this, openmc.Material): mgxs = _calculate_mgxs_elem_mat(this, types, library, orders, temperature, ce_cross_sections, enrichment) + elif isinstance(this, str): + mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, + temperature) else: raise TypeError("Invalid type") @@ -707,7 +774,7 @@ def _calculate_mgxs_nuc_macro(this, types, library, orders=None, Parameters ---------- - this : openmc.Nuclide or openmc.Macroscopic + this : str Object to source data from types : Iterable of str The type of cross sections to calculate; values can either be those @@ -845,8 +912,8 @@ def _calculate_mgxs_elem_mat(this, types, library, orders=None, Parameters ---------- - this : openmc.Element or openmc.Material - Object to source data from + this : str or openmc.Material + Object to source data from. Elements can be input as a str types : Iterable of str The type of cross sections to calculate; values can either be those in openmc.PLOT_TYPES_MGXS @@ -885,17 +952,17 @@ def _calculate_mgxs_elem_mat(this, types, library, orders=None, # Check to see if we have nuclides/elements or a macroscopic object if this._macroscopic is not None: # We have macroscopics - nuclides = {this._macroscopic: (this._macroscopic, this.density)} + nuclides = {this._macroscopic: this.density} else: # Expand elements in to nuclides with atomic densities nuclides = this.get_nuclide_atom_densities() # For ease of processing split out nuc and nuc_density - nuc_fraction = [nuclide[1][1] for nuclide in nuclides.items()] + nuc_fraction = list(nuclides.values()) else: T = temperature # Expand elements in to nuclides with atomic densities - nuclides = this.expand(100., 'ao', enrichment=enrichment, + nuclides = openmc.Element(this).expand(100., 'ao', enrichment=enrichment, cross_sections=ce_cross_sections) # For ease of processing split out nuc and nuc_fractions diff --git a/openmc/polynomial.py b/openmc/polynomial.py index 1259c61ca2..341cdff476 100644 --- a/openmc/polynomial.py +++ b/openmc/polynomial.py @@ -92,7 +92,7 @@ class Zernike(Polynomial): Parameters ---------- coef : Iterable of float - A list of coefficients of each term in radial only Zernike polynomials + A list of coefficients of each term in Zernike polynomials radius : float Domain of Zernike polynomials to be applied on. Default is 1. diff --git a/openmc/region.py b/openmc/region.py index b252ff4a72..cb9f3abd23 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -1,11 +1,14 @@ +from __future__ import annotations from abc import ABC, abstractmethod -from collections import OrderedDict from collections.abc import MutableSequence from copy import deepcopy +import warnings import numpy as np -from .checkvalue import check_type +import openmc +from .bounding_box import BoundingBox +from .plots import add_plot_params class Region(ABC): @@ -17,6 +20,11 @@ class Region(ABC): respective classes are typically not instantiated directly but rather are created through operators of the Surface and Region classes. + Attributes + ---------- + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the region + """ def __and__(self, other): return Intersection((self, other)) @@ -24,13 +32,19 @@ class Region(ABC): def __or__(self, other): return Union((self, other)) - def __invert__(self): - return Complement(self) + @abstractmethod + def __invert__(self) -> Region: + pass @abstractmethod def __contains__(self, point): pass + @property + @abstractmethod + def bounding_box(self) -> BoundingBox: + pass + @abstractmethod def __str__(self): pass @@ -46,17 +60,17 @@ class Region(ABC): Parameters ---------- - surfaces: collections.OrderedDict, optional + surfaces : dict, optional Dictionary mapping surface IDs to :class:`openmc.Surface` instances Returns ------- - surfaces: collections.OrderedDict + surfaces : dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances """ if surfaces is None: - surfaces = OrderedDict() + surfaces = {} for region in self: surfaces = region.get_surfaces(surfaces) return surfaces @@ -112,18 +126,29 @@ class Region(ABC): else: tokens.append(+surfaces[abs(j)]) + # When an opening parenthesis appears after a non-operator, + # there's an implicit intersection operator between them + if expression[i] == '(': + tokens.append(' ') + if expression[i] in '()|~': # For everything other than intersection, add the operator # to the list of tokens tokens.append(expression[i]) + + # If two parentheses appear immediately adjacent to one + # another, we need an intersection between them + if expression[i:i+2] == ')(': + tokens.append(' ') else: # Find next non-space character while expression[i+1] == ' ': i += 1 - # If previous token is a halfspace or right parenthesis and next token - # is not a left parenthese or union operator, that implies that the - # whitespace is to be interpreted as an intersection operator + # If previous token is a halfspace or right parenthesis and + # next token is not a left parenthesis or union operator, + # that implies that the whitespace is to be interpreted as + # an intersection operator if (i_start >= 0 or tokens[-1] == ')') and \ expression[i+1] not in ')|': tokens.append(' ') @@ -248,13 +273,18 @@ class Region(ABC): clone[:] = [n.clone(memo) for n in self] return clone - def translate(self, vector, memo=None): + def translate(self, vector, inplace=False, memo=None): """Translate region in given direction Parameters ---------- vector : iterable of float Direction in which region should be translated + inplace : bool + Whether or not to return a region based on new surfaces or one based + on the original surfaces that have been modified. + + .. versionadded:: 0.13.1 memo : dict or None Dictionary used for memoization. This parameter is used internally and should not be specified by the user. @@ -268,7 +298,7 @@ class Region(ABC): if memo is None: memo = {} - return type(self)(n.translate(vector, memo) for n in self) + return type(self)(n.translate(vector, inplace, memo) for n in self) def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, memo=None): @@ -297,7 +327,7 @@ class Region(ABC): :math:`\psi` about z. This corresponds to an x-y-z extrinsic rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`. - inplace : boolean + inplace : bool Whether or not to return a new instance of Surface or to modify the coefficients of this Surface in place. Defaults to False. memo : dict or None @@ -314,6 +344,23 @@ class Region(ABC): return type(self)(n.rotate(rotation, pivot=pivot, order=order, inplace=inplace, memo=memo) for n in self) + @add_plot_params + def plot(self, *args, **kwargs): + """Display a slice plot of the region. + + .. versionadded:: 0.15.0 + """ + for key in ('color_by', 'colors', 'legend', 'legend_kwargs'): + if key in kwargs: + warnings.warn(f"The '{key}' argument is present but won't be applied in a region plot") + + # Create cell while not perturbing use of autogenerated IDs + next_id = openmc.Cell.next_id + c = openmc.Cell(region=self) + openmc.Cell.used_ids.remove(c.id) + openmc.Cell.next_id = next_id + return c.plot(*args, **kwargs) + class Intersection(Region, MutableSequence): r"""Intersection of two or more regions. @@ -339,13 +386,16 @@ class Intersection(Region, MutableSequence): Attributes ---------- - bounding_box : tuple of numpy.array - Lower-left and upper-right coordinates of an axis-aligned bounding box + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the region """ def __init__(self, nodes): self._nodes = list(nodes) + for node in nodes: + if not isinstance(node, Region): + raise ValueError('Intersection operands must be of type Region') def __and__(self, other): new = Intersection(self) @@ -359,6 +409,9 @@ class Intersection(Region, MutableSequence): self.append(other) return self + def __invert__(self) -> Union: + return Union(~n for n in self) + # Implement mutable sequence protocol by delegating to list def __getitem__(self, key): return self._nodes[key] @@ -395,14 +448,11 @@ class Intersection(Region, MutableSequence): return '(' + ' '.join(map(str, self)) + ')' @property - def bounding_box(self): - lower_left = np.array([-np.inf, -np.inf, -np.inf]) - upper_right = np.array([np.inf, np.inf, np.inf]) + def bounding_box(self) -> BoundingBox: + box = BoundingBox.infinite() for n in self: - lower_left_n, upper_right_n = n.bounding_box - lower_left[:] = np.maximum(lower_left, lower_left_n) - upper_right[:] = np.minimum(upper_right, upper_right_n) - return lower_left, upper_right + box &= n.bounding_box + return box class Union(Region, MutableSequence): @@ -427,13 +477,16 @@ class Union(Region, MutableSequence): Attributes ---------- - bounding_box : 2-tuple of numpy.array - Lower-left and upper-right coordinates of an axis-aligned bounding box + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the region """ def __init__(self, nodes): self._nodes = list(nodes) + for node in nodes: + if not isinstance(node, Region): + raise ValueError('Union operands must be of type Region') def __or__(self, other): new = Union(self) @@ -447,6 +500,9 @@ class Union(Region, MutableSequence): self.append(other) return self + def __invert__(self) -> Intersection: + return Intersection(~n for n in self) + # Implement mutable sequence protocol by delegating to list def __getitem__(self, key): return self._nodes[key] @@ -483,14 +539,12 @@ class Union(Region, MutableSequence): return '(' + ' | '.join(map(str, self)) + ')' @property - def bounding_box(self): - lower_left = np.array([np.inf, np.inf, np.inf]) - upper_right = np.array([-np.inf, -np.inf, -np.inf]) + def bounding_box(self) -> BoundingBox: + bbox = BoundingBox(np.array([np.inf]*3), + np.array([-np.inf]*3)) for n in self: - lower_left_n, upper_right_n = n.bounding_box - lower_left[:] = np.minimum(lower_left, lower_left_n) - upper_right[:] = np.maximum(upper_right, upper_right_n) - return lower_left, upper_right + bbox |= n.bounding_box + return bbox class Complement(Region): @@ -517,12 +571,12 @@ class Complement(Region): ---------- node : openmc.Region Regions to take the complement of - bounding_box : tuple of numpy.array - Lower-left and upper-right coordinates of an axis-aligned bounding box + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the region """ - def __init__(self, node): + def __init__(self, node: Region): self.node = node def __contains__(self, point): @@ -541,6 +595,9 @@ class Complement(Region): """ return point not in self.node + def __invert__(self) -> Region: + return self.node + def __str__(self): return '~' + str(self.node) @@ -550,40 +607,30 @@ class Complement(Region): @node.setter def node(self, node): - check_type('node', node, Region) + if not isinstance(node, Region): + raise ValueError('Complement operand must be of type Region') self._node = node @property - def bounding_box(self): - # Use De Morgan's laws to distribute the complement operator so that it - # only applies to surface half-spaces, thus allowing us to calculate the - # bounding box in the usual recursive manner. - if isinstance(self.node, Union): - temp_region = Intersection(~n for n in self.node) - elif isinstance(self.node, Intersection): - temp_region = Union(~n for n in self.node) - elif isinstance(self.node, Complement): - temp_region = self.node.node - else: - temp_region = ~self.node - return temp_region.bounding_box + def bounding_box(self) -> BoundingBox: + return (~self.node).bounding_box def get_surfaces(self, surfaces=None): """Recursively find and return all the surfaces referenced by the node Parameters ---------- - surfaces: collections.OrderedDict, optional + surfaces : dict, optional Dictionary mapping surface IDs to :class:`openmc.Surface` instances Returns ------- - surfaces: collections.OrderedDict + surfaces : dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances """ if surfaces is None: - surfaces = OrderedDict() + surfaces = {} for region in self.node: surfaces = region.get_surfaces(surfaces) return surfaces @@ -611,10 +658,10 @@ class Complement(Region): clone.node = self.node.clone(memo) return clone - def translate(self, vector, memo=None): + def translate(self, vector, inplace=False, memo=None): if memo is None: memo = {} - return type(self)(self.node.translate(vector, memo)) + return type(self)(self.node.translate(vector, inplace, memo)) def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False, memo=None): diff --git a/openmc/search.py b/openmc/search.py index ee5dd1f077..70ce011b63 100644 --- a/openmc/search.py +++ b/openmc/search.py @@ -8,11 +8,11 @@ import openmc.model import openmc.checkvalue as cv -_SCALAR_BRACKETED_METHODS = ['brentq', 'brenth', 'ridder', 'bisect'] +_SCALAR_BRACKETED_METHODS = {'brentq', 'brenth', 'ridder', 'bisect'} def _search_keff(guess, target, model_builder, model_args, print_iterations, - print_output, guesses, results): + run_args, guesses, results): """Function which will actually create our model, run the calculation, and obtain the result. This function will be passed to the root finding algorithm @@ -31,8 +31,8 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations, print_iterations : bool Whether or not to print the guess and the resultant keff during the iteration process. - print_output : bool - Whether or not to print the OpenMC output during the iterations. + run_args : dict + Keyword arguments to pass to :meth:`openmc.Model.run`. guesses : Iterable of Real Running list of guesses thus far, to be updated during the execution of this function. @@ -51,9 +51,9 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations, model = model_builder(guess, **model_args) # Run the model and obtain keff - sp_filepath = model.run(output=print_output) + sp_filepath = model.run(**run_args) with openmc.StatePoint(sp_filepath) as sp: - keff = sp.k_combined + keff = sp.keff # Record the history guesses.append(guess) @@ -70,7 +70,7 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations, def search_for_keff(model_builder, initial_guess=None, target=1.0, bracket=None, model_args=None, tol=None, bracketed_method='bisect', print_iterations=False, - print_output=False, **kwargs): + run_args=None, **kwargs): """Function to perform a keff search by modifying a model parametrized by a single independent variable. @@ -102,9 +102,11 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, print_iterations : bool Whether or not to print the guess and the result during the iteration process. Defaults to False. - print_output : bool - Whether or not to print the OpenMC output during the iterations. - Defaults to False. + run_args : dict, optional + Keyword arguments to pass to :meth:`openmc.Model.run`. Defaults to no + arguments. + + .. versionadded:: 0.13.1 **kwargs All remaining keyword arguments are passed to the root-finding method. @@ -137,7 +139,10 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, cv.check_value('bracketed_method', bracketed_method, _SCALAR_BRACKETED_METHODS) cv.check_type('print_iterations', print_iterations, bool) - cv.check_type('print_output', print_output, bool) + if run_args is None: + run_args = {} + else: + cv.check_type('run_args', run_args, dict) cv.check_type('model_builder', model_builder, Callable) # Run the model builder function once to make sure it provides the correct @@ -188,7 +193,7 @@ def search_for_keff(model_builder, initial_guess=None, target=1.0, # Add information to be passed to the searching function args['args'] = (target, model_builder, model_args, print_iterations, - print_output, guesses, results) + run_args, guesses, results) # Create a new dictionary with the arguments from args and kwargs args.update(kwargs) diff --git a/openmc/settings.py b/openmc/settings.py index 9aef9a1fed..2f8a2b1248 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1,13 +1,22 @@ -from collections.abc import Iterable, MutableSequence, Mapping +from collections.abc import Iterable, Mapping, MutableSequence, Sequence from enum import Enum -from pathlib import Path -from numbers import Real, Integral -from xml.etree import ElementTree as ET +import itertools from math import ceil +from numbers import Integral, Real +from pathlib import Path +import lxml.etree as ET + +import openmc import openmc.checkvalue as cv -from . import VolumeCalculation, Source, RegularMesh, WeightWindows -from ._xml import clean_indentation, get_text, reorder_attributes +from openmc.checkvalue import PathLike +from openmc.stats.multivariate import MeshSpatial +from ._xml import clean_indentation, get_elem_list, get_text +from .mesh import _read_meshes, RegularMesh, MeshBase +from .source import SourceBase, MeshSource, IndependentSource +from .utility_funcs import input_path +from .volume import VolumeCalculation +from .weight_windows import WeightWindows, WeightWindowGenerator, WeightWindowsList class RunMode(Enum): @@ -18,12 +27,17 @@ class RunMode(Enum): PARTICLE_RESTART = 'particle restart' -_RES_SCAT_METHODS = ['dbrc', 'rvs'] +_RES_SCAT_METHODS = {'dbrc', 'rvs'} class Settings: """Settings used for an OpenMC simulation. + Parameters + ---------- + **kwargs : dict, optional + Any keyword arguments are used to set attributes on the instance. + Attributes ---------- batches : int @@ -36,14 +50,20 @@ class Settings: create_fission_neutrons : bool Indicate whether fission neutrons should be created or not. cutoff : dict - Dictionary defining weight cutoff and energy cutoff. The dictionary may - have six keys, 'weight', 'weight_avg', 'energy_neutron', 'energy_photon', - 'energy_electron', and 'energy_positron'. Value for 'weight' - should be a float indicating weight cutoff below which particle undergo - Russian roulette. Value for 'weight_avg' should be a float indicating - weight assigned to particles that are not killed after Russian - roulette. Value of energy should be a float indicating energy in eV - below which particle type will be killed. + Dictionary defining weight cutoff, energy cutoff and time cutoff. The + dictionary may have the following keys, 'weight', 'weight_avg', + 'survival_normalization', 'energy_neutron', 'energy_photon', + 'energy_electron', 'energy_positron', 'time_neutron', 'time_photon', + 'time_electron', and 'time_positron'. Value for 'weight' should be a + float indicating weight cutoff below which particle undergo Russian + roulette. Value for 'weight_avg' should be a float indicating weight + assigned to particles that are not killed after Russian roulette. Value + of energy should be a float indicating energy in eV below which particle + type will be killed. Value of time should be a float in seconds. + Particles will be killed exactly at the specified time. Value for + 'survival_normalization' is a bool indicating whether or not the weight + cutoff parameters will be applied relative to the particle's starting + weight or to its current weight. delayed_photon_scaling : bool Indicate whether to scale the fission photon yield by (EGP + EGD)/EGP where EGP is the energy release of prompt photons and EGD is the energy @@ -64,14 +84,22 @@ class Settings: history-based parallelism. .. versionadded:: 0.12 + free_gas_threshold : float + Energy multiplier (in units of :math:`kT`) below which the free gas + scattering treatment is applied for elastic scattering. If not + specified, a value of 400.0 is used. generations_per_batch : int Number of generations per batch + ifp_n_generation : int + Number of generations to consider for the Iterated Fission Probability + method. max_lost_particles : int Maximum number of lost particles .. versionadded:: 0.12 - rel_max_lost_particles : int - Maximum number of lost particles, relative to the total number of particles + rel_max_lost_particles : float + Maximum number of lost particles, relative to the total number of + particles .. versionadded:: 0.12 inactive : int @@ -94,12 +122,29 @@ class Settings: parallelism. .. versionadded:: 0.12 + max_particle_events : int + Maximum number of allowed particle events per source particle. + + .. versionadded:: 0.15.0 max_order : None or int Maximum scattering order to apply globally when in multi-group mode. - max_splits : int + max_history_splits : int Maximum number of times a particle can split during a history .. versionadded:: 0.13 + max_secondaries : int + Maximum secondary bank size + + .. versionadded:: 0.15.3 + max_tracks : int + Maximum number of tracks written to a track file (per MPI process). + + .. versionadded:: 0.13.1 + max_write_lost_particles : int + Maximum number of particle restart files (per MPI process) to write for + lost particles. + + .. versionadded:: 0.14.0 no_reduce : bool Indicate that all user-defined and global tallies should not be reduced across processes in a parallel calculation. @@ -114,8 +159,58 @@ class Settings: Number of particles per generation photon_transport : bool Whether to use photon transport. + plot_seed : int + Initial seed for randomly generated plot colors. ptables : bool Determine whether probability tables are used. + random_ray : dict + Options for configuring the random ray solver. Acceptable keys are: + + :distance_inactive: + Indicates the total active distance in [cm] a ray should travel + :distance_active: + Indicates the total active distance in [cm] a ray should travel + :ray_source: + Starting ray distribution (must be uniform in space and angle) as + specified by a :class:`openmc.SourceBase` object. + :volume_estimator: + Choice of volume estimator for the random ray solver. Options are + 'naive', 'simulation_averaged', or 'hybrid'. + The default is 'hybrid'. + :source_shape: + Assumed shape of the source distribution within each source region. + Options are 'flat' (default), 'linear', or 'linear_xy'. + :volume_normalized_flux_tallies: + Whether to normalize flux tallies by volume (bool). The default is + 'False'. When enabled, flux tallies will be reported in units of + cm/cm^3. When disabled, flux tallies will be reported in units of cm + (i.e., total distance traveled by neutrons in the spatial tally + region). + :adjoint: + Whether to run the random ray solver in adjoint mode (bool). The + default is 'False'. + :sample_method: + Sampling method for the ray starting location and direction of + travel. Options are `prng` (default) or 'halton`. + :source_region_meshes: + List of tuples where each tuple contains a mesh and a list of + domains. Each domain is an instance of openmc.Material, openmc.Cell, + or openmc.Universe. The mesh will be applied to the listed domains + to subdivide source regions so as to improve accuracy and/or conform + with tally meshes. + :diagonal_stabilization_rho: + The rho factor for use with diagonal stabilization. This technique is + applied when negative diagonal (in-group) elements are detected in + the scattering matrix of input MGXS data, which is a common feature + of transport corrected MGXS data. The default is 1.0, which ensures + no negative diagonal elements are present in the iteration matrix and + thus stabilizes the simulation. A value of 0.0 will disable diagonal + stabilization. Values between 0.0 and 1.0 will apply a degree of + stabilization, which may be desirable as stronger diagonal stabilization + also tends to dampen the convergence rate of the solver, thus requiring + more iterations to converge. + + .. versionadded:: 0.15.0 resonance_scattering : dict Settings for resonance elastic scattering. Accepted keys are 'enable' (bool), 'method' (str), 'energy_min' (float), 'energy_max' (float), and @@ -123,16 +218,22 @@ class Settings: rejection correction) or 'rvs' (relative velocity sampling). If not specified, 'rvs' is the default method. The 'energy_min' and 'energy_max' values indicate the minimum and maximum energies above and - below which the resonance elastic scattering method is to be - applied. The 'nuclides' list indicates what nuclides the method should - be applied to. In its absence, the method will be applied to all - nuclides with 0 K elastic scattering data present. + below which the resonance elastic scattering method is to be applied. + The 'nuclides' list indicates what nuclides the method should be applied + to. In its absence, the method will be applied to all nuclides with 0 K + elastic scattering data present. run_mode : {'eigenvalue', 'fixed source', 'plot', 'volume', 'particle restart'} The type of calculation to perform (default is 'eigenvalue') seed : int Seed for the linear congruential pseudorandom number generator - source : Iterable of openmc.Source + stride : int + Number of random numbers allocated for each source particle history + source : Iterable of openmc.SourceBase Distribution of source sites in space, angle, and energy + source_rejection_fraction : float + Minimum fraction of source sites that must be accepted when applying + rejection sampling based on constraints. If not specified, the default + value is 0.05. sourcepoint : dict Options for writing source points. Acceptable keys are: @@ -141,10 +242,11 @@ class Settings: :separate: bool indicating whether the source should be written as a separate file :write: bool indicating whether or not to write the source + :mcpl: bool indicating whether to write the source as an MCPL file statepoint : dict Options for writing state points. Acceptable keys are: - :batches: list of batches at which to write source + :batches: list of batches at which to write statepoint files surf_source_read : dict Options for reading surface source points. Acceptable keys are: @@ -154,17 +256,28 @@ class Settings: :surface_ids: List of surface ids at which crossing particles are to be banked (int) - :max_particles: Maximum number of particles to be banked on - surfaces per process (int) + :max_particles: Maximum number of particles to be banked on surfaces per + process (int) + :max_source_files: Maximum number of surface source files to be created (int) + :mcpl: Output in the form of an MCPL-file (bool) + :cell: Cell ID used to determine if particles crossing identified + surfaces are to be banked. Particles coming from or going to this + declared cell will be banked (int) + :cellfrom: Cell ID used to determine if particles crossing identified + surfaces are to be banked. Particles coming from this + declared cell will be banked (int) + :cellto: Cell ID used to determine if particles crossing identified + surfaces are to be banked. Particles going to this declared + cell will be banked (int) survival_biasing : bool Indicate whether survival biasing is to be used tabular_legendre : dict Determines if a multi-group scattering moment kernel expanded via Legendre polynomials is to be converted to a tabular distribution or - not. Accepted keys are 'enable' and 'num_points'. The value for - 'enable' is a bool stating whether the conversion to tabular is - performed; the value for 'num_points' sets the number of points to use - in the tabular distribution, should 'enable' be True. + not. Accepted keys are 'enable' and 'num_points'. The value for 'enable' + is a bool stating whether the conversion to tabular is performed; the + value for 'num_points' sets the number of points to use in the tabular + distribution, should 'enable' be True. temperature : dict Defines a default temperature and method for treating intermediate temperatures at which nuclear data doesn't exist. Accepted keys are @@ -172,18 +285,20 @@ class Settings: for 'default' should be a float representing the default temperature in Kelvin. The value for 'method' should be 'nearest' or 'interpolation'. If the method is 'nearest', 'tolerance' indicates a range of temperature - within which cross sections may be used. The value for 'range' should be - a pair a minimum and maximum temperatures which are used to indicate - that cross sections be loaded at all temperatures within the - range. 'multipole' is a boolean indicating whether or not the windowed - multipole method should be used to evaluate resolved resonance cross - sections. + within which cross sections may be used. If the method is + 'interpolation', 'tolerance' indicates the range of temperatures outside + of the available cross section temperatures where cross sections will + evaluate to the nearer bound. The value for 'range' should be a pair of + minimum and maximum temperatures which are used to indicate that cross + sections be loaded at all temperatures within the range. 'multipole' is + a boolean indicating whether or not the windowed multipole method should + be used to evaluate resolved resonance cross sections. trace : tuple or list Show detailed information about a single particle, indicated by three integers: the batch number, generation number, and particle number track : tuple or list Specify particles for which track files should be written. Each particle - is identified by a triplet with the batch number, generation number, and + is identified by a tuple with the batch number, generation number, and particle number. trigger_active : bool Indicate whether tally triggers are used @@ -193,33 +308,58 @@ class Settings: Maximum number of batches simulated. If this is set, the number of batches specified via ``batches`` is interpreted as the minimum number of batches + uniform_source_sampling : bool + Whether to sampling among multiple sources uniformly, applying their + strengths as weights to sampled particles. ufs_mesh : openmc.RegularMesh Mesh to be used for redistributing source sites via the uniform fission site (UFS) method. + use_decay_photons : bool + Produce decay photons from neutron reactions instead of prompt verbosity : int Verbosity during simulation between 1 and 10. Verbosity levels are described in :ref:`verbosity`. volume_calculations : VolumeCalculation or iterable of VolumeCalculation Stochastic volume calculation specifications - weight_windows : WeightWindows iterable of WeightWindows + weight_windows : WeightWindowsList Weight windows to use for variance reduction .. versionadded:: 0.13 + weight_window_checkpoints : dict + Indicates the checkpoints for weight window split/roulettes. Valid keys + include "collision" and "surface". Values must be of type bool. + + .. versionadded:: 0.14.0 + weight_window_generators : WeightWindowGenerator or iterable of WeightWindowGenerator + Weight windows generation parameters to apply during simulation + + .. versionadded:: 0.14.0 + + create_delayed_neutrons : bool + Whether delayed neutrons are created in fission. + + .. versionadded:: 0.13.3 weight_windows_on : bool Whether weight windows are enabled .. versionadded:: 0.13 + + weight_windows_file: Pathlike + Path to a weight window file to load during simulation initialization + + .. versionadded::0.14.0 write_initial_source : bool Indicate whether to write the initial source distribution to file """ - def __init__(self): + def __init__(self, **kwargs): self._run_mode = RunMode.EIGENVALUE self._batches = None self._generations_per_batch = None self._inactive = None self._max_lost_particles = None self._rel_max_lost_particles = None + self._max_write_lost_particles = None self._particles = None self._keff_trigger = None @@ -228,14 +368,19 @@ class Settings: self._max_order = None # Source subelement - self._source = cv.CheckedList(Source, 'source distributions') + self._source = cv.CheckedList(SourceBase, 'source distributions') + self._source_rejection_fraction = None self._confidence_intervals = None self._electron_treatment = None self._photon_transport = None + self._plot_seed = None self._ptables = None + self._uniform_source_sampling = None self._seed = None + self._stride = None self._survival_biasing = None + self._free_gas_threshold = None # Shannon entropy mesh self._entropy_mesh = None @@ -247,6 +392,9 @@ class Settings: self._output = None + # Iterated Fission Probability + self._ifp_n_generation = None + # Output options self._statepoint = {} self._sourcepoint = {} @@ -276,247 +424,118 @@ class Settings: VolumeCalculation, 'volume calculations') self._create_fission_neutrons = None + self._create_delayed_neutrons = None self._delayed_photon_scaling = None self._material_cell_offsets = None self._log_grid_bins = None self._event_based = None self._max_particles_in_flight = None + self._max_particle_events = None self._write_initial_source = None - self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows') + self._weight_windows = WeightWindowsList() + self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators') self._weight_windows_on = None - self._max_splits = None + self._weight_windows_file = None + self._weight_window_checkpoints = {} + self._max_history_splits = None + self._max_tracks = None + self._max_secondaries = None + self._use_decay_photons = None + + self._random_ray = {} + + for key, value in kwargs.items(): + setattr(self, key, value) @property - def run_mode(self): + def run_mode(self) -> str: return self._run_mode.value - @property - def batches(self): - return self._batches - - @property - def generations_per_batch(self): - return self._generations_per_batch - - @property - def inactive(self): - return self._inactive - - @property - def max_lost_particles(self): - return self._max_lost_particles - - @property - def rel_max_lost_particles(self): - return self._rel_max_lost_particles - - @property - def particles(self): - return self._particles - - @property - def keff_trigger(self): - return self._keff_trigger - - @property - def energy_mode(self): - return self._energy_mode - - @property - def max_order(self): - return self._max_order - - @property - def source(self): - return self._source - - @property - def confidence_intervals(self): - return self._confidence_intervals - - @property - def electron_treatment(self): - return self._electron_treatment - - @property - def ptables(self): - return self._ptables - - @property - def photon_transport(self): - return self._photon_transport - - @property - def seed(self): - return self._seed - - @property - def survival_biasing(self): - return self._survival_biasing - - @property - def entropy_mesh(self): - return self._entropy_mesh - - @property - def trigger_active(self): - return self._trigger_active - - @property - def trigger_max_batches(self): - return self._trigger_max_batches - - @property - def trigger_batch_interval(self): - return self._trigger_batch_interval - - @property - def output(self): - return self._output - - @property - def sourcepoint(self): - return self._sourcepoint - - @property - def statepoint(self): - return self._statepoint - - @property - def surf_source_read(self): - return self._surf_source_read - - @property - def surf_source_write(self): - return self._surf_source_write - - @property - def no_reduce(self): - return self._no_reduce - - @property - def verbosity(self): - return self._verbosity - - @property - def tabular_legendre(self): - return self._tabular_legendre - - @property - def temperature(self): - return self._temperature - - @property - def trace(self): - return self._trace - - @property - def track(self): - return self._track - - @property - def cutoff(self): - return self._cutoff - - @property - def ufs_mesh(self): - return self._ufs_mesh - - @property - def resonance_scattering(self): - return self._resonance_scattering - - @property - def volume_calculations(self): - return self._volume_calculations - - @property - def create_fission_neutrons(self): - return self._create_fission_neutrons - - @property - def delayed_photon_scaling(self): - return self._delayed_photon_scaling - - @property - def material_cell_offsets(self): - return self._material_cell_offsets - - @property - def log_grid_bins(self): - return self._log_grid_bins - - @property - def event_based(self): - return self._event_based - - @property - def max_particles_in_flight(self): - return self._max_particles_in_flight - - @property - def write_initial_source(self): - return self._write_initial_source - - @property - def weight_windows(self): - return self._weight_windows - - @property - def weight_windows_on(self): - return self._weight_windows_on - - @property - def max_splits(self): - return self._max_splits - @run_mode.setter - def run_mode(self, run_mode): + def run_mode(self, run_mode: str): cv.check_value('run mode', run_mode, {x.value for x in RunMode}) for mode in RunMode: if mode.value == run_mode: self._run_mode = mode + @property + def batches(self) -> int: + return self._batches + @batches.setter - def batches(self, batches): + def batches(self, batches: int): cv.check_type('batches', batches, Integral) cv.check_greater_than('batches', batches, 0) self._batches = batches + @property + def generations_per_batch(self) -> int: + return self._generations_per_batch + @generations_per_batch.setter - def generations_per_batch(self, generations_per_batch): + def generations_per_batch(self, generations_per_batch: int): cv.check_type('generations per patch', generations_per_batch, Integral) cv.check_greater_than('generations per batch', generations_per_batch, 0) self._generations_per_batch = generations_per_batch + @property + def inactive(self) -> int: + return self._inactive + @inactive.setter - def inactive(self, inactive): + def inactive(self, inactive: int): cv.check_type('inactive batches', inactive, Integral) cv.check_greater_than('inactive batches', inactive, 0, True) self._inactive = inactive + @property + def max_lost_particles(self) -> int: + return self._max_lost_particles + @max_lost_particles.setter - def max_lost_particles(self, max_lost_particles): + def max_lost_particles(self, max_lost_particles: int): cv.check_type('max_lost_particles', max_lost_particles, Integral) cv.check_greater_than('max_lost_particles', max_lost_particles, 0) self._max_lost_particles = max_lost_particles + @property + def rel_max_lost_particles(self) -> float: + return self._rel_max_lost_particles + @rel_max_lost_particles.setter - def rel_max_lost_particles(self, rel_max_lost_particles): + def rel_max_lost_particles(self, rel_max_lost_particles: float): cv.check_type('rel_max_lost_particles', rel_max_lost_particles, Real) cv.check_greater_than('rel_max_lost_particles', rel_max_lost_particles, 0) cv.check_less_than('rel_max_lost_particles', rel_max_lost_particles, 1) self._rel_max_lost_particles = rel_max_lost_particles + @property + def max_write_lost_particles(self) -> int: + return self._max_write_lost_particles + + @max_write_lost_particles.setter + def max_write_lost_particles(self, max_write_lost_particles: int): + cv.check_type('max_write_lost_particles', max_write_lost_particles, Integral) + cv.check_greater_than('max_write_lost_particles', max_write_lost_particles, 0) + self._max_write_lost_particles = max_write_lost_particles + + @property + def particles(self) -> int: + return self._particles + @particles.setter - def particles(self, particles): + def particles(self, particles: int): cv.check_type('particles', particles, Integral) cv.check_greater_than('particles', particles, 0) self._particles = particles + @property + def keff_trigger(self) -> dict: + return self._keff_trigger + @keff_trigger.setter - def keff_trigger(self, keff_trigger): + def keff_trigger(self, keff_trigger: dict): if not isinstance(keff_trigger, dict): msg = f'Unable to set a trigger on keff from "{keff_trigger}" ' \ 'which is not a Python dictionary' @@ -544,28 +563,166 @@ class Settings: self._keff_trigger = keff_trigger + @property + def energy_mode(self) -> str: + return self._energy_mode + @energy_mode.setter - def energy_mode(self, energy_mode): + def energy_mode(self, energy_mode: str): cv.check_value('energy mode', energy_mode, ['continuous-energy', 'multi-group']) self._energy_mode = energy_mode + @property + def max_order(self) -> int: + return self._max_order + @max_order.setter - def max_order(self, max_order): + def max_order(self, max_order: int | None): if max_order is not None: cv.check_type('maximum scattering order', max_order, Integral) cv.check_greater_than('maximum scattering order', max_order, 0, True) self._max_order = max_order + @property + def source(self) -> list[SourceBase]: + return self._source + @source.setter - def source(self, source): + def source(self, source: SourceBase | Iterable[SourceBase]): if not isinstance(source, MutableSequence): source = [source] - self._source = cv.CheckedList(Source, 'source distributions', source) + self._source = cv.CheckedList(SourceBase, 'source distributions', source) + + @property + def confidence_intervals(self) -> bool: + return self._confidence_intervals + + @confidence_intervals.setter + def confidence_intervals(self, confidence_intervals: bool): + cv.check_type('confidence interval', confidence_intervals, bool) + self._confidence_intervals = confidence_intervals + + @property + def electron_treatment(self) -> str: + return self._electron_treatment + + @electron_treatment.setter + def electron_treatment(self, electron_treatment: str): + cv.check_value('electron treatment', electron_treatment, ['led', 'ttb']) + self._electron_treatment = electron_treatment + + @property + def ptables(self) -> bool: + return self._ptables + + @ptables.setter + def ptables(self, ptables: bool): + cv.check_type('probability tables', ptables, bool) + self._ptables = ptables + + @property + def photon_transport(self) -> bool: + return self._photon_transport + + @photon_transport.setter + def photon_transport(self, photon_transport: bool): + cv.check_type('photon transport', photon_transport, bool) + self._photon_transport = photon_transport + + @property + def uniform_source_sampling(self) -> bool: + return self._uniform_source_sampling + + @uniform_source_sampling.setter + def uniform_source_sampling(self, uniform_source_sampling: bool): + cv.check_type('strength as weights', uniform_source_sampling, bool) + self._uniform_source_sampling = uniform_source_sampling + + @property + def plot_seed(self): + return self._plot_seed + + @plot_seed.setter + def plot_seed(self, seed): + cv.check_type('random plot color seed', seed, Integral) + cv.check_greater_than('random plot color seed', seed, 0) + self._plot_seed = seed + + @property + def seed(self) -> int: + return self._seed + + @seed.setter + def seed(self, seed: int): + cv.check_type('random number generator seed', seed, Integral) + cv.check_greater_than('random number generator seed', seed, 0) + self._seed = seed + + @property + def stride(self) -> int: + return self._stride + + @stride.setter + def stride(self, stride: int): + cv.check_type('random number generator stride', stride, Integral) + cv.check_greater_than('random number generator stride', stride, 0) + self._stride = stride + + @property + def survival_biasing(self) -> bool: + return self._survival_biasing + + @survival_biasing.setter + def survival_biasing(self, survival_biasing: bool): + cv.check_type('survival biasing', survival_biasing, bool) + self._survival_biasing = survival_biasing + + @property + def entropy_mesh(self) -> RegularMesh: + return self._entropy_mesh + + @entropy_mesh.setter + def entropy_mesh(self, entropy: RegularMesh): + cv.check_type('entropy mesh', entropy, RegularMesh) + self._entropy_mesh = entropy + + @property + def trigger_active(self) -> bool: + return self._trigger_active + + @trigger_active.setter + def trigger_active(self, trigger_active: bool): + cv.check_type('trigger active', trigger_active, bool) + self._trigger_active = trigger_active + + @property + def trigger_max_batches(self) -> int: + return self._trigger_max_batches + + @trigger_max_batches.setter + def trigger_max_batches(self, trigger_max_batches: int): + cv.check_type('trigger maximum batches', trigger_max_batches, Integral) + cv.check_greater_than('trigger maximum batches', trigger_max_batches, 0) + self._trigger_max_batches = trigger_max_batches + + @property + def trigger_batch_interval(self) -> int: + return self._trigger_batch_interval + + @trigger_batch_interval.setter + def trigger_batch_interval(self, trigger_batch_interval: int): + cv.check_type('trigger batch interval', trigger_batch_interval, Integral) + cv.check_greater_than('trigger batch interval', trigger_batch_interval, 0) + self._trigger_batch_interval = trigger_batch_interval + + @property + def output(self) -> dict: + return self._output @output.setter - def output(self, output): + def output(self, output: dict): cv.check_type('output', output, Mapping) for key, value in output.items(): cv.check_value('output key', key, ('summary', 'tallies', 'path')) @@ -575,15 +732,12 @@ class Settings: cv.check_type("output['path']", value, str) self._output = output - @verbosity.setter - def verbosity(self, verbosity): - cv.check_type('verbosity', verbosity, Integral) - cv.check_greater_than('verbosity', verbosity, 1, True) - cv.check_less_than('verbosity', verbosity, 10, True) - self._verbosity = verbosity + @property + def sourcepoint(self) -> dict: + return self._sourcepoint @sourcepoint.setter - def sourcepoint(self, sourcepoint): + def sourcepoint(self, sourcepoint: dict): cv.check_type('sourcepoint options', sourcepoint, Mapping) for key, value in sourcepoint.items(): if key == 'batches': @@ -596,13 +750,19 @@ class Settings: cv.check_type('sourcepoint write', value, bool) elif key == 'overwrite': cv.check_type('sourcepoint overwrite', value, bool) + elif key == 'mcpl': + cv.check_type('sourcepoint mcpl', value, bool) else: raise ValueError(f"Unknown key '{key}' encountered when " "setting sourcepoint options.") self._sourcepoint = sourcepoint + @property + def statepoint(self) -> dict: + return self._statepoint + @statepoint.setter - def statepoint(self, statepoint): + def statepoint(self, statepoint: dict): cv.check_type('statepoint options', statepoint, Mapping) for key, value in statepoint.items(): if key == 'batches': @@ -614,119 +774,96 @@ class Settings: "setting statepoint options.") self._statepoint = statepoint + @property + def surf_source_read(self) -> dict: + return self._surf_source_read + @surf_source_read.setter - def surf_source_read(self, surf_source_read): - cv.check_type('surface source reading options', surf_source_read, Mapping) - for key, value in surf_source_read.items(): + def surf_source_read(self, ssr: dict): + cv.check_type('surface source reading options', ssr, Mapping) + for key, value in ssr.items(): cv.check_value('surface source reading key', key, ('path')) if key == 'path': - cv.check_type('path to surface source file', value, str) - self._surf_source_read = surf_source_read + cv.check_type('path to surface source file', value, PathLike) + self._surf_source_read = dict(ssr) + + # Resolve path to surface source file + if 'path' in ssr: + self._surf_source_read['path'] = input_path(ssr['path']) + + @property + def surf_source_write(self) -> dict: + return self._surf_source_write @surf_source_write.setter - def surf_source_write(self, surf_source_write): - cv.check_type('surface source writing options', surf_source_write, Mapping) + def surf_source_write(self, surf_source_write: dict): + cv.check_type("surface source writing options", surf_source_write, Mapping) for key, value in surf_source_write.items(): - cv.check_value('surface source writing key', key, - ('surface_ids', 'max_particles')) - if key == 'surface_ids': - cv.check_type('surface ids for source banking', value, - Iterable, Integral) + cv.check_value( + "surface source writing key", + key, + ("surface_ids", "max_particles", "max_source_files", "mcpl", "cell", "cellfrom", "cellto"), + ) + if key == "surface_ids": + cv.check_type( + "surface ids for source banking", value, Iterable, Integral + ) for surf_id in value: - cv.check_greater_than('surface id for source banking', - surf_id, 0) - elif key == 'max_particles': - cv.check_type('maximum particle banks on surfaces per process', - value, Integral) - cv.check_greater_than('maximum particle banks on surfaces per process', - value, 0) + cv.check_greater_than("surface id for source banking", surf_id, 0) + + elif key == "mcpl": + cv.check_type("write to an MCPL-format file", value, bool) + elif key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"): + name = { + "max_particles": "maximum particle banks on surfaces per process", + "max_source_files": "maximun surface source files to be written", + "cell": "Cell ID for source banking (from or to)", + "cellfrom": "Cell ID for source banking (from only)", + "cellto": "Cell ID for source banking (to only)", + }[key] + cv.check_type(name, value, Integral) + cv.check_greater_than(name, value, 0) + self._surf_source_write = surf_source_write - @confidence_intervals.setter - def confidence_intervals(self, confidence_intervals): - cv.check_type('confidence interval', confidence_intervals, bool) - self._confidence_intervals = confidence_intervals - - @electron_treatment.setter - def electron_treatment(self, electron_treatment): - cv.check_value('electron treatment', electron_treatment, ['led', 'ttb']) - self._electron_treatment = electron_treatment - - @photon_transport.setter - def photon_transport(self, photon_transport): - cv.check_type('photon transport', photon_transport, bool) - self._photon_transport = photon_transport - - @ptables.setter - def ptables(self, ptables): - cv.check_type('probability tables', ptables, bool) - self._ptables = ptables - - @seed.setter - def seed(self, seed): - cv.check_type('random number generator seed', seed, Integral) - cv.check_greater_than('random number generator seed', seed, 0) - self._seed = seed - - @survival_biasing.setter - def survival_biasing(self, survival_biasing): - cv.check_type('survival biasing', survival_biasing, bool) - self._survival_biasing = survival_biasing - - @cutoff.setter - def cutoff(self, cutoff): - if not isinstance(cutoff, Mapping): - msg = f'Unable to set cutoff from "{cutoff}" which is not a '\ - 'Python dictionary' - raise ValueError(msg) - for key in cutoff: - if key == 'weight': - cv.check_type('weight cutoff', cutoff[key], Real) - cv.check_greater_than('weight cutoff', cutoff[key], 0.0) - elif key == 'weight_avg': - cv.check_type('average survival weight', cutoff[key], Real) - cv.check_greater_than('average survival weight', - cutoff[key], 0.0) - elif key in ['energy_neutron', 'energy_photon', 'energy_electron', - 'energy_positron']: - cv.check_type('energy cutoff', cutoff[key], Real) - cv.check_greater_than('energy cutoff', cutoff[key], 0.0) - else: - msg = f'Unable to set cutoff to "{key}" which is unsupported ' \ - 'by OpenMC' - - self._cutoff = cutoff - - @entropy_mesh.setter - def entropy_mesh(self, entropy): - cv.check_type('entropy mesh', entropy, RegularMesh) - self._entropy_mesh = entropy - - @trigger_active.setter - def trigger_active(self, trigger_active): - cv.check_type('trigger active', trigger_active, bool) - self._trigger_active = trigger_active - - @trigger_max_batches.setter - def trigger_max_batches(self, trigger_max_batches): - cv.check_type('trigger maximum batches', trigger_max_batches, Integral) - cv.check_greater_than('trigger maximum batches', trigger_max_batches, 0) - self._trigger_max_batches = trigger_max_batches - - @trigger_batch_interval.setter - def trigger_batch_interval(self, trigger_batch_interval): - cv.check_type('trigger batch interval', trigger_batch_interval, Integral) - cv.check_greater_than('trigger batch interval', trigger_batch_interval, 0) - self._trigger_batch_interval = trigger_batch_interval + @property + def no_reduce(self) -> bool: + return self._no_reduce @no_reduce.setter - def no_reduce(self, no_reduce): + def no_reduce(self, no_reduce: bool): cv.check_type('no reduction option', no_reduce, bool) self._no_reduce = no_reduce + @property + def verbosity(self) -> int: + return self._verbosity + + @verbosity.setter + def verbosity(self, verbosity: int): + cv.check_type('verbosity', verbosity, Integral) + cv.check_greater_than('verbosity', verbosity, 1, True) + cv.check_less_than('verbosity', verbosity, 10, True) + self._verbosity = verbosity + + @property + def ifp_n_generation(self) -> int: + return self._ifp_n_generation + + @ifp_n_generation.setter + def ifp_n_generation(self, ifp_n_generation: int): + if ifp_n_generation is not None: + cv.check_type("number of generations", ifp_n_generation, Integral) + cv.check_greater_than("number of generations", ifp_n_generation, 0) + self._ifp_n_generation = ifp_n_generation + + @property + def tabular_legendre(self) -> dict: + return self._tabular_legendre + @tabular_legendre.setter - def tabular_legendre(self, tabular_legendre): + def tabular_legendre(self, tabular_legendre: dict): cv.check_type('tabular_legendre settings', tabular_legendre, Mapping) for key, value in tabular_legendre.items(): cv.check_value('tabular_legendre key', key, @@ -738,8 +875,12 @@ class Settings: cv.check_greater_than('num_points tabular_legendre', value, 0) self._tabular_legendre = tabular_legendre + @property + def temperature(self) -> dict: + return self._temperature + @temperature.setter - def temperature(self, temperature): + def temperature(self, temperature: dict): cv.check_type('temperature settings', temperature, Mapping) for key, value in temperature.items(): @@ -762,8 +903,12 @@ class Settings: self._temperature = temperature + @property + def trace(self) -> Iterable: + return self._trace + @trace.setter - def trace(self, trace): + def trace(self, trace: Iterable): cv.check_type('trace', trace, Iterable, Integral) cv.check_length('trace', trace, 3) cv.check_greater_than('trace batch', trace[0], 0) @@ -771,29 +916,73 @@ class Settings: cv.check_greater_than('trace particle', trace[2], 0) self._trace = trace + @property + def track(self) -> Iterable[Iterable[int]]: + return self._track + @track.setter - def track(self, track): - cv.check_type('track', track, Iterable, Integral) - if len(track) % 3 != 0: - msg = f'Unable to set the track to "{track}" since its length is ' \ - 'not a multiple of 3' - raise ValueError(msg) - for t in zip(track[::3], track[1::3], track[2::3]): + def track(self, track: Iterable[Iterable[int]]): + cv.check_type('track', track, Sequence) + for t in track: + if len(t) != 3: + msg = f'Unable to set the track to "{t}" since its length is not 3' + raise ValueError(msg) cv.check_greater_than('track batch', t[0], 0) - cv.check_greater_than('track generation', t[0], 0) - cv.check_greater_than('track particle', t[0], 0) + cv.check_greater_than('track generation', t[1], 0) + cv.check_greater_than('track particle', t[2], 0) + cv.check_type('track batch', t[0], Integral) + cv.check_type('track generation', t[1], Integral) + cv.check_type('track particle', t[2], Integral) self._track = track + @property + def cutoff(self) -> dict: + return self._cutoff + + @cutoff.setter + def cutoff(self, cutoff: dict): + if not isinstance(cutoff, Mapping): + msg = f'Unable to set cutoff from "{cutoff}" which is not a '\ + 'Python dictionary' + raise ValueError(msg) + for key in cutoff: + if key == 'weight': + cv.check_type('weight cutoff', cutoff[key], Real) + cv.check_greater_than('weight cutoff', cutoff[key], 0.0) + elif key == 'weight_avg': + cv.check_type('average survival weight', cutoff[key], Real) + cv.check_greater_than('average survival weight', + cutoff[key], 0.0) + elif key == 'survival_normalization': + cv.check_type('survival normalization', cutoff[key], bool) + elif key in ['energy_neutron', 'energy_photon', 'energy_electron', + 'energy_positron']: + cv.check_type('energy cutoff', cutoff[key], Real) + cv.check_greater_than('energy cutoff', cutoff[key], 0.0) + else: + msg = f'Unable to set cutoff to "{key}" which is unsupported ' \ + 'by OpenMC' + + self._cutoff = cutoff + + @property + def ufs_mesh(self) -> RegularMesh: + return self._ufs_mesh + @ufs_mesh.setter - def ufs_mesh(self, ufs_mesh): + def ufs_mesh(self, ufs_mesh: RegularMesh): cv.check_type('UFS mesh', ufs_mesh, RegularMesh) cv.check_length('UFS mesh dimension', ufs_mesh.dimension, 3) cv.check_length('UFS mesh lower-left corner', ufs_mesh.lower_left, 3) cv.check_length('UFS mesh upper-right corner', ufs_mesh.upper_right, 3) self._ufs_mesh = ufs_mesh + @property + def resonance_scattering(self) -> dict: + return self._resonance_scattering + @resonance_scattering.setter - def resonance_scattering(self, res): + def resonance_scattering(self, res: dict): cv.check_type('resonance scattering settings', res, Mapping) keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') for key, value in res.items(): @@ -816,67 +1005,271 @@ class Settings: Iterable, str) self._resonance_scattering = res + @property + def volume_calculations(self) -> list[VolumeCalculation]: + return self._volume_calculations + @volume_calculations.setter - def volume_calculations(self, vol_calcs): + def volume_calculations( + self, vol_calcs: VolumeCalculation | Iterable[VolumeCalculation] + ): if not isinstance(vol_calcs, MutableSequence): vol_calcs = [vol_calcs] self._volume_calculations = cv.CheckedList( VolumeCalculation, 'stochastic volume calculations', vol_calcs) + @property + def create_fission_neutrons(self) -> bool: + return self._create_fission_neutrons + @create_fission_neutrons.setter - def create_fission_neutrons(self, create_fission_neutrons): + def create_fission_neutrons(self, create_fission_neutrons: bool): cv.check_type('Whether create fission neutrons', create_fission_neutrons, bool) self._create_fission_neutrons = create_fission_neutrons + @property + def create_delayed_neutrons(self) -> bool: + return self._create_delayed_neutrons + + @create_delayed_neutrons.setter + def create_delayed_neutrons(self, create_delayed_neutrons: bool): + cv.check_type('Whether create only prompt neutrons', + create_delayed_neutrons, bool) + self._create_delayed_neutrons = create_delayed_neutrons + + @property + def delayed_photon_scaling(self) -> bool: + return self._delayed_photon_scaling + @delayed_photon_scaling.setter - def delayed_photon_scaling(self, value): + def delayed_photon_scaling(self, value: bool): cv.check_type('delayed photon scaling', value, bool) self._delayed_photon_scaling = value - @event_based.setter - def event_based(self, value): - cv.check_type('event based', value, bool) - self._event_based = value - - @max_particles_in_flight.setter - def max_particles_in_flight(self, value): - cv.check_type('max particles in flight', value, Integral) - cv.check_greater_than('max particles in flight', value, 0) - self._max_particles_in_flight = value + @property + def material_cell_offsets(self) -> bool: + return self._material_cell_offsets @material_cell_offsets.setter - def material_cell_offsets(self, value): + def material_cell_offsets(self, value: bool): cv.check_type('material cell offsets', value, bool) self._material_cell_offsets = value + @property + def log_grid_bins(self) -> int: + return self._log_grid_bins + @log_grid_bins.setter - def log_grid_bins(self, log_grid_bins): + def log_grid_bins(self, log_grid_bins: int): cv.check_type('log grid bins', log_grid_bins, Real) cv.check_greater_than('log grid bins', log_grid_bins, 0) self._log_grid_bins = log_grid_bins + @property + def event_based(self) -> bool: + return self._event_based + + @event_based.setter + def event_based(self, value: bool): + cv.check_type('event based', value, bool) + self._event_based = value + + @property + def max_particles_in_flight(self) -> int: + return self._max_particles_in_flight + + @max_particles_in_flight.setter + def max_particles_in_flight(self, value: int): + cv.check_type('max particles in flight', value, Integral) + cv.check_greater_than('max particles in flight', value, 0) + self._max_particles_in_flight = value + + @property + def max_particle_events(self) -> int: + return self._max_particle_events + + @max_particle_events.setter + def max_particle_events(self, value: int): + cv.check_type('max particle events', value, Integral) + cv.check_greater_than('max particle events', value, 0) + self._max_particle_events = value + + @property + def write_initial_source(self) -> bool: + return self._write_initial_source + @write_initial_source.setter - def write_initial_source(self, value): + def write_initial_source(self, value: bool): cv.check_type('write initial source', value, bool) self._write_initial_source = value + @property + def weight_windows(self) -> WeightWindowsList: + return self._weight_windows + @weight_windows.setter - def weight_windows(self, value): - if not isinstance(value, MutableSequence): + def weight_windows(self, value: WeightWindows | Sequence[WeightWindows]): + if not isinstance(value, Sequence): value = [value] - self._weight_windows = cv.CheckedList(WeightWindows, 'weight windows', value) + self._weight_windows = WeightWindowsList(value) + + @property + def weight_windows_on(self) -> bool: + return self._weight_windows_on @weight_windows_on.setter - def weight_windows_on(self, value): + def weight_windows_on(self, value: bool): cv.check_type('weight windows on', value, bool) self._weight_windows_on = value - @max_splits.setter - def max_splits(self, value): + @property + def weight_window_checkpoints(self) -> dict: + return self._weight_window_checkpoints + + @weight_window_checkpoints.setter + def weight_window_checkpoints(self, weight_window_checkpoints: dict): + for key in weight_window_checkpoints.keys(): + cv.check_value('weight_window_checkpoints', key, ('collision', 'surface')) + self._weight_window_checkpoints = weight_window_checkpoints + + @property + def max_splits(self): + raise AttributeError('max_splits has been deprecated. Please use max_history_splits instead') + + @property + def max_history_splits(self) -> int: + return self._max_history_splits + + @max_history_splits.setter + def max_history_splits(self, value: int): cv.check_type('maximum particle splits', value, Integral) - cv.check_greater_than('max particles in flight', value, 0) - self._max_splits = value + cv.check_greater_than('max particle splits', value, 0) + self._max_history_splits = value + + @property + def max_secondaries(self) -> int: + return self._max_secondaries + + @max_secondaries.setter + def max_secondaries(self, value: int): + cv.check_type('maximum secondary bank size', value, Integral) + cv.check_greater_than('max secondary bank size', value, 0) + self._max_secondaries = value + + @property + def max_tracks(self) -> int: + return self._max_tracks + + @max_tracks.setter + def max_tracks(self, value: int): + cv.check_type('maximum particle tracks', value, Integral) + cv.check_greater_than('maximum particle tracks', value, 0, True) + self._max_tracks = value + + @property + def weight_windows_file(self) -> PathLike | None: + return self._weight_windows_file + + @weight_windows_file.setter + def weight_windows_file(self, value: PathLike): + cv.check_type('weight windows file', value, PathLike) + self._weight_windows_file = input_path(value) + + @property + def weight_window_generators(self) -> list[WeightWindowGenerator]: + return self._weight_window_generators + + @weight_window_generators.setter + def weight_window_generators(self, wwgs): + if not isinstance(wwgs, MutableSequence): + wwgs = [wwgs] + self._weight_window_generators = cv.CheckedList(WeightWindowGenerator, 'weight window generators', wwgs) + + @property + def random_ray(self) -> dict: + return self._random_ray + + @random_ray.setter + def random_ray(self, random_ray: dict): + if not isinstance(random_ray, Mapping): + raise ValueError(f'Unable to set random_ray from "{random_ray}" ' + 'which is not a dict.') + for key, value in random_ray.items(): + if key == 'distance_active': + cv.check_type('active ray length', value, Real) + cv.check_greater_than('active ray length', value, 0.0) + elif key == 'distance_inactive': + cv.check_type('inactive ray length', value, Real) + cv.check_greater_than('inactive ray length', + value, 0.0, True) + elif key == 'ray_source': + cv.check_type('random ray source', value, SourceBase) + elif key == 'volume_estimator': + cv.check_value('volume estimator', value, + ('naive', 'simulation_averaged', + 'hybrid')) + elif key == 'source_shape': + cv.check_value('source shape', value, + ('flat', 'linear', 'linear_xy')) + elif key == 'volume_normalized_flux_tallies': + cv.check_type('volume normalized flux tallies', value, bool) + elif key == 'adjoint': + cv.check_type('adjoint', value, bool) + elif key == 'source_region_meshes': + cv.check_type('source region meshes', value, Iterable) + for mesh, domains in value: + cv.check_type('mesh', mesh, MeshBase) + cv.check_type('domains', domains, Iterable) + valid_types = (openmc.Material, openmc.Cell, openmc.Universe) + for domain in domains: + if not isinstance(domain, valid_types): + raise ValueError( + f'Invalid domain type: {type(domain)}. Expected ' + 'openmc.Material, openmc.Cell, or openmc.Universe.') + elif key == 'sample_method': + cv.check_value('sample method', value, + ('prng', 'halton')) + elif key == 'diagonal_stabilization_rho': + cv.check_type('diagonal stabilization rho', value, Real) + cv.check_greater_than('diagonal stabilization rho', + value, 0.0, True) + else: + raise ValueError(f'Unable to set random ray to "{key}" which is ' + 'unsupported by OpenMC') + + self._random_ray = random_ray + + @property + def use_decay_photons(self) -> bool: + return self._use_decay_photons + + @use_decay_photons.setter + def use_decay_photons(self, value): + cv.check_type('use decay photons', value, bool) + self._use_decay_photons = value + + @property + def source_rejection_fraction(self) -> float: + return self._source_rejection_fraction + + @source_rejection_fraction.setter + def source_rejection_fraction(self, source_rejection_fraction: float): + cv.check_type('source_rejection_fraction', source_rejection_fraction, Real) + cv.check_greater_than('source_rejection_fraction', source_rejection_fraction, 0) + cv.check_less_than('source_rejection_fraction', source_rejection_fraction, 1) + self._source_rejection_fraction = source_rejection_fraction + + @property + def free_gas_threshold(self) -> float | None: + return self._free_gas_threshold + + @free_gas_threshold.setter + def free_gas_threshold(self, free_gas_threshold: float | None): + if free_gas_threshold is not None: + cv.check_type('free gas threshold', free_gas_threshold, Real) + cv.check_greater_than('free gas threshold', free_gas_threshold, 0.0) + self._free_gas_threshold = free_gas_threshold def _create_run_mode_subelement(self, root): elem = ET.SubElement(root, "run_mode") @@ -907,6 +1300,11 @@ class Settings: element = ET.SubElement(root, "rel_max_lost_particles") element.text = str(self._rel_max_lost_particles) + def _create_max_write_lost_particles_subelement(self, root): + if self._max_write_lost_particles is not None: + element = ET.SubElement(root, "max_write_lost_particles") + element.text = str(self._max_write_lost_particles) + def _create_particles_subelement(self, root): if self._particles is not None: element = ET.SubElement(root, "particles") @@ -929,9 +1327,19 @@ class Settings: element = ET.SubElement(root, "max_order") element.text = str(self._max_order) - def _create_source_subelement(self, root): + def _create_source_subelement(self, root, mesh_memo=None): for source in self.source: root.append(source.to_xml_element()) + if isinstance(source, IndependentSource) and isinstance(source.space, MeshSpatial): + path = f"./mesh[@id='{source.space.mesh.id}']" + if root.find(path) is None: + root.append(source.space.mesh.to_xml_element()) + if isinstance(source, MeshSource): + path = f"./mesh[@id='{source.mesh.id}']" + if root.find(path) is None: + root.append(source.mesh.to_xml_element()) + if mesh_memo is not None: + mesh_memo.add(source.mesh.id) def _create_volume_calcs_subelement(self, root): for calc in self.volume_calculations: @@ -960,6 +1368,11 @@ class Settings: subelement.text = ' '.join( str(x) for x in self._statepoint['batches']) + def _create_uniform_source_sampling_subelement(self, root): + if self._uniform_source_sampling is not None: + element = ET.SubElement(root, "uniform_source_sampling") + element.text = str(self._uniform_source_sampling).lower() + def _create_sourcepoint_subelement(self, root): if self._sourcepoint: element = ET.SubElement(root, "source_point") @@ -982,23 +1395,32 @@ class Settings: subelement = ET.SubElement(element, "overwrite_latest") subelement.text = str(self._sourcepoint['overwrite']).lower() + if 'mcpl' in self._sourcepoint: + subelement = ET.SubElement(element, "mcpl") + subelement.text = str(self._sourcepoint['mcpl']).lower() + def _create_surf_source_read_subelement(self, root): if self._surf_source_read: element = ET.SubElement(root, "surf_source_read") if 'path' in self._surf_source_read: subelement = ET.SubElement(element, "path") - subelement.text = self._surf_source_read['path'] + subelement.text = str(self._surf_source_read['path']) def _create_surf_source_write_subelement(self, root): if self._surf_source_write: element = ET.SubElement(root, "surf_source_write") - if 'surface_ids' in self._surf_source_write: + if "surface_ids" in self._surf_source_write: subelement = ET.SubElement(element, "surface_ids") - subelement.text = ' '.join( - str(x) for x in self._surf_source_write['surface_ids']) - if 'max_particles' in self._surf_source_write: - subelement = ET.SubElement(element, "max_particles") - subelement.text = str(self._surf_source_write['max_particles']) + subelement.text = " ".join( + str(x) for x in self._surf_source_write["surface_ids"] + ) + if "mcpl" in self._surf_source_write: + subelement = ET.SubElement(element, "mcpl") + subelement.text = str(self._surf_source_write["mcpl"]).lower() + for key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"): + if key in self._surf_source_write: + subelement = ET.SubElement(element, key) + subelement.text = str(self._surf_source_write[key]) def _create_confidence_intervals(self, root): if self._confidence_intervals is not None: @@ -1015,6 +1437,11 @@ class Settings: element = ET.SubElement(root, "photon_transport") element.text = str(self._photon_transport).lower() + def _create_plot_seed_subelement(self, root): + if self._plot_seed is not None: + element = ET.SubElement(root, "plot_seed") + element.text = str(self._plot_seed) + def _create_ptables_subelement(self, root): if self._ptables is not None: element = ET.SubElement(root, "ptables") @@ -1025,6 +1452,11 @@ class Settings: element = ET.SubElement(root, "seed") element.text = str(self._seed) + def _create_stride_subelement(self, root): + if self._stride is not None: + element = ET.SubElement(root, "stride") + element.text = str(self._stride) + def _create_survival_biasing_subelement(self, root): if self._survival_biasing is not None: element = ET.SubElement(root, "survival_biasing") @@ -1035,27 +1467,38 @@ class Settings: element = ET.SubElement(root, "cutoff") for key, value in self._cutoff.items(): subelement = ET.SubElement(element, key) - subelement.text = str(value) + subelement.text = str(value) if key != 'survival_normalization' \ + else str(value).lower() - def _create_entropy_mesh_subelement(self, root): - if self.entropy_mesh is not None: - # use default heuristic for entropy mesh if not set by user - if self.entropy_mesh.dimension is None: - if self.particles is None: - raise RuntimeError("Number of particles must be set in order to " \ - "use entropy mesh dimension heuristic") - else: - n = ceil((self.particles / 20.0)**(1.0 / 3.0)) - d = len(self.entropy_mesh.lower_left) - self.entropy_mesh.dimension = (n,)*d + def _create_entropy_mesh_subelement(self, root, mesh_memo=None): + if self.entropy_mesh is None: + return - # See if a element already exists -- if not, add it - path = f"./mesh[@id='{self.entropy_mesh.id}']" - if root.find(path) is None: - root.append(self.entropy_mesh.to_xml_element()) + # use default heuristic for entropy mesh if not set by user + if self.entropy_mesh.dimension is None: + if self.particles is None: + raise RuntimeError("Number of particles must be set in order to " \ + "use entropy mesh dimension heuristic") + else: + n = ceil((self.particles / 20.0)**(1.0 / 3.0)) + d = len(self.entropy_mesh.lower_left) + self.entropy_mesh.dimension = (n,)*d - subelement = ET.SubElement(root, "entropy_mesh") - subelement.text = str(self.entropy_mesh.id) + # add mesh ID to this element + subelement = ET.SubElement(root, "entropy_mesh") + subelement.text = str(self.entropy_mesh.id) + + # If this mesh has already been written outside the + # settings element, skip writing it again + if mesh_memo and self.entropy_mesh.id in mesh_memo: + return + + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{self.entropy_mesh.id}']" + if root.find(path) is None: + root.append(self.entropy_mesh.to_xml_element()) + if mesh_memo is not None: + mesh_memo.add(self.entropy_mesh.id) def _create_trigger_subelement(self, root): if self._trigger_active is not None: @@ -1076,6 +1519,11 @@ class Settings: element = ET.SubElement(root, "no_reduce") element.text = str(self._no_reduce).lower() + def _create_ifp_n_generation_subelement(self, root): + if self._ifp_n_generation is not None: + element = ET.SubElement(root, "ifp_n_generation") + element.text = str(self._ifp_n_generation) + def _create_tabular_legendre_subelements(self, root): if self.tabular_legendre: element = ET.SubElement(root, "tabular_legendre") @@ -1104,17 +1552,28 @@ class Settings: def _create_track_subelement(self, root): if self._track is not None: element = ET.SubElement(root, "track") - element.text = ' '.join(map(str, self._track)) + element.text = ' '.join(map(str, itertools.chain(*self._track))) - def _create_ufs_mesh_subelement(self, root): - if self.ufs_mesh is not None: - # See if a element already exists -- if not, add it - path = f"./mesh[@id='{self.ufs_mesh.id}']" - if root.find(path) is None: - root.append(self.ufs_mesh.to_xml_element()) + def _create_ufs_mesh_subelement(self, root, mesh_memo=None): + if self.ufs_mesh is None: + return - subelement = ET.SubElement(root, "ufs_mesh") - subelement.text = str(self.ufs_mesh.id) + subelement = ET.SubElement(root, "ufs_mesh") + subelement.text = str(self.ufs_mesh.id) + + if mesh_memo and self.ufs_mesh.id in mesh_memo: + return + + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{self.ufs_mesh.id}']" + if root.find(path) is None: + root.append(self.ufs_mesh.to_xml_element()) + if mesh_memo is not None: mesh_memo.add(self.ufs_mesh.id) + + def _create_use_decay_photons_subelement(self, root): + if self._use_decay_photons is not None: + element = ET.SubElement(root, "use_decay_photons") + element.text = str(self._use_decay_photons).lower() def _create_resonance_scattering_subelement(self, root): res = self.resonance_scattering @@ -1141,6 +1600,11 @@ class Settings: elem = ET.SubElement(root, "create_fission_neutrons") elem.text = str(self._create_fission_neutrons).lower() + def _create_create_delayed_neutrons_subelement(self, root): + if self._create_delayed_neutrons is not None: + elem = ET.SubElement(root, "create_delayed_neutrons") + elem.text = str(self._create_delayed_neutrons).lower() + def _create_delayed_photon_scaling_subelement(self, root): if self._delayed_photon_scaling is not None: elem = ET.SubElement(root, "delayed_photon_scaling") @@ -1156,6 +1620,11 @@ class Settings: elem = ET.SubElement(root, "max_particles_in_flight") elem.text = str(self._max_particles_in_flight).lower() + def _create_max_events_subelement(self, root): + if self._max_particle_events is not None: + elem = ET.SubElement(root, "max_particle_events") + elem.text = str(self._max_particle_events).lower() + def _create_material_cell_offsets_subelement(self, root): if self._material_cell_offsets is not None: elem = ET.SubElement(root, "material_cell_offsets") @@ -1171,24 +1640,119 @@ class Settings: elem = ET.SubElement(root, "write_initial_source") elem.text = str(self._write_initial_source).lower() - def _create_weight_windows_subelement(self, root): + def _create_weight_windows_subelement(self, root, mesh_memo=None): for ww in self._weight_windows: # Add weight window information root.append(ww.to_xml_element()) + # if this mesh has already been written, + # skip writing the mesh element + if mesh_memo and ww.mesh.id in mesh_memo: + continue + # See if a element already exists -- if not, add it path = f"./mesh[@id='{ww.mesh.id}']" if root.find(path) is None: root.append(ww.mesh.to_xml_element()) + if mesh_memo is not None: + mesh_memo.add(ww.mesh.id) + def _create_weight_windows_on_subelement(self, root): if self._weight_windows_on is not None: elem = ET.SubElement(root, "weight_windows_on") elem.text = str(self._weight_windows_on).lower() - def _create_max_splits_subelement(self, root): - if self._max_splits is not None: - elem = ET.SubElement(root, "max_splits") - elem.text = str(self._max_splits) + def _create_weight_window_generators_subelement(self, root, mesh_memo=None): + if not self.weight_window_generators: + return + elem = ET.SubElement(root, 'weight_window_generators') + for wwg in self.weight_window_generators: + elem.append(wwg.to_xml_element()) + + # ensure that mesh elements are created if needed + for wwg in self.weight_window_generators: + if mesh_memo is not None and wwg.mesh.id in mesh_memo: + continue + + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{wwg.mesh.id}']" + if root.find(path) is None: + root.append(wwg.mesh.to_xml_element()) + if mesh_memo is not None: + mesh_memo.add(wwg.mesh.id) + + def _create_weight_windows_file_element(self, root): + if self.weight_windows_file is not None: + element = ET.Element("weight_windows_file") + element.text = str(self.weight_windows_file) + root.append(element) + + def _create_weight_window_checkpoints_subelement(self, root): + if not self._weight_window_checkpoints: + return + element = ET.SubElement(root, "weight_window_checkpoints") + + if 'collision' in self._weight_window_checkpoints: + subelement = ET.SubElement(element, "collision") + subelement.text = str(self._weight_window_checkpoints['collision']).lower() + + if 'surface' in self._weight_window_checkpoints: + subelement = ET.SubElement(element, "surface") + subelement.text = str(self._weight_window_checkpoints['surface']).lower() + + def _create_max_history_splits_subelement(self, root): + if self._max_history_splits is not None: + elem = ET.SubElement(root, "max_history_splits") + elem.text = str(self._max_history_splits) + + def _create_max_secondaries_subelement(self, root): + if self._max_secondaries is not None: + elem = ET.SubElement(root, "max_secondaries") + elem.text = str(self._max_secondaries) + + def _create_max_tracks_subelement(self, root): + if self._max_tracks is not None: + elem = ET.SubElement(root, "max_tracks") + elem.text = str(self._max_tracks) + + def _create_random_ray_subelement(self, root, mesh_memo=None): + if self._random_ray: + element = ET.SubElement(root, "random_ray") + for key, value in self._random_ray.items(): + if key == 'ray_source' and isinstance(value, SourceBase): + source_element = value.to_xml_element() + element.append(source_element) + elif key == 'source_region_meshes': + subelement = ET.SubElement(element, 'source_region_meshes') + for mesh, domains in value: + mesh_elem = ET.SubElement(subelement, 'mesh') + mesh_elem.set('id', str(mesh.id)) + for domain in domains: + domain_elem = ET.SubElement(mesh_elem, 'domain') + domain_elem.set('id', str(domain.id)) + domain_elem.set('type', domain.__class__.__name__.lower()) + # See if a element already exists -- if not, add it + path = f"./mesh[@id='{mesh.id}']" + if root.find(path) is None: + root.append(mesh.to_xml_element()) + if mesh_memo is not None: + mesh_memo.add(mesh.id) + elif isinstance(value, bool): + subelement = ET.SubElement(element, key) + subelement.text = str(value).lower() + else: + subelement = ET.SubElement(element, key) + subelement.text = str(value) + + def _create_source_rejection_fraction_subelement(self, root): + if self._source_rejection_fraction is not None: + element = ET.SubElement(root, "source_rejection_fraction") + element.text = str(self._source_rejection_fraction) + + def _create_free_gas_threshold_subelement(self, root): + if self._free_gas_threshold is not None: + element = ET.SubElement(root, "free_gas_threshold") + element.text = str(self._free_gas_threshold) def _eigenvalue_from_xml_element(self, root): elem = root.find('eigenvalue') @@ -1199,6 +1763,7 @@ class Settings: self._inactive_from_xml_element(elem) self._max_lost_particles_from_xml_element(elem) self._rel_max_lost_particles_from_xml_element(elem) + self._max_write_lost_particles_from_xml_element(elem) self._generations_per_batch_from_xml_element(elem) def _run_mode_from_xml_element(self, root): @@ -1231,6 +1796,11 @@ class Settings: if text is not None: self.rel_max_lost_particles = float(text) + def _max_write_lost_particles_from_xml_element(self, root): + text = get_text(root, 'max_write_lost_particles') + if text is not None: + self.max_write_lost_particles = int(text) + def _generations_per_batch_from_xml_element(self, root): text = get_text(root, 'generations_per_batch') if text is not None: @@ -1243,9 +1813,11 @@ class Settings: threshold = float(get_text(elem, 'threshold')) self.keff_trigger = {'type': trigger, 'threshold': threshold} - def _source_from_xml_element(self, root): + def _source_from_xml_element(self, root, meshes=None): for elem in root.findall('source'): - self.source.append(Source.from_xml_element(elem)) + src = SourceBase.from_xml_element(elem, meshes) + # add newly constructed source object to the list + self.source.append(src) def _volume_calcs_from_xml_element(self, root): volume_elems = root.findall("volume_calc") @@ -1267,43 +1839,47 @@ class Settings: def _statepoint_from_xml_element(self, root): elem = root.find('state_point') if elem is not None: - text = get_text(elem, 'batches') - if text is not None: - self.statepoint['batches'] = [int(x) for x in text.split()] + batches = get_elem_list(elem, "batches", int) + if batches is not None: + self.statepoint['batches'] = batches def _sourcepoint_from_xml_element(self, root): elem = root.find('source_point') if elem is not None: - for key in ('separate', 'write', 'overwrite_latest', 'batches'): - value = get_text(elem, key) - if value is not None: - if key in ('separate', 'write'): - value = value in ('true', '1') - elif key == 'overwrite_latest': - value = value in ('true', '1') + for key in ('separate', 'write', 'overwrite_latest', 'batches', 'mcpl'): + if key in ('separate', 'write', 'mcpl', 'overwrite_latest'): + value = get_text(elem, key) in ('true', '1') + if key == 'overwrite_latest': key = 'overwrite' - else: - value = [int(x) for x in value.split()] + else: + value = get_elem_list(elem, key, int) + if value is not None: self.sourcepoint[key] = value def _surf_source_read_from_xml_element(self, root): elem = root.find('surf_source_read') if elem is not None: + ssr = {} value = get_text(elem, 'path') if value is not None: - self.surf_source_read['path'] = value + ssr['path'] = value + self.surf_source_read = ssr def _surf_source_write_from_xml_element(self, root): elem = root.find('surf_source_write') - if elem is not None: - for key in ('surface_ids', 'max_particles'): + if elem is None: + return + for key in ('surface_ids', 'max_particles', 'max_source_files', 'mcpl', 'cell', 'cellto', 'cellfrom'): + if key == 'surface_ids': + value = get_elem_list(elem, key, int) + else: value = get_text(elem, key) - if value is not None: - if key == 'surface_ids': - value = [int(x) for x in value.split()] - elif key in ('max_particles'): - value = int(value) - self.surf_source_write[key] = value + if value is not None: + if key == 'mcpl': + value = value in ('true', '1') + elif key in ('max_particles', 'max_source_files', 'cell', 'cellfrom', 'cellto'): + value = int(value) + self.surf_source_write[key] = value def _confidence_intervals_from_xml_element(self, root): text = get_text(root, 'confidence_intervals') @@ -1330,6 +1906,16 @@ class Settings: if text is not None: self.photon_transport = text in ('true', '1') + def _uniform_source_sampling_from_xml_element(self, root): + text = get_text(root, 'uniform_source_sampling') + if text is not None: + self.uniform_source_sampling = text in ('true', '1') + + def _plot_seed_from_xml_element(self, root): + text = get_text(root, 'plot_seed') + if text is not None: + self.plot_seed = int(text) + def _ptables_from_xml_element(self, root): text = get_text(root, 'ptables') if text is not None: @@ -1340,6 +1926,11 @@ class Settings: if text is not None: self.seed = int(text) + def _stride_from_xml_element(self, root): + text = get_text(root, 'stride') + if text is not None: + self.stride = int(text) + def _survival_biasing_from_xml_element(self, root): text = get_text(root, 'survival_biasing') if text is not None: @@ -1350,18 +1941,24 @@ class Settings: if elem is not None: self.cutoff = {} for key in ('energy_neutron', 'energy_photon', 'energy_electron', - 'energy_positron', 'weight', 'weight_avg'): + 'energy_positron', 'weight', 'weight_avg', 'time_neutron', + 'time_photon', 'time_electron', 'time_positron', + 'survival_normalization'): value = get_text(elem, key) if value is not None: - self.cutoff[key] = float(value) + if key == 'survival_normalization': + self.cutoff[key] = value in ('true', '1') + else: + self.cutoff[key] = float(value) - def _entropy_mesh_from_xml_element(self, root): + def _entropy_mesh_from_xml_element(self, root, meshes): text = get_text(root, 'entropy_mesh') - if text is not None: - path = f"./mesh[@id='{int(text)}']" - elem = root.find(path) - if elem is not None: - self.entropy_mesh = RegularMesh.from_xml_element(elem) + if text is None: + return + mesh_id = int(text) + if mesh_id not in meshes: + raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') + self.entropy_mesh = meshes[mesh_id] def _trigger_from_xml_element(self, root): elem = root.find('trigger') @@ -1384,6 +1981,11 @@ class Settings: if text is not None: self.verbosity = int(text) + def _ifp_n_generation_from_xml_element(self, root): + text = get_text(root, 'ifp_n_generation') + if text is not None: + self.ifp_n_generation = int(text) + def _tabular_legendre_from_xml_element(self, root): elem = root.find('tabular_legendre') if elem is not None: @@ -1403,44 +2005,46 @@ class Settings: text = get_text(root, 'temperature_method') if text is not None: self.temperature['method'] = text - text = get_text(root, 'temperature_range') + text = get_elem_list(root, "temperature_range", float) if text is not None: - self.temperature['range'] = [float(x) for x in text.split()] + self.temperature['range'] = text text = get_text(root, 'temperature_multipole') if text is not None: self.temperature['multipole'] = text in ('true', '1') def _trace_from_xml_element(self, root): - text = get_text(root, 'trace') + text = get_elem_list(root, "trace", int) if text is not None: - self.trace = [int(x) for x in text.split()] + self.trace = text def _track_from_xml_element(self, root): - text = get_text(root, 'track') - if text is not None: - self.track = [int(x) for x in text.split()] + values = get_elem_list(root, "track", int) + if values is not None: + self.track = list(zip(values[::3], values[1::3], values[2::3])) - def _ufs_mesh_from_xml_element(self, root): + def _ufs_mesh_from_xml_element(self, root, meshes): text = get_text(root, 'ufs_mesh') - if text is not None: - path = f"./mesh[@id='{int(text)}']" - elem = root.find(path) - if elem is not None: - self.ufs_mesh = RegularMesh.from_xml_element(elem) + if text is None: + return + mesh_id = int(text) + if mesh_id not in meshes: + raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') + self.ufs_mesh = meshes[mesh_id] def _resonance_scattering_from_xml_element(self, root): elem = root.find('resonance_scattering') if elem is not None: keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') for key in keys: - value = get_text(elem, key) + if key == 'nuclides': + value = get_elem_list(elem, key, str) + else: + value = get_text(elem, key) if value is not None: if key == 'enable': value = value in ('true', '1') elif key in ('energy_min', 'energy_max'): value = float(value) - elif key == 'nuclides': - value = value.split() self.resonance_scattering[key] = value def _create_fission_neutrons_from_xml_element(self, root): @@ -1448,6 +2052,11 @@ class Settings: if text is not None: self.create_fission_neutrons = text in ('true', '1') + def _create_delayed_neutrons_from_xml_element(self, root): + text = get_text(root, 'create_delayed_neutrons') + if text is not None: + self.create_delayed_neutrons = text in ('true', '1') + def _delayed_photon_scaling_from_xml_element(self, root): text = get_text(root, 'delayed_photon_scaling') if text is not None: @@ -1463,6 +2072,11 @@ class Settings: if text is not None: self.max_particles_in_flight = int(text) + def _max_particle_events_from_xml_element(self, root): + text = get_text(root, 'max_particle_events') + if text is not None: + self.max_particle_events = int(text) + def _material_cell_offsets_from_xml_element(self, root): text = get_text(root, 'material_cell_offsets') if text is not None: @@ -1478,21 +2092,189 @@ class Settings: if text is not None: self.write_initial_source = text in ('true', '1') - def _weight_windows_from_xml_element(self, root): + def _weight_window_generators_from_xml_element(self, root, meshes=None): + for elem in root.iter('weight_windows_generator'): + wwg = WeightWindowGenerator.from_xml_element(elem, meshes) + self.weight_window_generators.append(wwg) + + def _weight_windows_from_xml_element(self, root, meshes=None): for elem in root.findall('weight_windows'): - ww = WeightWindows.from_xml_element(elem, root) + ww = WeightWindows.from_xml_element(elem, meshes) self.weight_windows.append(ww) + def _weight_windows_on_from_xml_element(self, root): text = get_text(root, 'weight_windows_on') if text is not None: self.weight_windows_on = text in ('true', '1') - def _max_splits_from_xml_element(self, root): - text = get_text(root, 'max_splits') + def _weight_windows_file_from_xml_element(self, root): + text = get_text(root, 'weight_windows_file') if text is not None: - self.max_splits = int(text) + self.weight_windows_file = text - def export_to_xml(self, path='settings.xml'): + def _weight_window_checkpoints_from_xml_element(self, root): + elem = root.find('weight_window_checkpoints') + if elem is None: + return + for key in ('collision', 'surface'): + value = get_text(elem, key) + if value is not None: + value = value in ('true', '1') + self.weight_window_checkpoints[key] = value + + def _max_history_splits_from_xml_element(self, root): + text = get_text(root, 'max_history_splits') + if text is not None: + self.max_history_splits = int(text) + + def _max_secondaries_from_xml_element(self, root): + text = get_text(root, 'max_secondaries') + if text is not None: + self.max_secondaries = int(text) + + def _max_tracks_from_xml_element(self, root): + text = get_text(root, 'max_tracks') + if text is not None: + self.max_tracks = int(text) + + def _random_ray_from_xml_element(self, root, meshes=None): + elem = root.find('random_ray') + if elem is not None: + self.random_ray = {} + for child in elem: + if child.tag in ('distance_inactive', 'distance_active', 'diagonal_stabilization_rho'): + self.random_ray[child.tag] = float(child.text) + elif child.tag == 'source': + source = SourceBase.from_xml_element(child) + self.random_ray['ray_source'] = source + elif child.tag == 'volume_estimator': + self.random_ray['volume_estimator'] = child.text + elif child.tag == 'source_shape': + self.random_ray['source_shape'] = child.text + elif child.tag == 'volume_normalized_flux_tallies': + self.random_ray['volume_normalized_flux_tallies'] = ( + child.text in ('true', '1') + ) + elif child.tag == 'adjoint': + self.random_ray['adjoint'] = ( + child.text in ('true', '1') + ) + elif child.tag == 'sample_method': + self.random_ray['sample_method'] = child.text + elif child.tag == 'source_region_meshes': + self.random_ray['source_region_meshes'] = [] + for mesh_elem in child.findall('mesh'): + mesh_id = int(get_text(mesh_elem, 'id')) + if meshes and mesh_id in meshes: + mesh = meshes[mesh_id] + else: + mesh = MeshBase.from_xml_element(mesh_elem) + domains = [] + for domain_elem in mesh_elem.findall('domain'): + domain_id = int(get_text(domain_elem, "id")) + domain_type = get_text(domain_elem, "type") + if domain_type == 'material': + domain = openmc.Material(domain_id) + elif domain_type == 'cell': + domain = openmc.Cell(domain_id) + elif domain_type == 'universe': + domain = openmc.Universe(domain_id) + domains.append(domain) + self.random_ray['source_region_meshes'].append((mesh, domains)) + + def _use_decay_photons_from_xml_element(self, root): + text = get_text(root, 'use_decay_photons') + if text is not None: + self.use_decay_photons = text in ('true', '1') + + def _source_rejection_fraction_from_xml_element(self, root): + text = get_text(root, 'source_rejection_fraction') + if text is not None: + self.source_rejection_fraction = float(text) + + def _free_gas_threshold_from_xml_element(self, root): + text = get_text(root, 'free_gas_threshold') + if text is not None: + self.free_gas_threshold = float(text) + + def to_xml_element(self, mesh_memo=None): + """Create a 'settings' element to be written to an XML file. + + Parameters + ---------- + mesh_memo : set of ints + A set of mesh IDs to keep track of whether a mesh has already been written. + """ + # Reset xml element tree + element = ET.Element("settings") + + self._create_run_mode_subelement(element) + self._create_particles_subelement(element) + self._create_batches_subelement(element) + self._create_inactive_subelement(element) + self._create_max_lost_particles_subelement(element) + self._create_rel_max_lost_particles_subelement(element) + self._create_max_write_lost_particles_subelement(element) + self._create_generations_per_batch_subelement(element) + self._create_keff_trigger_subelement(element) + self._create_source_subelement(element, mesh_memo) + self._create_output_subelement(element) + self._create_statepoint_subelement(element) + self._create_sourcepoint_subelement(element) + self._create_surf_source_read_subelement(element) + self._create_surf_source_write_subelement(element) + self._create_confidence_intervals(element) + self._create_electron_treatment_subelement(element) + self._create_energy_mode_subelement(element) + self._create_max_order_subelement(element) + self._create_photon_transport_subelement(element) + self._create_uniform_source_sampling_subelement(element) + self._create_plot_seed_subelement(element) + self._create_ptables_subelement(element) + self._create_seed_subelement(element) + self._create_stride_subelement(element) + self._create_survival_biasing_subelement(element) + self._create_cutoff_subelement(element) + self._create_entropy_mesh_subelement(element, mesh_memo) + self._create_trigger_subelement(element) + self._create_no_reduce_subelement(element) + self._create_verbosity_subelement(element) + self._create_ifp_n_generation_subelement(element) + self._create_tabular_legendre_subelements(element) + self._create_temperature_subelements(element) + self._create_trace_subelement(element) + self._create_track_subelement(element) + self._create_ufs_mesh_subelement(element, mesh_memo) + self._create_resonance_scattering_subelement(element) + self._create_volume_calcs_subelement(element) + self._create_create_fission_neutrons_subelement(element) + self._create_create_delayed_neutrons_subelement(element) + self._create_delayed_photon_scaling_subelement(element) + self._create_event_based_subelement(element) + self._create_max_particles_in_flight_subelement(element) + self._create_max_events_subelement(element) + self._create_material_cell_offsets_subelement(element) + self._create_log_grid_bins_subelement(element) + self._create_write_initial_source_subelement(element) + self._create_weight_windows_subelement(element, mesh_memo) + self._create_weight_windows_on_subelement(element) + self._create_weight_window_generators_subelement(element, mesh_memo) + self._create_weight_windows_file_element(element) + self._create_weight_window_checkpoints_subelement(element) + self._create_max_history_splits_subelement(element) + self._create_max_tracks_subelement(element) + self._create_max_secondaries_subelement(element) + self._create_random_ray_subelement(element, mesh_memo) + self._create_use_decay_photons_subelement(element) + self._create_source_rejection_fraction_subelement(element) + self._create_free_gas_threshold_subelement(element) + + # Clean the indentation in the file to be user-readable + clean_indentation(element) + + return element + + def export_to_xml(self, path: PathLike = 'settings.xml'): """Export simulation settings to an XML file. Parameters @@ -1501,56 +2283,7 @@ class Settings: Path to file to write. Defaults to 'settings.xml'. """ - - # Reset xml element tree - root_element = ET.Element("settings") - - self._create_run_mode_subelement(root_element) - self._create_particles_subelement(root_element) - self._create_batches_subelement(root_element) - self._create_inactive_subelement(root_element) - self._create_max_lost_particles_subelement(root_element) - self._create_rel_max_lost_particles_subelement(root_element) - self._create_generations_per_batch_subelement(root_element) - self._create_keff_trigger_subelement(root_element) - self._create_source_subelement(root_element) - self._create_output_subelement(root_element) - self._create_statepoint_subelement(root_element) - self._create_sourcepoint_subelement(root_element) - self._create_surf_source_read_subelement(root_element) - self._create_surf_source_write_subelement(root_element) - self._create_confidence_intervals(root_element) - self._create_electron_treatment_subelement(root_element) - self._create_energy_mode_subelement(root_element) - self._create_max_order_subelement(root_element) - self._create_photon_transport_subelement(root_element) - self._create_ptables_subelement(root_element) - self._create_seed_subelement(root_element) - self._create_survival_biasing_subelement(root_element) - self._create_cutoff_subelement(root_element) - self._create_entropy_mesh_subelement(root_element) - self._create_trigger_subelement(root_element) - self._create_no_reduce_subelement(root_element) - self._create_verbosity_subelement(root_element) - self._create_tabular_legendre_subelements(root_element) - self._create_temperature_subelements(root_element) - self._create_trace_subelement(root_element) - self._create_track_subelement(root_element) - self._create_ufs_mesh_subelement(root_element) - self._create_resonance_scattering_subelement(root_element) - self._create_volume_calcs_subelement(root_element) - self._create_create_fission_neutrons_subelement(root_element) - self._create_delayed_photon_scaling_subelement(root_element) - self._create_event_based_subelement(root_element) - self._create_max_particles_in_flight_subelement(root_element) - self._create_material_cell_offsets_subelement(root_element) - self._create_log_grid_bins_subelement(root_element) - self._create_write_initial_source_subelement(root_element) - self._create_weight_windows_subelement(root_element) - self._create_max_splits_subelement(root_element) - - # Clean the indentation in the file to be user-readable - clean_indentation(root_element) + root_element = self.to_xml_element() # Check if path is a directory p = Path(path) @@ -1558,12 +2291,100 @@ class Settings: p /= 'settings.xml' # Write the XML Tree to the settings.xml file - reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod - def from_xml(cls, path='settings.xml'): + def from_xml_element(cls, elem, meshes=None): + """Generate settings from XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict or None + A dictionary with mesh IDs as keys and mesh instances as values that + have already been read from XML. Pre-existing meshes are used + and new meshes are added to when creating tally objects. + + Returns + ------- + openmc.Settings + Settings object + + """ + # read all meshes under the settings node and update + settings_meshes = _read_meshes(elem) + meshes = {} if meshes is None else meshes + meshes.update(settings_meshes) + + settings = cls() + settings._eigenvalue_from_xml_element(elem) + settings._run_mode_from_xml_element(elem) + settings._particles_from_xml_element(elem) + settings._batches_from_xml_element(elem) + settings._inactive_from_xml_element(elem) + settings._max_lost_particles_from_xml_element(elem) + settings._rel_max_lost_particles_from_xml_element(elem) + settings._max_write_lost_particles_from_xml_element(elem) + settings._generations_per_batch_from_xml_element(elem) + settings._keff_trigger_from_xml_element(elem) + settings._source_from_xml_element(elem, meshes) + settings._volume_calcs_from_xml_element(elem) + settings._output_from_xml_element(elem) + settings._statepoint_from_xml_element(elem) + settings._sourcepoint_from_xml_element(elem) + settings._surf_source_read_from_xml_element(elem) + settings._surf_source_write_from_xml_element(elem) + settings._confidence_intervals_from_xml_element(elem) + settings._electron_treatment_from_xml_element(elem) + settings._energy_mode_from_xml_element(elem) + settings._max_order_from_xml_element(elem) + settings._photon_transport_from_xml_element(elem) + settings._uniform_source_sampling_from_xml_element(elem) + settings._plot_seed_from_xml_element(elem) + settings._ptables_from_xml_element(elem) + settings._seed_from_xml_element(elem) + settings._stride_from_xml_element(elem) + settings._survival_biasing_from_xml_element(elem) + settings._cutoff_from_xml_element(elem) + settings._entropy_mesh_from_xml_element(elem, meshes) + settings._trigger_from_xml_element(elem) + settings._no_reduce_from_xml_element(elem) + settings._verbosity_from_xml_element(elem) + settings._ifp_n_generation_from_xml_element(elem) + settings._tabular_legendre_from_xml_element(elem) + settings._temperature_from_xml_element(elem) + settings._trace_from_xml_element(elem) + settings._track_from_xml_element(elem) + settings._ufs_mesh_from_xml_element(elem, meshes) + settings._resonance_scattering_from_xml_element(elem) + settings._create_fission_neutrons_from_xml_element(elem) + settings._create_delayed_neutrons_from_xml_element(elem) + settings._delayed_photon_scaling_from_xml_element(elem) + settings._event_based_from_xml_element(elem) + settings._max_particles_in_flight_from_xml_element(elem) + settings._max_particle_events_from_xml_element(elem) + settings._material_cell_offsets_from_xml_element(elem) + settings._log_grid_bins_from_xml_element(elem) + settings._write_initial_source_from_xml_element(elem) + settings._weight_windows_from_xml_element(elem, meshes) + settings._weight_windows_on_from_xml_element(elem) + settings._weight_windows_file_from_xml_element(elem) + settings._weight_window_generators_from_xml_element(elem, meshes) + settings._weight_window_checkpoints_from_xml_element(elem) + settings._max_history_splits_from_xml_element(elem) + settings._max_tracks_from_xml_element(elem) + settings._max_secondaries_from_xml_element(elem) + settings._random_ray_from_xml_element(elem, meshes) + settings._use_decay_photons_from_xml_element(elem) + settings._source_rejection_fraction_from_xml_element(elem) + settings._free_gas_threshold_from_xml_element(elem) + + return settings + + @classmethod + def from_xml(cls, path: PathLike = 'settings.xml'): """Generate settings from XML file .. versionadded:: 0.13.0 @@ -1579,55 +2400,8 @@ class Settings: Settings object """ - tree = ET.parse(path) + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) root = tree.getroot() - - settings = cls() - settings._eigenvalue_from_xml_element(root) - settings._run_mode_from_xml_element(root) - settings._particles_from_xml_element(root) - settings._batches_from_xml_element(root) - settings._inactive_from_xml_element(root) - settings._max_lost_particles_from_xml_element(root) - settings._rel_max_lost_particles_from_xml_element(root) - settings._generations_per_batch_from_xml_element(root) - settings._keff_trigger_from_xml_element(root) - settings._source_from_xml_element(root) - settings._volume_calcs_from_xml_element(root) - settings._output_from_xml_element(root) - settings._statepoint_from_xml_element(root) - settings._sourcepoint_from_xml_element(root) - settings._surf_source_read_from_xml_element(root) - settings._surf_source_write_from_xml_element(root) - settings._confidence_intervals_from_xml_element(root) - settings._electron_treatment_from_xml_element(root) - settings._energy_mode_from_xml_element(root) - settings._max_order_from_xml_element(root) - settings._photon_transport_from_xml_element(root) - settings._ptables_from_xml_element(root) - settings._seed_from_xml_element(root) - settings._survival_biasing_from_xml_element(root) - settings._cutoff_from_xml_element(root) - settings._entropy_mesh_from_xml_element(root) - settings._trigger_from_xml_element(root) - settings._no_reduce_from_xml_element(root) - settings._verbosity_from_xml_element(root) - settings._tabular_legendre_from_xml_element(root) - settings._temperature_from_xml_element(root) - settings._trace_from_xml_element(root) - settings._track_from_xml_element(root) - settings._ufs_mesh_from_xml_element(root) - settings._resonance_scattering_from_xml_element(root) - settings._create_fission_neutrons_from_xml_element(root) - settings._delayed_photon_scaling_from_xml_element(root) - settings._event_based_from_xml_element(root) - settings._max_particles_in_flight_from_xml_element(root) - settings._material_cell_offsets_from_xml_element(root) - settings._log_grid_bins_from_xml_element(root) - settings._write_initial_source_from_xml_element(root) - settings._weight_windows_from_xml_element(root) - settings._max_splits_from_xml_element(root) - - # TODO: Get volume calculations - - return settings + meshes = _read_meshes(root) + return cls.from_xml_element(root, meshes) diff --git a/openmc/source.py b/openmc/source.py index 1abe01f657..9b730cf1de 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -1,19 +1,253 @@ -from enum import Enum +from __future__ import annotations +from abc import ABC, abstractmethod +from collections.abc import Iterable, Sequence +from enum import IntEnum from numbers import Real -from xml.etree import ElementTree as ET +from pathlib import Path +import warnings +from typing import Any +from pathlib import Path +import lxml.etree as ET import numpy as np import h5py +import pandas as pd +import openmc import openmc.checkvalue as cv +from openmc.checkvalue import PathLike from openmc.stats.multivariate import UnitSphere, Spatial from openmc.stats.univariate import Univariate -from ._xml import get_text +from ._xml import get_elem_list, get_text +from .mesh import MeshBase, StructuredMesh, UnstructuredMesh +from .utility_funcs import input_path -class Source: +class SourceBase(ABC): + """Base class for external sources + + Parameters + ---------- + strength : float + Strength of the source + constraints : dict + Constraints on sampled source particles. Valid keys include 'domains', + 'time_bounds', 'energy_bounds', 'fissionable', and 'rejection_strategy'. + For 'domains', the corresponding value is an iterable of + :class:`openmc.Cell`, :class:`openmc.Material`, or + :class:`openmc.Universe` for which sampled sites must be within. For + 'time_bounds' and 'energy_bounds', the corresponding value is a sequence + of floats giving the lower and upper bounds on time in [s] or energy in + [eV] that the sampled particle must be within. For 'fissionable', the + value is a bool indicating that only sites in fissionable material + should be accepted. The 'rejection_strategy' indicates what should + happen when a source particle is rejected: either 'resample' (pick a new + particle) or 'kill' (accept and terminate). + + Attributes + ---------- + type : {'independent', 'file', 'compiled', 'mesh'} + Indicator of source type. + strength : float + Strength of the source + constraints : dict + Constraints on sampled source particles. Valid keys include + 'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds', + 'fissionable', and 'rejection_strategy'. + + """ + + def __init__( + self, + strength: float | None = 1.0, + constraints: dict[str, Any] | None = None + ): + self.strength = strength + self.constraints = constraints + + @property + def strength(self): + return self._strength + + @strength.setter + def strength(self, strength): + cv.check_type('source strength', strength, Real, none_ok=True) + if strength is not None: + cv.check_greater_than('source strength', strength, 0.0, True) + self._strength = strength + + @property + def constraints(self) -> dict[str, Any]: + return self._constraints + + @constraints.setter + def constraints(self, constraints: dict[str, Any] | None): + self._constraints = {} + if constraints is None: + return + + for key, value in constraints.items(): + if key == 'domains': + cv.check_type('domains', value, Iterable, + (openmc.Cell, openmc.Material, openmc.Universe)) + if isinstance(value[0], openmc.Cell): + self._constraints['domain_type'] = 'cell' + elif isinstance(value[0], openmc.Material): + self._constraints['domain_type'] = 'material' + elif isinstance(value[0], openmc.Universe): + self._constraints['domain_type'] = 'universe' + self._constraints['domain_ids'] = [d.id for d in value] + elif key == 'time_bounds': + cv.check_type('time bounds', value, Iterable, Real) + self._constraints['time_bounds'] = tuple(value) + elif key == 'energy_bounds': + cv.check_type('energy bounds', value, Iterable, Real) + self._constraints['energy_bounds'] = tuple(value) + elif key == 'fissionable': + cv.check_type('fissionable', value, bool) + self._constraints['fissionable'] = value + elif key == 'rejection_strategy': + cv.check_value('rejection strategy', value, ('resample', 'kill')) + self._constraints['rejection_strategy'] = value + else: + raise ValueError(f'Unknown key in constraints dictionary: {key}') + + @abstractmethod + def populate_xml_element(self, element): + """Add necessary source information to an XML element + + Returns + ------- + element : lxml.etree._Element + XML element containing source data + + """ + + def to_xml_element(self) -> ET.Element: + """Return XML representation of the source + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing source data + + """ + element = ET.Element("source") + element.set("type", self.type) + if self.strength is not None: + element.set("strength", str(self.strength)) + self.populate_xml_element(element) + constraints = self.constraints + if constraints: + constraints_elem = ET.SubElement(element, "constraints") + if "domain_ids" in constraints: + dt_elem = ET.SubElement(constraints_elem, "domain_type") + dt_elem.text = constraints["domain_type"] + id_elem = ET.SubElement(constraints_elem, "domain_ids") + id_elem.text = ' '.join(str(uid) for uid in constraints["domain_ids"]) + if "time_bounds" in constraints: + dt_elem = ET.SubElement(constraints_elem, "time_bounds") + dt_elem.text = ' '.join(str(t) for t in constraints["time_bounds"]) + if "energy_bounds" in constraints: + dt_elem = ET.SubElement(constraints_elem, "energy_bounds") + dt_elem.text = ' '.join(str(E) for E in constraints["energy_bounds"]) + if "fissionable" in constraints: + dt_elem = ET.SubElement(constraints_elem, "fissionable") + dt_elem.text = str(constraints["fissionable"]).lower() + if "rejection_strategy" in constraints: + dt_elem = ET.SubElement(constraints_elem, "rejection_strategy") + dt_elem.text = constraints["rejection_strategy"] + + return element + + @classmethod + def from_xml_element(cls, elem: ET.Element, meshes=None) -> SourceBase: + """Generate source from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict + Dictionary with mesh IDs as keys and openmc.MeshBase instances as + values + + Returns + ------- + openmc.SourceBase + Source generated from XML element + + """ + source_type = get_text(elem, 'type') + + if source_type is None: + # attempt to determine source type based on attributes + # for backward compatibility + if get_text(elem, 'file') is not None: + return FileSource.from_xml_element(elem) + elif get_text(elem, 'library') is not None: + return CompiledSource.from_xml_element(elem) + else: + return IndependentSource.from_xml_element(elem) + else: + if source_type == 'independent': + return IndependentSource.from_xml_element(elem, meshes) + elif source_type == 'compiled': + return CompiledSource.from_xml_element(elem) + elif source_type == 'file': + return FileSource.from_xml_element(elem) + elif source_type == 'mesh': + return MeshSource.from_xml_element(elem, meshes) + else: + raise ValueError(f'Source type {source_type} is not recognized') + + @staticmethod + def _get_constraints(elem: ET.Element) -> dict[str, Any]: + # Find element containing constraints + constraints_elem = elem.find("constraints") + elem = constraints_elem if constraints_elem is not None else elem + + constraints = {} + domain_type = get_text(elem, "domain_type") + if domain_type is not None: + domain_ids = get_elem_list(elem, "domain_ids", int) + + # Instantiate some throw-away domains that are used by the + # constructor to assign IDs + with warnings.catch_warnings(): + warnings.simplefilter('ignore', openmc.IDWarning) + if domain_type == 'cell': + domains = [openmc.Cell(uid) for uid in domain_ids] + elif domain_type == 'material': + domains = [openmc.Material(uid) for uid in domain_ids] + elif domain_type == 'universe': + domains = [openmc.Universe(uid) for uid in domain_ids] + constraints['domains'] = domains + + time_bounds = get_elem_list(elem, "time_bounds", float) + if time_bounds is not None: + constraints['time_bounds'] = time_bounds + + energy_bounds = get_elem_list(elem, "energy_bounds", float) + if energy_bounds is not None: + constraints['energy_bounds'] = energy_bounds + + fissionable = get_text(elem, "fissionable") + if fissionable is not None: + constraints['fissionable'] = fissionable in ('true', '1') + + rejection_strategy = get_text(elem, "rejection_strategy") + if rejection_strategy is not None: + constraints['rejection_strategy'] = rejection_strategy + + return constraints + + +class IndependentSource(SourceBase): """Distribution of phase space coordinates for source sites. + .. versionadded:: 0.14.0 + Parameters ---------- space : openmc.stats.Spatial @@ -24,18 +258,29 @@ class Source: Energy distribution of source sites time : openmc.stats.Univariate time distribution of source sites - filename : str - Source file from which sites should be sampled - library : str - Path to a custom source library - parameters : str - Parameters to be provided to the custom source library - - .. versionadded:: 0.12 strength : float Strength of the source - particle : {'neutron', 'photon'} + particle : {'neutron', 'photon', 'electron', 'positron'} Source particle type + domains : iterable of openmc.Cell, openmc.Material, or openmc.Universe + Domains to reject based on, i.e., if a sampled spatial location is not + within one of these domains, it will be rejected. + + .. deprecated:: 0.15.0 + Use the `constraints` argument instead. + constraints : dict + Constraints on sampled source particles. Valid keys include 'domains', + 'time_bounds', 'energy_bounds', 'fissionable', and 'rejection_strategy'. + For 'domains', the corresponding value is an iterable of + :class:`openmc.Cell`, :class:`openmc.Material`, or + :class:`openmc.Universe` for which sampled sites must be within. For + 'time_bounds' and 'energy_bounds', the corresponding value is a sequence + of floats giving the lower and upper bounds on time in [s] or energy in + [eV] that the sampled particle must be within. For 'fissionable', the + value is a bool indicating that only sites in fissionable material + should be accepted. The 'rejection_strategy' indicates what should + happen when a source particle is rejected: either 'resample' (pick a new + particle) or 'kill' (accept and terminate). Attributes ---------- @@ -47,28 +292,44 @@ class Source: Energy distribution of source sites time : openmc.stats.Univariate or None time distribution of source sites - file : str or None - Source file from which sites should be sampled - library : str or None - Path to a custom source library - parameters : str - Parameters to be provided to the custom source library strength : float Strength of the source - particle : {'neutron', 'photon'} + type : str + Indicator of source type: 'independent' + + .. versionadded:: 0.14.0 + + particle : {'neutron', 'photon', 'electron', 'positron'} Source particle type + constraints : dict + Constraints on sampled source particles. Valid keys include + 'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds', + 'fissionable', and 'rejection_strategy'. """ - def __init__(self, space=None, angle=None, energy=None, time=None, filename=None, - library=None, parameters=None, strength=1.0, particle='neutron'): + def __init__( + self, + space: openmc.stats.Spatial | None = None, + angle: openmc.stats.UnitSphere | None = None, + energy: openmc.stats.Univariate | None = None, + time: openmc.stats.Univariate | None = None, + strength: float = 1.0, + particle: str = 'neutron', + domains: Sequence[openmc.Cell | openmc.Material | openmc.Universe] | None = None, + constraints: dict[str, Any] | None = None + ): + if domains is not None: + warnings.warn("The 'domains' arguments has been replaced by the " + "'constraints' argument.", FutureWarning) + constraints = {'domains': domains} + + super().__init__(strength=strength, constraints=constraints) + self._space = None self._angle = None self._energy = None self._time = None - self._file = None - self._library = None - self._parameters = None if space is not None: self.space = space @@ -78,116 +339,85 @@ class Source: self.energy = energy if time is not None: self.time = time - if filename is not None: - self.file = filename - if library is not None: - self.library = library - if parameters is not None: - self.parameters = parameters - self.strength = strength self.particle = particle @property - def file(self): - return self._file + def type(self) -> str: + return 'independent' - @property - def library(self): - return self._library + def __getattr__(self, name): + cls_names = {'file': 'FileSource', 'library': 'CompiledSource', + 'parameters': 'CompiledSource'} + if name in cls_names: + raise AttributeError( + f'The "{name}" attribute has been deprecated on the ' + f'IndependentSource class. Please use the {cls_names[name]} class.') + else: + super().__getattribute__(name) - @property - def parameters(self): - return self._parameters + def __setattr__(self, name, value): + if name in ('file', 'library', 'parameters'): + # Ensure proper AttributeError is thrown + getattr(self, name) + else: + super().__setattr__(name, value) @property def space(self): return self._space - @property - def angle(self): - return self._angle - - @property - def energy(self): - return self._energy - - @property - def time(self): - return self._time - - @property - def strength(self): - return self._strength - - @property - def particle(self): - return self._particle - - @file.setter - def file(self, filename): - cv.check_type('source file', filename, str) - self._file = filename - - @library.setter - def library(self, library_name): - cv.check_type('library', library_name, str) - self._library = library_name - - @parameters.setter - def parameters(self, parameters_path): - cv.check_type('parameters', parameters_path, str) - self._parameters = parameters_path - @space.setter def space(self, space): cv.check_type('spatial distribution', space, Spatial) self._space = space + @property + def angle(self): + return self._angle + @angle.setter def angle(self, angle): cv.check_type('angular distribution', angle, UnitSphere) self._angle = angle + @property + def energy(self): + return self._energy + @energy.setter def energy(self, energy): cv.check_type('energy distribution', energy, Univariate) self._energy = energy + @property + def time(self): + return self._time + @time.setter def time(self, time): cv.check_type('time distribution', time, Univariate) self._time = time - @strength.setter - def strength(self, strength): - cv.check_type('source strength', strength, Real) - cv.check_greater_than('source strength', strength, 0.0, True) - self._strength = strength + @property + def particle(self): + return self._particle @particle.setter def particle(self, particle): - cv.check_value('source particle', particle, ['neutron', 'photon']) + cv.check_value('source particle', particle, + ['neutron', 'photon', 'electron', 'positron']) self._particle = particle - def to_xml_element(self): - """Return XML representation of the source + def populate_xml_element(self, element): + """Add necessary source information to an XML element Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing source data """ - element = ET.Element("source") - element.set("strength", str(self.strength)) - if self.particle != 'neutron': - element.set("particle", self.particle) - if self.file is not None: - element.set("file", self.file) - if self.library is not None: - element.set("library", self.library) - if self.parameters is not None: - element.set("parameters", self.parameters) + element.set("particle", self.particle) if self.space is not None: element.append(self.space.to_xml_element()) if self.angle is not None: @@ -196,16 +426,18 @@ class Source: element.append(self.energy.to_xml_element('energy')) if self.time is not None: element.append(self.time.to_xml_element('time')) - return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element, meshes=None) -> SourceBase: """Generate source from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element + meshes : dict + Dictionary with mesh IDs as keys and openmc.MeshBase instaces as + values Returns ------- @@ -213,7 +445,8 @@ class Source: Source generated from XML element """ - source = cls() + constraints = cls._get_constraints(elem) + source = cls(constraints=constraints) strength = get_text(elem, 'strength') if strength is not None: @@ -223,21 +456,9 @@ class Source: if particle is not None: source.particle = particle - filename = get_text(elem, 'file') - if filename is not None: - source.file = filename - - library = get_text(elem, 'library') - if library is not None: - source.library = library - - parameters = get_text(elem, 'parameters') - if parameters is not None: - source.parameters = parameters - space = elem.find('space') if space is not None: - source.space = Spatial.from_xml_element(space) + source.space = Spatial.from_xml_element(space, meshes) angle = elem.find('angle') if angle is not None: @@ -254,12 +475,488 @@ class Source: return source -class ParticleType(Enum): +class MeshSource(SourceBase): + """A source with a spatial distribution over mesh elements + + This class represents a mesh-based source in which random positions are + uniformly sampled within mesh elements and each element can have independent + angle, energy, and time distributions. The element sampled is chosen based + on the relative strengths of the sources applied to the elements. The + strength of the mesh source as a whole is the sum of all source strengths + applied to the elements. + + .. versionadded:: 0.15.0 + + Parameters + ---------- + mesh : openmc.MeshBase + The mesh over which source sites will be generated. + sources : sequence of openmc.SourceBase + Sources for each element in the mesh. Sources must be specified as + either a 1-D array in the order of the mesh indices or a + multidimensional array whose shape matches the mesh shape. If spatial + distributions are set on any of the source objects, they will be ignored + during source site sampling. + constraints : dict + Constraints on sampled source particles. Valid keys include 'domains', + 'time_bounds', 'energy_bounds', 'fissionable', and 'rejection_strategy'. + For 'domains', the corresponding value is an iterable of + :class:`openmc.Cell`, :class:`openmc.Material`, or + :class:`openmc.Universe` for which sampled sites must be within. For + 'time_bounds' and 'energy_bounds', the corresponding value is a sequence + of floats giving the lower and upper bounds on time in [s] or energy in + [eV] that the sampled particle must be within. For 'fissionable', the + value is a bool indicating that only sites in fissionable material + should be accepted. The 'rejection_strategy' indicates what should + happen when a source particle is rejected: either 'resample' (pick a new + particle) or 'kill' (accept and terminate). + + Attributes + ---------- + mesh : openmc.MeshBase + The mesh over which source sites will be generated. + sources : numpy.ndarray of openmc.SourceBase + Sources to apply to each element + strength : float + Strength of the source + type : str + Indicator of source type: 'mesh' + constraints : dict + Constraints on sampled source particles. Valid keys include + 'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds', + 'fissionable', and 'rejection_strategy'. + + """ + def __init__( + self, + mesh: MeshBase, + sources: Sequence[SourceBase], + constraints: dict[str, Any] | None = None, + ): + super().__init__(strength=None, constraints=constraints) + self.mesh = mesh + self.sources = sources + + @property + def type(self) -> str: + return "mesh" + + @property + def mesh(self) -> MeshBase: + return self._mesh + + @property + def strength(self) -> float: + return sum(s.strength for s in self.sources) + + @property + def sources(self) -> np.ndarray: + return self._sources + + @mesh.setter + def mesh(self, m): + cv.check_type('source mesh', m, MeshBase) + self._mesh = m + + @sources.setter + def sources(self, s): + cv.check_iterable_type('mesh sources', s, SourceBase, max_depth=3) + + s = np.asarray(s) + + if isinstance(self.mesh, StructuredMesh): + if s.size != self.mesh.num_mesh_cells: + raise ValueError( + f'The length of the source array ({s.size}) does not match ' + f'the number of mesh elements ({self.mesh.num_mesh_cells}).') + + # If user gave a multidimensional array, flatten in the order + # of the mesh indices + if s.ndim > 1: + s = s.ravel(order='F') + + elif isinstance(self.mesh, UnstructuredMesh): + if s.ndim > 1: + raise ValueError('Sources must be a 1-D array for unstructured mesh') + + self._sources = s + for src in self._sources: + if isinstance(src, IndependentSource) and src.space is not None: + warnings.warn('Some sources on the mesh have spatial ' + 'distributions that will be ignored at runtime.') + break + + @strength.setter + def strength(self, val): + if val is not None: + cv.check_type('mesh source strength', val, Real) + self.set_total_strength(val) + + def set_total_strength(self, strength: float): + """Scales the element source strengths based on a desired total strength. + + Parameters + ---------- + strength : float + Total source strength + + """ + current_strength = self.strength if self.strength != 0.0 else 1.0 + + for s in self.sources: + s.strength *= strength / current_strength + + def normalize_source_strengths(self): + """Update all element source strengths such that they sum to 1.0.""" + self.set_total_strength(1.0) + + def populate_xml_element(self, elem: ET.Element): + """Add necessary source information to an XML element + + Returns + ------- + element : lxml.etree._Element + XML element containing source data + + """ + elem.set("mesh", str(self.mesh.id)) + + # write in the order of mesh indices + for s in self.sources: + elem.append(s.to_xml_element()) + + @classmethod + def from_xml_element(cls, elem: ET.Element, meshes) -> openmc.MeshSource: + """ + Generate MeshSource from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict + A dictionary with mesh IDs as keys and openmc.MeshBase instances as + values + + Returns + ------- + openmc.MeshSource + MeshSource generated from the XML element + """ + mesh_id = int(get_text(elem, 'mesh')) + mesh = meshes[mesh_id] + + sources = [SourceBase.from_xml_element(e) for e in elem.iterchildren('source')] + constraints = cls._get_constraints(elem) + return cls(mesh, sources, constraints=constraints) + + +def Source(*args, **kwargs): + """ + A function for backward compatibility of sources. Will be removed in the + future. Please update to IndependentSource. + """ + warnings.warn("This class is deprecated in favor of 'IndependentSource'", FutureWarning) + return openmc.IndependentSource(*args, **kwargs) + + +class CompiledSource(SourceBase): + """A source based on a compiled shared library + + .. versionadded:: 0.14.0 + + Parameters + ---------- + library : path-like + Path to a compiled shared library + parameters : str + Parameters to be provided to the compiled shared library function + strength : float + Strength of the source + constraints : dict + Constraints on sampled source particles. Valid keys include 'domains', + 'time_bounds', 'energy_bounds', 'fissionable', and 'rejection_strategy'. + For 'domains', the corresponding value is an iterable of + :class:`openmc.Cell`, :class:`openmc.Material`, or + :class:`openmc.Universe` for which sampled sites must be within. For + 'time_bounds' and 'energy_bounds', the corresponding value is a sequence + of floats giving the lower and upper bounds on time in [s] or energy in + [eV] that the sampled particle must be within. For 'fissionable', the + value is a bool indicating that only sites in fissionable material + should be accepted. The 'rejection_strategy' indicates what should + happen when a source particle is rejected: either 'resample' (pick a new + particle) or 'kill' (accept and terminate). + + Attributes + ---------- + library : pathlib.Path + Path to a compiled shared library + parameters : str + Parameters to be provided to the compiled shared library function + strength : float + Strength of the source + type : str + Indicator of source type: 'compiled' + constraints : dict + Constraints on sampled source particles. Valid keys include + 'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds', + 'fissionable', and 'rejection_strategy'. + + """ + def __init__( + self, + library: PathLike, + parameters: str | None = None, + strength: float = 1.0, + constraints: dict[str, Any] | None = None + ) -> None: + super().__init__(strength=strength, constraints=constraints) + self.library = library + self._parameters = None + if parameters is not None: + self.parameters = parameters + + @property + def type(self) -> str: + return "compiled" + + @property + def library(self) -> Path: + return self._library + + @library.setter + def library(self, library_name: PathLike): + cv.check_type('library', library_name, PathLike) + self._library = input_path(library_name) + + @property + def parameters(self) -> str: + return self._parameters + + @parameters.setter + def parameters(self, parameters_path): + cv.check_type('parameters', parameters_path, str) + self._parameters = parameters_path + + def populate_xml_element(self, element): + """Add necessary compiled source information to an XML element + + Returns + ------- + element : lxml.etree._Element + XML element containing source data + + """ + element.set("library", str(self.library)) + + if self.parameters is not None: + element.set("parameters", self.parameters) + + @classmethod + def from_xml_element(cls, elem: ET.Element) -> openmc.CompiledSource: + """Generate a compiled source from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict + Dictionary with mesh IDs as keys and openmc.MeshBase instances as + values + + Returns + ------- + openmc.CompiledSource + Source generated from XML element + + """ + kwargs = {'constraints': cls._get_constraints(elem)} + kwargs['library'] = get_text(elem, 'library') + + source = cls(**kwargs) + + strength = get_text(elem, 'strength') + if strength is not None: + source.strength = float(strength) + + parameters = get_text(elem, 'parameters') + if parameters is not None: + source.parameters = parameters + + return source + + +class FileSource(SourceBase): + """A source based on particles stored in a file + + .. versionadded:: 0.14.0 + + Parameters + ---------- + path : path-like + Path to the source file from which sites should be sampled + strength : float + Strength of the source (default is 1.0) + constraints : dict + Constraints on sampled source particles. Valid keys include 'domains', + 'time_bounds', 'energy_bounds', 'fissionable', and 'rejection_strategy'. + For 'domains', the corresponding value is an iterable of + :class:`openmc.Cell`, :class:`openmc.Material`, or + :class:`openmc.Universe` for which sampled sites must be within. For + 'time_bounds' and 'energy_bounds', the corresponding value is a sequence + of floats giving the lower and upper bounds on time in [s] or energy in + [eV] that the sampled particle must be within. For 'fissionable', the + value is a bool indicating that only sites in fissionable material + should be accepted. The 'rejection_strategy' indicates what should + happen when a source particle is rejected: either 'resample' (pick a new + particle) or 'kill' (accept and terminate). + + Attributes + ---------- + path : Pathlike + Source file from which sites should be sampled + strength : float + Strength of the source + type : str + Indicator of source type: 'file' + constraints : dict + Constraints on sampled source particles. Valid keys include + 'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds', + 'fissionable', and 'rejection_strategy'. + + """ + + def __init__( + self, + path: PathLike, + strength: float = 1.0, + constraints: dict[str, Any] | None = None + ): + super().__init__(strength=strength, constraints=constraints) + self.path = path + + @property + def type(self) -> str: + return "file" + + @property + def path(self) -> PathLike: + return self._path + + @path.setter + def path(self, p: PathLike): + cv.check_type('source file', p, PathLike) + self._path = input_path(p) + + def populate_xml_element(self, element): + """Add necessary file source information to an XML element + + Returns + ------- + element : lxml.etree._Element + XML element containing source data + + """ + if self.path is not None: + element.set("file", str(self.path)) + + @classmethod + def from_xml_element(cls, elem: ET.Element) -> openmc.FileSource: + """Generate file source from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict + Dictionary with mesh IDs as keys and openmc.MeshBase instances as + values + + Returns + ------- + openmc.FileSource + Source generated from XML element + + """ + kwargs = {'constraints': cls._get_constraints(elem)} + kwargs['path'] = get_text(elem, 'file') + strength = get_text(elem, 'strength') + if strength is not None: + kwargs['strength'] = float(strength) + + return cls(**kwargs) + + +class ParticleType(IntEnum): + """ + IntEnum class representing a particle type. Type + values mirror those found in the C++ class. + """ NEUTRON = 0 PHOTON = 1 ELECTRON = 2 POSITRON = 3 + @classmethod + def from_string(cls, value: str): + """ + Constructs a ParticleType instance from a string. + + Parameters + ---------- + value : str + The string representation of the particle type. + + Returns + ------- + The corresponding ParticleType instance. + """ + try: + return cls[value.upper()] + except KeyError: + raise ValueError(f"Invalid string for creation of {cls.__name__}: {value}") + + @classmethod + def from_pdg_number(cls, pdg_number: int) -> ParticleType: + """Constructs a ParticleType instance from a PDG number. + + The Particle Data Group at LBNL publishes a Monte Carlo particle + numbering scheme as part of the `Review of Particle Physics + <10.1103/PhysRevD.110.030001>`_. This method maps PDG numbers to the + corresponding :class:`ParticleType`. + + Parameters + ---------- + pdg_number : int + The PDG number of the particle type. + + Returns + ------- + The corresponding ParticleType instance. + """ + try: + return { + 2112: ParticleType.NEUTRON, + 22: ParticleType.PHOTON, + 11: ParticleType.ELECTRON, + -11: ParticleType.POSITRON, + }[pdg_number] + except KeyError: + raise ValueError(f"Unrecognized PDG number: {pdg_number}") + + def __repr__(self) -> str: + """ + Returns a string representation of the ParticleType instance. + + Returns: + str: The lowercase name of the ParticleType instance. + """ + return self.name.lower() + + # needed for < Python 3.11 + def __str__(self) -> str: + return self.__repr__() + class SourceParticle: """Source particle @@ -287,8 +984,18 @@ class SourceParticle: Type of the particle """ - def __init__(self, r=(0., 0., 0.), u=(0., 0., 1.), E=1.0e6, time=0.0, wgt=1.0, - delayed_group=0, surf_id=0, particle=ParticleType.NEUTRON): + def __init__( + self, + r: Iterable[float] = (0., 0., 0.), + u: Iterable[float] = (0., 0., 1.), + E: float = 1.0e6, + time: float = 0.0, + wgt: float = 1.0, + delayed_group: int = 0, + surf_id: int = 0, + particle: ParticleType = ParticleType.NEUTRON + ): + self.r = tuple(r) self.u = tuple(u) self.E = float(E) @@ -298,7 +1005,11 @@ class SourceParticle: self.surf_id = surf_id self.particle = particle - def to_tuple(self): + def __repr__(self): + name = self.particle.name.lower() + return f'' + + def to_tuple(self) -> tuple: """Return source particle attributes as a tuple Returns @@ -311,7 +1022,10 @@ class SourceParticle: self.delayed_group, self.surf_id, self.particle.value) -def write_source_file(source_particles, filename, **kwargs): +def write_source_file( + source_particles: Iterable[SourceParticle], + filename: PathLike, **kwargs +): """Write a source file using a collection of source particles Parameters @@ -328,25 +1042,202 @@ def write_source_file(source_particles, filename, **kwargs): openmc.SourceParticle """ - # Create compound datatype for source particles - pos_dtype = np.dtype([('x', ' ParticleList: + """Create particle list from an HDF5 file. + + Parameters + ---------- + filename : path-like + Path to source file to read. + + Returns + ------- + ParticleList instance + + """ + with h5py.File(filename, 'r') as fh: + filetype = fh.attrs['filetype'] + arr = fh['source_bank'][...] + + if filetype != b'source': + raise ValueError(f'File {filename} is not a source file') + + source_particles = [ + SourceParticle(*params, ParticleType(particle)) + for *params, particle in arr + ] + return cls(source_particles) + + @classmethod + def from_mcpl(cls, filename: PathLike) -> ParticleList: + """Create particle list from an MCPL file. + + Parameters + ---------- + filename : path-like + Path to MCPL file to read. + + Returns + ------- + ParticleList instance + + """ + import mcpl + # Process .mcpl file + particles = [] + with mcpl.MCPLFile(filename) as f: + for particle in f.particles: + # Determine particle type based on the PDG number + try: + particle_type = ParticleType.from_pdg_number(particle.pdgcode) + except ValueError: + particle_type = "UNKNOWN" + + # Create a source particle instance. Note that MCPL stores + # energy in MeV and time in ms. + source_particle = SourceParticle( + r=tuple(particle.position), + u=tuple(particle.direction), + E=1.0e6*particle.ekin, + time=1.0e-3*particle.time, + wgt=particle.weight, + particle=particle_type + ) + particles.append(source_particle) + + return cls(particles) + + def __getitem__(self, index): + """ + Return a new ParticleList object containing the particle(s) + at the specified index or slice. + + Parameters + ---------- + index : int, slice or list + The index, slice or list to select from the list of particles + + Returns + ------- + openmc.ParticleList or openmc.SourceParticle + A new object with the selected particle(s) + """ + if isinstance(index, int): + # If it's a single integer, return the corresponding particle + return super().__getitem__(index) + elif isinstance(index, slice): + # If it's a slice, return a new ParticleList object with the + # sliced particles + return ParticleList(super().__getitem__(index)) + elif isinstance(index, list): + # If it's a list of integers, return a new ParticleList object with + # the selected particles. Note that Python 3.10 gets confused if you + # use super() here, so we call list.__getitem__ directly. + return ParticleList([list.__getitem__(self, i) for i in index]) + else: + raise TypeError(f"Invalid index type: {type(index)}. Must be int, " + "slice, or list of int.") + + def to_dataframe(self) -> pd.DataFrame: + """A dataframe representing the source particles + + Returns + ------- + pandas.DataFrame + DataFrame containing the source particles attributes. + """ + # Extract the attributes of the source particles into a list of tuples + data = [(sp.r[0], sp.r[1], sp.r[2], sp.u[0], sp.u[1], sp.u[2], + sp.E, sp.time, sp.wgt, sp.delayed_group, sp.surf_id, + sp.particle.name.lower()) for sp in self] + + # Define the column names for the DataFrame + columns = ['x', 'y', 'z', 'u_x', 'u_y', 'u_z', 'E', 'time', 'wgt', + 'delayed_group', 'surf_id', 'particle'] + + # Create the pandas DataFrame from the data + return pd.DataFrame(data, columns=columns) + + def export_to_hdf5(self, filename: PathLike, **kwargs): + """Export particle list to an HDF5 file. + + This method write out an .h5 file that can be used as a source file in + conjunction with the :class:`openmc.FileSource` class. + + Parameters + ---------- + filename : path-like + Path to source file to write + **kwargs + Keyword arguments to pass to :class:`h5py.File` + + See Also + -------- + openmc.FileSource + + """ + # Create compound datatype for source particles + pos_dtype = np.dtype([('x', ' ParticleList: + """Read a source file and return a list of source particles. + + .. versionadded:: 0.15.0 + + Parameters + ---------- + filename : str or path-like + Path to source file to read + + Returns + ------- + openmc.ParticleList + + See Also + -------- + openmc.SourceParticle + + """ + filename = Path(filename) + if filename.suffix not in ('.h5', '.mcpl'): + raise ValueError('Source file must have a .h5 or .mcpl extension.') + + if filename.suffix == '.h5': + return ParticleList.from_hdf5(filename) + else: + return ParticleList.from_mcpl(filename) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 894730cbe9..a763db3971 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -1,4 +1,5 @@ from datetime import datetime +from collections import namedtuple import glob import re import os @@ -6,12 +7,17 @@ import warnings import h5py import numpy as np +from pathlib import Path from uncertainties import ufloat +from uncertainties.unumpy import uarray import openmc import openmc.checkvalue as cv -_VERSION_STATEPOINT = 17 +_VERSION_STATEPOINT = 18 + + +KineticsParameters = namedtuple("KineticsParameters", ["generation_time", "beta_effective"]) class StatePoint: @@ -63,6 +69,8 @@ class StatePoint: datatype has fields 'name', 'sum', 'sum_sq', 'mean', and 'std_dev'. k_combined : uncertainties.UFloat Combined estimator for k-effective + + .. deprecated:: 0.13.1 k_col_abs : float Cross-product of collision and absorption estimates of k-effective k_col_tra : float @@ -71,6 +79,10 @@ class StatePoint: Cross-product of absorption and tracklength estimates of k-effective k_generation : numpy.ndarray Estimate of k-effective for each batch/generation + keff : uncertainties.UFloat + Combined estimator for k-effective + + .. versionadded:: 0.13.1 meshes : dict Dictionary whose keys are mesh IDs and whose values are MeshBase objects n_batches : int @@ -92,6 +104,8 @@ class StatePoint: and whose values are time values in seconds. seed : int Pseudorandom number generator seed + stride : int + Number of random numbers allocated for each particle history source : numpy.ndarray of compound datatype Array of source sites. The compound datatype has fields 'r', 'u', 'E', 'wgt', 'delayed_group', 'surf_id', and 'particle', corresponding to @@ -229,7 +243,7 @@ class StatePoint: if self._global_tallies is None: data = self._f['global_tallies'][()] gt = np.zeros(data.shape[0], dtype=[ - ('name', 'a14'), ('sum', 'f8'), ('sum_sq', 'f8'), + ('name', 'S14'), ('sum', 'f8'), ('sum_sq', 'f8'), ('mean', 'f8'), ('std_dev', 'f8')]) gt['name'] = ['k-collision', 'k-absorption', 'k-tracklength', 'leakage'] @@ -260,12 +274,20 @@ class StatePoint: return None @property - def k_combined(self): + def keff(self): if self.run_mode == 'eigenvalue': return ufloat(*self._f['k_combined'][()]) else: return None + @property + def k_combined(self): + warnings.warn( + "The 'k_combined' property has been renamed to 'keff' and will be " + "removed in a future version of OpenMC.", FutureWarning + ) + return self.keff + @property def k_col_abs(self): if self.run_mode == 'eigenvalue': @@ -341,6 +363,10 @@ class StatePoint: def seed(self): return self._f['seed'][()] + @property + def stride(self): + return self._f['stride'][()] + @property def source(self): return self._f['source_bank'][()] if self.source_present else None @@ -353,6 +379,26 @@ class StatePoint: def sparse(self): return self._sparse + @sparse.setter + def sparse(self, sparse): + """Convert tally data from NumPy arrays to SciPy list of lists (LIL) + sparse matrices, and vice versa. + + This property may be used to reduce the amount of data in memory during + tally data processing. The tally data will be stored as SciPy LIL + matrices internally within each Tally object. All tally data access + properties and methods will return data as a dense NumPy array. + + """ + + cv.check_type('sparse', sparse, bool) + self._sparse = sparse + + # Update tally sparsities + if self._tallies_read: + for tally_id in self.tallies: + self.tallies[tally_id].sparse = self.sparse + @property def tallies(self): if self.tallies_present and not self._tallies_read: @@ -381,9 +427,13 @@ class StatePoint: # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) - tally._sp_filename = self._f.filename + tally._sp_filename = Path(self._f.filename) tally.name = group['name'][()].decode() if 'name' in group else '' + # Check if tally has multiply_density attribute + if "multiply_density" in group.attrs: + tally.multiply_density = group.attrs["multiply_density"].item() > 0 + # Read the number of realizations n_realizations = group['n_realizations'][()] @@ -410,14 +460,11 @@ class StatePoint: nuclide_names = group['nuclides'][()] # Add all nuclides to the Tally - for name in nuclide_names: - nuclide = openmc.Nuclide(name.decode().strip()) - tally.nuclides.append(nuclide) + tally.nuclides = [name.decode().strip() for name in nuclide_names] # Add the scores to the Tally scores = group['score_bins'][()] - for score in scores: - tally.scores.append(score.decode()) + tally.scores = [score.decode() for score in scores] # Add Tally to the global dictionary of all Tallies tally.sparse = self.sparse @@ -466,26 +513,6 @@ class StatePoint: def summary(self): return self._summary - @sparse.setter - def sparse(self, sparse): - """Convert tally data from NumPy arrays to SciPy list of lists (LIL) - sparse matrices, and vice versa. - - This property may be used to reduce the amount of data in memory during - tally data processing. The tally data will be stored as SciPy LIL - matrices internally within each Tally object. All tally data access - properties and methods will return data as a dense NumPy array. - - """ - - cv.check_type('sparse', sparse, bool) - self._sparse = sparse - - # Update tally sparsities - if self._tallies_read: - for tally_id in self.tallies: - self.tallies[tally_id].sparse = self.sparse - def close(self): """Close the statepoint HDF5 file and the corresponding summary HDF5 file if present. @@ -508,15 +535,16 @@ class StatePoint: def get_tally(self, scores=[], filters=[], nuclides=[], name=None, id=None, estimator=None, exact_filters=False, - exact_nuclides=False, exact_scores=False): + exact_nuclides=False, exact_scores=False, + multiply_density=None, derivative=None, filter_type=None): """Finds and returns a Tally object with certain properties. This routine searches the list of Tallies and returns the first Tally found which satisfies all of the input parameters. NOTE: If any of the "exact" parameters are False (default), the input - parameters do not need to match the complete Tally specification and - may only represent a subset of the Tally's properties. If an "exact" + parameters do not need to match the complete Tally specification and may + only represent a subset of the Tally's properties. If an "exact" parameter is True then number of scores, filters, or nuclides in the parameters must precisely match those of any matching Tally. @@ -543,9 +571,18 @@ class StatePoint: to those in the matching Tally. If False (default), the nuclides in the parameters may be a subset of those in the matching Tally. exact_scores : bool - If True, the number of scores in the parameters must be identical - to those in the matching Tally. If False (default), the scores - in the parameters may be a subset of those in the matching Tally. + If True, the number of scores in the parameters must be identical to + those in the matching Tally. If False (default), the scores in the + parameters may be a subset of those in the matching Tally. Default + is None (no check). + multiply_density : bool, optional + If not None, the Tally must have the multiply_density attribute set + to the same value as this parameter. + derivative : openmc.TallyDerivative, optional + TallyDerivative object to match. + filter_type : type, optional + If not None, the Tally must have at least one Filter that is an + instance of this type. For example `openmc.MeshFilter`. Returns ------- @@ -573,17 +610,25 @@ class StatePoint: if id and id != test_tally.id: continue - # Determine if Tally has queried estimator - if estimator and estimator != test_tally.estimator: + # Determine if Tally has queried estimator, only move on to next tally + # if the estimator is both specified and the tally estimtor does not + # match + if estimator is not None and estimator != test_tally.estimator: continue # The number of filters, nuclides and scores must exactly match if exact_scores and len(scores) != test_tally.num_scores: continue - if exact_nuclides and len(nuclides) != test_tally.num_nuclides: + if exact_nuclides and nuclides and len(nuclides) != test_tally.num_nuclides: + continue + if exact_nuclides and not nuclides and test_tally.nuclides != ['total']: continue if exact_filters and len(filters) != test_tally.num_filters: continue + if derivative is not None and derivative != test_tally.derivative: + continue + if multiply_density is not None and multiply_density != test_tally.multiply_density: + continue # Determine if Tally has the queried score(s) if scores: @@ -611,6 +656,10 @@ class StatePoint: if not contains_filters: continue + if filter_type is not None: + if not any(isinstance(f, filter_type) for f in test_tally.filters): + continue + # Determine if Tally has the queried Nuclide(s) if nuclides: if not all(nuclide in test_tally.nuclides for nuclide in nuclides): @@ -673,3 +722,56 @@ class StatePoint: tally_filter.paths = cell.paths self._summary = summary + + def get_kinetics_parameters(self) -> KineticsParameters: + """Get kinetics parameters from IFP tallies. + + This method searches the tallies in the statepoint for the tallies + required to compute kinetics parameters using the Iterated Fission + Probability (IFP) method. + + Returns + ------- + KineticsParameters + A named tuple containing the generation time and effective delayed + neutron fraction. If the necessary tallies for one or both + parameters are not found, that parameter is returned as None. + + """ + + denom_tally = None + gen_time_tally = None + beta_tally = None + for tally in self.tallies.values(): + if 'ifp-denominator' in tally.scores: + denom_tally = self.get_tally(scores=['ifp-denominator']) + if 'ifp-time-numerator' in tally.scores: + gen_time_tally = self.get_tally(scores=['ifp-time-numerator']) + if 'ifp-beta-numerator' in tally.scores: + beta_tally = self.get_tally(scores=['ifp-beta-numerator']) + + if denom_tally is None: + return KineticsParameters(None, None) + + def get_ufloat(tally, score): + return uarray(tally.get_values(scores=[score]), + tally.get_values(scores=[score], value='std_dev')) + + denom_values = get_ufloat(denom_tally, 'ifp-denominator') + if gen_time_tally is None: + generation_time = None + else: + gen_time_values = get_ufloat(gen_time_tally, 'ifp-time-numerator') + gen_time_values /= denom_values*self.keff + generation_time = gen_time_values.flatten()[0] + + if beta_tally is None: + beta_effective = None + else: + beta_values = get_ufloat(beta_tally, 'ifp-beta-numerator') + beta_values /= denom_values + beta_effective = beta_values.flatten() + if beta_effective.size == 1: + beta_effective = beta_effective[0] + + return KineticsParameters(generation_time, beta_effective) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 8635bcd71c..222d2d18a5 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -1,14 +1,18 @@ +from __future__ import annotations from abc import ABC, abstractmethod -from collections.abc import Iterable -from math import pi +from collections.abc import Iterable, Sequence +from math import cos, pi from numbers import Real -from xml.etree import ElementTree as ET +from warnings import warn +import lxml.etree as ET import numpy as np +import openmc import openmc.checkvalue as cv -from .._xml import get_text -from .univariate import Univariate, Uniform +from .._xml import get_elem_list, get_text +from ..mesh import MeshBase +from .univariate import PowerLaw, Uniform, Univariate class UnitSphere(ABC): @@ -101,15 +105,15 @@ class PolarAzimuthal(UnitSphere): def mu(self): return self._mu - @property - def phi(self): - return self._phi - @mu.setter def mu(self, mu): cv.check_type('cosine of polar angle', mu, Univariate) self._mu = mu + @property + def phi(self): + return self._phi + @phi.setter def phi(self, phi): cv.check_type('azimuthal angle', phi, Univariate) @@ -120,7 +124,7 @@ class PolarAzimuthal(UnitSphere): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing angular distribution data """ @@ -138,7 +142,7 @@ class PolarAzimuthal(UnitSphere): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -148,9 +152,9 @@ class PolarAzimuthal(UnitSphere): """ mu_phi = cls() - params = get_text(elem, 'parameters') - if params is not None: - mu_phi.reference_uvw = [float(x) for x in params.split()] + uvw = get_elem_list(elem, "reference_uvw", float) + if uvw is not None: + mu_phi.reference_uvw = uvw mu_phi.mu = Univariate.from_xml_element(elem.find('mu')) mu_phi.phi = Univariate.from_xml_element(elem.find('phi')) return mu_phi @@ -167,7 +171,7 @@ class Isotropic(UnitSphere): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing isotropic distribution data """ @@ -176,12 +180,12 @@ class Isotropic(UnitSphere): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate isotropic distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -208,8 +212,7 @@ class Monodirectional(UnitSphere): """ - - def __init__(self, reference_uvw=[1., 0., 0.]): + def __init__(self, reference_uvw: Sequence[float] = [1., 0., 0.]): super().__init__(reference_uvw) def to_xml_element(self): @@ -217,7 +220,7 @@ class Monodirectional(UnitSphere): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing monodirectional distribution data """ @@ -228,12 +231,12 @@ class Monodirectional(UnitSphere): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate monodirectional distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -243,9 +246,9 @@ class Monodirectional(UnitSphere): """ monodirectional = cls() - params = get_text(elem, 'parameters') - if params is not None: - monodirectional.reference_uvw = [float(x) for x in params.split()] + uvw = get_elem_list(elem, "reference_uvw", float) + if uvw is not None: + monodirectional.reference_uvw = uvw return monodirectional @@ -262,7 +265,7 @@ class Spatial(ABC): @classmethod @abstractmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem, meshes=None): distribution = get_text(elem, 'type') if distribution == 'cartesian': return CartesianIndependent.from_xml_element(elem) @@ -274,6 +277,10 @@ class Spatial(ABC): return Box.from_xml_element(elem) elif distribution == 'point': return Point.from_xml_element(elem) + elif distribution == 'mesh': + return MeshSpatial.from_xml_element(elem, meshes) + elif distribution == 'cloud': + return PointCloud.from_xml_element(elem) class CartesianIndependent(Spatial): @@ -302,7 +309,12 @@ class CartesianIndependent(Spatial): """ - def __init__(self, x, y, z): + def __init__( + self, + x: openmc.stats.Univariate, + y: openmc.stats.Univariate, + z: openmc.stats.Univariate + ): self.x = x self.y = y self.z = z @@ -311,24 +323,24 @@ class CartesianIndependent(Spatial): def x(self): return self._x - @property - def y(self): - return self._y - - @property - def z(self): - return self._z - @x.setter def x(self, x): cv.check_type('x coordinate', x, Univariate) self._x = x + @property + def y(self): + return self._y + @y.setter def y(self, y): cv.check_type('y coordinate', y, Univariate) self._y = y + @property + def z(self): + return self._z + @z.setter def z(self, z): cv.check_type('z coordinate', z, Univariate) @@ -339,7 +351,7 @@ class CartesianIndependent(Spatial): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing spatial distribution data """ @@ -351,12 +363,12 @@ class CartesianIndependent(Spatial): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate spatial distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -375,19 +387,22 @@ class SphericalIndependent(Spatial): r"""Spatial distribution represented in spherical coordinates. This distribution allows one to specify coordinates whose :math:`r`, - :math:`\theta`, and :math:`\phi` components are sampled independently from - one another and centered on the coordinates (x0, y0, z0). + :math:`\theta`, and :math:`\phi` components are sampled independently + from one another and centered on the coordinates (x0, y0, z0). - .. versionadded: 0.12 + .. versionadded:: 0.12 + + .. versionchanged:: 0.13.1 + Accepts ``cos_theta`` instead of ``theta`` Parameters ---------- r : openmc.stats.Univariate Distribution of r-coordinates in a reference frame specified by the origin parameter - theta : openmc.stats.Univariate - Distribution of theta-coordinates (angle relative to the z-axis) in a - reference frame specified by the origin parameter + cos_theta : openmc.stats.Univariate + Distribution of the cosine of the theta-coordinates (angle relative to + the z-axis) in a reference frame specified by the origin parameter phi : openmc.stats.Univariate Distribution of phi-coordinates (azimuthal angle) in a reference frame specified by the origin parameter @@ -399,9 +414,9 @@ class SphericalIndependent(Spatial): ---------- r : openmc.stats.Univariate Distribution of r-coordinates in the local reference frame - theta : openmc.stats.Univariate - Distribution of theta-coordinates (angle relative to the z-axis) in the - local reference frame + cos_theta : openmc.stats.Univariate + Distribution of the cosine of the theta-coordinates (angle relative to + the z-axis) in the local reference frame phi : openmc.stats.Univariate Distribution of phi-coordinates (azimuthal angle) in the local reference frame @@ -411,9 +426,9 @@ class SphericalIndependent(Spatial): """ - def __init__(self, r, theta, phi, origin=(0.0, 0.0, 0.0)): + def __init__(self, r, cos_theta, phi, origin=(0.0, 0.0, 0.0)): self.r = r - self.theta = theta + self.cos_theta = cos_theta self.phi = phi self.origin = origin @@ -421,33 +436,33 @@ class SphericalIndependent(Spatial): def r(self): return self._r - @property - def theta(self): - return self._theta - - @property - def phi(self): - return self._phi - - @property - def origin(self): - return self._origin - @r.setter def r(self, r): cv.check_type('r coordinate', r, Univariate) self._r = r - @theta.setter - def theta(self, theta): - cv.check_type('theta coordinate', theta, Univariate) - self._theta = theta + @property + def cos_theta(self): + return self._cos_theta + + @cos_theta.setter + def cos_theta(self, cos_theta): + cv.check_type('cos_theta coordinate', cos_theta, Univariate) + self._cos_theta = cos_theta + + @property + def phi(self): + return self._phi @phi.setter def phi(self, phi): cv.check_type('phi coordinate', phi, Univariate) self._phi = phi + @property + def origin(self): + return self._origin + @origin.setter def origin(self, origin): cv.check_type('origin coordinates', origin, Iterable, Real) @@ -459,25 +474,25 @@ class SphericalIndependent(Spatial): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing spatial distribution data """ element = ET.Element('space') element.set('type', 'spherical') element.append(self.r.to_xml_element('r')) - element.append(self.theta.to_xml_element('theta')) + element.append(self.cos_theta.to_xml_element('cos_theta')) element.append(self.phi.to_xml_element('phi')) element.set("origin", ' '.join(map(str, self.origin))) return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate spatial distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -487,10 +502,10 @@ class SphericalIndependent(Spatial): """ r = Univariate.from_xml_element(elem.find('r')) - theta = Univariate.from_xml_element(elem.find('theta')) + cos_theta = Univariate.from_xml_element(elem.find('cos_theta')) phi = Univariate.from_xml_element(elem.find('phi')) - origin = [float(x) for x in elem.get('origin').split()] - return cls(r, theta, phi, origin=origin) + origin = get_elem_list(elem, "origin", float) + return cls(r, cos_theta, phi, origin=origin) class CylindricalIndependent(Spatial): @@ -543,33 +558,33 @@ class CylindricalIndependent(Spatial): def r(self): return self._r - @property - def phi(self): - return self._phi - - @property - def z(self): - return self._z - - @property - def origin(self): - return self._origin - @r.setter def r(self, r): cv.check_type('r coordinate', r, Univariate) self._r = r + @property + def phi(self): + return self._phi + @phi.setter def phi(self, phi): cv.check_type('phi coordinate', phi, Univariate) self._phi = phi + @property + def z(self): + return self._z + @z.setter def z(self, z): cv.check_type('z coordinate', z, Univariate) self._z = z + @property + def origin(self): + return self._origin + @origin.setter def origin(self, origin): cv.check_type('origin coordinates', origin, Iterable, Real) @@ -581,7 +596,7 @@ class CylindricalIndependent(Spatial): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing spatial distribution data """ @@ -594,12 +609,12 @@ class CylindricalIndependent(Spatial): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate spatial distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -611,10 +626,244 @@ class CylindricalIndependent(Spatial): r = Univariate.from_xml_element(elem.find('r')) phi = Univariate.from_xml_element(elem.find('phi')) z = Univariate.from_xml_element(elem.find('z')) - origin = [float(x) for x in elem.get('origin').split()] + origin = get_elem_list(elem, "origin", float) return cls(r, phi, z, origin=origin) +class MeshSpatial(Spatial): + """Spatial distribution for a mesh. + + This distribution specifies a mesh to sample over with source strengths + specified for each mesh element. + + .. versionadded:: 0.13.3 + + Parameters + ---------- + mesh : openmc.MeshBase + The mesh instance used for sampling + strengths : iterable of float, optional + An iterable of values that represents the weights of each element. If no + source strengths are specified, they will be equal for all mesh + elements. + volume_normalized : bool, optional + Whether or not the strengths will be multiplied by element volumes at + runtime. Default is True. + + Attributes + ---------- + mesh : openmc.MeshBase + The mesh instance used for sampling + strengths : numpy.ndarray or None + An array of source strengths for each mesh element + volume_normalized : bool + Whether or not the strengths will be multiplied by element volumes at + runtime. + """ + + def __init__(self, mesh, strengths=None, volume_normalized=True): + self.mesh = mesh + self.strengths = strengths + self.volume_normalized = volume_normalized + + @property + def mesh(self): + return self._mesh + + @mesh.setter + def mesh(self, mesh): + if mesh is not None: + cv.check_type('mesh instance', mesh, MeshBase) + self._mesh = mesh + + @property + def volume_normalized(self): + return self._volume_normalized + + @volume_normalized.setter + def volume_normalized(self, volume_normalized): + cv.check_type('Multiply strengths by element volumes', volume_normalized, bool) + self._volume_normalized = volume_normalized + + @property + def strengths(self): + return self._strengths + + @strengths.setter + def strengths(self, given_strengths): + if given_strengths is not None: + cv.check_type('strengths array passed in', given_strengths, Iterable, Real) + self._strengths = np.asarray(given_strengths, dtype=float).flatten() + else: + self._strengths = None + + @property + def num_strength_bins(self): + if self.strengths is None: + raise ValueError('Strengths are not set') + return self.strengths.size + + def to_xml_element(self): + """Return XML representation of the spatial distribution + + Returns + ------- + element : lxml.etree._Element + XML element containing spatial distribution data + + """ + element = ET.Element('space') + + element.set('type', 'mesh') + element.set("mesh_id", str(self.mesh.id)) + element.set("volume_normalized", str(self.volume_normalized)) + + if self.strengths is not None: + subelement = ET.SubElement(element, 'strengths') + subelement.text = ' '.join(str(e) for e in self.strengths) + + return element + + @classmethod + def from_xml_element(cls, elem, meshes): + """Generate spatial distribution from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict + A dictionary with mesh IDs as keys and openmc.MeshBase instances as + values + + Returns + ------- + openmc.stats.MeshSpatial + Spatial distribution generated from XML element + + """ + + mesh_id = int(get_text(elem, "mesh_id")) + + # check if this mesh has been read in from another location already + if mesh_id not in meshes: + raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') + + volume_normalized = get_text(elem, 'volume_normalized').lower() == 'true' + strengths = get_elem_list(elem, 'strengths', float) + return cls(meshes[mesh_id], strengths, volume_normalized) + + +class PointCloud(Spatial): + """Spatial distribution from a point cloud. + + This distribution specifies a discrete list of points, with corresponding + relative probabilities. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + positions : iterable of 3-tuples + The points in space to be sampled + strengths : iterable of float, optional + An iterable of values that represents the relative probabilty of each + point. + + Attributes + ---------- + positions : numpy.ndarray + The points in space to be sampled with shape (N, 3) + strengths : numpy.ndarray or None + An array of relative probabilities for each mesh point + """ + + def __init__( + self, + positions: Sequence[Sequence[float]], + strengths: Sequence[float] | None = None + ): + self.positions = positions + self.strengths = strengths + + @property + def positions(self) -> np.ndarray: + return self._positions + + @positions.setter + def positions(self, positions): + positions = np.array(positions, dtype=float) + if positions.ndim != 2: + raise ValueError('positions must be a 2D array') + elif positions.shape[1] != 3: + raise ValueError('Each position must have 3 values') + self._positions = positions + + @property + def strengths(self) -> np.ndarray: + return self._strengths + + @strengths.setter + def strengths(self, strengths): + if strengths is not None: + strengths = np.array(strengths, dtype=float) + if strengths.ndim != 1: + raise ValueError('strengths must be a 1D array') + elif strengths.size != self.positions.shape[0]: + raise ValueError('strengths must have the same length as positions') + self._strengths = strengths + + @property + def num_strength_bins(self) -> int: + if self.strengths is None: + raise ValueError('Strengths are not set') + return self.strengths.size + + def to_xml_element(self) -> ET.Element: + """Return XML representation of the spatial distribution + + Returns + ------- + element : lxml.etree._Element + XML element containing spatial distribution data + + """ + element = ET.Element('space') + element.set('type', 'cloud') + + subelement = ET.SubElement(element, 'coords') + subelement.text = ' '.join(str(e) for e in self.positions.flatten()) + + if self.strengths is not None: + subelement = ET.SubElement(element, 'strengths') + subelement.text = ' '.join(str(e) for e in self.strengths) + + return element + + @classmethod + def from_xml_element(cls, elem: ET.Element) -> PointCloud: + """Generate spatial distribution from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + + Returns + ------- + openmc.stats.PointCloud + Spatial distribution generated from XML element + + + """ + coord_data = get_elem_list(elem, 'coords', float) + positions = np.array(coord_data).reshape((-1, 3)) + + strengths = get_elem_list(elem, 'strengths', float) + + return cls(positions, strengths) + + class Box(Spatial): """Uniform distribution of coordinates in a rectangular cuboid. @@ -628,6 +877,9 @@ class Box(Spatial): Whether spatial sites should only be accepted if they occur in fissionable materials + .. deprecated:: 0.15.0 + Use the `constraints` argument when defining a source object instead. + Attributes ---------- lower_left : Iterable of float @@ -638,10 +890,17 @@ class Box(Spatial): Whether spatial sites should only be accepted if they occur in fissionable materials + .. deprecated:: 0.15.0 + Use the `constraints` argument when defining a source object instead. + """ - - def __init__(self, lower_left, upper_right, only_fissionable=False): + def __init__( + self, + lower_left: Sequence[float], + upper_right: Sequence[float], + only_fissionable: bool = False + ): self.lower_left = lower_left self.upper_right = upper_right self.only_fissionable = only_fissionable @@ -650,37 +909,41 @@ class Box(Spatial): def lower_left(self): return self._lower_left - @property - def upper_right(self): - return self._upper_right - - @property - def only_fissionable(self): - return self._only_fissionable - @lower_left.setter def lower_left(self, lower_left): cv.check_type('lower left coordinate', lower_left, Iterable, Real) cv.check_length('lower left coordinate', lower_left, 3) self._lower_left = lower_left + @property + def upper_right(self): + return self._upper_right + @upper_right.setter def upper_right(self, upper_right): cv.check_type('upper right coordinate', upper_right, Iterable, Real) cv.check_length('upper right coordinate', upper_right, 3) self._upper_right = upper_right + @property + def only_fissionable(self): + return self._only_fissionable + @only_fissionable.setter def only_fissionable(self, only_fissionable): cv.check_type('only fissionable', only_fissionable, bool) self._only_fissionable = only_fissionable + if only_fissionable: + warn("The 'only_fissionable' has been deprecated. Use the " + "'constraints' argument when defining a source instead.", + FutureWarning) def to_xml_element(self): """Return XML representation of the box distribution Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing box distribution data """ @@ -695,12 +958,12 @@ class Box(Spatial): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate box distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -710,7 +973,7 @@ class Box(Spatial): """ only_fissionable = get_text(elem, 'type') == 'fission' - params = [float(x) for x in get_text(elem, 'parameters').split()] + params = get_elem_list(elem, "parameters", float) lower_left = params[:len(params)//2] upper_right = params[len(params)//2:] return cls(lower_left, upper_right, only_fissionable) @@ -734,7 +997,7 @@ class Point(Spatial): """ - def __init__(self, xyz=(0., 0., 0.)): + def __init__(self, xyz: Sequence[float] = (0., 0., 0.)): self.xyz = xyz @property @@ -752,7 +1015,7 @@ class Point(Spatial): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing point distribution location """ @@ -763,12 +1026,12 @@ class Point(Spatial): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate point distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -777,5 +1040,49 @@ class Point(Spatial): Point distribution generated from XML element """ - xyz = [float(x) for x in get_text(elem, 'parameters').split()] + xyz = get_elem_list(elem, "parameters", float) return cls(xyz) + + +def spherical_uniform( + r_outer: float, + r_inner: float = 0.0, + thetas: Sequence[float] = (0., pi), + phis: Sequence[float] = (0., 2*pi), + origin: Sequence[float] = (0., 0., 0.) + ): + """Return a uniform spatial distribution over a spherical shell. + + This function provides a uniform spatial distribution over a spherical + shell between `r_inner` and `r_outer`. Optionally, the range of angles + can be restricted by the `thetas` and `phis` arguments. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + r_outer : float + Outer radius of the spherical shell in [cm] + r_inner : float + Inner radius of the spherical shell in [cm] + thetas : iterable of float + Starting and ending theta coordinates (angle relative to + the z-axis) in radius in a reference frame centered at `origin` + phis : iterable of float + Starting and ending phi coordinates (azimuthal angle) in + radians in a reference frame centered at `origin` + origin: iterable of float + Coordinates (x0, y0, z0) of the center of the spherical + reference frame for the distribution. + + Returns + ------- + openmc.stats.SphericalIndependent + Uniform distribution over the spherical shell + """ + + r_dist = PowerLaw(r_inner, r_outer, 2) + cos_thetas_dist = Uniform(cos(thetas[1]), cos(thetas[0])) + phis_dist = Uniform(phis[0], phis[1]) + + return SphericalIndependent(r_dist, cos_thetas_dist, phis_dist, origin) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index f8e50044fc..c48cc00757 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -1,22 +1,27 @@ +from __future__ import annotations +import math from abc import ABC, abstractmethod -from collections.abc import Iterable +from collections import defaultdict +from collections.abc import Iterable, Sequence +from copy import deepcopy from numbers import Real -from xml.etree import ElementTree as ET +from warnings import warn +import lxml.etree as ET import numpy as np +from scipy.integrate import trapezoid import openmc.checkvalue as cv -from .._xml import get_text +from .._xml import get_elem_list, get_text from ..mixin import EqualityMixin - -_INTERPOLATION_SCHEMES = [ +_INTERPOLATION_SCHEMES = { 'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log' -] +} class Univariate(EqualityMixin, ABC): @@ -51,7 +56,8 @@ class Univariate(EqualityMixin, ABC): elif distribution == 'normal': return Normal.from_xml_element(elem) elif distribution == 'muir': - return Muir.from_xml_element(elem) + # Support older files where Muir had its own class + return muir(*get_elem_list(elem, "parameters", float)) elif distribution == 'tabular': return Tabular.from_xml_element(elem) elif distribution == 'legendre': @@ -59,6 +65,75 @@ class Univariate(EqualityMixin, ABC): elif distribution == 'mixture': return Mixture.from_xml_element(elem) + @abstractmethod + def sample(n_samples: int = 1, seed: int | None = None): + """Sample the univariate distribution + + Parameters + ---------- + n_samples : int + Number of sampled values to generate + seed : int or None + Initial random number seed. + + Returns + ------- + numpy.ndarray + A 1-D array of sampled values + """ + pass + + def integral(self): + """Return integral of distribution + + .. versionadded:: 0.13.1 + + Returns + ------- + float + Integral of distribution + """ + return 1.0 + + +def _intensity_clip(intensity: Sequence[float], tolerance: float = 1e-6) -> np.ndarray: + """Clip low-importance points from an array of intensities. + + Given an array of intensities, this function returns an array of indices for + points that contribute non-negligibly to the total sum of intensities. + + Parameters + ---------- + intensity : sequence of float + Intensities in arbitrary units. + tolerance : float + Maximum fraction of intensities that will be discarded. + + Returns + ------- + Array of indices + + """ + # Get indices of intensities from largest to smallest + index_sort = np.argsort(intensity)[::-1] + + # Get intensities from largest to smallest + sorted_intensity = np.asarray(intensity)[index_sort] + + # Determine cumulative sum of probabilities + cumsum = np.cumsum(sorted_intensity) + cumsum /= cumsum[-1] + + # Find index that satisfies cutoff + index_cutoff = np.searchsorted(cumsum, 1.0 - tolerance) + + # Now get indices up to cutoff + new_indices = index_sort[:index_cutoff + 1] + + # Put back in the order of the original array and return + new_indices.sort() + return new_indices + class Discrete(Univariate): """Distribution characterized by a probability mass function. @@ -76,9 +151,9 @@ class Discrete(Univariate): Attributes ---------- - x : Iterable of float + x : numpy.ndarray Values of the random variable - p : Iterable of float + p : numpy.ndarray Discrete probability for each value """ @@ -94,16 +169,16 @@ class Discrete(Univariate): def x(self): return self._x - @property - def p(self): - return self._p - @x.setter def x(self, x): if isinstance(x, Real): x = [x] cv.check_type('discrete values', x, Iterable, Real) - self._x = x + self._x = np.array(x, dtype=float) + + @property + def p(self): + return self._p @p.setter def p(self, p): @@ -112,7 +187,20 @@ class Discrete(Univariate): cv.check_type('discrete probabilities', p, Iterable, Real) for pk in p: cv.check_greater_than('discrete probability', pk, 0.0, True) - self._p = p + self._p = np.array(p, dtype=float) + + def cdf(self): + return np.insert(np.cumsum(self.p), 0, 0.0) + + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + p = self.p / self.p.sum() + return rng.choice(self.x, n_samples, p=p) + + def normalize(self): + """Normalize the probabilities stored on the distribution""" + norm = sum(self.p) + self.p = [val / norm for val in self.p] def to_xml_element(self, element_name): """Return XML representation of the discrete distribution @@ -124,7 +212,7 @@ class Discrete(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing discrete distribution data """ @@ -137,12 +225,12 @@ class Discrete(Univariate): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate discrete distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -151,11 +239,139 @@ class Discrete(Univariate): Discrete distribution generated from XML element """ - params = [float(x) for x in get_text(elem, 'parameters').split()] + params = get_elem_list(elem, "parameters", float) x = params[:len(params)//2] p = params[len(params)//2:] return cls(x, p) + @classmethod + def merge( + cls, + dists: Sequence[Discrete], + probs: Sequence[int] + ): + """Merge multiple discrete distributions into a single distribution + + .. versionadded:: 0.13.1 + + Parameters + ---------- + dists : iterable of openmc.stats.Discrete + Discrete distributions to combine + probs : iterable of float + Probability of each distribution + + Returns + ------- + openmc.stats.Discrete + Combined discrete distribution + + """ + if len(dists) != len(probs): + raise ValueError("Number of distributions and probabilities must match.") + + # Combine distributions accounting for duplicate x values + x_merged = set() + p_merged = defaultdict(float) + for dist, p_dist in zip(dists, probs): + for x, p in zip(dist.x, dist.p): + x_merged.add(x) + p_merged[x] += p*p_dist + + # Create values and probabilities as arrays + x_arr = np.array(sorted(x_merged)) + p_arr = np.array([p_merged[x] for x in x_arr]) + return cls(x_arr, p_arr) + + def integral(self): + """Return integral of distribution + + .. versionadded:: 0.13.1 + + Returns + ------- + float + Integral of discrete distribution + """ + return np.sum(self.p) + + def mean(self) -> float: + """Return mean of the discrete distribution + + The mean is the weighted average of the discrete values. + + .. versionadded:: 0.15.3 + + Returns + ------- + float + Mean of discrete distribution + """ + return np.sum(self.x * self.p) / np.sum(self.p) + + def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Discrete: + r"""Remove low-importance points from discrete distribution. + + Given a probability mass function :math:`p(x)` with :math:`\{x_1, x_2, + x_3, \dots\}` the possible values of the random variable with + corresponding probabilities :math:`\{p_1, p_2, p_3, \dots\}`, this + function will remove any low-importance points such that :math:`\sum_i + x_i p_i` is preserved to within some threshold. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + tolerance : float + Maximum fraction of :math:`\sum_i x_i p_i` that will be discarded. + inplace : bool + Whether to modify the current object in-place or return a new one. + + Returns + ------- + Discrete distribution with low-importance points removed + + """ + cv.check_less_than("tolerance", tolerance, 1.0, equality=True) + cv.check_greater_than("tolerance", tolerance, 0.0, equality=True) + + # Compute intensities + intensity = self.p * self.x + + # Get indices for intensities above threshold + indices = _intensity_clip(intensity, tolerance=tolerance) + + # Create new discrete distribution + if inplace: + self.x = self.x[indices] + self.p = self.p[indices] + return self + else: + new_x = self.x[indices] + new_p = self.p[indices] + return type(self)(new_x, new_p) + + +def delta_function(value: float, intensity: float = 1.0) -> Discrete: + """Return a discrete distribution with a single point. + + .. versionadded:: 0.15.1 + + Parameters + ---------- + value : float + Value of the random variable. + intensity : float, optional + When used for an energy distribution, this can be used to assign an + intensity. + + Returns + ------- + Discrete distribution with a single point + + """ + return Discrete([value], [intensity]) + class Uniform(Univariate): """Distribution with constant probability over a finite interval [a,b] @@ -176,7 +392,7 @@ class Uniform(Univariate): """ - def __init__(self, a=0.0, b=1.0): + def __init__(self, a: float = 0.0, b: float = 1.0): self.a = a self.b = b @@ -187,15 +403,15 @@ class Uniform(Univariate): def a(self): return self._a - @property - def b(self): - return self._b - @a.setter def a(self, a): cv.check_type('Uniform a', a, Real) self._a = a + @property + def b(self): + return self._b + @b.setter def b(self, b): cv.check_type('Uniform b', b, Real) @@ -207,7 +423,23 @@ class Uniform(Univariate): t.c = [0., 1.] return t - def to_xml_element(self, element_name): + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + return rng.uniform(self.a, self.b, n_samples) + + def mean(self) -> float: + """Return mean of the uniform distribution + + .. versionadded:: 0.15.3 + + Returns + ------- + float + Mean of uniform distribution + """ + return 0.5 * (self.a + self.b) + + def to_xml_element(self, element_name: str): """Return XML representation of the uniform distribution Parameters @@ -217,22 +449,22 @@ class Uniform(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing uniform distribution data """ element = ET.Element(element_name) element.set("type", "uniform") - element.set("parameters", '{} {}'.format(self.a, self.b)) + element.set("parameters", f'{self.a} {self.b}') return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate uniform distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -241,8 +473,8 @@ class Uniform(Univariate): Uniform distribution generated from XML element """ - params = get_text(elem, 'parameters').split() - return cls(*map(float, params)) + params = get_elem_list(elem, "parameters", float) + return cls(*params) class PowerLaw(Univariate): @@ -273,7 +505,10 @@ class PowerLaw(Univariate): """ - def __init__(self, a=0.0, b=1.0, n=0): + def __init__(self, a: float = 0.0, b: float = 1.0, n: float = 0.): + if a >= b: + raise ValueError( + "Lower bound of sampling interval must be less than upper bound.") self.a = a self.b = b self.n = n @@ -285,30 +520,44 @@ class PowerLaw(Univariate): def a(self): return self._a + @a.setter + def a(self, a): + cv.check_type('interval lower bound', a, Real) + if a < 0: + raise ValueError( + "PowerLaw sampling is restricted to positive-valued intervals.") + self._a = a + @property def b(self): return self._b - @property - def n(self): - return self._n - - @a.setter - def a(self, a): - cv.check_type('interval lower bound', a, Real) - self._a = a - @b.setter def b(self, b): cv.check_type('interval upper bound', b, Real) + if b < 0: + raise ValueError( + "PowerLaw sampling is restricted to positive-valued intervals.") self._b = b + @property + def n(self): + return self._n + @n.setter def n(self, n): cv.check_type('power law exponent', n, Real) self._n = n - def to_xml_element(self, element_name): + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + xi = rng.random(n_samples) + pwr = self.n + 1 + offset = self.a**pwr + span = self.b**pwr - offset + return np.power(offset + xi * span, 1/pwr) + + def to_xml_element(self, element_name: str): """Return XML representation of the power law distribution Parameters @@ -318,7 +567,7 @@ class PowerLaw(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing distribution data """ @@ -328,12 +577,12 @@ class PowerLaw(Univariate): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate power law distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -342,8 +591,8 @@ class PowerLaw(Univariate): Distribution generated from XML element """ - params = get_text(elem, 'parameters').split() - return cls(*map(float, params)) + params = get_elem_list(elem, "parameters", float) + return cls(*params) class Maxwell(Univariate): @@ -381,7 +630,19 @@ class Maxwell(Univariate): cv.check_greater_than('Maxwell temperature', theta, 0.0) self._theta = theta - def to_xml_element(self, element_name): + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + return self.sample_maxwell(self.theta, n_samples, rng=rng) + + @staticmethod + def sample_maxwell(t, n_samples: int, rng=None): + if rng is None: + rng = np.random.default_rng() + r1, r2, r3 = rng.random((3, n_samples)) + c = np.cos(0.5 * np.pi * r3) + return -t * (np.log(r1) + np.log(r2) * c * c) + + def to_xml_element(self, element_name: str): """Return XML representation of the Maxwellian distribution Parameters @@ -391,7 +652,7 @@ class Maxwell(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Maxwellian distribution data """ @@ -401,12 +662,12 @@ class Maxwell(Univariate): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate Maxwellian distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -453,23 +714,30 @@ class Watt(Univariate): def a(self): return self._a - @property - def b(self): - return self._b - @a.setter def a(self, a): cv.check_type('Watt a', a, Real) cv.check_greater_than('Watt a', a, 0.0) self._a = a + @property + def b(self): + return self._b + @b.setter def b(self, b): cv.check_type('Watt b', b, Real) cv.check_greater_than('Watt b', b, 0.0) self._b = b - def to_xml_element(self, element_name): + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + w = Maxwell.sample_maxwell(self.a, n_samples, rng=rng) + u = rng.uniform(-1., 1., n_samples) + aab = self.a * self.a * self.b + return w + 0.25*aab + u*np.sqrt(aab*w) + + def to_xml_element(self, element_name: str): """Return XML representation of the Watt distribution Parameters @@ -479,22 +747,22 @@ class Watt(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Watt distribution data """ element = ET.Element(element_name) element.set("type", "watt") - element.set("parameters", '{} {}'.format(self.a, self.b)) + element.set("parameters", f'{self.a} {self.b}') return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate Watt distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -503,8 +771,8 @@ class Watt(Univariate): Watt distribution generated from XML element """ - params = get_text(elem, 'parameters').split() - return cls(*map(float, params)) + params = get_elem_list(elem, "parameters", float) + return cls(*params) class Normal(Univariate): @@ -540,22 +808,26 @@ class Normal(Univariate): def mean_value(self): return self._mean_value - @property - def std_dev(self): - return self._std_dev - @mean_value.setter def mean_value(self, mean_value): cv.check_type('Normal mean_value', mean_value, Real) self._mean_value = mean_value + @property + def std_dev(self): + return self._std_dev + @std_dev.setter def std_dev(self, std_dev): cv.check_type('Normal std_dev', std_dev, Real) cv.check_greater_than('Normal std_dev', std_dev, 0.0) self._std_dev = std_dev - def to_xml_element(self, element_name): + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + return rng.normal(self.mean_value, self.std_dev, n_samples) + + def to_xml_element(self, element_name: str): """Return XML representation of the Normal distribution Parameters @@ -565,22 +837,22 @@ class Normal(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing Watt distribution data """ element = ET.Element(element_name) element.set("type", "normal") - element.set("parameters", '{} {}'.format(self.mean_value, self.std_dev)) + element.set("parameters", f'{self.mean_value} {self.std_dev}') return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate Normal distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -589,114 +861,49 @@ class Normal(Univariate): Normal distribution generated from XML element """ - params = get_text(elem, 'parameters').split() - return cls(*map(float, params)) + params = get_elem_list(elem, "parameters", float) + return cls(*params) -class Muir(Univariate): - """Muir energy spectrum. +def muir(e0: float, m_rat: float, kt: float): + """Generate a Muir energy spectrum - The Muir energy spectrum is a Gaussian spectrum, but for - convenience reasons allows the user 3 parameters to define - the distribution, e0 the mean energy of particles, the mass - of reactants m_rat, and the ion temperature kt. + The Muir energy spectrum is a normal distribution, but for convenience + reasons allows the user to specify three parameters to define the + distribution: the mean energy of particles ``e0``, the mass of reactants + ``m_rat``, and the ion temperature ``kt``. + + .. versionadded:: 0.13.2 Parameters ---------- e0 : float - Mean of the Muir distribution in units of eV + Mean of the Muir distribution in [eV] m_rat : float - Ratio of the sum of the masses of the reaction inputs to an - AMU + Ratio of the sum of the masses of the reaction inputs to 1 amu kt : float - Ion temperature for the Muir distribution in units of eV + Ion temperature for the Muir distribution in [eV] - Attributes - ---------- - e0 : float - Mean of the Muir distribution in units of eV - m_rat : float - Ratio of the sum of the masses of the reaction inputs to an - AMU - kt : float - Ion temperature for the Muir distribution in units of eV + Returns + ------- + openmc.stats.Normal + Corresponding normal distribution """ + # https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-05411-MS + std_dev = math.sqrt(2 * e0 * kt / m_rat) + return Normal(e0, std_dev) - def __init__(self, e0=14.08e6, m_rat = 5., kt = 20000.): - self.e0 = e0 - self.m_rat = m_rat - self.kt = kt - def __len__(self): - return 3 - - @property - def e0(self): - return self._e0 - - @property - def m_rat(self): - return self._m_rat - - @property - def kt(self): - return self._kt - - @e0.setter - def e0(self, e0): - cv.check_type('Muir e0', e0, Real) - cv.check_greater_than('Muir e0', e0, 0.0) - self._e0 = e0 - - @m_rat.setter - def m_rat(self, m_rat): - cv.check_type('Muir m_rat', m_rat, Real) - cv.check_greater_than('Muir m_rat', m_rat, 0.0) - self._m_rat = m_rat - - @kt.setter - def kt(self, kt): - cv.check_type('Muir kt', kt, Real) - cv.check_greater_than('Muir kt', kt, 0.0) - self._kt = kt - - def to_xml_element(self, element_name): - """Return XML representation of the Watt distribution - - Parameters - ---------- - element_name : str - XML element name - - Returns - ------- - element : xml.etree.ElementTree.Element - XML element containing Watt distribution data - - """ - element = ET.Element(element_name) - element.set("type", "muir") - element.set("parameters", '{} {} {}'.format(self._e0, self._m_rat, self._kt)) - return element - - @classmethod - def from_xml_element(cls, elem): - """Generate Muir distribution from an XML element - - Parameters - ---------- - elem : xml.etree.ElementTree.Element - XML element - - Returns - ------- - openmc.stats.Muir - Muir distribution generated from XML element - - """ - params = get_text(elem, 'parameters').split() - return cls(*map(float, params)) +# Retain deprecated name for the time being +def Muir(*args, **kwargs): + # warn of name change + warn( + "The Muir(...) class has been replaced by the muir(...) function and " + "will be removed in a future version of OpenMC. Use muir(...) instead.", + FutureWarning + ) + return muir(*args, **kwargs) class Tabular(Univariate): @@ -711,34 +918,66 @@ class Tabular(Univariate): x : Iterable of float Tabulated values of the random variable p : Iterable of float - Tabulated probabilities + Tabulated probabilities. For histogram interpolation, if the length of + `p` is the same as `x`, the last value is ignored. Probabilities `p` are + given per unit of `x`. interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'}, optional - Indicate whether the density function is constant between tabulated - points or linearly-interpolated. Defaults to 'linear-linear'. + Indicates how the density function is interpolated between tabulated + points. Defaults to 'linear-linear'. ignore_negative : bool Ignore negative probabilities Attributes ---------- - x : Iterable of float + x : numpy.ndarray Tabulated values of the random variable - p : Iterable of float + p : numpy.ndarray Tabulated probabilities - interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'}, optional - Indicate whether the density function is constant between tabulated - points or linearly-interpolated. + interpolation : {'histogram', 'linear-linear', 'linear-log', 'log-linear', 'log-log'} + Indicates how the density function is interpolated between tabulated + points. Defaults to 'linear-linear'. + + Notes + ----- + The probabilities `p` are interpreted per unit of the corresponding + independent variable `x`. This follows the definition of a probability + density function (PDF) in probability theory, where the PDF represents the + relative likelihood of the random variable taking on a particular value per + unit of the variable. For example, if `x` represents energy in eV, then `p` + should represent probabilities per eV. """ - def __init__(self, x, p, interpolation='linear-linear', - ignore_negative=False): - self._ignore_negative = ignore_negative - self.x = x - self.p = p + def __init__( + self, + x: Sequence[float], + p: Sequence[float], + interpolation: str = 'linear-linear', + ignore_negative: bool = False + ): self.interpolation = interpolation + cv.check_type('tabulated values', x, Iterable, Real) + cv.check_type('tabulated probabilities', p, Iterable, Real) + + x = np.array(x, dtype=float) + p = np.array(p, dtype=float) + + if p.size > x.size: + raise ValueError('Number of probabilities exceeds number of table values.') + if self.interpolation != 'histogram' and x.size != p.size: + raise ValueError(f'Tabulated values ({x.size}) and probabilities ' + f'({p.size}) should have the same length') + + if not ignore_negative: + for pk in p: + cv.check_greater_than('tabulated probability', pk, 0.0, True) + + self._x = x + self._p = p + def __len__(self): - return len(self.x) + return self.p.size @property def x(self): @@ -752,25 +991,126 @@ class Tabular(Univariate): def interpolation(self): return self._interpolation - @x.setter - def x(self, x): - cv.check_type('tabulated values', x, Iterable, Real) - self._x = x - - @p.setter - def p(self, p): - cv.check_type('tabulated probabilities', p, Iterable, Real) - if not self._ignore_negative: - for pk in p: - cv.check_greater_than('tabulated probability', pk, 0.0, True) - self._p = p - @interpolation.setter def interpolation(self, interpolation): cv.check_value('interpolation', interpolation, _INTERPOLATION_SCHEMES) self._interpolation = interpolation - def to_xml_element(self, element_name): + def cdf(self): + c = np.zeros_like(self.x) + x = self.x + p = self.p + + if self.interpolation == 'histogram': + c[1:] = p[:x.size-1] * np.diff(x) + elif self.interpolation == 'linear-linear': + c[1:] = 0.5 * (p[:-1] + p[1:]) * np.diff(x) + else: + raise NotImplementedError('Can only generate CDFs for tabular ' + 'distributions using histogram or ' + 'linear-linear interpolation') + + + return np.cumsum(c) + + def mean(self): + """Compute the mean of the tabular distribution""" + if self.interpolation == 'linear-linear': + mean = 0.0 + for i in range(1, len(self.x)): + y_min = self.p[i-1] + y_max = self.p[i] + x_min = self.x[i-1] + x_max = self.x[i] + + m = (y_max - y_min) / (x_max - x_min) + + exp_val = (1./3.) * m * (x_max**3 - x_min**3) + exp_val += 0.5 * m * x_min * (x_min**2 - x_max**2) + exp_val += 0.5 * y_min * (x_max**2 - x_min**2) + mean += exp_val + + elif self.interpolation == 'histogram': + x_l = self.x[:-1] + x_r = self.x[1:] + p_l = self.p[:self.x.size-1] + mean = (0.5 * (x_l + x_r) * (x_r - x_l) * p_l).sum() + else: + raise NotImplementedError('Can only compute mean for tabular ' + 'distributions using histogram ' + 'or linear-linear interpolation.') + + # Normalize for when integral of distribution is not 1 + mean /= self.integral() + + return mean + + def normalize(self): + """Normalize the probabilities stored on the distribution""" + self._p /= self.cdf().max() + + def sample(self, n_samples: int = 1, seed: int | None = None): + rng = np.random.RandomState(seed) + xi = rng.random(n_samples) + + # always use normalized probabilities when sampling + cdf = self.cdf() + p = self.p / cdf.max() + cdf /= cdf.max() + + # get CDF bins that are above the + # sampled values + c_i = np.full(n_samples, cdf[0]) + cdf_idx = np.zeros(n_samples, dtype=int) + for i, val in enumerate(cdf[:-1]): + mask = xi > val + c_i[mask] = val + cdf_idx[mask] = i + + # get table values at each index where + # the random number is less than the next cdf + # entry + x_i = self.x[cdf_idx] + p_i = p[cdf_idx] + + if self.interpolation == 'histogram': + # mask where probability is greater than zero + pos_mask = p_i > 0.0 + # probabilities greater than zero are set proportional to the + # position of the random numebers in relation to the cdf value + p_i[pos_mask] = x_i[pos_mask] + (xi[pos_mask] - c_i[pos_mask]) \ + / p_i[pos_mask] + # probabilities smaller than zero are set to the random number value + p_i[~pos_mask] = x_i[~pos_mask] + + samples_out = p_i + + elif self.interpolation == 'linear-linear': + # get variable and probability values for the + # next entry + x_i1 = self.x[cdf_idx + 1] + p_i1 = p[cdf_idx + 1] + # compute slope between entries + m = (p_i1 - p_i) / (x_i1 - x_i) + # set values for zero slope + zero = m == 0.0 + m[zero] = x_i[zero] + (xi[zero] - c_i[zero]) / p_i[zero] + # set values for non-zero slope + non_zero = ~zero + quad = np.power(p_i[non_zero], 2) + 2.0 * m[non_zero] * (xi[non_zero] - c_i[non_zero]) + quad[quad < 0.0] = 0.0 + m[non_zero] = x_i[non_zero] + (np.sqrt(quad) - p_i[non_zero]) / m[non_zero] + samples_out = m + + else: + raise NotImplementedError('Can only sample tabular distributions ' + 'using histogram or ' + 'linear-linear interpolation') + + assert all(samples_out < self.x[-1]) + return samples_out + + def to_xml_element(self, element_name: str): """Return XML representation of the tabular distribution Parameters @@ -780,7 +1120,7 @@ class Tabular(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing tabular distribution data """ @@ -794,12 +1134,12 @@ class Tabular(Univariate): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate tabular distribution from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -809,11 +1149,30 @@ class Tabular(Univariate): """ interpolation = get_text(elem, 'interpolation') - params = [float(x) for x in get_text(elem, 'parameters').split()] - x = params[:len(params)//2] - p = params[len(params)//2:] + params = get_elem_list(elem, "parameters", float) + m = (len(params) + 1)//2 # +1 for when len(params) is odd + x = params[:m] + p = params[m:] return cls(x, p, interpolation) + def integral(self): + """Return integral of distribution + + .. versionadded:: 0.13.1 + + Returns + ------- + float + Integral of tabular distrbution + """ + if self.interpolation == 'histogram': + return np.sum(np.diff(self.x) * self.p[:self.x.size-1]) + elif self.interpolation == 'linear-linear': + return trapezoid(self.p, self.x) + else: + raise NotImplementedError( + f'integral() not supported for {self.inteprolation} interpolation') + class Legendre(Univariate): r"""Probability density given by a Legendre polynomial expansion @@ -833,7 +1192,7 @@ class Legendre(Univariate): """ - def __init__(self, coefficients): + def __init__(self, coefficients: Sequence[float]): self.coefficients = coefficients self._legendre_poly = None @@ -857,6 +1216,9 @@ class Legendre(Univariate): def coefficients(self, coefficients): self._coefficients = np.asarray(coefficients) + def sample(self, n_samples=1, seed=None): + raise NotImplementedError + def to_xml_element(self, element_name): raise NotImplementedError @@ -884,7 +1246,11 @@ class Mixture(Univariate): """ - def __init__(self, probability, distribution): + def __init__( + self, + probability: Sequence[float], + distribution: Sequence[Univariate] + ): self.probability = probability self.distribution = distribution @@ -895,10 +1261,6 @@ class Mixture(Univariate): def probability(self): return self._probability - @property - def distribution(self): - return self._distribution - @probability.setter def probability(self, probability): cv.check_type('mixture distribution probabilities', probability, @@ -906,7 +1268,11 @@ class Mixture(Univariate): for p in probability: cv.check_greater_than('mixture distribution probabilities', p, 0.0, True) - self._probability = probability + self._probability = np.array(probability, dtype=float) + + @property + def distribution(self): + return self._distribution @distribution.setter def distribution(self, distribution): @@ -914,7 +1280,34 @@ class Mixture(Univariate): Iterable, Univariate) self._distribution = distribution - def to_xml_element(self, element_name): + def cdf(self): + return np.insert(np.cumsum(self.probability), 0, 0.0) + + def sample(self, n_samples=1, seed=None): + rng = np.random.RandomState(seed) + + # Get probability of each distribution accounting for its intensity + p = np.array([prob*dist.integral() for prob, dist in + zip(self.probability, self.distribution)]) + p /= p.sum() + + # Sample from the distributions + idx = rng.choice(range(len(self.distribution)), n_samples, p=p) + + # Draw samples from the distributions sampled above + out = np.empty_like(idx, dtype=float) + for i in np.unique(idx): + n_dist_samples = np.count_nonzero(idx == i) + samples = self.distribution[i].sample(n_dist_samples) + out[idx == i] = samples + return out + + def normalize(self): + """Normalize the probabilities stored on the distribution""" + norm = sum(self.probability) + self.probability = [val / norm for val in self.probability] + + def to_xml_element(self, element_name: str): """Return XML representation of the mixture distribution .. versionadded:: 0.13.0 @@ -926,7 +1319,7 @@ class Mixture(Univariate): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing mixture distribution data """ @@ -941,14 +1334,14 @@ class Mixture(Univariate): return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate mixture distribution from an XML element .. versionadded:: 0.13.0 Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -964,3 +1357,156 @@ class Mixture(Univariate): distribution.append(Univariate.from_xml_element(pair.find("dist"))) return cls(probability, distribution) + + def integral(self): + """Return integral of the distribution + + .. versionadded:: 0.13.1 + + Returns + ------- + float + Integral of the distribution + """ + return sum([ + p*dist.integral() + for p, dist in zip(self.probability, self.distribution) + ]) + + def mean(self) -> float: + """Return mean of the mixture distribution + + The mean is the weighted average of the means of the component + distributions, weighted by probability * integral. + + .. versionadded:: 0.15.3 + + Returns + ------- + float + Mean of the mixture distribution + """ + # Weight each component by its probability and integral + weights = [p*dist.integral() for p, dist in + zip(self.probability, self.distribution)] + total_weight = sum(weights) + + if total_weight == 0: + return 0.0 + + return sum([w*dist.mean() for w, dist in + zip(weights, self.distribution)]) / total_weight + + def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture: + r"""Remove low-importance points / distributions + + Like :meth:`Discrete.clip`, this method will remove low-importance + points from discrete distributions contained within the mixture but it + will also clip any distributions that have negligible contributions to + the overall intensity. + + .. versionadded:: 0.14.0 + + Parameters + ---------- + tolerance : float + Maximum fraction of intensities that will be discarded. + inplace : bool + Whether to modify the current object in-place or return a new one. + + Returns + ------- + Distribution with low-importance points / distributions removed + + """ + # Calculate mean * integral for original distribution to compare later. + original_mean_integral = self.mean() * self.integral() + + # Determine indices for any distributions that contribute non-negligibly + # to overall mean * integral + mean_integrals = [prob*dist.mean()*dist.integral() for prob, dist in + zip(self.probability, self.distribution)] + indices = _intensity_clip(mean_integrals, tolerance=tolerance) + + # Clip mixture of distributions + probability = self.probability[indices] + distribution = [self.distribution[i] for i in indices] + + # Clip points from Discrete distributions + distribution = [ + dist.clip(tolerance, inplace) if isinstance(dist, Discrete) else dist + for dist in distribution + ] + + if inplace: + # Set attributes of current object and return + self.probability = probability + self.distribution = distribution + new_dist = self + else: + # Create new distribution + new_dist = type(self)(probability, distribution) + + # Show warning if mean * integral of new distribution is not within + # tolerance of original. For energy distributions, mean * integral + # represents total energy. + new_mean_integral = new_dist.mean() * new_dist.integral() + diff = (original_mean_integral - new_mean_integral)/original_mean_integral + if diff > tolerance: + warn("Clipping mixture distribution resulted in a mean*integral " + f"that is lower by a fraction of {diff} when tolerance={tolerance}.") + + return new_dist + + +def combine_distributions( + dists: Sequence[Univariate], + probs: Sequence[float] +): + """Combine distributions with specified probabilities + + This function can be used to combine multiple instances of + :class:`~openmc.stats.Discrete` and `~openmc.stats.Tabular`. Multiple + discrete distributions are merged into a single distribution and the + remainder of the distributions are put into a :class:`~openmc.stats.Mixture` + distribution. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + dists : iterable of openmc.stats.Univariate + Distributions to combine + probs : iterable of float + Probability (or intensity) of each distribution + + """ + # Get copy of distribution list so as not to modify the argument + dist_list = deepcopy(dists) + + # Get list of discrete/continuous distribution indices + discrete_index = [i for i, d in enumerate(dist_list) if isinstance(d, Discrete)] + cont_index = [i for i, d in enumerate(dist_list) if isinstance(d, Tabular)] + + # Apply probabilites to continuous distributions + for i in cont_index: + dist = dist_list[i] + dist._p *= probs[i] + + if discrete_index: + # Create combined discrete distribution + dist_discrete = [dist_list[i] for i in discrete_index] + discrete_probs = [probs[i] for i in discrete_index] + combined_dist = Discrete.merge(dist_discrete, discrete_probs) + + # Replace multiple discrete distributions with merged + for idx in reversed(discrete_index): + dist_list.pop(idx) + dist_list.append(combined_dist) + + # Combine discrete and continuous if present + if len(dist_list) > 1: + probs = [1.0]*len(dist_list) + dist_list[:] = [Mixture(probs, dist_list.copy())] + + return dist_list[0] diff --git a/openmc/summary.py b/openmc/summary.py index 41bae7f26b..ca5cfadd73 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -14,6 +14,11 @@ _VERSION_SUMMARY = 6 class Summary: """Summary of model used in a simulation. + Parameters + ---------- + filename : str or path-like + Path to file to load + Attributes ---------- date_and_time : str @@ -33,8 +38,9 @@ class Summary: """ def __init__(self, filename): + filename = str(filename) if not filename.endswith(('.h5', '.hdf5')): - msg = 'Unable to open "{0}" which is not an HDF5 summary file' + msg = f'Unable to open "{filename}" which is not an HDF5 summary file' raise ValueError(msg) self._f = h5py.File(filename, 'r') @@ -121,11 +127,23 @@ class Summary: self._fast_materials[material.id] = material def _read_surfaces(self): + periodic_surface_ids = set() for group in self._f['geometry/surfaces'].values(): surface = openmc.Surface.from_hdf5(group) # surface may be None for DAGMC surfaces if surface: self._fast_surfaces[surface.id] = surface + if surface.boundary_type == "periodic": + periodic_surface_ids.add(surface.id) + + # Assign periodic surfaces when information is in file + for surface_id in periodic_surface_ids: + group = self._f[f'geometry/surfaces/surface {surface_id}'] + surface = self._fast_surfaces[surface_id] + if 'periodic_surface_id' in group: + periodic_surface_id = int(group['periodic_surface_id'][()]) + surface.periodic_surface = self._fast_surfaces[periodic_surface_id] + def _read_cells(self): @@ -234,4 +252,9 @@ class Summary: Results from a stochastic volume calculation """ - self.geometry.add_volume_information(volume_calc) + if volume_calc.domain_type == "material" and self.materials: + for material in self.materials: + if material.id in volume_calc.volumes: + material.add_volume_information(volume_calc) + else: + self.geometry.add_volume_information(volume_calc) diff --git a/openmc/surface.py b/openmc/surface.py index 146e16c286..4839783ffa 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,20 +1,23 @@ +from __future__ import annotations from abc import ABC, abstractmethod -from collections import OrderedDict from collections.abc import Iterable from copy import deepcopy import math from numbers import Real -from xml.etree import ElementTree as ET from warnings import warn, catch_warnings, simplefilter +import lxml.etree as ET import numpy as np -from .checkvalue import check_type, check_value, check_length +from .checkvalue import check_type, check_value, check_length, check_greater_than from .mixin import IDManagerMixin, IDWarning from .region import Region, Intersection, Union +from .bounding_box import BoundingBox +from ._xml import get_elem_list, get_text -_BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic', 'white'] +_BOUNDARY_TYPES = {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} +_ALBEDO_BOUNDARIES = {'reflective', 'periodic', 'white'} _WARNING_UPPER = """\ "{}(...) accepts an argument named '{}', not '{}'. Future versions of OpenMC \ @@ -117,21 +120,27 @@ class Surface(IDManagerMixin, ABC): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Note that periodic boundary conditions can only be applied to x-, y-, and z-planes, and only axis-aligned periodicity is supported. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the surface. If not specified, the name will be the empty string. Attributes ---------- - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -147,10 +156,12 @@ class Surface(IDManagerMixin, ABC): used_ids = set() _atol = 1.e-12 - def __init__(self, surface_id=None, boundary_type='transmission', name=''): + def __init__(self, surface_id=None, boundary_type='transmission', + albedo=1., name=''): self.id = surface_id self.name = name self.boundary_type = boundary_type + self.albedo = albedo # A dictionary of the quadratic surface coefficients # Key - coefficient name @@ -165,16 +176,20 @@ class Surface(IDManagerMixin, ABC): def __repr__(self): string = 'Surface\n' - string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) - string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type) - string += '{0: <16}{1}{2}\n'.format('\tBoundary', '=\t', self._boundary_type) + string += '{0: <20}{1}{2}\n'.format('\tID', '=\t', self._id) + string += '{0: <20}{1}{2}\n'.format('\tName', '=\t', self._name) + string += '{0: <20}{1}{2}\n'.format('\tType', '=\t', self._type) + string += '{0: <20}{1}{2}\n'.format('\tBoundary', '=\t', + self._boundary_type) + if (self._boundary_type in _ALBEDO_BOUNDARIES and + not math.isclose(self._albedo, 1.0)): + string += '{0: <20}{1}{2}\n'.format('\tBoundary Albedo', '=\t', + self._albedo) - coefficients = '{0: <16}'.format('\tCoefficients') + '\n' + coefficients = '{0: <20}'.format('\tCoefficients') + '\n' for coeff in self._coefficients: - coefficients += '{0: <16}{1}{2}\n'.format( - coeff, '=\t', self._coefficients[coeff]) + coefficients += f'{coeff: <20}=\t{self._coefficients[coeff]}\n' string += coefficients @@ -184,18 +199,6 @@ class Surface(IDManagerMixin, ABC): def name(self): return self._name - @property - def type(self): - return self._type - - @property - def boundary_type(self): - return self._boundary_type - - @property - def coefficients(self): - return self._coefficients - @name.setter def name(self, name): if name is not None: @@ -204,12 +207,34 @@ class Surface(IDManagerMixin, ABC): else: self._name = '' + @property + def type(self): + return self._type + + @property + def boundary_type(self): + return self._boundary_type + @boundary_type.setter def boundary_type(self, boundary_type): check_type('boundary type', boundary_type, str) check_value('boundary type', boundary_type, _BOUNDARY_TYPES) self._boundary_type = boundary_type + @property + def albedo(self): + return self._albedo + + @albedo.setter + def albedo(self, albedo): + check_type('albedo', albedo, Real) + check_greater_than('albedo', albedo, 0.0) + self._albedo = float(albedo) + + @property + def coefficients(self): + return self._coefficients + def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -233,8 +258,7 @@ class Surface(IDManagerMixin, ABC): desired half-space """ - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def clone(self, memo=None): """Create a copy of this surface with a new unique ID. @@ -286,7 +310,7 @@ class Surface(IDManagerMixin, ABC): coeffs = self._get_base_coeffs() coeffs = np.asarray(coeffs) nonzeros = ~np.isclose(coeffs, 0., rtol=0., atol=self._atol) - norm_factor = np.abs(coeffs[nonzeros][0]) + norm_factor = coeffs[nonzeros][0] return tuple([c/norm_factor for c in coeffs]) def is_equal(self, other): @@ -336,9 +360,9 @@ class Surface(IDManagerMixin, ABC): ---------- vector : iterable of float Direction in which surface should be translated - inplace : boolean + inplace : bool Whether or not to return a new instance of this Surface or to - modify the coefficients of this Surface. Defaults to False + modify the coefficients of this Surface. Returns ------- @@ -374,7 +398,7 @@ class Surface(IDManagerMixin, ABC): :math:`\psi` about z. This corresponds to an x-y-z extrinsic rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`. - inplace : boolean + inplace : bool Whether or not to return a new instance of Surface or to modify the coefficients of this Surface in place. Defaults to False. @@ -390,7 +414,7 @@ class Surface(IDManagerMixin, ABC): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing source data """ @@ -403,6 +427,9 @@ class Surface(IDManagerMixin, ABC): element.set("type", self._type) if self.boundary_type != 'transmission': element.set("boundary", self.boundary_type) + if (self.boundary_type in _ALBEDO_BOUNDARIES and + not math.isclose(self.albedo, 1.0)): + element.set("albedo", str(self.albedo)) element.set("coeffs", ' '.join([str(self._coefficients.setdefault(key, 0.0)) for key in self._coeff_keys])) @@ -414,7 +441,7 @@ class Surface(IDManagerMixin, ABC): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -425,15 +452,17 @@ class Surface(IDManagerMixin, ABC): """ # Determine appropriate class - surf_type = elem.get('type') + surf_type = get_text(elem, "type") cls = _SURFACE_CLASSES[surf_type] - # Determine ID, boundary type, coefficients + # Determine ID, boundary type, boundary albedo, coefficients kwargs = {} - kwargs['surface_id'] = int(elem.get('id')) - kwargs['boundary_type'] = elem.get('boundary', 'transmission') - kwargs['name'] = elem.get('name') - coeffs = [float(x) for x in elem.get('coeffs').split()] + kwargs['surface_id'] = int(get_text(elem, "id")) + kwargs['boundary_type'] = get_text(elem, "boundary", "transmission") + if kwargs['boundary_type'] in _ALBEDO_BOUNDARIES: + kwargs['albedo'] = float(get_text(elem, "albedo", 1.0)) + kwargs['name'] = get_text(elem, "name") + coeffs = get_elem_list(elem, "coeffs", float) kwargs.update(dict(zip(cls._coeff_keys, coeffs))) return cls(**kwargs) @@ -463,8 +492,13 @@ class Surface(IDManagerMixin, ABC): name = group['name'][()].decode() if 'name' in group else '' bc = group['boundary_type'][()].decode() + if 'albedo' in group: + bc_alb = float(group['albedo'][()].decode()) + else: + bc_alb = 1.0 coeffs = group['coefficients'][...] - kwargs = {'boundary_type': bc, 'name': name, 'surface_id': surface_id} + kwargs = {'boundary_type': bc, 'albedo': bc_alb, 'name': name, + 'surface_id': surface_id} surf_type = group['type'][()].decode() cls = _SURFACE_CLASSES[surf_type] @@ -539,7 +573,7 @@ class PlaneMixin: else: ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) - return (ll, ur) + return BoundingBox(ll, ur) def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -568,9 +602,9 @@ class PlaneMixin: ---------- vector : iterable of float Direction in which surface should be translated - inplace : boolean + inplace : bool Whether or not to return a new instance of a Plane or to modify the - coefficients of this plane. Defaults to False + coefficients of this plane. Returns ------- @@ -608,7 +642,9 @@ class PlaneMixin: # Compute new rotated coefficients a, b, c a, b, c = Rmat @ [a, b, c] - kwargs = {'boundary_type': surf.boundary_type, 'name': surf.name} + kwargs = {'boundary_type': surf.boundary_type, + 'albedo': surf.albedo, + 'name': surf.name} if inplace: kwargs['surface_id'] = surf.id @@ -621,7 +657,7 @@ class PlaneMixin: Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing source data """ @@ -648,10 +684,14 @@ class Plane(PlaneMixin, Surface): The 'C' parameter for the plane. Defaults to 0. d : float, optional The 'D' parameter for the plane. Defaults to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the plane. If not specified, the name will be the empty string. surface_id : int, optional @@ -668,9 +708,11 @@ class Plane(PlaneMixin, Surface): The 'C' parameter for the plane d : float The 'D' parameter for the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight periodic_surface : openmc.Surface If a periodic boundary condition is used, the surface with which this one is periodic with @@ -737,22 +779,38 @@ class Plane(PlaneMixin, Surface): Plane Plane that passes through the three points + Raises + ------ + ValueError + If all three points lie along a line + """ # Convert to numpy arrays - p1 = np.asarray(p1) - p2 = np.asarray(p2) - p3 = np.asarray(p3) + p1 = np.asarray(p1, dtype=float) + p2 = np.asarray(p2, dtype=float) + p3 = np.asarray(p3, dtype=float) # Find normal vector to plane by taking cross product of two vectors # connecting p1->p2 and p1->p3 n = np.cross(p2 - p1, p3 - p1) + # Check for points along a line + if np.allclose(n, 0.): + raise ValueError("All three points appear to lie along a line.") + # The equation of the plane will by n·( - p1) = 0. Determine # coefficients a, b, c, and d based on that a, b, c = n d = np.dot(n, p1) return cls(a=a, b=b, c=c, d=d, **kwargs) + def flip_normal(self): + """Modify plane coefficients to reverse the normal vector.""" + self.a = -self.a + self.b = -self.b + self.c = -self.c + self.d = -self.d + class XPlane(PlaneMixin, Surface): """A plane perpendicular to the x axis of the form :math:`x - x_0 = 0` @@ -760,12 +818,16 @@ class XPlane(PlaneMixin, Surface): Parameters ---------- x0 : float, optional - Location of the plane. Defaults to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + Location of the plane in [cm]. Defaults to 0. + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is supported, i.e., x-planes can only be paired with x-planes. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the plane. If not specified, the name will be the empty string. surface_id : int, optional @@ -775,10 +837,12 @@ class XPlane(PlaneMixin, Surface): Attributes ---------- x0 : float - Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Location of the plane in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight periodic_surface : openmc.Surface If a periodic boundary condition is used, the surface with which this one is periodic with @@ -819,12 +883,16 @@ class YPlane(PlaneMixin, Surface): Parameters ---------- y0 : float, optional - Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + Location of the plane in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is - supported, i.e., x-planes can only be paired with x-planes. + supported, i.e., y-planes can only be paired with y-planes. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the plane. If not specified, the name will be the empty string. surface_id : int, optional @@ -834,10 +902,12 @@ class YPlane(PlaneMixin, Surface): Attributes ---------- y0 : float - Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Location of the plane in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight periodic_surface : openmc.Surface If a periodic boundary condition is used, the surface with which this one is periodic with @@ -878,12 +948,16 @@ class ZPlane(PlaneMixin, Surface): Parameters ---------- z0 : float, optional - Location of the plane. Defaults to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional + Location of the plane in [cm]. Defaults to 0. + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is - supported, i.e., x-planes can only be paired with x-planes. + supported, i.e., z-planes can only be paired with z-planes. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the plane. If not specified, the name will be the empty string. surface_id : int, optional @@ -893,10 +967,12 @@ class ZPlane(PlaneMixin, Surface): Attributes ---------- z0 : float - Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} + Location of the plane in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight periodic_surface : openmc.Surface If a periodic boundary condition is used, the surface with which this one is periodic with @@ -991,8 +1067,8 @@ class QuadricMixin: Parameters ---------- point : 3-tuple of float - The Cartesian coordinates, :math:`(x',y',z')`, at which the surface - equation should be evaluated. + The Cartesian coordinates, :math:`(x',y',z')`, in [cm] at which the + surface equation should be evaluated. Returns ------- @@ -1012,9 +1088,8 @@ class QuadricMixin: ---------- vector : iterable of float Direction in which surface should be translated - inplace : boolean + inplace : bool Whether to return a clone of the Surface or the Surface itself. - Defaults to False Returns ------- @@ -1069,7 +1144,8 @@ class QuadricMixin: else: base_cls = type(tsurf)._virtual_base # Copy necessary surface attributes to new kwargs dictionary - kwargs = {'boundary_type': tsurf.boundary_type, 'name': tsurf.name} + kwargs = {'boundary_type': tsurf.boundary_type, + 'albedo': tsurf.albedo, 'name': tsurf.name} if inplace: kwargs['surface_id'] = tsurf.id kwargs.update({k: getattr(tsurf, k) for k in base_cls._coeff_keys}) @@ -1106,13 +1182,13 @@ class Cylinder(QuadricMixin, Surface): Parameters ---------- x0 : float, optional - x-coordinate for the origin of the Cylinder. Defaults to 0 + x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 y0 : float, optional - y-coordinate for the origin of the Cylinder. Defaults to 0 + y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 z0 : float, optional - z-coordinate for the origin of the Cylinder. Defaults to 0 + z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 r : float, optional - Radius of the cylinder. Defaults to 1. + Radius of the cylinder in [cm]. Defaults to 1. dx : float, optional x-component of the vector representing the axis of the cylinder. Defaults to 0. @@ -1122,10 +1198,14 @@ class Cylinder(QuadricMixin, Surface): dz : float, optional z-component of the vector representing the axis of the cylinder. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. @@ -1136,22 +1216,24 @@ class Cylinder(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate for the origin of the Cylinder + x-coordinate for the origin of the Cylinder in [cm] y0 : float - y-coordinate for the origin of the Cylinder + y-coordinate for the origin of the Cylinder in [cm] z0 : float - z-coordinate for the origin of the Cylinder + z-coordinate for the origin of the Cylinder in [cm] r : float - Radius of the cylinder + Radius of the cylinder in [cm] dx : float x-component of the vector representing the axis of the cylinder dy : float y-component of the vector representing the axis of the cylinder dz : float z-component of the vector representing the axis of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1194,11 +1276,9 @@ class Cylinder(QuadricMixin, Surface): else -np.inf for xi, dxi in zip(self._origin, self._axis)] ur = [xi + r if np.isclose(dxi, 0., rtol=0., atol=self._atol) else np.inf for xi, dxi in zip(self._origin, self._axis)] - return (np.array(ll), np.array(ur)) - + return BoundingBox(np.array(ll), np.array(ur)) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def _get_base_coeffs(self): # Get x, y, z coordinates of two points @@ -1241,9 +1321,9 @@ class Cylinder(QuadricMixin, Surface): Parameters ---------- p1, p2 : 3-tuples - Points that pass through the plane, p1 will be used as (x0, y0, z0) + Points that pass through the cylinder axis. r : float, optional - Radius of the cylinder. Defaults to 1. + Radius of the cylinder in [cm]. Defaults to 1. kwargs : dict Keyword arguments passed to the :class:`Cylinder` constructor @@ -1267,7 +1347,7 @@ class Cylinder(QuadricMixin, Surface): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing source data """ @@ -1275,8 +1355,8 @@ class Cylinder(QuadricMixin, Surface): # since the C++ layer doesn't support Cylinders right now with catch_warnings(): simplefilter('ignore', IDWarning) - kwargs = {'boundary_type': self.boundary_type, 'name': self.name, - 'surface_id': self.id} + kwargs = {'boundary_type': self.boundary_type, 'albedo': self.albedo, + 'name': self.name, 'surface_id': self.id} quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) return quad_rep.to_xml_element() @@ -1288,15 +1368,19 @@ class XCylinder(QuadricMixin, Surface): Parameters ---------- y0 : float, optional - y-coordinate for the origin of the Cylinder. Defaults to 0 + y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 z0 : float, optional - z-coordinate for the origin of the Cylinder. Defaults to 0 + z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 r : float, optional - Radius of the cylinder. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Radius of the cylinder in [cm]. Defaults to 1. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. @@ -1307,14 +1391,16 @@ class XCylinder(QuadricMixin, Surface): Attributes ---------- y0 : float - y-coordinate for the origin of the Cylinder + y-coordinate for the origin of the Cylinder in [cm] z0 : float - z-coordinate for the origin of the Cylinder + z-coordinate for the origin of the Cylinder in [cm] r : float - Radius of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Radius of the cylinder in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1360,11 +1446,12 @@ class XCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), - np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) + return BoundingBox( + np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), + np.array([np.inf, self.y0 + self.r, self.z0 + self.r]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def evaluate(self, point): y = point[1] - self.y0 @@ -1379,15 +1466,19 @@ class YCylinder(QuadricMixin, Surface): Parameters ---------- x0 : float, optional - x-coordinate for the origin of the Cylinder. Defaults to 0 + x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 z0 : float, optional - z-coordinate for the origin of the Cylinder. Defaults to 0 + z-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 r : float, optional - Radius of the cylinder. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Radius of the cylinder in [cm]. Defaults to 1. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. @@ -1398,14 +1489,16 @@ class YCylinder(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate for the origin of the Cylinder + x-coordinate for the origin of the Cylinder in [cm] z0 : float - z-coordinate for the origin of the Cylinder + z-coordinate for the origin of the Cylinder in [cm] r : float - Radius of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Radius of the cylinder in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1451,11 +1544,12 @@ class YCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), - np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) + return BoundingBox( + np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), + np.array([self.x0 + self.r, np.inf, self.z0 + self.r]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 @@ -1470,15 +1564,19 @@ class ZCylinder(QuadricMixin, Surface): Parameters ---------- x0 : float, optional - x-coordinate for the origin of the Cylinder. Defaults to 0 + x-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 y0 : float, optional - y-coordinate for the origin of the Cylinder. Defaults to 0 + y-coordinate for the origin of the Cylinder in [cm]. Defaults to 0 r : float, optional - Radius of the cylinder. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Radius of the cylinder in [cm]. Defaults to 1. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. @@ -1489,14 +1587,16 @@ class ZCylinder(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate for the origin of the Cylinder + x-coordinate for the origin of the Cylinder in [cm] y0 : float - y-coordinate for the origin of the Cylinder + y-coordinate for the origin of the Cylinder in [cm] r : float - Radius of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Radius of the cylinder in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1542,11 +1642,12 @@ class ZCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), - np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) + return BoundingBox( + np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), + np.array([self.x0 + self.r, self.y0 + self.r, np.inf]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 @@ -1560,17 +1661,21 @@ class Sphere(QuadricMixin, Surface): Parameters ---------- x0 : float, optional - x-coordinate of the center of the sphere. Defaults to 0. + x-coordinate of the center of the sphere in [cm]. Defaults to 0. y0 : float, optional - y-coordinate of the center of the sphere. Defaults to 0. + y-coordinate of the center of the sphere in [cm]. Defaults to 0. z0 : float, optional - z-coordinate of the center of the sphere. Defaults to 0. + z-coordinate of the center of the sphere in [cm]. Defaults to 0. r : float, optional - Radius of the sphere. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Radius of the sphere in [cm]. Defaults to 1. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the sphere. If not specified, the name will be the empty string. surface_id : int, optional @@ -1580,16 +1685,18 @@ class Sphere(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate of the center of the sphere + x-coordinate of the center of the sphere in [cm] y0 : float - y-coordinate of the center of the sphere + y-coordinate of the center of the sphere in [cm] z0 : float - z-coordinate of the center of the sphere + z-coordinate of the center of the sphere in [cm] r : float - Radius of the sphere - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Radius of the sphere in [cm] + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1632,13 +1739,12 @@ class Sphere(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, - self.z0 - self.r]), - np.array([self.x0 + self.r, self.y0 + self.r, - self.z0 + self.r])) + return BoundingBox( + np.array([self.x0 - self.r, self.y0 - self.r, self.z0 - self.r]), + np.array([self.x0 + self.r, self.y0 + self.r, self.z0 + self.r]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() def evaluate(self, point): x = point[0] - self.x0 @@ -1648,56 +1754,68 @@ class Sphere(QuadricMixin, Surface): class Cone(QuadricMixin, Surface): - """A conical surface parallel to the x-, y-, or z-axis. + r"""A conical surface parallel to the x-, y-, or z-axis. + + .. Note:: + This creates a double cone, which is two one-sided cones that meet at their apex. + For a one-sided cone see :class:`~openmc.model.XConeOneSided`, + :class:`~openmc.model.YConeOneSided`, and :class:`~openmc.model.ZConeOneSided`. Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. dx : float, optional x-component of the vector representing the axis of the cone. - Defaults to 0. dy : float, optional y-component of the vector representing the axis of the cone. - Defaults to 0. dz : float, optional z-component of the vector representing the axis of the cone. - Defaults to 1. surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. + name : str Name of the cone. If not specified, the name will be the empty string. Attributes ---------- x0 : float - x-coordinate of the apex + x-coordinate of the apex in [cm] y0 : float - y-coordinate of the apex + y-coordinate of the apex in [cm] z0 : float - z-coordinate of the apex + z-coordinate of the apex in [cm] r2 : float - Parameter related to the aperature + Parameter related to the aperture dx : float x-component of the vector representing the axis of the cone. dy : float y-component of the vector representing the axis of the cone. dz : float z-component of the vector representing the axis of the cone. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1775,7 +1893,7 @@ class Cone(QuadricMixin, Surface): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing source data """ @@ -1783,30 +1901,43 @@ class Cone(QuadricMixin, Surface): # since the C++ layer doesn't support Cones right now with catch_warnings(): simplefilter('ignore', IDWarning) - kwargs = {'boundary_type': self.boundary_type, 'name': self.name, + kwargs = {'boundary_type': self.boundary_type, + 'albedo': self.albedo, + 'name': self.name, 'surface_id': self.id} quad_rep = Quadric(*self._get_base_coeffs(), **kwargs) return quad_rep.to_xml_element() class XCone(QuadricMixin, Surface): - """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = + r"""A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = r^2 (x - x_0)^2`. + .. Note:: + This creates a double cone, which is two one-sided cones that meet at their apex. + For a one-sided cone see :class:`~openmc.model.XConeOneSided`. + Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cone. If not specified, the name will be the empty string. surface_id : int, optional @@ -1816,16 +1947,18 @@ class XCone(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate of the apex + x-coordinate of the apex in [cm] y0 : float - y-coordinate of the apex + y-coordinate of the apex in [cm] z0 : float - z-coordinate of the apex + z-coordinate of the apex in [cm] r2 : float - Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Parameter related to the aperture + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1879,23 +2012,34 @@ class XCone(QuadricMixin, Surface): class YCone(QuadricMixin, Surface): - """A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = + r"""A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = r^2 (y - y_0)^2`. + .. Note:: + This creates a double cone, which is two one-sided cones that meet at their apex. + For a one-sided cone see :class:`~openmc.model.YConeOneSided`. + Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cone. If not specified, the name will be the empty string. surface_id : int, optional @@ -1905,16 +2049,18 @@ class YCone(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate of the apex + x-coordinate of the apex in [cm] y0 : float - y-coordinate of the apex + y-coordinate of the apex in [cm] z0 : float - z-coordinate of the apex + z-coordinate of the apex in [cm] r2 : float - Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Parameter related to the aperture + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -1968,23 +2114,34 @@ class YCone(QuadricMixin, Surface): class ZCone(QuadricMixin, Surface): - """A cone parallel to the x-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = + r"""A cone parallel to the z-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = r^2 (z - z_0)^2`. + .. Note:: + This creates a double cone, which is two one-sided cones that meet at their apex. + For a one-sided cone see :class:`~openmc.model.ZConeOneSided`. + Parameters ---------- x0 : float, optional - x-coordinate of the apex. Defaults to 0. + x-coordinate of the apex in [cm]. y0 : float, optional - y-coordinate of the apex. Defaults to 0. + y-coordinate of the apex in [cm]. z0 : float, optional - z-coordinate of the apex. Defaults to 0. + z-coordinate of the apex in [cm]. r2 : float, optional - Parameter related to the aperature. Defaults to 1. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + The square of the slope of the cone. It is defined as + :math:`\left(\frac{r}{h}\right)^2` for a radius, :math:`r` and an axial + distance :math:`h` from the apex. An easy way to define this quantity is + to take the square of the radius of the cone (in cm) 1 cm from the apex. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the cone. If not specified, the name will be the empty string. surface_id : int, optional @@ -1994,16 +2151,18 @@ class ZCone(QuadricMixin, Surface): Attributes ---------- x0 : float - x-coordinate of the apex + x-coordinate of the apex in [cm] y0 : float - y-coordinate of the apex + y-coordinate of the apex in [cm] z0 : float - z-coordinate of the apex + z-coordinate of the apex in [cm] r2 : float - Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Parameter related to the aperture. + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -2064,10 +2223,14 @@ class Quadric(QuadricMixin, Surface): ---------- a, b, c, d, e, f, g, h, j, k : float, optional coefficients for the surface. All default to 0. - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. + albedo : float, optional + Albedo of the surfaces as a ratio of particle weight after interaction + with the surface to the initial weight. Values must be positive. Only + applicable if the boundary type is 'reflective', 'periodic', or 'white'. name : str, optional Name of the surface. If not specified, the name will be the empty string. surface_id : int, optional @@ -2078,9 +2241,11 @@ class Quadric(QuadricMixin, Surface): ---------- a, b, c, d, e, f, g, h, j, k : float coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -2176,7 +2341,9 @@ class TorusMixin: # Create rotated torus kwargs = { - 'boundary_type': surf.boundary_type, 'name': surf.name, + 'boundary_type': surf.boundary_type, + 'albedo': surf.albedo, + 'name': surf.name, 'a': surf.a, 'b': surf.b, 'c': surf.c } if inplace: @@ -2198,37 +2365,39 @@ class XTorus(TorusMixin, Surface): Parameters ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float - Minor radius of the torus (perpendicular to axis of revolution) + Minor radius of the torus in [cm] (perpendicular to axis of revolution) kwargs : dict Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float - Minor radius of the torus (perpendicular to axis of revolution) - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Minor radius of the torus in [cm] (perpendicular to axis of revolution) + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -2254,11 +2423,13 @@ class XTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - b, y0 - a - c, z0 - a - c]), - np.array([x0 + b, y0 + a + c, z0 + a + c])) + return BoundingBox( + np.array([x0 - b, y0 - a - c, z0 - a - c]), + np.array([x0 + b, y0 + a + c, z0 + a + c]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() + class YTorus(TorusMixin, Surface): r"""A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - @@ -2269,37 +2440,39 @@ class YTorus(TorusMixin, Surface): Parameters ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float - Minor radius of the torus (perpendicular to axis of revolution) + Minor radius of the torus in [cm] (perpendicular to axis of revolution) kwargs : dict Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float Minor radius of the torus (perpendicular to axis of revolution) - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -2325,11 +2498,13 @@ class YTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - a - c, y0 - b, z0 - a - c]), - np.array([x0 + a + c, y0 + b, z0 + a + c])) + return BoundingBox( + np.array([x0 - a - c, y0 - b, z0 - a - c]), + np.array([x0 + a + c, y0 + b, z0 + a + c]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() + class ZTorus(TorusMixin, Surface): r"""A torus of the form :math:`(z - z_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (y - @@ -2340,37 +2515,39 @@ class ZTorus(TorusMixin, Surface): Parameters ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float - Minor radius of the torus (perpendicular to axis of revolution) + Minor radius of the torus in [cm] (perpendicular to axis of revolution) kwargs : dict Keyword arguments passed to the :class:`Surface` constructor Attributes ---------- x0 : float - x-coordinate of the center of the axis of revolution + x-coordinate of the center of the axis of revolution in [cm] y0 : float - y-coordinate of the center of the axis of revolution + y-coordinate of the center of the axis of revolution in [cm] z0 : float - z-coordinate of the center of the axis of revolution + z-coordinate of the center of the axis of revolution in [cm] a : float - Major radius of the torus + Major radius of the torus in [cm] b : float - Minor radius of the torus (parallel to axis of revolution) + Minor radius of the torus in [cm] (parallel to axis of revolution) c : float - Minor radius of the torus (perpendicular to axis of revolution) - boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} + Minor radius of the torus in [cm] (perpendicular to axis of revolution) + boundary_type : {'transmission', 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. + albedo : float + Boundary albedo as a positive multiplier of particle weight coefficients : dict Dictionary of surface coefficients id : int @@ -2396,11 +2573,12 @@ class ZTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - a - c, y0 - a - c, z0 - b]), - np.array([x0 + a + c, y0 + a + c, z0 + b])) + return BoundingBox( + np.array([x0 - a - c, y0 - a - c, z0 - b]), + np.array([x0 + a + c, y0 + a + c, z0 + b]) + ) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox.infinite() class Halfspace(Region): @@ -2435,7 +2613,7 @@ class Halfspace(Region): Surface which divides Euclidean space. side : {'+', '-'} Indicates whether the positive or negative half-space is used. - bounding_box : tuple of numpy.ndarray + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box """ @@ -2456,7 +2634,7 @@ class Halfspace(Region): else: return Union((self, other)) - def __invert__(self): + def __invert__(self) -> Halfspace: return -self.surface if self.side == '+' else +self.surface def __contains__(self, point): @@ -2509,17 +2687,17 @@ class Halfspace(Region): Parameters ---------- - surfaces: collections.OrderedDict, optional + surfaces : dict, optional Dictionary mapping surface IDs to :class:`openmc.Surface` instances Returns ------- - surfaces: collections.OrderedDict + surfaces : dict Dictionary mapping surface IDs to :class:`openmc.Surface` instances """ if surfaces is None: - surfaces = OrderedDict() + surfaces = {} surfaces[self.surface.id] = self.surface return surfaces @@ -2563,7 +2741,7 @@ class Halfspace(Region): clone.surface = self.surface.clone(memo) return clone - def translate(self, vector, memo=None): + def translate(self, vector, inplace=False, memo=None): """Translate half-space in given direction Parameters @@ -2585,7 +2763,7 @@ class Halfspace(Region): # If translated surface not in memo, add it key = (self.surface, tuple(vector)) if key not in memo: - memo[key] = self.surface.translate(vector) + memo[key] = self.surface.translate(vector, inplace) # Return translated half-space return type(self)(memo[key], self.side) @@ -2617,7 +2795,7 @@ class Halfspace(Region): :math:`\psi` about z. This corresponds to an x-y-z extrinsic rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`. - inplace : boolean + inplace : bool Whether or not to return a new instance of Surface or to modify the coefficients of this Surface in place. Defaults to False. memo : dict or None @@ -2633,7 +2811,7 @@ class Halfspace(Region): memo = {} # If rotated surface not in memo, add it - key = (self.surface, tuple(rotation), tuple(pivot), order, inplace) + key = (self.surface, tuple(np.ravel(rotation)), tuple(pivot), order, inplace) if key not in memo: memo[key] = self.surface.rotate(rotation, pivot=pivot, order=order, inplace=inplace) diff --git a/openmc/tallies.py b/openmc/tallies.py index dbcc6d55f5..075b1e9911 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -1,11 +1,12 @@ +from __future__ import annotations from collections.abc import Iterable, MutableSequence import copy -from functools import partial, reduce +from functools import partial, reduce, wraps from itertools import product from numbers import Integral, Real import operator from pathlib import Path -from xml.etree import ElementTree as ET +import lxml.etree as ET import h5py import numpy as np @@ -14,7 +15,7 @@ import scipy.sparse as sps import openmc import openmc.checkvalue as cv -from ._xml import clean_indentation, reorder_attributes +from ._xml import clean_indentation, get_elem_list, get_text from .mixin import IDManagerMixin from .mesh import MeshBase @@ -33,7 +34,7 @@ _NUCLIDE_CLASSES = (str, openmc.CrossNuclide, openmc.AggregateNuclide) _FILTER_CLASSES = (openmc.Filter, openmc.CrossFilter, openmc.AggregateFilter) # Valid types of estimators -ESTIMATOR_TYPES = ['tracklength', 'collision', 'analog'] +ESTIMATOR_TYPES = {'tracklength', 'collision', 'analog'} class Tally(IDManagerMixin): @@ -54,14 +55,20 @@ class Tally(IDManagerMixin): Unique identifier for the tally name : str Name of the tally + multiply_density : bool + Whether reaction rates should be multiplied by atom density + + .. versionadded:: 0.14.0 filters : list of openmc.Filter List of specified filters for the tally - nuclides : list of openmc.Nuclide + nuclides : list of str List of nuclides to score results for scores : list of str List of defined scores, e.g. 'flux', 'fission', etc. estimator : {'analog', 'tracklength', 'collision'} - Type of estimator for the tally + Type of estimator for the tally. If unset (None), OpenMC will automatically + select an appropriate estimator based on the tally filters and scores + with a preference for 'tracklength'. triggers : list of openmc.Trigger List of tally triggers num_scores : int @@ -88,6 +95,10 @@ class Tally(IDManagerMixin): An array containing the sample mean for each bin std_dev : numpy.ndarray An array containing the sample standard deviation for each bin + figure_of_merit : numpy.ndarray + An array containing the figure of merit for each bin + + .. versionadded:: 0.15.3 derived : bool Whether or not the tally is derived from one or more other tallies sparse : bool @@ -111,6 +122,7 @@ class Tally(IDManagerMixin): self._estimator = None self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers') self._derivative = None + self._multiply_density = True self._num_realizations = 0 self._with_summary = False @@ -119,6 +131,7 @@ class Tally(IDManagerMixin): self._sum_sq = None self._mean = None self._std_dev = None + self._simulation_time = None self._with_batch_statistics = False self._derived = False self._sparse = False @@ -126,6 +139,36 @@ class Tally(IDManagerMixin): self._sp_filename = None self._results_read = False + def __eq__(self, other): + if other.id != self.id: + return False + if other.name != self.name: + return False + # estimators are automatically set based on the tally filters and scores + # during OpenMC initialization if this value is None, so it is not + # considered a requirement for equivalence if it is unset on either + # tally as it implies that the user is allowing OpenMC to select an + # appropriate estimator. If the value is explicitly set on both tallies, + # then the values must match for the tallies to be considered equivalent. + if self.estimator is not None and other.estimator is not None and other.estimator != self.estimator: + return False + if other.filters != self.filters: + return False + # for tallies are loaded from statpoint files + # an empty nuclide list is equivalent to a list with 'total' + other_nuclides = other.nuclides.copy() + self_nuclides = self.nuclides.copy() + if 'total' in other_nuclides: + other_nuclides.remove('total') + if 'total' in self_nuclides: + self_nuclides.remove('total') + if other_nuclides != self_nuclides: + return False + for attr in {'scores', 'triggers', 'derivative', 'multiply_density'}: + if getattr(other, attr) != getattr(self, attr): + return False + return True + def __repr__(self): parts = ['Tally'] parts.append('{: <15}=\t{}'.format('ID', self.id)) @@ -138,20 +181,88 @@ class Tally(IDManagerMixin): parts.append('{: <15}=\t{}'.format('Nuclides', nuclides)) parts.append('{: <15}=\t{}'.format('Scores', self.scores)) parts.append('{: <15}=\t{}'.format('Estimator', self.estimator)) + parts.append('{: <15}=\t{}'.format('Multiply dens.', self.multiply_density)) return '\n\t'.join(parts) + @staticmethod + def ensure_results(f): + """A decorator to be applied to any method that might use tally results. + Results will be loaded if appropriate based on the tally properties. + + Args: + f function: Tally method to wrap + + Returns: + function: Wrapped function that reads tally results before calling + the methodif necessary + """ + @wraps(f) + def read(self): + if self._sp_filename is not None and not self.derived: + self._read_results() + return f(self) + return read + @property def name(self): return self._name + @name.setter + def name(self, name): + cv.check_type('tally name', name, str, none_ok=True) + self._name = name + + @property + def multiply_density(self): + return self._multiply_density + + @multiply_density.setter + def multiply_density(self, value): + cv.check_type('multiply density', value, bool) + self._multiply_density = value + @property def filters(self): return self._filters + @filters.setter + def filters(self, filters): + cv.check_type('tally filters', filters, MutableSequence) + + # If the filter is already in the Tally, raise an error + visited_filters = set() + for f in filters: + if f in visited_filters: + msg = (f'Unable to add a duplicate filter "{f}" to Tally ' + f'ID="{self.id}" since duplicate filters are not ' + 'supported in the OpenMC Python API') + raise ValueError(msg) + visited_filters.add(f) + + self._filters = cv.CheckedList(_FILTER_CLASSES, 'tally filters', filters) + @property + @ensure_results def nuclides(self): return self._nuclides + @nuclides.setter + def nuclides(self, nuclides): + cv.check_type('tally nuclides', nuclides, MutableSequence) + + # If the nuclide is already in the Tally, raise an error + visited_nuclides = set() + for nuc in nuclides: + if nuc in visited_nuclides: + msg = (f'Unable to add a duplicate nuclide "{nuc}" to Tally ID=' + f'"{self.id}" since duplicate nuclides are not supported ' + 'in the OpenMC Python API') + raise ValueError(msg) + visited_nuclides.add(nuc) + + self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, 'tally nuclides', + nuclides) + @property def num_nuclides(self): return len(self._nuclides) @@ -160,6 +271,33 @@ class Tally(IDManagerMixin): def scores(self): return self._scores + @scores.setter + def scores(self, scores): + cv.check_type('tally scores', scores, MutableSequence) + + visited_scores = set() + for i, score in enumerate(scores): + # If the score is already in the Tally, raise an error + if score in visited_scores: + msg = (f'Unable to add a duplicate score "{score}" to Tally ' + f'ID="{self.id}" since duplicate scores are not ' + 'supported in the OpenMC Python API') + raise ValueError(msg) + visited_scores.add(score) + + # If score is a string, strip whitespace + if isinstance(score, str): + # Check to see if scores are deprecated before storing + for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p', + 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', + 'flux-y', 'total-y']: + if score.strip().startswith(deprecated): + msg = score.strip() + ' is no longer supported.' + raise ValueError(msg) + scores[i] = score.strip() + + self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) + @property def num_scores(self): return len(self._scores) @@ -184,26 +322,58 @@ class Tally(IDManagerMixin): def estimator(self): return self._estimator + @estimator.setter + def estimator(self, estimator): + # allow the estimator to be set to None (let OpenMC choose the estimator at runtime) + cv.check_value('estimator', estimator, ESTIMATOR_TYPES | {None}) + self._estimator = estimator + @property def triggers(self): return self._triggers + @triggers.setter + def triggers(self, triggers): + cv.check_type('tally triggers', triggers, MutableSequence) + self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers', + triggers) + @property + @ensure_results def num_realizations(self): return self._num_realizations + @num_realizations.setter + def num_realizations(self, num_realizations): + cv.check_type('number of realizations', num_realizations, Integral) + cv.check_greater_than('number of realizations', num_realizations, 0, True) + self._num_realizations = num_realizations + @property def with_summary(self): return self._with_summary + @with_summary.setter + def with_summary(self, with_summary): + cv.check_type('with_summary', with_summary, bool) + self._with_summary = with_summary + def _read_results(self): if self._results_read: return # Open the HDF5 statepoint file with h5py.File(self._sp_filename, 'r') as f: + # Set number of realizations + group = f[f'tallies/tally {self.id}'] + self._num_realizations = int(group['n_realizations'][()]) + + # Update nuclides + nuclide_names = group['nuclides'][()] + self._nuclides = [name.decode().strip() for name in nuclide_names] + # Extract Tally data from the file - data = f[f'tallies/tally {self.id}/results'] + data = group['results'] sum_ = data[:, :, 0] sum_sq = data[:, :, 1] @@ -220,35 +390,44 @@ class Tally(IDManagerMixin): self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape) self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape) + # Read simulation time (needed for figure of merit) + self._simulation_time = f["runtime"]["simulation"][()] + # Indicate that Tally results have been read self._results_read = True @property + @ensure_results def sum(self): if not self._sp_filename or self.derived: return None - # Make sure results have been read - self._read_results() - if self.sparse: return np.reshape(self._sum.toarray(), self.shape) else: return self._sum + @sum.setter + def sum(self, sum): + cv.check_type('sum', sum, Iterable) + self._sum = sum + @property + @ensure_results def sum_sq(self): if not self._sp_filename or self.derived: return None - # Make sure results have been read - self._read_results() - if self.sparse: return np.reshape(self._sum_sq.toarray(), self.shape) else: return self._sum_sq + @sum_sq.setter + def sum_sq(self, sum_sq): + cv.check_type('sum_sq', sum_sq, Iterable) + self._sum_sq = sum_sq + @property def mean(self): if self._mean is None: @@ -291,10 +470,25 @@ class Tally(IDManagerMixin): else: return self._std_dev + @property + def figure_of_merit(self): + mean = self.mean + std_dev = self.std_dev + fom = np.zeros_like(mean) + nonzero = np.abs(mean) > 0 + fom[nonzero] = 1.0 / ( + (std_dev[nonzero] / mean[nonzero])**2 * self._simulation_time) + return fom + @property def with_batch_statistics(self): return self._with_batch_statistics + @with_batch_statistics.setter + def with_batch_statistics(self, with_batch_statistics): + cv.check_type('with_batch_statistics', with_batch_statistics, bool) + self._with_batch_statistics = with_batch_statistics + @property def derived(self): return self._derived @@ -303,117 +497,15 @@ class Tally(IDManagerMixin): def derivative(self): return self._derivative - @property - def sparse(self): - return self._sparse - - @estimator.setter - def estimator(self, estimator): - cv.check_value('estimator', estimator, ESTIMATOR_TYPES) - self._estimator = estimator - - @triggers.setter - def triggers(self, triggers): - cv.check_type('tally triggers', triggers, MutableSequence) - self._triggers = cv.CheckedList(openmc.Trigger, 'tally triggers', - triggers) - - @name.setter - def name(self, name): - cv.check_type('tally name', name, str, none_ok=True) - self._name = name - @derivative.setter def derivative(self, deriv): cv.check_type('tally derivative', deriv, openmc.TallyDerivative, none_ok=True) self._derivative = deriv - @filters.setter - def filters(self, filters): - cv.check_type('tally filters', filters, MutableSequence) - - # If the filter is already in the Tally, raise an error - visited_filters = set() - for f in filters: - if f in visited_filters: - msg = (f'Unable to add a duplicate filter "{f}" to Tally ' - f'ID="{self.id}" since duplicate filters are not ' - 'supported in the OpenMC Python API') - raise ValueError(msg) - visited_filters.add(f) - - self._filters = cv.CheckedList(_FILTER_CLASSES, 'tally filters', filters) - - @nuclides.setter - def nuclides(self, nuclides): - cv.check_type('tally nuclides', nuclides, MutableSequence) - - # If the nuclide is already in the Tally, raise an error - visited_nuclides = set() - for nuc in nuclides: - if nuc in visited_nuclides: - msg = (f'Unable to add a duplicate nuclide "{nuc}" to Tally ID=' - f'"{self.id}" since duplicate nuclides are not supported ' - 'in the OpenMC Python API') - raise ValueError(msg) - visited_nuclides.add(nuc) - - self._nuclides = cv.CheckedList(_NUCLIDE_CLASSES, 'tally nuclides', - nuclides) - - @scores.setter - def scores(self, scores): - cv.check_type('tally scores', scores, MutableSequence) - - visited_scores = set() - for i, score in enumerate(scores): - # If the score is already in the Tally, raise an error - if score in visited_scores: - msg = (f'Unable to add a duplicate score "{score}" to Tally ' - f'ID="{self.id}" since duplicate scores are not ' - 'supported in the OpenMC Python API') - raise ValueError(msg) - visited_scores.add(score) - - # If score is a string, strip whitespace - if isinstance(score, str): - # Check to see if scores are deprecated before storing - for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p', - 'nu-scatter-p', 'scatter-y', 'nu-scatter-y', - 'flux-y', 'total-y']: - if score.strip().startswith(deprecated): - msg = score.strip() + ' is no longer supported.' - raise ValueError(msg) - scores[i] = score.strip() - - self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores) - - @num_realizations.setter - def num_realizations(self, num_realizations): - cv.check_type('number of realizations', num_realizations, Integral) - cv.check_greater_than('number of realizations', num_realizations, 0, True) - self._num_realizations = num_realizations - - @with_summary.setter - def with_summary(self, with_summary): - cv.check_type('with_summary', with_summary, bool) - self._with_summary = with_summary - - @with_batch_statistics.setter - def with_batch_statistics(self, with_batch_statistics): - cv.check_type('with_batch_statistics', with_batch_statistics, bool) - self._with_batch_statistics = with_batch_statistics - - @sum.setter - def sum(self, sum): - cv.check_type('sum', sum, Iterable) - self._sum = sum - - @sum_sq.setter - def sum_sq(self, sum_sq): - cv.check_type('sum_sq', sum_sq, Iterable) - self._sum_sq = sum_sq + @property + def sparse(self): + return self._sparse @sparse.setter def sparse(self, sparse): @@ -496,7 +588,7 @@ class Tally(IDManagerMixin): Parameters ---------- - nuclide : openmc.Nuclide + nuclide : str Nuclide to remove """ @@ -815,7 +907,7 @@ class Tally(IDManagerMixin): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing tally data """ @@ -829,6 +921,10 @@ class Tally(IDManagerMixin): if self.name != '': element.set("name", self.name) + # Multiply by density + if not self.multiply_density: + element.set("multiply_density", str(self.multiply_density).lower()) + # Optional Tally filters if len(self.filters) > 0: subelement = ET.SubElement(element, "filters") @@ -864,6 +960,35 @@ class Tally(IDManagerMixin): return element + def add_results(self, statepoint: cv.PathLike | openmc.StatePoint): + """Add results from the provided statepoint file to this tally instance + + .. versionadded:: 0.15.1 + + Parameters + ---------- + statepoint : openmc.PathLike or openmc.StatePoint + Statepoint used to update tally results + """ + # derived tallies are populated with data based on combined tallies + # and should not be modified + if self.derived: + return + + if isinstance(statepoint, openmc.StatePoint): + self._sp_filename = Path(statepoint._f.filename) + else: + self._sp_filename = Path(str(statepoint)) + + # reset these properties to ensure that any results access after this + # point are based on the current statepoint file + self._sum = None + self._sum_sq = None + self._mean = None + self._std_dev = None + self._num_realizations = 0 + self._results_read = False + @classmethod def from_xml_element(cls, elem, **kwargs): """Generate tally object from an XML element @@ -872,7 +997,7 @@ class Tally(IDManagerMixin): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -881,30 +1006,33 @@ class Tally(IDManagerMixin): Tally object """ - tally_id = int(elem.get('id')) - name = elem.get('name', '') + tally_id = int(get_text(elem, "id")) + name = get_text(elem, "name", "") tally = cls(tally_id=tally_id, name=name) + text = get_text(elem, 'multiply_density') + if text is not None: + tally.multiply_density = text in ('true', '1') + # Read filters - filters_elem = elem.find('filters') - if filters_elem is not None: - filter_ids = [int(x) for x in filters_elem.text.split()] + filter_ids = get_elem_list(elem, "filters", int) + if filter_ids is not None: tally.filters = [kwargs['filters'][uid] for uid in filter_ids] # Read nuclides - nuclides_elem = elem.find('nuclides') - if nuclides_elem is not None: - tally.nuclides = nuclides_elem.text.split() + nuclides = get_elem_list(elem, "nuclides", str) + if nuclides is not None: + tally.nuclides = nuclides # Read scores - scores_elem = elem.find('scores') - if scores_elem is not None: - tally.scores = scores_elem.text.split() + scores = get_elem_list(elem, "scores", str) + if scores is not None: + tally.scores = scores # Set estimator - estimator_elem = elem.find('estimator') - if estimator_elem is not None: - tally.estimator = estimator_elem.text + estimator = get_text(elem, "estimator") + if estimator is not None: + tally.estimator = estimator # Read triggers tally.triggers = [ @@ -913,9 +1041,9 @@ class Tally(IDManagerMixin): ] # Read tally derivative - deriv_elem = elem.find('derivative') - if deriv_elem is not None: - deriv_id = int(deriv_elem.text) + deriv = get_text(elem, "derivative") + if deriv is not None: + deriv_id = int(deriv) tally.derivative = kwargs['derivatives'][deriv_id] return tally @@ -997,17 +1125,10 @@ class Tally(IDManagerMixin): in the Tally. """ - # Look for the user-requested nuclide in all of the Tally's Nuclides + # Look for the user-requested nuclide in all of the Tally's nuclides for i, test_nuclide in enumerate(self.nuclides): - # If the Summary was linked, then values are Nuclide objects - if isinstance(test_nuclide, openmc.Nuclide): - if test_nuclide.name == nuclide: - return i - - # If the Summary has not been linked, then values are ZAIDs - else: - if test_nuclide == nuclide: - return i + if test_nuclide == nuclide: + return i msg = (f'Unable to get the nuclide index for Tally since "{nuclide}" ' 'is not one of the nuclides') @@ -1093,23 +1214,11 @@ class Tally(IDManagerMixin): for j, test_filter in enumerate(filters): if type(self_filter) is test_filter: bins = filter_bins[j] + indices = np.array([self_filter.get_bin_index(b) for b in bins]) break else: - # If not a user-requested Filter, get all bins - if isinstance(self_filter, openmc.DistribcellFilter): - # Create list of cell instance IDs for distribcell Filters - bins = list(range(self_filter.num_bins)) + indices = np.arange(self_filter.num_bins) - elif isinstance(self_filter, openmc.EnergyFunctionFilter): - # EnergyFunctionFilters don't have bins so just add a None - bins = [None] - - else: - # Create list of IDs for bins for all other filter types - bins = self_filter.bins - - # Add indices for each bin in this Filter to the list - indices = np.array([self_filter.get_bin_index(b) for b in bins]) filter_indices.append(indices) # Account for stride in each of the previous filters @@ -1346,9 +1455,7 @@ class Tally(IDManagerMixin): column_name = 'nuclide' for nuclide in self.nuclides: - if isinstance(nuclide, openmc.Nuclide): - nuclides.append(nuclide.name) - elif isinstance(nuclide, openmc.AggregateNuclide): + if isinstance(nuclide, openmc.AggregateNuclide): nuclides.append(nuclide.name) column_name = f'{nuclide.aggregate_op}(nuclide)' else: @@ -1412,12 +1519,12 @@ class Tally(IDManagerMixin): df.columns = pd.MultiIndex.from_tuples(columns) # Modify the df.to_string method so that it prints formatted strings. - # Credit to http://stackoverflow.com/users/3657742/chrisb for this trick + # Credit to https://stackoverflow.com/users/3657742/chrisb for this trick df.to_string = partial(df.to_string, float_format=float_format.format) return df - def get_reshaped_data(self, value='mean'): + def get_reshaped_data(self, value='mean', expand_dims=False): """Returns an array of tally data with one dimension per filter. The tally data in OpenMC is stored as a 3D array with the dimensions @@ -1429,17 +1536,26 @@ class Tally(IDManagerMixin): This builds and returns a reshaped version of the tally data array with unique dimensions corresponding to each tally filter. For example, - suppose this tally has arrays of data with shape (8,5,5) corresponding - to two filters (2 and 4 bins, respectively), five nuclides and five + suppose this tally has arrays of data with shape (30,5,5) corresponding + to two filters (2 and 15 bins, respectively), five nuclides and five scores. This method will return a version of the data array with the - with a new shape of (2,4,5,5) such that the first two dimensions - correspond directly to the two filters with two and four bins. + with a new shape of (2,15,5,5) such that the first two dimensions + correspond directly to the two filters with two and fifteen bins. If + expand_dims is True and our filter above with 15 bins is an instance of + :class:`openmc.MeshFilter` with a shape of (3,5,1). The resulting tally + data array will have a new shape of (2,3,5,1,5,5). Parameters ---------- value : str A string for the type of value to return - 'mean' (default), 'std_dev', 'rel_err', 'sum', or 'sum_sq' are accepted + expand_dims : bool, optional + Whether or not to expand the dimensions of filters with multiple + dimensions. This will result in more than one dimension per filter + for the returned data array. + + .. versionadded:: 0.13.3 Returns ------- @@ -1451,12 +1567,32 @@ class Tally(IDManagerMixin): # Get the 3D array of data in filters, nuclides and scores data = self.get_values(value=value) - # Build a new array shape with one dimension per filter - new_shape = tuple(f.num_bins for f in self.filters) + # Build a new array shape with one dimension per filter or expand + # multidimensional filters if desired + new_shape = tuple() + idx0 = None + for i, f in enumerate(self.filters): + if expand_dims: + # Mesh filter indices are backwards so we need to flip them + if type(f) in {openmc.MeshFilter, openmc.MeshBornFilter}: + fshape = f.shape[::-1] + new_shape += fshape + idx0, idx1 = i, i + len(fshape) - 1 + else: + new_shape += f.shape + else: + new_shape += (np.prod(f.shape),) + new_shape += (self.num_nuclides, self.num_scores) # Reshape the data with one dimension for each filter data = np.reshape(data, new_shape) + + # If we had a MeshFilter we should swap the axes to have the same shape + # for the data and the filter + if idx0 is not None: + data = np.swapaxes(data, idx0, idx1) + return data def hybrid_product(self, other, binary_op, filter_product=None, @@ -2109,7 +2245,7 @@ class Tally(IDManagerMixin): new_tally.sparse = self.sparse else: - msg = 'Unable to add "{}" to Tally ID="{}"'.format(other, self.id) + msg = f'Unable to add "{other}" to Tally ID="{self.id}"' raise ValueError(msg) return new_tally @@ -2180,7 +2316,7 @@ class Tally(IDManagerMixin): new_tally.sparse = self.sparse else: - msg = 'Unable to subtract "{}" from Tally ID="{}"'.format(other, self.id) + msg = f'Unable to subtract "{other}" from Tally ID="{self.id}"' raise ValueError(msg) return new_tally @@ -2251,7 +2387,7 @@ class Tally(IDManagerMixin): new_tally.sparse = self.sparse else: - msg = 'Unable to multiply Tally ID="{}" by "{}"'.format(self.id, other) + msg = f'Unable to multiply Tally ID="{self.id}" by "{other}"' raise ValueError(msg) return new_tally @@ -2310,7 +2446,8 @@ class Tally(IDManagerMixin): new_tally.name = self.name new_tally._mean = self.mean / other new_tally._std_dev = self.std_dev * np.abs(1. / other) - new_tally.estimator = self.estimator + if self.estimator is not None: + new_tally.estimator = self.estimator new_tally.with_summary = self.with_summary new_tally.num_realizations = self.num_realizations @@ -2322,7 +2459,7 @@ class Tally(IDManagerMixin): new_tally.sparse = self.sparse else: - msg = 'Unable to divide Tally ID="{}" by "{}"'.format(self.id, other) + msg = f'Unable to divide Tally ID="{self.id}" by "{other}"' raise ValueError(msg) return new_tally @@ -2397,7 +2534,7 @@ class Tally(IDManagerMixin): new_tally.sparse = self.sparse else: - msg = 'Unable to raise Tally ID="{}" to power "{}"'.format(self.id, power) + msg = f'Unable to raise Tally ID="{self.id}" to power "{power}"' raise ValueError(msg) return new_tally @@ -2603,8 +2740,8 @@ class Tally(IDManagerMixin): # Determine the nuclide indices from any of the requested nuclides for nuclide in self.nuclides: - if nuclide.name not in nuclides: - nuclide_index = self.get_nuclide_index(nuclide.name) + if nuclide not in nuclides: + nuclide_index = self.get_nuclide_index(nuclide) nuclide_indices.append(nuclide_index) # Loop over indices in reverse to remove excluded Nuclides @@ -3065,8 +3202,7 @@ class Tallies(cv.CheckedList): """ if not isinstance(tally, Tally): - msg = 'Unable to add a non-Tally "{}" to the ' \ - 'Tallies instance'.format(tally) + msg = f'Unable to add a non-Tally "{tally}" to the Tallies instance' raise TypeError(msg) if merge: @@ -3127,21 +3263,34 @@ class Tallies(cv.CheckedList): # Continue iterating from the first loop break + def add_results(self, statepoint: cv.PathLike | openmc.StatePoint): + """Add results from the provided statepoint file + + .. versionadded:: 0.15.1 + + Parameters + ---------- + statepoint : openmc.PathLike or openmc.StatePoint + Statepoint used to update tally results + """ + for tally in self: + tally.add_results(statepoint) + def _create_tally_subelements(self, root_element): for tally in self: root_element.append(tally.to_xml_element()) - def _create_mesh_subelements(self, root_element): - already_written = set() + def _create_mesh_subelements(self, root_element, memo=None): + already_written = memo if memo else set() for tally in self: for f in tally.filters: if isinstance(f, openmc.MeshFilter): - if f.mesh.id not in already_written: - if len(f.mesh.name) > 0: - root_element.append(ET.Comment(f.mesh.name)) - - root_element.append(f.mesh.to_xml_element()) - already_written.add(f.mesh.id) + if f.mesh.id in already_written: + continue + if len(f.mesh.name) > 0: + root_element.append(ET.Comment(f.mesh.name)) + root_element.append(f.mesh.to_xml_element()) + already_written.add(f.mesh.id) def _create_filter_subelements(self, root_element): already_written = dict() @@ -3167,6 +3316,21 @@ class Tallies(cv.CheckedList): for d in derivs: root_element.append(d.to_xml_element()) + def to_xml_element(self, memo=None): + """Creates a 'tallies' element to be written to an XML file. + """ + memo = memo if memo is not None else set() + element = ET.Element("tallies") + self._create_mesh_subelements(element, memo) + self._create_filter_subelements(element) + self._create_tally_subelements(element) + self._create_derivative_subelements(element) + + # Clean the indentation in the file to be user-readable + clean_indentation(element) + + return element + def export_to_xml(self, path='tallies.xml'): """Create a tallies.xml file that can be used for a simulation. @@ -3176,15 +3340,7 @@ class Tallies(cv.CheckedList): Path to file to write. Defaults to 'tallies.xml'. """ - - root_element = ET.Element("tallies") - self._create_mesh_subelements(root_element) - self._create_filter_subelements(root_element) - self._create_tally_subelements(root_element) - self._create_derivative_subelements(root_element) - - # Clean the indentation in the file to be user-readable - clean_indentation(root_element) + root_element = self.to_xml_element() # Check if path is a directory p = Path(path) @@ -3192,10 +3348,56 @@ class Tallies(cv.CheckedList): p /= 'tallies.xml' # Write the XML Tree to the tallies.xml file - reorder_attributes(root_element) # TODO: Remove when support is Python 3.8+ tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') + @classmethod + def from_xml_element(cls, elem, meshes=None): + """Generate tallies from an XML element + + Parameters + ---------- + elem : lxml.etree._Element + XML element + meshes : dict or None + A dictionary with mesh IDs as keys and mesh instances as values that + have already been read from XML. Pre-existing meshes are used + and new meshes are added to when creating tally objects. + + Returns + ------- + openmc.Tallies + Tallies object + + """ + # Read mesh elements + meshes = {} if meshes is None else meshes + for e in elem.findall('mesh'): + mesh = MeshBase.from_xml_element(e) + meshes[mesh.id] = mesh + + # Read filter elements + filters = {} + for e in elem.findall('filter'): + filter = openmc.Filter.from_xml_element(e, meshes=meshes) + filters[filter.id] = filter + + # Read derivative elements + derivatives = {} + for e in elem.findall('derivative'): + deriv = openmc.TallyDerivative.from_xml_element(e) + derivatives[deriv.id] = deriv + + # Read tally elements + tallies = [] + for e in elem.findall('tally'): + tally = openmc.Tally.from_xml_element( + e, filters=filters, derivatives=derivatives + ) + tallies.append(tally) + + return cls(tallies) + @classmethod def from_xml(cls, path='tallies.xml'): """Generate tallies from XML file @@ -3211,33 +3413,7 @@ class Tallies(cv.CheckedList): Tallies object """ - tree = ET.parse(path) + parser = ET.XMLParser(huge_tree=True) + tree = ET.parse(path, parser=parser) root = tree.getroot() - - # Read mesh elements - meshes = {} - for elem in root.findall('mesh'): - mesh = MeshBase.from_xml_element(elem) - meshes[mesh.id] = mesh - - # Read filter elements - filters = {} - for elem in root.findall('filter'): - filter = openmc.Filter.from_xml_element(elem, meshes=meshes) - filters[filter.id] = filter - - # Read derivative elements - derivatives = {} - for elem in root.findall('derivative'): - deriv = openmc.TallyDerivative.from_xml_element(elem) - derivatives[deriv.id] = deriv - - # Read tally elements - tallies = [] - for elem in root.findall('tally'): - tally = openmc.Tally.from_xml_element( - elem, filters=filters, derivatives=derivatives - ) - tallies.append(tally) - - return cls(tallies) + return cls.from_xml_element(root) diff --git a/openmc/tally_derivative.py b/openmc/tally_derivative.py index 05a27681d5..f7ba5dce58 100644 --- a/openmc/tally_derivative.py +++ b/openmc/tally_derivative.py @@ -1,8 +1,10 @@ from numbers import Integral -from xml.etree import ElementTree as ET + +import lxml.etree as ET import openmc.checkvalue as cv from .mixin import EqualityMixin, IDManagerMixin +from ._xml import get_text class TallyDerivative(EqualityMixin, IDManagerMixin): @@ -60,14 +62,6 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): def variable(self): return self._variable - @property - def material(self): - return self._material - - @property - def nuclide(self): - return self._nuclide - @variable.setter def variable(self, var): if var is not None: @@ -76,12 +70,20 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): ('density', 'nuclide_density', 'temperature')) self._variable = var + @property + def material(self): + return self._material + @material.setter def material(self, mat): if mat is not None: cv.check_type('derivative material', mat, Integral) self._material = mat + @property + def nuclide(self): + return self._nuclide + @nuclide.setter def nuclide(self, nuc): if nuc is not None: @@ -93,7 +95,7 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing derivative data """ @@ -112,7 +114,7 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -121,8 +123,8 @@ class TallyDerivative(EqualityMixin, IDManagerMixin): Tally derivative object """ - derivative_id = int(elem.get("id")) - variable = elem.get("variable") - material = int(elem.get("material")) - nuclide = elem.get("nuclide") if variable == "nuclide_density" else None + derivative_id = int(get_text(elem, "id")) + variable = get_text(elem, "variable") + material = int(get_text(elem, "material")) + nuclide = get_text(elem, "nuclide") if variable == "nuclide_density" else None return cls(derivative_id, variable, material, nuclide) diff --git a/openmc/tracks.py b/openmc/tracks.py new file mode 100644 index 0000000000..61e5a72442 --- /dev/null +++ b/openmc/tracks.py @@ -0,0 +1,346 @@ +from collections import namedtuple +from collections.abc import Sequence + +import h5py + +from .checkvalue import check_filetype_version +from .source import SourceParticle, ParticleType + +from pathlib import Path + +ParticleTrack = namedtuple('ParticleTrack', ['particle', 'states']) +ParticleTrack.__doc__ = """\ +Particle track information + +Parameters +---------- +particle : openmc.ParticleType + Type of the particle +states : numpy.ndarray + Structured array containing each state of the particle. The structured array + contains the following fields: ``r`` (position; each direction in [cm]), + ``u`` (direction), ``E`` (energy in [eV]), ``time`` (time in [s]), ``wgt`` + (weight), ``cell_id`` (cell ID) , ``cell_instance`` (cell instance), and + ``material_id`` (material ID). + +""" +def _particle_track_repr(self): + return f"" +ParticleTrack.__repr__ = _particle_track_repr + + +_VERSION_TRACK = 3 + + +def _identifier(dset_name): + """Return (batch, gen, particle) tuple given dataset name""" + _, batch, gen, particle = dset_name.split('_') + return (int(batch), int(gen), int(particle)) + + +class Track(Sequence): + """Tracks resulting from a single source particle + + This class stores information for all tracks resulting from a primary source + particle and any secondary particles that it created. The track for each + primary/secondary particle is stored in the :attr:`particle_tracks` + attribute. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + dset : h5py.Dataset + Dataset to read track data from + + Attributes + ---------- + identifier : tuple + Tuple of (batch, generation, particle number) + particle_tracks : list + List of tuples containing (particle type, array of track states) + sources : list + List of :class:`SourceParticle` representing each primary/secondary + particle + + """ + + def __init__(self, dset): + tracks = dset[()] + offsets = dset.attrs['offsets'] + particles = dset.attrs['particles'] + self.identifier = _identifier(dset.name) + + # Construct list of track histories + tracks_list = [] + for particle, start, end in zip(particles, offsets[:-1], offsets[1:]): + ptype = ParticleType(particle) + tracks_list.append(ParticleTrack(ptype, tracks[start:end])) + self.particle_tracks = tracks_list + + def __repr__(self): + return f'' + + def __getitem__(self, index): + return self.particle_tracks[index] + + def __len__(self): + return len(self.particle_tracks) + + def filter(self, particle=None, state_filter=None): + """Filter particle tracks by given criteria + + Parameters + ---------- + particle : {'neutron', 'photon', 'electron', 'positron'} + Matching particle type + state_filter : function + Function that takes a state (structured datatype) and returns a bool + depending on some criteria. + + Returns + ------- + Track + New instance with only matching :class:`openmc.ParticleTrack` objects + + Examples + -------- + Get all particle tracks for photons: + + >>> track.filter(particle='photon') + + Get all particle tracks that entered cell with ID=15: + + >>> track.filter(state_filter=lambda s: s['cell_id'] == 15) + + Get all particle tracks in entered material with ID=2: + + >>> track.filter(state_filter=lambda s: s['material_id'] == 2) + + See Also + -------- + openmc.ParticleTrack + + """ + matching = [] + for t in self: + # Check for matching particle + if particle is not None: + if t.particle.name.lower() != particle: + continue + + # Apply arbitrary state filter + match = True + if state_filter is not None: + for state in t.states: + if state_filter(state): + break + else: + match = False + + if match: + matching.append(t) + + # Return new Track instance with only matching particle tracks + track = type(self).__new__(type(self)) + track.identifier = self.identifier + track.particle_tracks = matching + return track + + def plot(self, axes=None): + """Produce a 3D plot of particle tracks + + Parameters + ---------- + axes : matplotlib.axes.Axes, optional + Axes for plot + + Returns + ------- + axes : matplotlib.axes.Axes + Axes for plot + + """ + import matplotlib.pyplot as plt + + # Setup axes is one wasn't passed + if axes is None: + fig = plt.figure() + ax = plt.axes(projection='3d') + ax.set_xlabel('x [cm]') + ax.set_ylabel('y [cm]') + ax.set_zlabel('z [cm]') + else: + ax = axes + + # Plot each particle track + for _, states in self: + r = states['r'] + ax.plot3D(r['x'], r['y'], r['z']) + + return ax + + @property + def sources(self): + sources = [] + for particle_track in self: + particle_type = ParticleType(particle_track.particle) + state = particle_track.states[0] + sources.append( + SourceParticle( + r=state['r'], u=state['u'], E=state['E'], + time=state['time'], wgt=state['wgt'], + particle=particle_type + ) + ) + return sources + + +class Tracks(list): + """Collection of particle tracks + + This class behaves like a list and can be indexed using the normal subscript + notation. Each element in the list is a :class:`openmc.Track` object. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + filepath : str or pathlib.Path + Path of file to load + + """ + + def __init__(self, filepath='tracks.h5'): + # Read data from track file + with h5py.File(filepath, 'r') as fh: + # Check filetype and version + check_filetype_version(fh, 'track', _VERSION_TRACK) + + for dset_name in sorted(fh, key=_identifier): + dset = fh[dset_name] + self.append(Track(dset)) + + def filter(self, particle=None, state_filter=None): + """Filter tracks by given criteria + + Parameters + ---------- + particle : {'neutron', 'photon', 'electron', 'positron'} + Matching particle type + state_filter : function + Function that takes a state (structured datatype) and returns a bool + depending on some criteria. + + Returns + ------- + Tracks + List of :class:`openmc.Track` objects + + See Also + -------- + openmc.Track.filter + + """ + # Create a new Tracks instance but avoid call to __init__ + matching = type(self).__new__(type(self)) + + # Append matching Track objects + for track in self: + if track.filter(particle, state_filter): + matching.append(track) + return matching + + def plot(self): + """Produce a 3D plot of particle tracks + + Returns + ------- + matplotlib.axes.Axes + Axes for plot + + """ + import matplotlib.pyplot as plt + fig = plt.figure() + ax = plt.axes(projection='3d') + ax.set_xlabel('x [cm]') + ax.set_ylabel('y [cm]') + ax.set_zlabel('z [cm]') + for track in self: + track.plot(ax) + return ax + + def write_to_vtk(self, filename=Path('tracks.vtp')): + """Creates a VTP file of the tracks + + Parameters + ---------- + filename : path-like + Name of the VTP file to write. + + Returns + ------- + vtk.vtkPolyData + the VTK vtkPolyData object produced + """ + + import vtk + + # Initialize data arrays and offset. + points = vtk.vtkPoints() + cells = vtk.vtkCellArray() + + point_offset = 0 + for particle in self: + for pt in particle.particle_tracks: + for state in pt.states: + points.InsertNextPoint(state['r']) + + # Create VTK line and assign points to line. + n = pt.states.size + line = vtk.vtkPolyLine() + line.GetPointIds().SetNumberOfIds(n) + for i in range(n): + line.GetPointIds().SetId(i, point_offset + i) + point_offset += n + + # Add line to cell array + cells.InsertNextCell(line) + + data = vtk.vtkPolyData() + data.SetPoints(points) + data.SetLines(cells) + + writer = vtk.vtkXMLPPolyDataWriter() + if vtk.vtkVersion.GetVTKMajorVersion() > 5: + writer.SetInputData(data) + else: + writer.SetInput(data) + writer.SetFileName(str(filename)) # SetFileName requires a string + writer.Write() + + return data + + @staticmethod + def combine(track_files, path='tracks.h5'): + """Combine multiple track files into a single track file + + Parameters + ---------- + track_files : list of path-like + Paths to track files to combine + path : path-like + Path of combined track file to create + + """ + with h5py.File(path, 'w') as h5_out: + for i, fname in enumerate(track_files): + with h5py.File(fname, 'r') as h5_in: + # Copy file attributes for first file + if i == 0: + h5_out.attrs['filetype'] = h5_in.attrs['filetype'] + h5_out.attrs['version'] = h5_in.attrs['version'] + + # Copy each 'track_*' dataset from input file + for dset in h5_in: + h5_in.copy(dset, h5_out) diff --git a/openmc/trigger.py b/openmc/trigger.py index c5151aa7e0..70b6b7a039 100644 --- a/openmc/trigger.py +++ b/openmc/trigger.py @@ -1,9 +1,11 @@ from collections.abc import Iterable from numbers import Real -from xml.etree import ElementTree as ET + +import lxml.etree as ET import openmc.checkvalue as cv from .mixin import EqualityMixin +from ._xml import get_elem_list, get_text class Trigger(EqualityMixin): @@ -16,6 +18,12 @@ class Trigger(EqualityMixin): relative error of scores. threshold : float The threshold for the trigger type. + ignore_zeros : bool + Whether to allow zero tally bins to be ignored. Note that this option + can cause the trigger to fire prematurely if there are zero scores in + any bin at the first evaluation. + + .. versionadded:: 0.15.0 Attributes ---------- @@ -26,18 +34,22 @@ class Trigger(EqualityMixin): The threshold for the trigger type. scores : list of str Scores which should be checked against the trigger + ignore_zeros : bool + Whether to allow zero tally bins to be ignored. """ - def __init__(self, trigger_type, threshold): + def __init__(self, trigger_type: str, threshold: float, ignore_zeros: bool = False): self.trigger_type = trigger_type self.threshold = threshold + self.ignore_zeros = ignore_zeros self._scores = [] def __repr__(self): string = 'Trigger\n' string += '{: <16}=\t{}\n'.format('\tType', self._trigger_type) string += '{: <16}=\t{}\n'.format('\tThreshold', self._threshold) + string += '{: <16}=\t{}\n'.format('\tIgnore Zeros', self._ignore_zeros) string += '{: <16}=\t{}\n'.format('\tScores', self._scores) return string @@ -45,25 +57,34 @@ class Trigger(EqualityMixin): def trigger_type(self): return self._trigger_type - @property - def threshold(self): - return self._threshold - - @property - def scores(self): - return self._scores - @trigger_type.setter def trigger_type(self, trigger_type): cv.check_value('tally trigger type', trigger_type, ['variance', 'std_dev', 'rel_err']) self._trigger_type = trigger_type + @property + def threshold(self): + return self._threshold + @threshold.setter def threshold(self, threshold): cv.check_type('tally trigger threshold', threshold, Real) self._threshold = threshold + @property + def ignore_zeros(self): + return self._ignore_zeros + + @ignore_zeros.setter + def ignore_zeros(self, ignore_zeros): + cv.check_type('tally trigger ignores zeros', ignore_zeros, bool) + self._ignore_zeros = ignore_zeros + + @property + def scores(self): + return self._scores + @scores.setter def scores(self, scores): cv.check_type('trigger scores', scores, Iterable, str) @@ -79,7 +100,7 @@ class Trigger(EqualityMixin): Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing trigger data """ @@ -87,17 +108,19 @@ class Trigger(EqualityMixin): element = ET.Element("trigger") element.set("type", self._trigger_type) element.set("threshold", str(self._threshold)) + if self._ignore_zeros: + element.set("ignore_zeros", "true") if len(self._scores) != 0: element.set("scores", ' '.join(self._scores)) return element @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem: ET.Element): """Generate trigger object from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -107,13 +130,16 @@ class Trigger(EqualityMixin): """ # Generate trigger object - trigger_type = elem.get("type") - threshold = float(elem.get("threshold")) - trigger = cls(trigger_type, threshold) + trigger_type = get_text(elem, "type") + threshold = float(get_text(elem, "threshold")) + ignore_zeros = str(get_text(elem, "ignore_zeros", "false")).lower() + # Try to convert to bool. Let Trigger error out on instantiation. + ignore_zeros = ignore_zeros in ('true', '1') + trigger = cls(trigger_type, threshold, ignore_zeros) # Add scores if present - scores = elem.get("scores") + scores = get_elem_list(elem, "scores", str) if scores is not None: - trigger.scores = scores.split() + trigger.scores = scores return trigger diff --git a/openmc/universe.py b/openmc/universe.py index a7d7f096ca..0e64693ba8 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1,18 +1,14 @@ +from __future__ import annotations from abc import ABC, abstractmethod -from collections import OrderedDict from collections.abc import Iterable -from copy import copy, deepcopy from numbers import Real -import random -from xml.etree import ElementTree as ET import numpy as np import openmc import openmc.checkvalue as cv -from ._xml import get_text from .mixin import IDManagerMixin -from .plots import _SVG_COLORS +from .plots import add_plot_params class UniverseBase(ABC, IDManagerMixin): @@ -38,7 +34,7 @@ class UniverseBase(ABC, IDManagerMixin): # Keys - Cell IDs # Values - Cells - self._cells = OrderedDict() + self._cells = {} def __repr__(self): string = 'Universe\n' @@ -51,8 +47,8 @@ class UniverseBase(ABC, IDManagerMixin): return self._name @property - def volume(self): - return self._volume + def cells(self): + return self._cells @name.setter def name(self, name): @@ -62,6 +58,10 @@ class UniverseBase(ABC, IDManagerMixin): else: self._name = '' + @property + def volume(self): + return self._volume + @volume.setter def volume(self, volume): if volume is not None: @@ -82,17 +82,41 @@ class UniverseBase(ABC, IDManagerMixin): self._volume = volume_calc.volumes[self.id].n self._atoms = volume_calc.atoms[self.id] else: - raise ValueError('No volume information found for this universe.') + raise ValueError( + 'No volume information found for this universe.') else: raise ValueError('No volume information found for this universe.') + def get_all_universes(self, memo=None): + """Return all universes that are contained within this one. + + Returns + ------- + universes : dict + Dictionary whose keys are universe IDs and values are + :class:`Universe` instances + + """ + if memo is None: + memo = set() + elif self in memo: + return {} + memo.add(self) + + # Append all Universes within each Cell to the dictionary + universes = {} + for cell in self.get_all_cells().values(): + universes.update(cell.get_all_universes(memo)) + + return universes + @abstractmethod def create_xml_subelement(self, xml_element, memo=None): """Add the universe xml representation to an incoming xml element Parameters ---------- - xml_element : xml.etree.ElementTree.Element + xml_element : lxml.etree._Element XML element to be added to memo : set or None @@ -106,6 +130,137 @@ class UniverseBase(ABC, IDManagerMixin): """ + def _determine_paths(self, path='', instances_only=False): + """Count the number of instances for each cell in the universe, and + record the count in the :attr:`Cell.num_instances` properties.""" + + univ_path = path + f'u{self.id}' + + for cell in self.cells.values(): + cell_path = f'{univ_path}->c{cell.id}' + fill = cell._fill + fill_type = cell.fill_type + + # If universe-filled, recursively count cells in filling universe + if fill_type == 'universe': + fill._determine_paths(cell_path + '->', instances_only) + # If lattice-filled, recursively call for all universes in lattice + elif fill_type == 'lattice': + latt = fill + + # Count instances in each universe in the lattice + for index in latt._natural_indices: + latt_path = '{}->l{}({})->'.format( + cell_path, latt.id, ",".join(str(x) for x in index)) + univ = latt.get_universe(index) + univ._determine_paths(latt_path, instances_only) + + else: + if fill_type == 'material': + mat = fill + elif fill_type == 'distribmat': + mat = fill[cell._num_instances] + else: + mat = None + + if mat is not None: + mat._num_instances += 1 + if not instances_only: + mat._paths.append(f'{cell_path}->m{mat.id}') + + # Append current path + cell._num_instances += 1 + if not instances_only: + cell._paths.append(cell_path) + + def add_cells(self, cells): + """Add multiple cells to the universe. + + Parameters + ---------- + cells : Iterable of openmc.Cell + Cells to add + + """ + + if not isinstance(cells, Iterable): + msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ + f'"{cells}" is not iterable' + raise TypeError(msg) + + for cell in cells: + self.add_cell(cell) + + @abstractmethod + def add_cell(self, cell): + pass + + @abstractmethod + def remove_cell(self, cell): + pass + + def clear_cells(self): + """Remove all cells from the universe.""" + + self._cells.clear() + + def get_all_cells(self, memo=None): + """Return all cells that are contained within the universe + + Returns + ------- + cells : dict + Dictionary whose keys are cell IDs and values are :class:`Cell` + instances + + """ + + if memo is None: + memo = set() + elif self in memo: + return {} + memo.add(self) + + # Add this Universe's cells to the dictionary + cells = {} + cells.update(self._cells) + + # Append all Cells in each Cell in the Universe to the dictionary + for cell in self._cells.values(): + cells.update(cell.get_all_cells(memo)) + + return cells + + def get_all_materials(self, memo=None): + """Return all materials that are contained within the universe + + Returns + ------- + materials : dict + Dictionary whose keys are material IDs and values are + :class:`Material` instances + + """ + + if memo is None: + memo = set() + + materials = {} + + # Append all Cells in each Cell in the Universe to the dictionary + cells = self.get_all_cells(memo) + for cell in cells.values(): + materials.update(cell.get_all_materials(memo)) + + return materials + + @abstractmethod + def _partial_deepcopy(self): + """Deepcopy all parameters of an openmc.UniverseBase object except its cells. + This should only be used from the openmc.UniverseBase.clone() context. + + """ + def clone(self, clone_materials=True, clone_regions=True, memo=None): """Create a copy of this universe with a new unique ID, and clones all cells within this universe. @@ -133,108 +288,19 @@ class UniverseBase(ABC, IDManagerMixin): # If no memoize'd clone exists, instantiate one if self not in memo: - clone = deepcopy(self) - clone.id = None + clone = self._partial_deepcopy() # Clone all cells for the universe clone - clone._cells = OrderedDict() + clone._cells = {} for cell in self._cells.values(): clone.add_cell(cell.clone(clone_materials, clone_regions, - memo)) + memo)) # Memoize the clone memo[self] = clone return memo[self] - -class Universe(UniverseBase): - """A collection of cells that can be repeated. - - Parameters - ---------- - universe_id : int, optional - Unique identifier of the universe. If not specified, an identifier will - automatically be assigned - name : str, optional - Name of the universe. If not specified, the name is the empty string. - cells : Iterable of openmc.Cell, optional - Cells to add to the universe. By default no cells are added. - - Attributes - ---------- - id : int - Unique identifier of the universe - name : str - Name of the universe - cells : collections.OrderedDict - Dictionary whose keys are cell IDs and values are :class:`Cell` - instances - volume : float - Volume of the universe in cm^3. This can either be set manually or - calculated in a stochastic volume calculation and added via the - :meth:`Universe.add_volume_information` method. - bounding_box : 2-tuple of numpy.array - Lower-left and upper-right coordinates of an axis-aligned bounding box - of the universe. - - """ - - def __init__(self, universe_id=None, name='', cells=None): - super().__init__(universe_id, name) - - if cells is not None: - self.add_cells(cells) - - def __repr__(self): - string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') - string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) - return string - - @property - def cells(self): - return self._cells - - @property - def bounding_box(self): - regions = [c.region for c in self.cells.values() - if c.region is not None] - if regions: - return openmc.Union(regions).bounding_box - else: - # Infinite bounding box - return openmc.Intersection([]).bounding_box - - @classmethod - def from_hdf5(cls, group, cells): - """Create universe from HDF5 group - - Parameters - ---------- - group : h5py.Group - Group in HDF5 file - cells : dict - Dictionary mapping cell IDs to instances of :class:`openmc.Cell`. - - Returns - ------- - openmc.Universe - Universe instance - - """ - universe_id = int(group.name.split('/')[-1].lstrip('universe ')) - cell_ids = group['cells'][()] - - # Create this Universe - universe = cls(universe_id) - - # Add each Cell to the Universe - for cell_id in cell_ids: - universe.add_cell(cells[cell_id]) - - return universe - def find(self, point): """Find cells/universes/lattices which contain a given point @@ -265,191 +331,13 @@ class Universe(UniverseBase): return [self, cell] + cell.fill.find(p) return [] - def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200), - basis='xy', color_by='cell', colors=None, seed=None, - **kwargs): + @add_plot_params + def plot(self, *args, **kwargs): """Display a slice plot of the universe. - - To display or save the plot, call :func:`matplotlib.pyplot.show` or - :func:`matplotlib.pyplot.savefig`. In a Jupyter notebook, enabling the - matplotlib inline backend will show the plot inline. - - Parameters - ---------- - origin : Iterable of float - Coordinates at the origin of the plot - width : Iterable of float - Width of the plot in each basis direction - pixels : Iterable of int - Number of pixels to use in each basis direction - basis : {'xy', 'xz', 'yz'} - The basis directions for the plot - color_by : {'cell', 'material'} - Indicate whether the plot should be colored by cell or by material - colors : dict - Assigns colors to specific materials or cells. Keys are instances of - :class:`Cell` or :class:`Material` and values are RGB 3-tuples, RGBA - 4-tuples, or strings indicating SVG color names. Red, green, blue, - and alpha should all be floats in the range [0.0, 1.0], for example: - - .. code-block:: python - - # Make water blue - water = openmc.Cell(fill=h2o) - universe.plot(..., colors={water: (0., 0., 1.)) - - seed : hashable object or None - Hashable object which is used to seed the random number generator - used to select colors. If None, the generator is seeded from the - current time. - **kwargs - All keyword arguments are passed to - :func:`matplotlib.pyplot.imshow`. - - Returns - ------- - matplotlib.image.AxesImage - Resulting image - """ - import matplotlib.pyplot as plt - - # Seed the random number generator - if seed is not None: - random.seed(seed) - - if colors is None: - # Create default dictionary if none supplied - colors = {} - else: - # Convert to RGBA if necessary - colors = copy(colors) - for obj, color in colors.items(): - if isinstance(color, str): - if color.lower() not in _SVG_COLORS: - raise ValueError(f"'{color}' is not a valid color.") - colors[obj] = [x/255 for x in - _SVG_COLORS[color.lower()]] + [1.0] - elif len(color) == 3: - colors[obj] = list(color) + [1.0] - - if basis == 'xy': - x_min = origin[0] - 0.5*width[0] - x_max = origin[0] + 0.5*width[0] - y_min = origin[1] - 0.5*width[1] - y_max = origin[1] + 0.5*width[1] - elif basis == 'yz': - # The x-axis will correspond to physical y and the y-axis will - # correspond to physical z - x_min = origin[1] - 0.5*width[0] - x_max = origin[1] + 0.5*width[0] - y_min = origin[2] - 0.5*width[1] - y_max = origin[2] + 0.5*width[1] - elif basis == 'xz': - # The y-axis will correspond to physical z - x_min = origin[0] - 0.5*width[0] - x_max = origin[0] + 0.5*width[0] - y_min = origin[2] - 0.5*width[1] - y_max = origin[2] + 0.5*width[1] - - # Determine locations to determine cells at - x_coords = np.linspace(x_min, x_max, pixels[0], endpoint=False) + \ - 0.5*(x_max - x_min)/pixels[0] - y_coords = np.linspace(y_max, y_min, pixels[1], endpoint=False) - \ - 0.5*(y_max - y_min)/pixels[1] - - # Initialize output image in RGBA format. Flip the pixels from - # traditional (x, y) to (y, x) used in graphics. - img = np.zeros((pixels[1], pixels[0], 4)) - for i, x in enumerate(x_coords): - for j, y in enumerate(y_coords): - if basis == 'xy': - path = self.find((x, y, origin[2])) - elif basis == 'yz': - path = self.find((origin[0], x, y)) - elif basis == 'xz': - path = self.find((x, origin[1], y)) - - if len(path) > 0: - try: - if color_by == 'cell': - obj = path[-1] - elif color_by == 'material': - if path[-1].fill_type == 'material': - obj = path[-1].fill - else: - continue - except AttributeError: - continue - if obj not in colors: - colors[obj] = (random.random(), random.random(), - random.random(), 1.0) - img[j, i, :] = colors[obj] - - # Display image - return plt.imshow(img, extent=(x_min, x_max, y_min, y_max), - interpolation='nearest', **kwargs) - - def add_cell(self, cell): - """Add a cell to the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to add - - """ - - if not isinstance(cell, openmc.Cell): - msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ - f'"{cell}" is not a Cell' - raise TypeError(msg) - - cell_id = cell.id - - if cell_id not in self._cells: - self._cells[cell_id] = cell - - def add_cells(self, cells): - """Add multiple cells to the universe. - - Parameters - ---------- - cells : Iterable of openmc.Cell - Cells to add - - """ - - if not isinstance(cells, Iterable): - msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ - f'"{cells}" is not iterable' - raise TypeError(msg) - - for cell in cells: - self.add_cell(cell) - - def remove_cell(self, cell): - """Remove a cell from the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to remove - - """ - - if not isinstance(cell, openmc.Cell): - msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ - f'since "{cell}" is not a Cell' - raise TypeError(msg) - - # If the Cell is in the Universe's list of Cells, delete it - self._cells.pop(cell.id, None) - - def clear_cells(self): - """Remove all cells from the universe.""" - - self._cells.clear() + model = openmc.Model() + model.geometry = openmc.Geometry(self) + return model.plot(*args, **kwargs) def get_nuclides(self): """Returns all nuclides in the universe @@ -476,19 +364,18 @@ class Universe(UniverseBase): Returns ------- - nuclides : collections.OrderedDict + nuclides : dict Dictionary whose keys are nuclide names and values are 2-tuples of (nuclide, density) """ - nuclides = OrderedDict() + nuclides = {} - if self._atoms is not None: + if self._atoms: volume = self.volume for name, atoms in self._atoms.items(): - nuclide = openmc.Nuclide(name) density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm - nuclides[name] = (nuclide, density) + nuclides[name] = (name, density) else: raise RuntimeError( 'Volume information is needed to calculate microscopic cross ' @@ -498,81 +385,141 @@ class Universe(UniverseBase): return nuclides - def get_all_cells(self, memo=None): - """Return all cells that are contained within the universe + + +class Universe(UniverseBase): + """A collection of cells that can be repeated. + + Parameters + ---------- + universe_id : int, optional + Unique identifier of the universe. If not specified, an identifier will + automatically be assigned + name : str, optional + Name of the universe. If not specified, the name is the empty string. + cells : Iterable of openmc.Cell, optional + Cells to add to the universe. By default no cells are added. + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + cells : dict + Dictionary whose keys are cell IDs and values are :class:`Cell` + instances + volume : float + Volume of the universe in cm^3. This can either be set manually or + calculated in a stochastic volume calculation and added via the + :meth:`Universe.add_volume_information` method. + bounding_box : openmc.BoundingBox + Lower-left and upper-right coordinates of an axis-aligned bounding box + of the universe. + + """ + + def __init__(self, universe_id=None, name='', cells=None): + super().__init__(universe_id, name) + + if cells is not None: + self.add_cells(cells) + + def __repr__(self): + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') + string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) + return string + + @property + def bounding_box(self) -> openmc.BoundingBox: + regions = [c.region for c in self.cells.values() + if c.region is not None] + if regions: + return openmc.Union(regions).bounding_box + else: + return openmc.BoundingBox.infinite() + + @classmethod + def from_hdf5(cls, group, cells): + """Create universe from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + cells : dict + Dictionary mapping cell IDs to instances of :class:`openmc.Cell`. Returns ------- - cells : collections.OrderedDict - Dictionary whose keys are cell IDs and values are :class:`Cell` - instances + openmc.Universe + Universe instance + + """ + universe_id = int(group.name.split('/')[-1].lstrip('universe ')) + cell_ids = group['cells'][()] + + # Create this Universe + universe = cls(universe_id) + + # Add each Cell to the Universe + for cell_id in cell_ids: + universe.add_cell(cells[cell_id]) + + return universe + + + def add_cell(self, cell): + """Add a cell to the universe. + + Parameters + ---------- + cell : openmc.Cell + Cell to add """ - cells = OrderedDict() + if not isinstance(cell, openmc.Cell): + msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ + f'"{cell}" is not a Cell' + raise TypeError(msg) - if memo and self in memo: - return cells + cell_id = cell.id - if memo is not None: - memo.add(self) + if cell_id not in self._cells: + self._cells[cell_id] = cell - # Add this Universe's cells to the dictionary - cells.update(self._cells) + def remove_cell(self, cell): + """Remove a cell from the universe. - # Append all Cells in each Cell in the Universe to the dictionary - for cell in self._cells.values(): - cells.update(cell.get_all_cells(memo)) - - return cells - - def get_all_materials(self, memo=None): - """Return all materials that are contained within the universe - - Returns - ------- - materials : collections.OrderedDict - Dictionary whose keys are material IDs and values are - :class:`Material` instances + Parameters + ---------- + cell : openmc.Cell + Cell to remove """ - materials = OrderedDict() + if not isinstance(cell, openmc.Cell): + msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ + f'since "{cell}" is not a Cell' + raise TypeError(msg) - # Append all Cells in each Cell in the Universe to the dictionary - cells = self.get_all_cells(memo) - for cell in cells.values(): - materials.update(cell.get_all_materials(memo)) - - return materials - - def get_all_universes(self): - """Return all universes that are contained within this one. - - Returns - ------- - universes : collections.OrderedDict - Dictionary whose keys are universe IDs and values are - :class:`Universe` instances - - """ - # Append all Universes within each Cell to the dictionary - universes = OrderedDict() - for cell in self.get_all_cells().values(): - universes.update(cell.get_all_universes()) - - return universes + # If the Cell is in the Universe's list of Cells, delete it + self._cells.pop(cell.id, None) def create_xml_subelement(self, xml_element, memo=None): + if memo is None: + memo = set() + # Iterate over all Cells for cell in self._cells.values(): # If the cell was already written, move on - if memo and cell in memo: + if cell in memo: continue - if memo is not None: - memo.add(cell) + memo.add(cell) # Create XML subelement for this Cell cell_element = cell.create_xml_subelement(xml_element, memo) @@ -581,208 +528,11 @@ class Universe(UniverseBase): cell_element.set("universe", str(self._id)) xml_element.append(cell_element) - def _determine_paths(self, path='', instances_only=False): - """Count the number of instances for each cell in the universe, and - record the count in the :attr:`Cell.num_instances` properties.""" - - univ_path = path + f'u{self.id}' - - for cell in self.cells.values(): - cell_path = f'{univ_path}->c{cell.id}' - fill = cell._fill - fill_type = cell.fill_type - - # If universe-filled, recursively count cells in filling universe - if fill_type == 'universe': - fill._determine_paths(cell_path + '->', instances_only) - - # If lattice-filled, recursively call for all universes in lattice - elif fill_type == 'lattice': - latt = fill - - # Count instances in each universe in the lattice - for index in latt._natural_indices: - latt_path = '{}->l{}({})->'.format( - cell_path, latt.id, ",".join(str(x) for x in index)) - univ = latt.get_universe(index) - univ._determine_paths(latt_path, instances_only) - - else: - if fill_type == 'material': - mat = fill - elif fill_type == 'distribmat': - mat = fill[cell._num_instances] - else: - mat = None - - if mat is not None: - mat._num_instances += 1 - if not instances_only: - mat._paths.append(f'{cell_path}->m{mat.id}') - - # Append current path - cell._num_instances += 1 - if not instances_only: - cell._paths.append(cell_path) - - -class DAGMCUniverse(UniverseBase): - """A reference to a DAGMC file to be used in the model. - - .. versionadded:: 0.13.0 - - Parameters - ---------- - filename : str - Path to the DAGMC file used to represent this universe. - universe_id : int, optional - Unique identifier of the universe. If not specified, an identifier will - automatically be assigned. - name : str, optional - Name of the universe. If not specified, the name is the empty string. - auto_geom_ids : bool - Set IDs automatically on initialization (True) or report overlaps - in ID space between CSG and DAGMC (False) - auto_mat_ids : bool - Set IDs automatically on initialization (True) or report overlaps - in ID space between OpenMC and UWUW materials (False) - - Attributes - ---------- - id : int - Unique identifier of the universe - name : str - Name of the universe - filename : str - Path to the DAGMC file used to represent this universe. - auto_geom_ids : bool - Set IDs automatically on initialization (True) or report overlaps - in ID space between CSG and DAGMC (False) - auto_mat_ids : bool - Set IDs automatically on initialization (True) or report overlaps - in ID space between OpenMC and UWUW materials (False) - """ - - def __init__(self, - filename, - universe_id=None, - name='', - auto_geom_ids=False, - auto_mat_ids=False): - super().__init__(universe_id, name) - # Initialize class attributes - self.filename = filename - self.auto_geom_ids = auto_geom_ids - self.auto_mat_ids = auto_mat_ids - - def __repr__(self): - string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') - string += '{: <16}=\t{}\n'.format('\tFile', self.filename) - return string - - @property - def filename(self): - return self._filename - - @filename.setter - def filename(self, val): - cv.check_type('DAGMC filename', val, str) - self._filename = val - - @property - def auto_geom_ids(self): - return self._auto_geom_ids - - @auto_geom_ids.setter - def auto_geom_ids(self, val): - cv.check_type('DAGMC automatic geometry ids', val, bool) - self._auto_geom_ids = val - - @property - def auto_mat_ids(self): - return self._auto_mat_ids - - @auto_mat_ids.setter - def auto_mat_ids(self, val): - cv.check_type('DAGMC automatic material ids', val, bool) - self._auto_mat_ids = val - - def get_all_cells(self, memo=None): - return OrderedDict() - - def get_all_materials(self, memo=None): - return OrderedDict() - - def create_xml_subelement(self, xml_element, memo=None): - if memo and self in memo: - return - - if memo is not None: - memo.add(self) - - # Set xml element values - dagmc_element = ET.Element('dagmc_universe') - dagmc_element.set('id', str(self.id)) - - if self.auto_geom_ids: - dagmc_element.set('auto_geom_ids', 'true') - if self.auto_mat_ids: - dagmc_element.set('auto_mat_ids', 'true') - dagmc_element.set('filename', self.filename) - xml_element.append(dagmc_element) - - @classmethod - def from_hdf5(cls, group): - """Create DAGMC universe from HDF5 group - - Parameters - ---------- - group : h5py.Group - Group in HDF5 file - - Returns - ------- - openmc.DAGMCUniverse - DAGMCUniverse instance - + def _partial_deepcopy(self): + """Clone all of the openmc.Universe object's attributes except for its cells, + as they are copied within the clone function. This should only to be + used within the openmc.UniverseBase.clone() context. """ - id = int(group.name.split('/')[-1].lstrip('universe ')) - fname = group['filename'][()].decode() - name = group['name'][()].decode() if 'name' in group else None - - out = cls(fname, universe_id=id, name=name) - - out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) - out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) - - return out - - @classmethod - def from_xml_element(cls, elem): - """Generate DAGMC universe from XML element - - Parameters - ---------- - elem : xml.etree.ElementTree.Element - `` element - - Returns - ------- - openmc.DAGMCUniverse - DAGMCUniverse instance - - """ - id = int(get_text(elem, 'id')) - fname = get_text(elem, 'filename') - - out = cls(fname, universe_id=id) - - name = get_text(elem, 'name') - if name is not None: - out.name = name - - out.auto_geom_ids = bool(elem.get('auto_geom_ids')) - out.auto_mat_ids = bool(elem.get('auto_mat_ids')) - - return out + clone = openmc.Universe(name=self.name) + clone.volume = self.volume + return clone diff --git a/openmc/utility_funcs.py b/openmc/utility_funcs.py new file mode 100644 index 0000000000..da9f73b165 --- /dev/null +++ b/openmc/utility_funcs.py @@ -0,0 +1,59 @@ +from contextlib import contextmanager +import os +from pathlib import Path +from tempfile import TemporaryDirectory + +import openmc +from .checkvalue import PathLike + + +@contextmanager +def change_directory(working_dir: PathLike | None = None, *, tmpdir: bool = False): + """Context manager for executing in a provided working directory + + Parameters + ---------- + working_dir : path-like + Directory to switch to. + tmpdir : bool + Whether to use a temporary directory instead of a specific working directory + + """ + orig_dir = Path.cwd() + + # Set up temporary directory if requested + if tmpdir: + tmp = TemporaryDirectory() + working_dir = tmp.name + elif working_dir is None: + raise ValueError('Must pass working_dir argument or specify tmpdir=True.') + + working_dir = Path(working_dir) + working_dir.mkdir(parents=True, exist_ok=True) + os.chdir(working_dir) + try: + yield + finally: + os.chdir(orig_dir) + if tmpdir: + tmp.cleanup() + + +def input_path(filename: PathLike) -> Path: + """Return a path object for an input file based on global configuration + + Parameters + ---------- + filename : PathLike + Path to input file + + Returns + ------- + pathlib.Path + Path object + + """ + if openmc.config['resolve_paths']: + return Path(filename).resolve() + else: + return Path(filename) diff --git a/openmc/volume.py b/openmc/volume.py index 5411b3c1d6..c44adf98a5 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -1,17 +1,16 @@ -from collections import OrderedDict from collections.abc import Iterable, Mapping from numbers import Real, Integral -from xml.etree import ElementTree as ET import warnings +import h5py +import lxml.etree as ET import numpy as np import pandas as pd -import h5py from uncertainties import ufloat import openmc import openmc.checkvalue as cv -from openmc._xml import get_text +from openmc._xml import get_elem_list, get_text _VERSION_VOLUME = 1 @@ -122,34 +121,83 @@ class VolumeCalculation: raise ValueError('Could not automatically determine bounding box ' 'for stochastic volume calculation.') + if np.isinf(self.lower_left).any() or np.isinf(self.upper_right).any(): + raise ValueError('Lower-left and upper-right bounding box ' + 'coordinates must be finite.') + @property def ids(self): return self._ids + @ids.setter + def ids(self, ids): + cv.check_type('domain IDs', ids, Iterable, Real) + self._ids = ids + @property def samples(self): return self._samples + @samples.setter + def samples(self, samples): + cv.check_type('number of samples', samples, Integral) + cv.check_greater_than('number of samples', samples, 0) + self._samples = samples + @property def lower_left(self): return self._lower_left + @lower_left.setter + def lower_left(self, lower_left): + name = 'lower-left bounding box coordinates', + cv.check_type(name, lower_left, Iterable, Real) + cv.check_length(name, lower_left, 3) + self._lower_left = lower_left + @property def upper_right(self): return self._upper_right + @upper_right.setter + def upper_right(self, upper_right): + name = 'upper-right bounding box coordinates' + cv.check_type(name, upper_right, Iterable, Real) + cv.check_length(name, upper_right, 3) + self._upper_right = upper_right + @property def threshold(self): return self._threshold + @threshold.setter + def threshold(self, threshold): + name = 'volume std. dev. threshold' + cv.check_type(name, threshold, Real) + cv.check_greater_than(name, threshold, 0.0) + self._threshold = threshold + @property def trigger_type(self): return self._trigger_type + @trigger_type.setter + def trigger_type(self, trigger_type): + cv.check_value('tally trigger type', trigger_type, + ('variance', 'std_dev', 'rel_err')) + self._trigger_type = trigger_type + @property def iterations(self): return self._iterations + @iterations.setter + def iterations(self, iterations): + name = 'volume calculation iterations' + cv.check_type(name, iterations, Integral) + cv.check_greater_than(name, iterations, 0) + self._iterations = iterations + @property def domain_type(self): return self._domain_type @@ -158,10 +206,20 @@ class VolumeCalculation: def atoms(self): return self._atoms + @atoms.setter + def atoms(self, atoms): + cv.check_type('atoms', atoms, Mapping) + self._atoms = atoms + @property def volumes(self): return self._volumes + @volumes.setter + def volumes(self, volumes): + cv.check_type('volumes', volumes, Mapping) + self._volumes = volumes + @property def atoms_dataframe(self): items = [] @@ -172,61 +230,6 @@ class VolumeCalculation: return pd.DataFrame.from_records(items, columns=columns) - @ids.setter - def ids(self, ids): - cv.check_type('domain IDs', ids, Iterable, Real) - self._ids = ids - - @samples.setter - def samples(self, samples): - cv.check_type('number of samples', samples, Integral) - cv.check_greater_than('number of samples', samples, 0) - self._samples = samples - - @lower_left.setter - def lower_left(self, lower_left): - name = 'lower-left bounding box coordinates', - cv.check_type(name, lower_left, Iterable, Real) - cv.check_length(name, lower_left, 3) - self._lower_left = lower_left - - @upper_right.setter - def upper_right(self, upper_right): - name = 'upper-right bounding box coordinates' - cv.check_type(name, upper_right, Iterable, Real) - cv.check_length(name, upper_right, 3) - self._upper_right = upper_right - - @threshold.setter - def threshold(self, threshold): - name = 'volume std. dev. threshold' - cv.check_type(name, threshold, Real) - cv.check_greater_than(name, threshold, 0.0) - self._threshold = threshold - - @trigger_type.setter - def trigger_type(self, trigger_type): - cv.check_value('tally trigger type', trigger_type, - ('variance', 'std_dev', 'rel_err')) - self._trigger_type = trigger_type - - @iterations.setter - def iterations(self, iterations): - name = 'volume calculation iterations' - cv.check_type(name, iterations, Integral) - cv.check_greater_than(name, iterations, 0) - self._iterations = iterations - - @volumes.setter - def volumes(self, volumes): - cv.check_type('volumes', volumes, Mapping) - self._volumes = volumes - - @atoms.setter - def atoms(self, atoms): - cv.check_type('atoms', atoms, Mapping) - self._atoms = atoms - def set_trigger(self, threshold, trigger_type): """Set a trigger on the volume calculation @@ -281,7 +284,7 @@ class VolumeCalculation: volumes[domain_id] = volume nucnames = group['nuclides'][()] atoms_ = group['atoms'][()] - atom_dict = OrderedDict() + atom_dict = {} for name_i, atoms_i in zip(nucnames, atoms_): atom_dict[name_i.decode()] = ufloat(*atoms_i) atoms[domain_id] = atom_dict @@ -333,7 +336,7 @@ class VolumeCalculation: Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing volume calculation data """ @@ -362,7 +365,7 @@ class VolumeCalculation: Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element Returns @@ -372,13 +375,10 @@ class VolumeCalculation: """ domain_type = get_text(elem, "domain_type") - domain_ids = get_text(elem, "domain_ids").split() - ids = [int(x) for x in domain_ids] + ids = get_elem_list(elem, "domain_ids", int) samples = int(get_text(elem, "samples")) - lower_left = get_text(elem, "lower_left").split() - lower_left = tuple([float(x) for x in lower_left]) - upper_right = get_text(elem, "upper_right").split() - upper_right = tuple([float(x) for x in upper_right]) + lower_left = tuple(get_elem_list(elem, "lower_left", float)) + upper_right = tuple(get_elem_list(elem, "upper_right", float)) # Instantiate some throw-away domains that are used by the constructor # to assign IDs diff --git a/openmc/waste.py b/openmc/waste.py new file mode 100644 index 0000000000..80cfc0adcb --- /dev/null +++ b/openmc/waste.py @@ -0,0 +1,286 @@ +from __future__ import annotations + +import openmc +from openmc.data import half_life + + +def _waste_classification(mat: openmc.Material, metal: bool = True) -> str: + """Classify a material for near-surface waste disposal. + + This method determines a waste classification for a material based on the + NRC regulations (10 CFR 61.55). + + Parameters + ---------- + mat : openmc.Material + The material to classify. + metal : bool, optional + Whether or not the material is in metal form. This changes the + acceptable limits in Tables 1 and 2 for certain nuclides. + + Returns + ------- + str + The waste disposal classification, which can be "Class A", "Class B", + "Class C", or "GTCC" (greater than class C). + + """ + # Determine metrics based on Tables 1 and 2 using sum of fractions rule for + # mixture of radionuclides from §61.55(a)(7) + ratio1 = _waste_disposal_rating(mat, 'NRC_long', metal=metal) + ratio2 = [ + _waste_disposal_rating(mat, 'NRC_short_A', metal=metal), + _waste_disposal_rating(mat, 'NRC_short_B', metal=metal), + _waste_disposal_rating(mat, 'NRC_short_C', metal=metal), + ] + + # Determine which nuclides are present in Table 1 and Table 2 + table1_nuclides_present = (ratio1 > 0.0) + table2_nuclides_present = any(x > 0.0 for x in ratio2) + + # Helper function for classifying based on Table 2 + def classify_table2(col1, col2, col3): + if col1 < 1.0: + return "Class A" + elif col2 < 1.0: + return "Class B" + elif col3 < 1.0: + return "Class C" + else: + return "GTCC" + + if table1_nuclides_present and table2_nuclides_present: + # Classification based on §61.55(a)(5) + if ratio1 < 0.1: + return classify_table2(*ratio2) + elif ratio1 < 1.0: + return "Class C" if ratio2[2] < 1.0 else "GTCC" + else: + return "GTCC" + + elif table1_nuclides_present: + # Classification based on §61.55(a)(3) + if ratio1 < 0.1: + return "Class A" + elif ratio1 < 1.0: + return "Class C" + else: + return "GTCC" + + elif table2_nuclides_present: + # Classification based on §61.55(a)(4) + return classify_table2(*ratio2) + + else: + # Classification based on §61.55(a)(6) + return "Class A" + + +def _waste_disposal_rating( + mat: openmc.Material, + limits: str | dict[str, float] = 'Fetter', + metal: bool = False, + by_nuclide: bool = False, +) -> float | dict[str, float]: + """Return the waste disposal rating for a material. + + This method returns a waste disposal rating for the material based on a set + of specific activity limits. The waste disposal rating is a single number + that represents the sum of the ratios of the specific activity for each + radionuclide in the material against a nuclide-specific limit. A value less + than 1.0 indicates that the material "meets" the limits whereas a value + greater than 1.0 exceeds the limits. + + Parameters + ---------- + mat : openmc.Material + The material to classify. + limits : str or dict, optional + The name of a predefined set of specific activity limits or a dictionary + that contains specific activity limits for radionuclides, where keys are + nuclide names and values are activities in units of [Ci/m3]. The + predefined options are: + + - 'Fetter': Uses limits from Fetter et al. (1990) + - 'NRC_long': Uses the 10 CFR 61.55 limits for long-lived radionuclides + - 'NRC_short_A': Uses the 10 CFR 61.55 class A limits for short-lived + radionuclides + - 'NRC_short_B': Uses the 10 CFR 61.55 class B limits for short-lived + radionuclides + - 'NRC_short_C': Uses the 10 CFR 61.55 class C limits for short-lived + radionuclides + metal : bool, optional + Whether or not the material is in metal form (only applicable for NRC + based limits) + by_nuclide : bool, optional + Whether to return the waste disposal rating for each nuclide in the + material. If True, a dictionary is returned where the keys are the + nuclide names and the values are the waste disposal ratings for each + nuclide. If False, a single float value is returned that represents the + overall waste disposal rating for the material. + + Returns + ------- + float or dict + The waste disposal rating for the material or its constituent nuclides. + + """ + if limits == 'Fetter': + # Specific activity limits for radionuclides with half-lives between 5 + # years and 1e12 years from Table 2 in Fetter + limits = { + "Be10": 5.0e3, + "C14": 6.0e2, + "Al26": 9.0e-2, + "Si32": 6.0e2, + "Cl36": 1.0e1, + "Ar39": 2.0e4, + "Ar42": 2.0e4, + "K40": 2.0e0, + "Ca41": 1.0e4, + "Ti44": 2.0e2, + "Fe60": 1.0e-1, + "Co60": 3.0e8, + "Ni59": 9.0e2, + "Ni63": 7.0e5, + "Se79": 5.0e1, + "Kr81": 3.0e1, + "Sr90": 8.0e5, + "Nb91": 2.0e2, + "Nb92": 2.0e-1, + "Nb94": 2.0e-1, + "Mo93": 4.0e3, + "Tc97": 4.0e-1, + "Tc98": 1.0e-2, + "Tc99": 6.0e-2, + "Pd107": 9.0e2, + "Ag108_m1": 3.0e0, + "Sn121_m1": 7.0e5, + "Sn126": 1.0e-1, + "I129": 2.0e0, + "Cs137": 5.0e4, + "Ba133": 2.0e8, + "La137": 2.0e2, + "Sm151": 5.0e7, + "Eu150_m1": 3.0e3, + "Eu152": 3.0e5, + "Eu154": 5.0e6, + "Gd148": 2.0e5, + "Gd150": 2.0e3, + "Tb157": 5.0e3, + "Tb158": 4.0e0, + "Dy154": 1.0e3, + "Ho166_m1": 2.0e-1, + "Hf178_m1": 9.0e3, + "Hf182": 2.0e-1, + "Re186_m1": 2.0e1, + "Ir192_m1": 1.0e0, + "Pt193": 2.0e8, + "Hg194": 5.0e-1, + "Pb202": 6.0e-1, + "Pb210": 3.0e7, + "Bi207": 9.0e3, + "Bi208": 8.0e-2, + "Bi210_m1": 1.0e0, + "Po209": 3.0e3, + "Ra226": 1.0e-1, + "Ra228": 3.0e7, + "Ac227": 5.0e5, + "Th229": 2.0e0, + "Th230": 3.0e-1, + "Th232": 1.0e-1, + "Pa231": 7.0e-1, + "U232": 3.0e1, + "U233": 2.0e1, + "U234": 9.0e1, + "U235": 2.0e0, + "Np236": 1.0e0, + "Np237": 1.0e0, + "Pu238": 7.0e4, + "Pu239": 1.0e3, + "Pu240": 1.0e3, + "Pu241": 2.0e3, + "Pu242": 1.0e3, + "Pu244": 9.0e-1, + "Am241": 5.0e1, + "Am242_m1": 3.0e2, + "Am243": 2.0e0, + "Cm243": 6.0e2, + "Cm244": 5.0e5, + "Cm245": 5.0e0, + "Cm246": 8.0e2, + "Cm248": 8.0e2, + } + + elif limits == 'NRC_long': + # Specific activity limits for long-lived radionuclides from Table 1 in + # 10 CFR 61.55 in Ci/m3. + limits = { + 'C14': 8.0, + 'Tc99': 3.0, + 'I129': 0.08, + } + if metal: + limits['C14'] = 80.0 + limits['Ni59'] = 220.0 + limits['Nb94'] = 0.2 + + # Convert values in nCi/g to Ci/m3 + factor = (1e6 * mat.get_mass_density()) / 1e9 + limits.update({ + 'Pu241': 3500.0 * factor, + 'Cm242': 20000.0 * factor, + 'Np237': 100.0 * factor, + 'Pu238': 100.0 * factor, + 'Pu239': 100.0 * factor, + 'Pu240': 100.0 * factor, + 'Pu242': 100.0 * factor, + 'Pu244': 100.0 * factor, + 'Am241': 100.0 * factor, + 'Am243': 100.0 * factor, + 'Cm243': 100.0 * factor, + 'Cm244': 100.0 * factor, + 'Cm245': 100.0 * factor, + 'Cm246': 100.0 * factor, + 'Cm247': 100.0 * factor, + 'Cm248': 100.0 * factor, + 'Bk247': 100.0 * factor, + 'Cf249': 100.0 * factor, + 'Cf250': 100.0 * factor, + 'Cf251': 100.0 * factor, + }) + + elif limits == 'NRC_short_A': + # Get Class A specific activity limits for short-lived radionuclides + # from Table 2 in 10 CFR 61.55 + limits = { + 'H3': 40.0, + 'Co60': 700.0, + 'Ni63': 35.0 if metal else 3.5, + 'Sr90': 0.04, + 'Cs137': 1.0 + } + + # Add radionuclides with half-lives < 5 years to limits for class A + five_years = 60.0 * 60.0 * 24.0 * 365.25 * 5.0 + for nuc in mat.get_nuclides(): + if half_life(nuc) is not None and half_life(nuc) < five_years: + limits[nuc] = 700.0 + + elif limits == 'NRC_short_B': + # Get Class B specific activity limits for short-lived radionuclides + # from Table 2 in 10 CFR 61.55 + limits = {'Ni63': 700.0 if metal else 70.0, 'Sr90': 150.0, 'Cs137': 44.0} + + elif limits == 'NRC_short_C': + # Get Class C specific activity limits for short-lived radionuclides + # from Table 2 in 10 CFR 61.55 + limits = {'Ni63': 7000.0 if metal else 700.0, 'Sr90': 7000.0, 'Cs137': 4600.0} + + # Calculate the sum of the fractions of the activity of each radionuclide + # compared to the specified limits + ratio = {} + for nuc, ci_m3 in mat.get_activity(units="Ci/m3", by_nuclide=True).items(): + if nuc in limits: + ratio[nuc] = ci_m3 / limits[nuc] + return ratio if by_nuclide else sum(ratio.values()) diff --git a/openmc/weight_windows.py b/openmc/weight_windows.py index e07e8a84f8..5d52a579a1 100644 --- a/openmc/weight_windows.py +++ b/openmc/weight_windows.py @@ -1,15 +1,22 @@ -from collections.abc import Iterable +from __future__ import annotations from numbers import Real, Integral +from collections.abc import Iterable, Sequence +from pathlib import Path +from typing import Self +import warnings -from xml.etree import ElementTree as ET +import lxml.etree as ET import numpy as np +import h5py +import openmc from openmc.filter import _PARTICLES -from openmc.mesh import MeshBase +from openmc.mesh import MeshBase, RectilinearMesh, CylindricalMesh, SphericalMesh, UnstructuredMesh import openmc.checkvalue as cv - -from ._xml import get_text +from openmc.checkvalue import PathLike +from ._xml import get_elem_list, get_text, clean_indentation from .mixin import IDManagerMixin +from .utility_funcs import change_directory class WeightWindows(IDManagerMixin): @@ -40,9 +47,10 @@ class WeightWindows(IDManagerMixin): window upper_bound_ratio : float Ratio of the lower to upper weight window bounds - energy_bins : Iterable of Real + energy_bounds : Iterable of Real A list of values for which each successive pair constitutes a range of - energies in [eV] for a single bin + energies in [eV] for a single bin. If no energy bins are provided, the + maximum and minimum energy for the data available at runtime. particle_type : {'neutron', 'photon'} Particle type the weight windows apply to survival_ratio : float @@ -50,38 +58,42 @@ class WeightWindows(IDManagerMixin): rouletting max_lower_bound_ratio : float Maximum allowed ratio of a particle's weight to the weight window's - lower bound. A factor will be applied to raise the weight window to be lower - than the particle's weight by a factor of max_lower_bound_ratio + lower bound. A factor will be applied to raise the weight window to be + lower than the particle's weight by a factor of max_lower_bound_ratio during transport if exceeded. max_split : int Maximum allowable number of particles when splitting weight_cutoff : float Threshold below which particles will be terminated id : int - Unique identifier for the weight window settings. If not - specified an identifier will automatically be assigned. + Unique identifier for the weight window settings. If not specified, an + identifier will automatically be assigned. Attributes ---------- id : int Unique identifier for the weight window settings. mesh : openmc.MeshBase - Mesh for the weight windows + Mesh for the weight windows with dimension (ni, nj, nk) particle_type : str Particle type the weight windows apply to - energy_bins : Iterable of Real + energy_bounds : Iterable of Real A list of values for which each successive pair constitutes a range of energies in [eV] for a single bin - lower_ww_bounds : Iterable of Real - A list of values for which each value is the lower bound of a weight - window - upper_ww_bounds : Iterable of Real - A list of values for which each value is the upper bound of a weight - window + num_energy_bins : int + Number of energy bins + lower_ww_bounds : numpy.ndarray of float + An array of values for which each value is the lower bound of a weight + window. Shape: (ni, nj, nk, num_energy_bins) for StructuredMesh; + (num_elements, num_energy_bins) for UnstructuredMesh + upper_ww_bounds : numpy.ndarray of float + An array of values for which each value is the upper bound of a weight + window. Shape: (ni, nj, nk, num_energy_bins) for StructuredMesh; + (num_elements, num_energy_bins) for UnstructuredMesh survival_ratio : float Ratio of the survival weight to the lower weight window bound for rouletting - max_lower_bound_ratio: float + max_lower_bound_ratio : float Maximum allowed ratio of a particle's weight to the weight window's lower bound. (Default: 1.0) max_split : int @@ -97,18 +109,28 @@ class WeightWindows(IDManagerMixin): next_id = 1 used_ids = set() - def __init__(self, mesh, lower_ww_bounds, upper_ww_bounds=None, - upper_bound_ratio=None, energy_bins=None, particle_type='neutron', - survival_ratio=3, max_lower_bound_ratio=None, max_split=10, - weight_cutoff=1.e-38, id=None): + def __init__( + self, + mesh: MeshBase, + lower_ww_bounds: Iterable[float], + upper_ww_bounds: Iterable[float] | None = None, + upper_bound_ratio: float | None = None, + energy_bounds: Iterable[Real] | None = None, + particle_type: str = 'neutron', + survival_ratio: float = 3.0, + max_lower_bound_ratio: float | None = None, + max_split: int = 10, + weight_cutoff: float = 1.e-38, + id: int | None = None + ): self.mesh = mesh self.id = id self.particle_type = particle_type - self.energy_bins = energy_bins + self._energy_bounds = None + if energy_bounds is not None: + self.energy_bounds = energy_bounds self.lower_ww_bounds = lower_ww_bounds - cv.check_length('Lower window bounds', self.lower_ww_bounds, len(self.energy_bins)) - if upper_ww_bounds is not None and upper_bound_ratio: raise ValueError("Exactly one of upper_ww_bounds and " "upper_bound_ratio must be present.") @@ -126,8 +148,8 @@ class WeightWindows(IDManagerMixin): self.upper_ww_bounds = upper_ww_bounds if len(self.lower_ww_bounds) != len(self.upper_ww_bounds): - raise ValueError('Size of the lower and upper weight window bounds ' - 'do not match') + raise ValueError('Size of the lower and upper weight ' + 'window bounds do not match') self.survival_ratio = survival_ratio @@ -138,12 +160,13 @@ class WeightWindows(IDManagerMixin): self.max_split = max_split self.weight_cutoff = weight_cutoff - def __repr__(self): + def __repr__(self) -> str: string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) - string += '{: <16}=\t{}\n'.format('\tMesh:', self.mesh) + string += '{: <16}=\t{}\n'.format('\tMesh', self.mesh) string += '{: <16}=\t{}\n'.format('\tParticle Type', self._particle_type) - string += '{: <16}=\t{}\n'.format('\tEnergy Bins', self._energy_bins) + string += '{: <16}=\t{}\n'.format('\tEnergy Bounds', self._energy_bounds) + string += '{: <16}=\t{}\n'.format('\tMax lower bound ratio', self.max_lower_bound_ratio) string += '{: <16}=\t{}\n'.format('\tLower WW Bounds', self._lower_ww_bounds) string += '{: <16}=\t{}\n'.format('\tUpper WW Bounds', self._upper_ww_bounds) string += '{: <16}=\t{}\n'.format('\tSurvival Ratio', self._survival_ratio) @@ -151,96 +174,151 @@ class WeightWindows(IDManagerMixin): string += '{: <16}=\t{}\n'.format('\tWeight Cutoff', self._weight_cutoff) return string + def __eq__(self, other: WeightWindows) -> bool: + # ensure that `other` is a WeightWindows object + if not isinstance(other, WeightWindows): + return False + + # TODO: add ability to check mesh equality + + # check several attributes directly + attrs = ('particle_type', + 'survival_ratio', + 'max_lower_bound_ratio', + 'max_split', + 'weight_cutoff') + for attr in attrs: + if getattr(self, attr) != getattr(other, attr): + return False + + # save most expensive checks for last + if not np.array_equal(self.energy_bounds, other.energy_bounds): + return False + + if not np.array_equal(self.lower_ww_bounds, other.lower_ww_bounds): + return False + + if not np.array_equal(self.upper_ww_bounds, other.upper_ww_bounds): + return False + + return True + @property - def mesh(self): + def mesh(self) -> MeshBase: return self._mesh @mesh.setter - def mesh(self, mesh): + def mesh(self, mesh: MeshBase): cv.check_type('Weight window mesh', mesh, MeshBase) self._mesh = mesh @property - def particle_type(self): + def particle_type(self) -> str: return self._particle_type @particle_type.setter - def particle_type(self, pt): + def particle_type(self, pt: str): cv.check_value('Particle type', pt, _PARTICLES) self._particle_type = pt @property - def energy_bins(self): - return self._energy_bins + def energy_bounds(self) -> Iterable[Real]: + return self._energy_bounds - @energy_bins.setter - def energy_bins(self, bins): - cv.check_type('Energy bins', bins, Iterable, Real) - self._energy_bins = np.array(bins) + @energy_bounds.setter + def energy_bounds(self, bounds: Iterable[float]): + cv.check_type('Energy bounds', bounds, Iterable, Real) + self._energy_bounds = np.asarray(bounds) @property - def lower_ww_bounds(self): + def num_energy_bins(self) -> int: + if self.energy_bounds is None: + return 1 + return self.energy_bounds.size - 1 + + @property + def lower_ww_bounds(self) -> np.ndarray: return self._lower_ww_bounds @lower_ww_bounds.setter - def lower_ww_bounds(self, bounds): - cv.check_type('Lower WW bounds', bounds, Iterable, Real) - self._lower_ww_bounds = np.array(bounds) + def lower_ww_bounds(self, bounds: Iterable[float]): + cv.check_iterable_type('Lower WW bounds', + bounds, + Real, + min_depth=1, + max_depth=4) + # reshape data according to mesh and energy bins + bounds = np.asarray(bounds) + if isinstance(self.mesh, UnstructuredMesh): + bounds = bounds.reshape(-1, self.num_energy_bins) + else: + bounds = bounds.reshape(*self.mesh.dimension, self.num_energy_bins) + self._lower_ww_bounds = bounds @property - def upper_ww_bounds(self): + def upper_ww_bounds(self) -> np.ndarray: return self._upper_ww_bounds @upper_ww_bounds.setter - def upper_ww_bounds(self, bounds): - cv.check_type('Upper WW bounds', bounds, Iterable, Real) - self._upper_ww_bounds = np.array(bounds) + def upper_ww_bounds(self, bounds: Iterable[float]): + cv.check_iterable_type('Upper WW bounds', + bounds, + Real, + min_depth=1, + max_depth=4) + # reshape data according to mesh and energy bins + bounds = np.asarray(bounds) + if isinstance(self.mesh, UnstructuredMesh): + bounds = bounds.reshape(-1, self.num_energy_bins) + else: + bounds = bounds.reshape(*self.mesh.dimension, self.num_energy_bins) + self._upper_ww_bounds = bounds @property - def survival_ratio(self): + def survival_ratio(self) -> float: return self._survival_ratio @survival_ratio.setter - def survival_ratio(self, val): + def survival_ratio(self, val: float): cv.check_type('Survival ratio', val, Real) cv.check_greater_than('Survival ratio', val, 1.0, True) self._survival_ratio = val @property - def max_lower_bound_ratio(self): + def max_lower_bound_ratio(self) -> float: return self._max_lower_bound_ratio @max_lower_bound_ratio.setter - def max_lower_bound_ratio(self, val): + def max_lower_bound_ratio(self, val: float): cv.check_type('Maximum lower bound ratio', val, Real) - cv.check_greater_than('Maximum lower bound ratio', val, 1.0) + cv.check_greater_than('Maximum lower bound ratio', val, 1.0, equality=True) self._max_lower_bound_ratio = val @property - def max_split(self): + def max_split(self) -> int: return self._max_split @max_split.setter - def max_split(self, val): + def max_split(self, val: int): cv.check_type('Max split', val, Integral) self._max_split = val @property - def weight_cutoff(self): + def weight_cutoff(self) -> float: return self._weight_cutoff @weight_cutoff.setter - def weight_cutoff(self, cutoff): + def weight_cutoff(self, cutoff: float): cv.check_type('Weight cutoff', cutoff, Real) cv.check_greater_than('Weight cutoff', cutoff, 0.0, True) self._weight_cutoff = cutoff - def to_xml_element(self): + def to_xml_element(self) -> ET.Element: """Return an XML representation of the weight window settings Returns ------- - element : xml.etree.ElementTree.Element + element : lxml.etree._Element XML element containing the weight window information """ element = ET.Element('weight_windows') @@ -253,14 +331,15 @@ class WeightWindows(IDManagerMixin): subelement = ET.SubElement(element, 'particle_type') subelement.text = self.particle_type - subelement = ET.SubElement(element, 'energy_bins') - subelement.text = ' '.join(str(e) for e in self.energy_bins) + if self.energy_bounds is not None: + subelement = ET.SubElement(element, 'energy_bounds') + subelement.text = ' '.join(str(e) for e in self.energy_bounds) subelement = ET.SubElement(element, 'lower_ww_bounds') - subelement.text = ' '.join(str(b) for b in self.lower_ww_bounds) + subelement.text = ' '.join(str(b) for b in self.lower_ww_bounds.ravel('F')) subelement = ET.SubElement(element, 'upper_ww_bounds') - subelement.text = ' '.join(str(b) for b in self.upper_ww_bounds) + subelement.text = ' '.join(str(b) for b in self.upper_ww_bounds.ravel('F')) subelement = ET.SubElement(element, 'survival_ratio') subelement.text = str(self.survival_ratio) @@ -278,15 +357,15 @@ class WeightWindows(IDManagerMixin): return element @classmethod - def from_xml_element(cls, elem, root): + def from_xml_element(cls, elem: ET.Element, meshes: dict[int, MeshBase]) -> Self: """Generate weight window settings from an XML element Parameters ---------- - elem : xml.etree.ElementTree.Element + elem : lxml.etree._Element XML element - root : xml.etree.ElementTree.Element - Root element for the file where meshes can be found + meshes : dict + Dictionary mapping IDs to mesh objects Returns ------- @@ -295,18 +374,21 @@ class WeightWindows(IDManagerMixin): """ # Get mesh for weight windows mesh_id = int(get_text(elem, 'mesh')) - path = f"./mesh[@id='{mesh_id}']" - mesh_elem = root.find(path) - if mesh_elem is not None: - mesh = MeshBase.from_xml_element(mesh_elem) + if mesh_id not in meshes: + raise ValueError(f'Could not locate mesh with ID "{mesh_id}"') + mesh = meshes[mesh_id] # Read all other parameters - lower_ww_bounds = [float(l) for l in get_text(elem, 'lower_ww_bounds').split()] - upper_ww_bounds = [float(u) for u in get_text(elem, 'upper_ww_bounds').split()] - ebins = [float(b) for b in get_text(elem, 'energy_bins').split()] + lower_ww_bounds = get_elem_list(elem, "lower_ww_bounds", float) + upper_ww_bounds = get_elem_list(elem, "upper_ww_bounds", float) + e_bounds = get_elem_list(elem, "energy_bounds", float) particle_type = get_text(elem, 'particle_type') survival_ratio = float(get_text(elem, 'survival_ratio')) + ww_shape = (len(e_bounds) - 1,) + mesh.dimension[::-1] + lower_ww_bounds = np.array(lower_ww_bounds).reshape(ww_shape).T + upper_ww_bounds = np.array(upper_ww_bounds).reshape(ww_shape).T + max_lower_bound_ratio = None if get_text(elem, 'max_lower_bound_ratio'): max_lower_bound_ratio = float(get_text(elem, 'max_lower_bound_ratio')) @@ -319,7 +401,7 @@ class WeightWindows(IDManagerMixin): mesh=mesh, lower_ww_bounds=lower_ww_bounds, upper_ww_bounds=upper_ww_bounds, - energy_bins=ebins, + energy_bounds=e_bounds, particle_type=particle_type, survival_ratio=survival_ratio, max_lower_bound_ratio=max_lower_bound_ratio, @@ -329,7 +411,7 @@ class WeightWindows(IDManagerMixin): ) @classmethod - def from_hdf5(cls, group, meshes): + def from_hdf5(cls, group: h5py.Group, meshes: dict[int, MeshBase]) -> Self: """Create weight windows from HDF5 group Parameters @@ -347,10 +429,15 @@ class WeightWindows(IDManagerMixin): id = int(group.name.split('/')[-1].lstrip('weight_windows')) mesh_id = group['mesh'][()] + mesh = meshes[mesh_id] + ptype = group['particle_type'][()].decode() - ebins = group['energy_bins'][()] - lower_ww_bounds = group['lower_ww_bounds'][()] - upper_ww_bounds = group['upper_ww_bounds'][()] + e_bounds = group['energy_bounds'][()] + # weight window bounds are stored with the shape (e, k, j, i) + # in C++ and HDF5 -- the opposite of how they are stored here + shape = (e_bounds.size - 1, *mesh.dimension[::-1]) + lower_ww_bounds = group['lower_ww_bounds'][()].reshape(shape).T + upper_ww_bounds = group['upper_ww_bounds'][()].reshape(shape).T survival_ratio = group['survival_ratio'][()] max_lower_bound_ratio = None @@ -361,10 +448,10 @@ class WeightWindows(IDManagerMixin): weight_cutoff = group['weight_cutoff'][()] return cls( - mesh=meshes[mesh_id], + mesh=mesh, lower_ww_bounds=lower_ww_bounds, upper_ww_bounds=upper_ww_bounds, - energy_bins=ebins, + energy_bounds=e_bounds, particle_type=ptype, survival_ratio=survival_ratio, max_lower_bound_ratio=max_lower_bound_ratio, @@ -372,3 +459,585 @@ class WeightWindows(IDManagerMixin): weight_cutoff=weight_cutoff, id=id ) + + +def wwinp_to_wws(path: PathLike) -> WeightWindowsList: + """Create WeightWindows instances from a wwinp file + + .. versionadded:: 0.13.1 + + Parameters + ---------- + path : str or pathlib.Path + Path to the wwinp file + + Returns + ------- + WeightWindowsList + """ + warnings.warn( + "This function is deprecated in favor of 'WeightWindowsList.from_wwinp'", + FutureWarning + ) + return WeightWindowsList.from_wwinp(path) + + +class WeightWindowGenerator: + """Class passed to setting to govern weight window generation + using the OpenMC executable + + Parameters + ---------- + mesh : :class:`openmc.MeshBase` + Mesh used to represent the weight windows spatially + energy_bounds : Iterable of Real + A list of values for which each successive pair constitutes a range of + energies in [eV] for a single bin. If no energy bins are provided, the + maximum and minimum energy for the data available at runtime. + particle_type : {'neutron', 'photon'} + Particle type the weight windows apply to + method : {'magic', 'fw_cadis'} + The weight window generation methodology applied during an update. + max_realizations : int + The upper limit for number of tally realizations when generating weight + windows. + update_interval : int + The number of tally realizations between updates. + on_the_fly : bool + Whether or not to apply weight windows on the fly. + + Attributes + ---------- + mesh : openmc.MeshBase + Mesh used to represent the weight windows spatially + energy_bounds : Iterable of Real + A list of values for which each successive pair constitutes a range of + energies in [eV] for a single bin + particle_type : {'neutron', 'photon'} + Particle type the weight windows apply to + method : {'magic', 'fw_cadis'} + The weight window generation methodology applied during an update. + max_realizations : int + The upper limit for number of tally realizations when generating weight + windows. + update_interval : int + The number of tally realizations between updates. + update_parameters : dict + A set of parameters related to the update. + on_the_fly : bool + Whether or not to apply weight windows on the fly. + """ + + _MAGIC_PARAMS = {'value': str, 'threshold': float, 'ratio': float} + + def __init__( + self, + mesh: openmc.MeshBase, + energy_bounds: Sequence[float] | None = None, + particle_type: str = 'neutron', + method: str = 'magic', + max_realizations: int = 1, + update_interval: int = 1, + on_the_fly: bool = True + ): + self._update_parameters = None + + self.mesh = mesh + self._energy_bounds = None + if energy_bounds is not None: + self.energy_bounds = energy_bounds + self.particle_type = particle_type + self.method = method + self.max_realizations = max_realizations + self.update_interval = update_interval + self.on_the_fly = on_the_fly + + def __repr__(self): + string = type(self).__name__ + '\n' + string += f'\t{"Mesh":<20}=\t{self.mesh.id}\n' + string += f'\t{"Particle:":<20}=\t{self.particle_type}\n' + string += f'\t{"Energy Bounds:":<20}=\t{self.energy_bounds}\n' + string += f'\t{"Method":<20}=\t{self.method}\n' + string += f'\t{"Max Realizations:":<20}=\t{self.max_realizations}\n' + string += f'\t{"Update Interval:":<20}=\t{self.update_interval}\n' + string += f'\t{"On The Fly:":<20}=\t{self.on_the_fly}\n' + if self.update_parameters is not None: + string += f'\t{"Update Parameters:":<20}\n\t\t\t{self.update_parameters}\n' + string + + return string + + @property + def mesh(self) -> openmc.MeshBase: + return self._mesh + + @mesh.setter + def mesh(self, m: openmc.MeshBase): + cv.check_type('mesh', m, openmc.MeshBase) + self._mesh = m + + @property + def energy_bounds(self) -> Iterable[Real]: + return self._energy_bounds + + @energy_bounds.setter + def energy_bounds(self, eb: Iterable[float]): + cv.check_type('energy bounds', eb, Iterable, Real) + self._energy_bounds = eb + + @property + def particle_type(self) -> str: + return self._particle_type + + @particle_type.setter + def particle_type(self, pt: str): + cv.check_value('particle type', pt, ('neutron', 'photon')) + self._particle_type = pt + + @property + def method(self) -> str: + return self._method + + @method.setter + def method(self, m: str): + cv.check_type('generation method', m, str) + cv.check_value('generation method', m, ('magic', 'fw_cadis')) + self._method = m + if self._update_parameters is not None: + try: + self._check_update_parameters() + except (TypeError, KeyError): + warnings.warn(f'Update parameters are invalid for the "{m}" method.') + + @property + def max_realizations(self) -> int: + return self._max_realizations + + @max_realizations.setter + def max_realizations(self, m: int): + cv.check_type('max tally realizations', m, Integral) + cv.check_greater_than('max tally realizations', m, 0) + self._max_realizations = m + + @property + def update_interval(self) -> int: + return self._update_interval + + @update_interval.setter + def update_interval(self, ui: int): + cv.check_type('update interval', ui, Integral) + cv.check_greater_than('update interval', ui , 0) + self._update_interval = ui + + @property + def update_parameters(self) -> dict: + return self._update_parameters + + def _check_update_parameters(self, params: dict): + if self.method == 'magic' or self.method == 'fw_cadis': + check_params = self._MAGIC_PARAMS + + for key, val in params.items(): + if key not in check_params: + raise ValueError(f'Invalid param "{key}" for {self.method} ' + 'weight window generation') + cv.check_type(f'weight window generation param: "{key}"', val, self._MAGIC_PARAMS[key]) + + @update_parameters.setter + def update_parameters(self, params: dict): + self._check_update_parameters(params) + self._update_parameters = params + + @property + def on_the_fly(self) -> bool: + return self._on_the_fly + + @on_the_fly.setter + def on_the_fly(self, otf: bool): + cv.check_type('on the fly generation', otf, bool) + self._on_the_fly = otf + + def _update_parameters_subelement(self, element: ET.Element): + if not self.update_parameters: + return + params_element = ET.SubElement(element, 'update_parameters') + for pname, value in self.update_parameters.items(): + param_element = ET.SubElement(params_element, pname) + param_element.text = str(value) + + @classmethod + def _sanitize_update_parameters(cls, method: str, update_parameters: dict): + """ + Attempt to convert update parameters to their appropriate types + + Parameters + ---------- + method : str + The update method for which these update parameters should comply + update_parameters : dict + The update parameters as-read from the XML node (keys: str, values: str) + """ + if method == 'magic' or method == 'fw_cadis': + check_params = cls._MAGIC_PARAMS + + for param, param_type in check_params.items(): + if param in update_parameters: + update_parameters[param] = param_type(update_parameters[param]) + + def to_xml_element(self): + """Creates a 'weight_window_generator' element to be written to an XML file. + """ + element = ET.Element('weight_windows_generator') + + mesh_elem = ET.SubElement(element, 'mesh') + mesh_elem.text = str(self.mesh.id) + if self.energy_bounds is not None: + subelement = ET.SubElement(element, 'energy_bounds') + subelement.text = ' '.join(str(e) for e in self.energy_bounds) + particle_elem = ET.SubElement(element, 'particle_type') + particle_elem.text = self.particle_type + realizations_elem = ET.SubElement(element, 'max_realizations') + realizations_elem.text = str(self.max_realizations) + update_interval_elem = ET.SubElement(element, 'update_interval') + update_interval_elem.text = str(self.update_interval) + otf_elem = ET.SubElement(element, 'on_the_fly') + otf_elem.text = str(self.on_the_fly).lower() + method_elem = ET.SubElement(element, 'method') + method_elem.text = self.method + if self.update_parameters is not None: + self._update_parameters_subelement(element) + + clean_indentation(element) + + return element + + @classmethod + def from_xml_element(cls, elem: ET.Element, meshes: dict) -> Self: + """ + Create a weight window generation object from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + meshes : dict + A dictionary with IDs as keys and openmc.MeshBase instances as values + + Returns + ------- + openmc.WeightWindowGenerator + """ + + mesh_id = int(get_text(elem, 'mesh')) + mesh = meshes[mesh_id] + + energy_bounds = get_elem_list(elem, "energy_bounds, float") + particle_type = get_text(elem, 'particle_type') + + wwg = cls(mesh, energy_bounds, particle_type) + + wwg.max_realizations = int(get_text(elem, 'max_realizations')) + wwg.update_interval = int(get_text(elem, 'update_interval')) + wwg.on_the_fly = bool(get_text(elem, 'on_the_fly')) + wwg.method = get_text(elem, 'method') + + if elem.find('update_parameters') is not None: + update_parameters = {} + params_elem = elem.find('update_parameters') + for entry in params_elem: + update_parameters[entry.tag] = entry.text + + cls._sanitize_update_parameters(wwg.method, update_parameters) + wwg.update_parameters = update_parameters + + return wwg + +def hdf5_to_wws(path='weight_windows.h5') -> WeightWindowsList: + """Create a WeightWindowsList from a weight windows HDF5 file + + .. versionadded:: 0.14.0 + + Parameters + ---------- + path : cv.PathLike + Path to the weight windows hdf5 file + + Returns + ------- + WeightWindowsList + """ + warnings.warn( + "This function is deprecated in favor of 'WeightWindowsList.from_hdf5'", + FutureWarning + ) + return WeightWindowsList.from_hdf5(path) + + +class WeightWindowsList(list): + """A list of WeightWindows objects. + + .. versionadded:: 0.15.3 + + Parameters + ---------- + iterable : iterable of openmc.WeightWindows + An iterable of WeightWindows objects to initialize the list with + + """ + def __init__(self, iterable: Iterable[WeightWindows] = ()): + super().__init__(iterable) + + @classmethod + def from_hdf5(cls, path: PathLike = 'weight_windows.h5') -> Self: + """Create WeightWindowsList from a weight windows HDF5 file. + + Parameters + ---------- + path : PathLike + Path to the weight windows hdf5 file + + Returns + ------- + WeightWindowsList + A list of WeightWindows objects read from the file + """ + + with h5py.File(path) as h5_file: + # read in all of the meshes in the mesh node + meshes = {} + for mesh_group in h5_file['meshes']: + mesh = MeshBase.from_hdf5(h5_file['meshes'][mesh_group]) + meshes[mesh.id] = mesh + wws = [ + WeightWindows.from_hdf5(ww, meshes) + for ww in h5_file['weight_windows'].values() + ] + + return cls(wws) + + @classmethod + def from_wwinp(cls, path: PathLike) -> Self: + """Create WeightWindowsList from a wwinp file. + + Parameters + ---------- + path : PathLike + Path to the wwinp file + + Returns + ------- + WeightWindowsList + A list of WeightWindows objects read from the file + """ + + with open(path) as wwinp: + # BLOCK 1 + header = wwinp.readline().split(None, 4) + # read file type, time-dependence, number of + # particles, mesh type and problem identifier + _if, iv, ni, nr = [int(x) for x in header[:4]] + + # header value checks + if _if != 1: + raise ValueError(f'Found incorrect file type, if: {_if}') + + if iv > 1: + # read number of time bins for each particle, 'nt(1...ni)' + nt = np.fromstring(wwinp.readline(), sep=' ', dtype=int) + + # raise error if time bins are present for now + raise ValueError('Time-dependent weight windows ' + 'are not yet supported') + else: + nt = ni * [1] + + # read number of energy bins for each particle, 'ne(1...ni)' + ne = np.fromstring(wwinp.readline(), sep=' ', dtype=int) + + # read coarse mesh dimensions and lower left corner + mesh_description = np.fromstring(wwinp.readline(), sep=' ') + nfx, nfy, nfz = mesh_description[:3].astype(int) + xyz0 = mesh_description[3:] + + # read cylindrical and spherical mesh vectors if present + if nr == 16: + # read number of coarse bins + line_arr = np.fromstring(wwinp.readline(), sep=' ') + ncx, ncy, ncz = line_arr[:3].astype(int) + # read polar vector (x1, y1, z1) + xyz1 = line_arr[3:] + # read azimuthal vector (x2, y2, z2) + line_arr = np.fromstring(wwinp.readline(), sep=' ') + xyz2 = line_arr[:3] + + # Get polar and azimuthal axes + polar_axis = xyz1 - xyz0 + azimuthal_axis = xyz2 - xyz0 + + # Check for polar axis other than (0, 0, 1) + norm = np.linalg.norm(polar_axis) + if not np.isclose(polar_axis[2]/norm, 1.0): + raise NotImplementedError('Polar axis not aligned to z-axis not supported') + + # Check for azimuthal axis other than (1, 0, 0) + norm = np.linalg.norm(azimuthal_axis) + if not np.isclose(azimuthal_axis[0]/norm, 1.0): + raise NotImplementedError('Azimuthal axis not aligned to x-axis not supported') + + # read geometry type + nwg = int(line_arr[-1]) + + elif nr == 10: + # read rectilinear data: + # number of coarse mesh bins and mesh type + ncx, ncy, ncz, nwg = \ + np.fromstring(wwinp.readline(), sep=' ').astype(int) + else: + raise RuntimeError(f'Invalid mesh description (nr) found: {nr}') + + # read BLOCK 2 and BLOCK 3 data into a single array + ww_data = np.fromstring(wwinp.read(), sep=' ') + + # extract mesh data from the ww_data array + start_idx = 0 + + # first values in the mesh definition arrays are the first + # coordinate of the grid + end_idx = start_idx + 1 + 3 * ncx + i0, i_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + start_idx = end_idx + + end_idx = start_idx + 1 + 3 * ncy + j0, j_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + start_idx = end_idx + + end_idx = start_idx + 1 + 3 * ncz + k0, k_vals = ww_data[start_idx], ww_data[start_idx+1:end_idx] + start_idx = end_idx + + # mesh consistency checks + if nr == 16 and nwg == 1 or nr == 10 and nwg != 1: + raise ValueError(f'Mesh description in header ({nr}) ' + f'does not match the mesh type ({nwg})') + + if nr == 10 and (xyz0 != (i0, j0, k0)).any(): + raise ValueError(f'Mesh origin in the header ({xyz0}) ' + f' does not match the origin in the mesh ' + f' description ({i0, j0, k0})') + + # create openmc mesh object + grids = [] + mesh_definition = [(i0, i_vals, nfx), (j0, j_vals, nfy), (k0, k_vals, nfz)] + for grid0, grid_vals, n_pnts in mesh_definition: + # file spec checks for the mesh definition + if (grid_vals[2::3] != 1.0).any(): + raise ValueError('One or more mesh ratio value, qx, ' + 'is not equal to one') + + s = int(grid_vals[::3].sum()) + if s != n_pnts: + raise ValueError(f'Sum of the fine bin entries, {s}, does ' + f'not match the number of fine bins, {n_pnts}') + + # extend the grid based on the next coarse bin endpoint, px + # and the number of fine bins in the coarse bin, sx + intervals = grid_vals.reshape(-1, 3) + coords = [grid0] + for sx, px, qx in intervals: + coords += np.linspace(coords[-1], px, int(sx + 1)).tolist()[1:] + + grids.append(np.array(coords)) + + if nwg == 1: + mesh = RectilinearMesh() + mesh.x_grid, mesh.y_grid, mesh.z_grid = grids + elif nwg == 2: + mesh = CylindricalMesh( + r_grid=grids[0], + z_grid=grids[1], + phi_grid=grids[2], + origin = xyz0, + ) + elif nwg == 3: + mesh = SphericalMesh( + r_grid=grids[0], + theta_grid=grids[1], + phi_grid=grids[2], + origin = xyz0 + ) + + # extract weight window values from array + wws = cls() + for ne_i, nt_i, particle_type in zip(ne, nt, ('neutron', 'photon')): + # no information to read for this particle if + # either the energy bins or time bins are empty + if ne_i == 0 or nt_i == 0: + continue + + if iv > 1: + # time bins are parsed but unused for now + end_idx = start_idx + nt_i + time_bounds = ww_data[start_idx:end_idx] + np.insert(time_bounds, (0,), (0.0,)) + start_idx = end_idx + + # read energy boundaries + end_idx = start_idx + ne_i + energy_bounds = np.insert(ww_data[start_idx:end_idx], (0,), (0.0,)) + # convert from MeV to eV + energy_bounds *= 1e6 + start_idx = end_idx + + # read weight window values + end_idx = start_idx + (nfx * nfy * nfz) * nt_i * ne_i + + # read values and reshape according to ordering + # slowest to fastest: t, e, z, y, x + # reorder with transpose since our ordering is x, y, z, e, t + ww_shape = (nt_i, ne_i, nfz, nfy, nfx) + ww_values = ww_data[start_idx:end_idx].reshape(ww_shape).T + # Only use first time bin since we don't support time dependent weight + # windows yet. + ww_values = ww_values[:, :, :, :, 0] + start_idx = end_idx + + # create a weight window object + ww = WeightWindows(id=None, + mesh=mesh, + lower_ww_bounds=ww_values, + upper_bound_ratio=5.0, + energy_bounds=energy_bounds, + particle_type=particle_type) + wws.append(ww) + + return wws + + def export_to_hdf5(self, path: PathLike = 'weight_windows.h5', **init_kwargs): + """Write weight windows to an HDF5 file. + + Parameters + ---------- + path : PathLike + Path to the file to write weight windows to + **init_kwargs + Keyword arguments passed to :func:`openmc.lib.init` + + """ + import openmc.lib + cv.check_type('path', path, PathLike) + + # Create a temporary model with the weight windows + model = openmc.Model() + sph = openmc.Sphere(boundary_type='vacuum') + cell = openmc.Cell(region=-sph) + model.geometry = openmc.Geometry([cell]) + model.settings.weight_windows = self + model.settings.particles = 100 + model.settings.batches = 1 + + # Get absolute path before moving to temporary directory + path = Path(path).resolve() + + # Load the model with openmc.lib and then export it to an HDF5 file + with openmc.lib.TemporarySession(model, **init_kwargs): + openmc.lib.export_weight_windows(path) diff --git a/pyproject.toml b/pyproject.toml index ac67355937..2d67e83401 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,77 @@ [build-system] -requires = ["setuptools", "wheel", "numpy<1.22", "cython"] +requires = ["setuptools", "setuptools-scm", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "openmc" +authors = [ + {name = "The OpenMC Development Team", email = "openmc@anl.gov"}, +] +description = "OpenMC" +dynamic = ["version"] +requires-python = ">=3.11" +license = {file = "LICENSE"} +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Topic :: Scientific/Engineering", + "Programming Language :: C++", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ + "numpy", + "h5py", + "scipy", + "ipython", + "matplotlib", + "pandas", + "lxml", + "uncertainties", + "setuptools", + "endf", +] + +[project.optional-dependencies] +depletion-mpi = ["mpi4py"] +docs = [ + "sphinx", + "sphinxcontrib-katex", + "sphinx-numfig", + "jupyter", + "sphinxcontrib-svg2pdfconverter", + "sphinx-rtd-theme" +] +test = [ + "packaging", + "pytest", + "pytest-cov>=4.0", + "pytest-rerunfailures", + "colorama", + "openpyxl", +] +ci = ["coverage>=7.4", "gcovr>=7.2"] +vtk = ["vtk"] + +[project.urls] +Homepage = "https://openmc.org" +Documentation = "https://docs.openmc.org" +Repository = "https://github.com/openmc-dev/openmc" +Issues = "https://github.com/openmc-dev/openmc/issues" + +[tool.setuptools.packages.find] +include = ['openmc*'] +exclude = ['tests*'] + +[tool.setuptools.package-data] +"openmc.data.effective_dose" = ["**/*.txt"] +"openmc.data" = ["*.txt", "*.DAT", "*.json", "*.h5"] +"openmc.lib" = ["libopenmc.dylib", "libopenmc.so"] + +[tool.setuptools_scm] diff --git a/schemas.xml b/schemas.xml deleted file mode 100644 index 3e586ec6ac..0000000000 --- a/schemas.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/scripts/openmc-ace-to-hdf5 b/scripts/openmc-ace-to-hdf5 deleted file mode 100755 index 7c391fd6fc..0000000000 --- a/scripts/openmc-ace-to-hdf5 +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python3 - -"""This script can be used to create HDF5 nuclear data libraries used by -OpenMC. There are four different ways you can specify ACE libraries that are to -be converted: - -1. List each ACE library as a positional argument. This is very useful in - conjunction with the usual shell utilities (ls, find, etc.). -2. Use the --xsdir option to specify a MCNP xsdir file. -3. Use the --xsdata option to specify a Serpent xsdata file. - -The script does not use any extra information from xsdir/xsdata files to -determine whether the nuclide is metastable. Instead, the --metastable argument -can be used to specify whether the ZAID naming convention follows the NNDC data -convention (1000*Z + A + 300 + 100*m), or the MCNP data convention (essentially -the same as NNDC, except that the first metastable state of Am242 is 95242 and -the ground state is 95642). - -""" - -import argparse -from functools import partial -import os -from pathlib import Path -import warnings - -import openmc.data -from openmc.data.ace import TableType - - -class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, - argparse.RawDescriptionHelpFormatter): - pass - - -parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=CustomFormatter -) -parser.add_argument('libraries', nargs='*', - help='ACE libraries to convert to HDF5') -parser.add_argument('-d', '--destination', type=Path, default=Path.cwd(), - help='Directory to create new library in') -parser.add_argument('-m', '--metastable', choices=['mcnp', 'nndc'], - default='nndc', - help='How to interpret ZAIDs for metastable nuclides') -parser.add_argument('--xsdir', help='MCNP xsdir file that lists ' - 'ACE libraries') -parser.add_argument('--xsdata', help='Serpent xsdata file that lists ' - 'ACE libraries') -parser.add_argument('--libver', choices=['earliest', 'latest'], - default='earliest', help="Output HDF5 versioning. Use " - "'earliest' for backwards compatibility or 'latest' for " - "performance") -args = parser.parse_args() - -if not args.destination.is_dir(): - args.destination.mkdir(parents=True, exist_ok=True) - -ace_libraries = [] -if args.xsdir is not None: - ace_libraries.extend(openmc.data.ace.get_libraries_from_xsdir(args.xsdir)) -elif args.xsdata is not None: - ace_libraries.extend(openmc.data.ace.get_libraries_from_xsdata(args.xsdata)) -else: - ace_libraries = [Path(lib) for lib in args.libraries] - -converted = {} -library = openmc.data.DataLibrary() - -for path in ace_libraries: - # Check that ACE library exists - if not os.path.exists(path): - warnings.warn("ACE library '{}' does not exist.".format(path)) - continue - - lib = openmc.data.ace.Library(path) - for table in lib.tables: - # Check type of the ACE table and determine appropriate class / - # conversion function - if table.data_type == TableType.NEUTRON_CONTINUOUS: - name = table.zaid - cls = openmc.data.IncidentNeutron - converter = partial(cls.from_ace, metastable_scheme=args.metastable) - elif table.data_type == TableType.THERMAL_SCATTERING: - # Adjust name to be the new thermal scattering name - name = openmc.data.get_thermal_name(table.zaid) - cls = openmc.data.ThermalScattering - converter = cls.from_ace - else: - print("Can't convert ACE table {}".format(table.name)) - continue - - if name not in converted: - try: - data = converter(table) - except Exception as e: - print('Failed to convert {}: {}'.format(table.name, e)) - continue - - print('Converting {} (ACE) to {} (HDF5)'.format(table.name, data.name)) - - # Determine output filename - outfile = args.destination / (data.name.replace('.', '_') + '.h5') - data.export_to_hdf5(outfile, 'w', libver=args.libver) - - # Register with library - library.register_file(outfile) - - # Add nuclide to list - converted[name] = outfile - else: - # Read existing HDF5 file - data = cls.from_hdf5(converted[name]) - - # Add data for new temperature - try: - print('Converting {} (ACE) to {} (HDF5)' - .format(table.name, data.name)) - data.add_temperature_from_ace(table, args.metastable) - except Exception as e: - print('Failed to convert {}: {}'.format(table.name, e)) - continue - - # Re-export - data.export_to_hdf5(converted[name], 'w', libver=args.libver) - -# Write cross_sections.xml -library.export_to_xml(args.destination / 'cross_sections.xml') diff --git a/scripts/openmc-make-test-data b/scripts/openmc-make-test-data deleted file mode 100755 index 4d26db7fb0..0000000000 --- a/scripts/openmc-make-test-data +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python3 - -""" -Download ENDF/B-VII.1 ENDF and ACE files from NNDC and WMP files from GitHub and -generate a full HDF5 library with incident neutron, incident photon, thermal -scattering data, and windowed multipole data. This data is used for OpenMC's -regression test suite. -""" - -import glob -import os -from pathlib import Path -import tarfile -import tempfile -from urllib.parse import urljoin -import zipfile - -import openmc.data -from openmc._utils import download - -base_ace = 'https://www.nndc.bnl.gov/endf/b7.1/aceFiles/' -base_endf = 'https://www.nndc.bnl.gov/endf/b7.1/zips/' -base_wmp = 'https://github.com/mit-crpg/WMP_Library/releases/download/v1.1/' -files = [ - (base_ace, 'ENDF-B-VII.1-neutron-293.6K.tar.gz', '9729a17eb62b75f285d8a7628ace1449'), - (base_ace, 'ENDF-B-VII.1-tsl.tar.gz', 'e17d827c92940a30f22f096d910ea186'), - (base_endf, 'ENDF-B-VII.1-neutrons.zip', 'e5d7f441fc4c92893322c24d1725e29c'), - (base_endf, 'ENDF-B-VII.1-photoat.zip', '5192f94e61f0b385cf536f448ffab4a4'), - (base_endf, 'ENDF-B-VII.1-atomic_relax.zip', 'fddb6035e7f2b6931e51a58fc754bd10'), - (base_wmp, 'WMP_Library_v1.1.tar.gz', '8523895928dd6ba63fba803e3a45d4f3') -] - - -def fix_zaid(table, old, new): - filename = os.path.join('tsl', table) - with open(filename, 'r') as fh: - text = fh.read() - text = text.replace(old, new, 1) - with open(filename, 'w') as fh: - fh.write(text) - -pwd = Path.cwd() -output_dir = pwd / 'nndc_hdf5' -os.makedirs('nndc_hdf5/photon', exist_ok=True) - -with tempfile.TemporaryDirectory() as tmpdir: - # Temporarily change dir - os.chdir(tmpdir) - - # ========================================================================= - # Download files from NNDC server - for base, fname, checksum in files: - download(urljoin(base, fname), checksum) - - # ========================================================================= - # EXTRACT FILES FROM TGZ - - for _, f, _ in files: - print('Extracting {}...'.format(f)) - path = Path(f) - if path.suffix == '.gz': - with tarfile.open(f, 'r') as tgz: - if 'tsl' in f: - tgz.extractall(path='tsl') - else: - tgz.extractall() - elif path.suffix == '.zip': - zipfile.ZipFile(f).extractall() - - # ========================================================================= - # FIX ZAID ASSIGNMENTS FOR VARIOUS S(A,B) TABLES - - print('Fixing ZAIDs for S(a,b) tables') - fix_zaid('bebeo.acer', '8016', ' 0') - fix_zaid('obeo.acer', '4009', ' 0') - - library = openmc.data.DataLibrary() - - # ========================================================================= - # INCIDENT NEUTRON DATA - - neutron_files = sorted(glob.glob('ENDF-B-VII.1-neutron-293.6K/*.ace')) - for f in neutron_files: - print('Converting {}...'.format(os.path.basename(f))) - data = openmc.data.IncidentNeutron.from_ace(f) - - # Check for fission energy release data on MF=1, MT=458 - endf_filename = 'neutrons/n-{:03}_{}_{:03}{}.endf'.format( - data.atomic_number, - data.atomic_symbol, - data.mass_number, - 'm{}'.format(data.metastable) if data.metastable else '' - ) - ev = openmc.data.endf.Evaluation(endf_filename) - if (1, 458) in ev.section: - endf_data = openmc.data.IncidentNeutron.from_endf(ev) - data.fission_energy = endf_data.fission_energy - - # Add 0K elastic scattering data for select nuclides - if data.name in ('U235', 'U238', 'Pu239'): - data.add_elastic_0K_from_endf(endf_filename) - - # Determine filename - outfile = output_dir / (data.name + '.h5') - data.export_to_hdf5(outfile, 'w', 'earliest') - - # Register with library - library.register_file(outfile) - - # ========================================================================= - # THERMAL SCATTERING DATA - - thermal_files = sorted(glob.glob('tsl/*.acer')) - for f in thermal_files: - print('Converting {}...'.format(os.path.basename(f))) - data = openmc.data.ThermalScattering.from_ace(f) - - # Determine filename - outfile = output_dir / (data.name + '.h5') - data.export_to_hdf5(outfile, 'w', 'earliest') - - # Register with library - library.register_file(outfile) - - # ========================================================================= - # INCIDENT PHOTON DATA - - for z in range(1, 101): - element = openmc.data.ATOMIC_SYMBOL[z] - print('Generating HDF5 file for Z={} ({})...'.format(z, element)) - - # Generate instance of IncidentPhoton - photo_file = Path('photoat') / 'photoat-{:03}_{}_000.endf'.format(z, element) - atom_file = Path('atomic_relax') / 'atom-{:03}_{}_000.endf'.format(z, element) - data = openmc.data.IncidentPhoton.from_endf(photo_file, atom_file) - - # Write HDF5 file and register it - outfile = output_dir / 'photon' / (element + '.h5') - data.export_to_hdf5(outfile, 'w', 'earliest') - library.register_file(outfile) - - # ========================================================================= - # WINDOWED MULTIPOLE DATA - - # Move data into output directory - os.rename('WMP_Library', str(output_dir / 'wmp')) - - # Add multipole data to library - for f in sorted(glob.glob('{}/wmp/*.h5'.format(output_dir))): - print('Registering WMP file {}...'.format(f)) - library.register_file(f) - - library.export_to_xml(output_dir / 'cross_sections.xml') - - # ========================================================================= - # CREATE TARBALL AND MOVE BACK - - print('Creating compressed archive...') - test_tar = pwd / 'nndc_hdf5_test.tar.xz' - with tarfile.open(str(test_tar), 'w:xz') as txz: - txz.add(output_dir) - - # Change back to original directory - os.chdir(str(pwd)) diff --git a/scripts/openmc-plot-mesh-tally b/scripts/openmc-plot-mesh-tally deleted file mode 100755 index 2a4fcd7434..0000000000 --- a/scripts/openmc-plot-mesh-tally +++ /dev/null @@ -1,344 +0,0 @@ -#!/usr/bin/env python3 - -"""Python script to plot tally data generated by OpenMC.""" - - -import os -import sys -import argparse -import tkinter as tk -import tkinter.filedialog as filedialog -import tkinter.font as font -import tkinter.messagebox as messagebox -import tkinter.ttk as ttk - -import matplotlib -matplotlib.use("TkAgg") -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk -from matplotlib.figure import Figure -import matplotlib.pyplot as plt -import numpy as np - -from openmc import StatePoint, MeshFilter, UnstructuredMesh - -_COMBOBOX_SELECTED = '<>' - - -def mesh_filter_check(filter): - """ - Check that the filter is a usable mesh filter - """ - return isinstance(filter, MeshFilter) and not isinstance(filter.mesh, UnstructuredMesh) - - -class MeshPlotter(tk.Frame): - def __init__(self, parent, filename): - super().__init__(parent) - - self.labels = { - 'Cell': 'Cell:', - 'Cellborn': 'Cell born:', - 'Surface': 'Surface:', - 'Material': 'Material:', - 'Universe': 'Universe:', - 'Energy': 'Energy in:', - 'Energyout': 'Energy out:' - } - - self.filterBoxes = {} - - # Read data from source or leakage fraction file - self.get_file_data(filename) - - # Set up top-level window - top = self.winfo_toplevel() - top.title('Mesh Tally Plotter: ' + filename) - top.rowconfigure(0, weight=1) - top.columnconfigure(0, weight=1) - self.grid(sticky=tk.W+tk.N) - - # Create widgets and draw to screen - self.create_widgets() - self.update() - - def create_widgets(self): - figureFrame = tk.Frame(self) - figureFrame.grid(row=0, column=0) - - # Create the Figure and Canvas - self.dpi = 100 - self.fig = Figure((5.0, 5.0), dpi=self.dpi) - self.canvas = FigureCanvasTkAgg(self.fig, master=figureFrame) - self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) - - # Create the navigation toolbar, tied to the canvas - self.mpl_toolbar = NavigationToolbar2Tk(self.canvas, figureFrame) - self.mpl_toolbar.update() - self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) - - # Create frame for comboboxes - self.selectFrame = tk.Frame(self) - self.selectFrame.grid(row=1, column=0, sticky=tk.W+tk.E) - - # Tally selection - labelTally = tk.Label(self.selectFrame, text='Tally:') - labelTally.grid(row=0, column=0, sticky=tk.W) - self.tallyBox = ttk.Combobox(self.selectFrame, state='readonly') - self.tallyBox['values'] = [self.datafile.tallies[i].id - for i in self.meshTallies] - self.tallyBox.current(0) - self.tallyBox.grid(row=0, column=1, sticky=tk.W+tk.E) - self.tallyBox.bind(_COMBOBOX_SELECTED, self.update) - - # Planar basis selection - labelBasis = tk.Label(self.selectFrame, text='Basis:') - labelBasis.grid(row=1, column=0, sticky=tk.W) - self.basisBox = ttk.Combobox(self.selectFrame, state='readonly') - self.basisBox['values'] = ('xy', 'yz', 'xz') - self.basisBox.current(0) - self.basisBox.grid(row=1, column=1, sticky=tk.W+tk.E) - self.basisBox.bind(_COMBOBOX_SELECTED, self.update) - - # Axial level - labelAxial = tk.Label(self.selectFrame, text='Axial level:') - labelAxial.grid(row=2, column=0, sticky=tk.W) - self.axialBox = ttk.Combobox(self.selectFrame, state='readonly') - self.axialBox.grid(row=2, column=1, sticky=tk.W+tk.E) - self.axialBox.bind(_COMBOBOX_SELECTED, self.redraw) - - # Option for mean/uncertainty - labelMean = tk.Label(self.selectFrame, text='Mean/Uncertainty:') - labelMean.grid(row=3, column=0, sticky=tk.W) - self.meanBox = ttk.Combobox(self.selectFrame, state='readonly') - self.meanBox['values'] = ('Mean', 'Absolute uncertainty', - 'Relative uncertainty') - self.meanBox.current(0) - self.meanBox.grid(row=3, column=1, sticky=tk.W+tk.E) - self.meanBox.bind(_COMBOBOX_SELECTED, self.update) - - # Scores - labelScore = tk.Label(self.selectFrame, text='Score:') - labelScore.grid(row=4, column=0, sticky=tk.W) - self.scoreBox = ttk.Combobox(self.selectFrame, state='readonly') - self.scoreBox.grid(row=4, column=1, sticky=tk.W+tk.E) - self.scoreBox.bind(_COMBOBOX_SELECTED, self.redraw) - - # Filter label - boldfont = font.Font(weight='bold') - labelFilters = tk.Label(self.selectFrame, text='Filters:', - font=boldfont) - labelFilters.grid(row=5, column=0, sticky=tk.W) - - def update(self, event=None): - widget = event.widget if event else None - - tally_id = self.meshTallies[self.tallyBox.current()] - selectedTally = self.datafile.tallies[tally_id] - - # Get mesh for selected tally - self.mesh = selectedTally.find_filter(MeshFilter).mesh - - # Get mesh dimensions - if len(self.mesh.dimension) == 2: - self.nx, self.ny = self.mesh.dimension - self.nz = 1 - else: - self.nx, self.ny, self.nz = self.mesh.dimension - - # Repopulate comboboxes baesd on current basis selection - text = self.basisBox.get() - if text == 'xy': - self.axialBox['values'] = [str(i+1) for i in range(self.nz)] - elif text == 'yz': - self.axialBox['values'] = [str(i+1) for i in range(self.nx)] - else: - self.axialBox['values'] = [str(i+1) for i in range(self.ny)] - self.axialBox.current(0) - - # If update() was called by a change in the basis combobox, we don't - # need to repopulate the filters - if widget == self.basisBox: - self.redraw() - return - - # Update scores - self.scoreBox['values'] = selectedTally.scores - self.scoreBox.current(0) - - # Remove any filter labels/comboboxes that exist - for row in range(6, self.selectFrame.grid_size()[1]): - for w in self.selectFrame.grid_slaves(row=row): - w.grid_forget() - w.destroy() - - # create a label/combobox for each filter in selected tally - count = 0 - for f in selectedTally.filters: - filterType = f.short_name - if filterType == 'Mesh': - continue - count += 1 - - # Create label and combobox for this filter - label = tk.Label(self.selectFrame, text=self.labels[filterType]) - label.grid(row=count+6, column=0, sticky=tk.W) - combobox = ttk.Combobox(self.selectFrame, state='readonly') - self.filterBoxes[filterType] = combobox - - # Set combobox items - if filterType in ['Energy', 'Energyout']: - combobox['values'] = ['{} to {}'.format(*ebin) - for ebin in f.bins] - else: - combobox['values'] = [str(i) for i in f.bins] - - combobox.current(0) - combobox.grid(row=count+6, column=1, sticky=tk.W+tk.E) - combobox.bind(_COMBOBOX_SELECTED, self.redraw) - - # If There are no filters, leave a 'None available' message - if count == 0: - count += 1 - label = tk.Label(self.selectFrame, text="None Available") - label.grid(row=count+6, column=0, sticky=tk.W) - - self.redraw() - - def redraw(self, event=None): - basis = self.basisBox.current() + 1 - axial_level = self.axialBox.current() + 1 - mbvalue = self.meanBox.get() - - # Get selected tally - tally_id = self.meshTallies[self.tallyBox.current()] - selectedTally = self.datafile.tallies[tally_id] - - # Create spec_list - spec_list = [] - for f in selectedTally.filters: - if f.short_name == 'Mesh': - mesh_filter = f - continue - elif f.short_name in ['Energy', 'Energyout']: - index = self.filterBoxes[f.short_name].current() - ebin = f.bins[index] - spec_list.append((type(f), (ebin,))) - else: - index = self.filterBoxes[f.short_name].current() - spec_list.append((type(f), (index,))) - - dims = (self.nx, self.ny, self.nz) - - text = self.basisBox.get() - if text == 'xy': - h_ind = 0 - v_ind = 1 - elif text == 'yz': - h_ind = 1 - v_ind = 2 - else: - h_ind = 0 - v_ind = 2 - - axial_ind = 3 - (h_ind + v_ind) - dims = (dims[h_ind], dims[v_ind]) - - mesh_dim = len(self.mesh.dimension) - if mesh_dim == 3: - mesh_indices = [0,0,0] - else: - mesh_indices = [0,0] - - matrix = np.zeros(dims) - for i in range(dims[0]): - for j in range(dims[1]): - if mesh_dim == 3: - mesh_indices[h_ind] = i + 1 - mesh_indices[v_ind] = j + 1 - mesh_indices[axial_ind] = axial_level - else: - mesh_indices[0] = i + 1 - mesh_indices[1] = j + 1 - filters, filter_bins = zip(*spec_list + [ - (type(mesh_filter), (tuple(mesh_indices),))]) - mean = selectedTally.get_values( - [self.scoreBox.get()], filters, filter_bins) - stdev = selectedTally.get_values( - [self.scoreBox.get()], filters, filter_bins, - value='std_dev') - if mbvalue == 'Mean': - matrix[i, j] = mean - elif mbvalue == 'Absolute uncertainty': - matrix[i, j] = stdev - else: - if mean > 0.: - matrix[i, j] = stdev/mean - else: - matrix[i, j] = 0. - - # Clear the figure - self.fig.clear() - - # Make figure, set up color bar - self.axes = self.fig.add_subplot(111) - cax = self.axes.imshow(matrix.transpose(), vmin=0.0, vmax=matrix.max(), - interpolation='none', origin='lower') - self.fig.colorbar(cax) - - self.axes.set_xticks([]) - self.axes.set_yticks([]) - self.axes.set_aspect('equal') - - # Draw canvas - self.canvas.draw() - - def get_file_data(self, filename): - # Create StatePoint object and read in data - self.datafile = StatePoint(filename) - - meshes = self.datafile.meshes - if any(isinstance(m, UnstructuredMesh) for m in meshes.values()): - warn_msg = "Unstructured meshes are present in the" \ - " statepoint file but are not currently" \ - " supported by this script" - messagebox.showwarning("Unstructured Meshes", message=warn_msg) - - # Find which tallies are mesh tallies - self.meshTallies = [] - for itally, tally in self.datafile.tallies.items(): - if any([mesh_filter_check(f) for f in tally.filters]): - self.meshTallies.append(itally) - - if not self.meshTallies: - messagebox.showerror("Invalid StatePoint File", - "File does not contain mesh tallies!") - sys.exit(1) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('statepoint', nargs='?', help='Statepoint file') - args = parser.parse_args() - - # Hide root window - root = tk.Tk() - root.withdraw() - - # If no filename given as command-line argument, open file dialog - if args.statepoint is None: - filename = filedialog.askopenfilename(title='Select statepoint file', - initialdir='.') - else: - filename = args.statepoint - - if filename: - # Check to make sure file exists - if not os.path.isfile(filename): - messagebox.showerror("File not found", - "Could not find regular file: " + filename) - sys.exit(1) - - app = MeshPlotter(root, filename) - root.deiconify() - root.mainloop() diff --git a/scripts/openmc-track-to-vtk b/scripts/openmc-track-to-vtk deleted file mode 100755 index 3c781d19ec..0000000000 --- a/scripts/openmc-track-to-vtk +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 - -"""Convert HDF5 particle track to VTK poly data. - -""" - -import os -import argparse -import struct - -import h5py -import vtk - - -def _parse_args(): - # Create argument parser. - parser = argparse.ArgumentParser( - description='Convert particle track file to a .pvtp file.') - parser.add_argument('input', metavar='IN', type=str, nargs='+', - help='Input particle track data filename(s).') - parser.add_argument('-o', '--out', metavar='OUT', type=str, dest='out', - help='Output VTK poly data filename.') - - # Parse and return commandline arguments. - return parser.parse_args() - - -def main(): - # Parse commandline arguments. - args = _parse_args() - - # Make sure that the output filename ends with '.pvtp'. - if not args.out: - args.out = 'tracks.pvtp' - elif not args.out.endswith('.pvtp'): - args.out += '.pvtp' - - # Initialize data arrays and offset. - points = vtk.vtkPoints() - cells = vtk.vtkCellArray() - point_offset = 0 - for fname in args.input: - # Write coordinate values to points array. - track = h5py.File(fname) - n_particles = track.attrs['n_particles'] - n_coords = track.attrs['n_coords'] - coords = [] - for i in range(n_particles): - coords.append(track['coordinates_' + str(i + 1)][()]) - for j in range(n_coords[i]): - points.InsertNextPoint(coords[i][j,:]) - - for i in range(n_particles): - # Create VTK line and assign points to line. - line = vtk.vtkPolyLine() - line.GetPointIds().SetNumberOfIds(n_coords[i]) - for j in range(n_coords[i]): - line.GetPointIds().SetId(j, point_offset + j) - - # Add line to cell array - cells.InsertNextCell(line) - point_offset += n_coords[i] - - data = vtk.vtkPolyData() - data.SetPoints(points) - data.SetLines(cells) - - writer = vtk.vtkXMLPPolyDataWriter() - if vtk.vtkVersion.GetVTKMajorVersion() > 5: - writer.SetInputData(data) - else: - writer.SetInput(data) - writer.SetFileName(args.out) - writer.Write() - - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-update-inputs b/scripts/openmc-update-inputs deleted file mode 100755 index c47f888c83..0000000000 --- a/scripts/openmc-update-inputs +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python3 -"""Update OpenMC's input XML files to the latest format. - -""" - -import argparse -from difflib import get_close_matches -from itertools import chain -from random import randint -from shutil import move -import xml.etree.ElementTree as ET - -import openmc.data - - -description = "Update OpenMC's input XML files to the latest format." -epilog = """\ -If any of the given files do not match the most up-to-date formatting, then they -will be automatically rewritten. The old out-of-date files will not be deleted; -they will be moved to a new file with '.original' appended to their name. - -Formatting changes that will be made: - -geometry.xml: Lattices containing 'outside' attributes/tags will be replaced - with lattices containing 'outer' attributes, and the appropriate - cells/universes will be added. Any 'surfaces' attributes/elements on a cell - will be renamed 'region'. - -materials.xml: Nuclide names will be changed from ACE aliases (e.g., Am-242m) to - HDF5/GND names (e.g., Am242_m1). Thermal scattering table names will be - changed from ACE aliases (e.g., HH2O) to HDF5/GND names (e.g., c_H_in_H2O). - -""" - - -def parse_args(): - """Read the input files from the commandline.""" - # Create argument parser. - parser = argparse.ArgumentParser( - description=description, - epilog=epilog, - formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('input', metavar='IN', type=str, nargs='+', - help='Input XML file(s).') - - # Parse and return commandline arguments. - return parser.parse_args() - - -def get_universe_ids(geometry_root): - """Return a set of universe id numbers.""" - root = geometry_root - out = set() - - # Get the ids of universes defined by cells. - for cell in root.iter('cell'): - # Get universe attributes/elements - if 'universe' in cell.attrib: - uid = cell.attrib['universe'] - out.add(int(uid)) - elif cell.find('universe') is not None: - elem = cell.find('universe') - uid = elem.text - out.add(int(uid)) - else: - # Default to universe 0 - out.add(0) - - # Get the ids of universes defined by lattices. - for lat in root.iter('lattice'): - # Get id attributes. - if 'id' in lat.attrib: - uid = lat.attrib['id'] - out.add(int(uid)) - - # Get id elements. - elif lat.find('id') is not None: - elem = lat.find('id') - uid = elem.text - out.add(int(uid)) - - return out - - -def get_cell_ids(geometry_root): - """Return a set of cell id numbers.""" - root = geometry_root - out = set() - - # Get the ids of universes defined by cells. - for cell in root.iter('cell'): - # Get id attributes. - if 'id' in cell.attrib: - cid = cell.attrib['id'] - out.add(int(cid)) - - # Get id elements. - elif cell.find('id') is not None: - elem = cell.find('id') - cid = elem.text - out.add(int(cid)) - - return out - - -def find_new_id(current_ids, preferred=None): - """Return a new id that is not already present in current_ids.""" - distance_from_preferred = 21 - max_random_attempts = 10000 - - # First, try to find an id near the preferred number. - if preferred is not None: - assert isinstance(preferred, int) - for i in range(1, distance_from_preferred): - if (preferred - i not in current_ids) and (preferred - i > 0): - return preferred - i - if (preferred + i not in current_ids) and (preferred + i > 0): - return preferred + i - - # If that was unsuccessful, attempt to randomly guess a new id number. - for i in range(max_random_attempts): - num = randint(1, 2147483647) - if num not in current_inds: - return num - - # Raise an error if an id was not found. - raise RuntimeError('Could not find a unique id number for a new universe.') - - -def get_lat_id(lattice_element): - """Return the id integer of the lattice_element.""" - assert isinstance(lattice_element, ET.Element) - if 'id' in lattice_element.attrib: - return int(lattice_element.attrib['id'].strip()) - elif any([child.tag == 'id' for child in lattice_element]): - elem = lattice_element.find('id') - return int(elem.text.strip()) - else: - raise RuntimeError('Could not find the id for a lattice.') - - -def pop_lat_outside(lattice_element): - """Return lattice's outside material and remove from attributes/elements.""" - assert isinstance(lattice_element, ET.Element) - - # Check attributes. - if 'outside' in lattice_element.attrib: - material = lattice_element.attrib['outside'].strip() - del lattice_element.attrib['outside'] - - # Check subelements. - elif any([child.tag == 'outside' for child in lattice_element]): - elem = lattice_element.find('outside') - material = elem.text.strip() - lattice_element.remove(elem) - - # No 'outside' specified. This means the outside is a void. - else: - material = 'void' - - return material - - -def update_geometry(geometry_root): - """Update the given XML geometry tree. Return True if changes were made.""" - root = geometry_root - was_updated = False - - # Get a set of already-used universe and cell ids. - uids = get_universe_ids(root) - cids = get_cell_ids(root) - taken_ids = uids.union(cids) - - # Replace 'outside' with 'outer' in lattices. - for lat in chain(root.iter('lattice'), root.iter('hex_lattice')): - # Get the lattice's id. - lat_id = get_lat_id(lat) - - # Ignore lattices that have 'outer' specified. - if any([child.tag == 'outer' for child in lat]): continue - if 'outer' in lat.attrib: continue - - # Pop the 'outside' material. - material = pop_lat_outside(lat) - - # Get an id number for a new outer universe. Ideally, the id should - # be close to the lattice's id. - new_uid = find_new_id(taken_ids, preferred=lat_id) - assert new_uid not in taken_ids - - # Add the new universe filled with the old 'outside' material to the - # geometry. - new_cell = ET.Element('cell') - new_cell.attrib['id'] = str(new_uid) - new_cell.attrib['universe'] = str(new_uid) - new_cell.attrib['material'] = material - root.append(new_cell) - taken_ids.add(new_uid) - - # Add the new universe to the lattice's 'outer' attribute. - lat.attrib['outer'] = str(new_uid) - - was_updated = True - - # Remove 'type' from lattice definitions. - for lat in root.iter('lattice'): - elem = lat.find('type') - if elem is not None: - lat.remove(elem) - was_updated = True - if 'type' in lat.attrib: - del lat.attrib['type'] - was_updated = True - - # Change 'width' to 'pitch' in lattice definitions. - for lat in root.iter('lattice'): - elem = lat.find('width') - if elem is not None: - elem.tag = 'pitch' - was_updated = True - if 'width' in lat.attrib: - lat.attrib['pitch'] = lat.attrib['width'] - del lat.attrib['width'] - was_updated = True - - # Change 'surfaces' to 'region' in cell definitions - for cell in root.iter('cell'): - elem = cell.find('surfaces') - if elem is not None: - elem.tag = 'region' - was_updated = True - if 'surfaces' in cell.attrib: - cell.attrib['region'] = cell.attrib['surfaces'] - del cell.attrib['surfaces'] - was_updated = True - - return was_updated - -def update_materials(root): - """Update the given XML materials tree. Return True if changes were made.""" - was_updated = False - - for material in root.findall('material'): - for nuclide in material.findall('nuclide'): - if 'name' in nuclide.attrib: - nucname = nuclide.attrib['name'].replace('-', '') - # If a nuclide name is in the ZAID notation (e.g., a number), - # convert it to the proper nuclide name. - if nucname.strip().isnumeric(): - nucname = openmc.data.ace.get_metadata(int(nucname))[0] - nucname = nucname.replace('Nat', '0') - if nucname.endswith('m'): - nucname = nucname[:-1] + '_m1' - nuclide.set('name', nucname) - was_updated = True - - elif nuclide.find('name') is not None: - name_elem = nuclide.find('name') - nucname = name_elem.text - nucname = nucname.replace('-', '') - nucname = nucname.replace('Nat', '0') - if nucname.endswith('m'): - nucname = nucname[:-1] + '_m1' - name_elem.text = nucname - was_updated = True - - for sab in material.findall('sab'): - if 'name' in sab.attrib: - sabname = sab.attrib['name'] - sab.set('name', openmc.data.get_thermal_name(sabname)) - was_updated = True - - elif sab.find('name') is not None: - name_elem = sab.find('name') - sabname = name_elem.text - name_elem.text = openmc.data.get_thermal_name(sabname) - was_updated = True - - return was_updated - - -if __name__ == '__main__': - args = parse_args() - for fname in args.input: - # Parse the XML data. - tree = ET.parse(fname) - root = tree.getroot() - was_updated = False - - if root.tag == 'geometry': - was_updated = update_geometry(root) - elif root.tag == 'materials': - was_updated = update_materials(root) - - if was_updated: - # Move the original geometry file to preserve it. - move(fname, fname + '.original') - - # Write a new geometry file. - tree.write(fname, xml_declaration=True) diff --git a/scripts/openmc-update-mgxs b/scripts/openmc-update-mgxs deleted file mode 100755 index aac6959b7e..0000000000 --- a/scripts/openmc-update-mgxs +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python3 -"""Update OpenMC's deprecated multi-group cross section XML files to the latest -HDF5-based format. - -""" - -import os -import warnings -import xml.etree.ElementTree as ET - -import argparse -import numpy as np - -import openmc.mgxs_library - - -def parse_args(): - """Read the input files from the commandline.""" - # Create argument parser - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('-i', '--input', type=argparse.FileType('r'), - help='input XML file') - parser.add_argument('-o', '--output', nargs='?', default='', - help='output file, in HDF5 format') - args = vars(parser.parse_args()) - - if args['output'] == '': - filename = args['input'].name - extension = os.path.splitext(filename) - if extension == '.xml': - filename = filename[:filename.rfind('.')] + '.h5' - args['output'] = filename - - # Parse and return commandline arguments. - return args - - -def get_data(element, entry): - value = element.find(entry) - if value is not None: - value = value.text.strip() - elif entry in element.attrib: - value = element.attrib[entry].strip() - else: - value = None - - return value - - -def main(): - args = parse_args() - - # Parse the XML data. - tree = ET.parse(args['input']) - root = tree.getroot() - - # Get old metadata - group_structure = tree.find('group_structure').text.strip() - group_structure = np.array(group_structure.split(), dtype=float) - # Convert from MeV to eV - group_structure *= 1.e6 - energy_groups = openmc.mgxs.EnergyGroups(group_structure) - - inverse_velocity = tree.find('inverse-velocity') - if inverse_velocity is not None: - inverse_velocity = inverse_velocity.text.split() - inverse_velocity = np.array(inverse_velocity, dtype=float) - else: - inverse_velocity = None - - xsd = [] - names = [] - - # Now move on to the cross section data itself - for xsdata_elem in root.iter('xsdata'): - name = get_data(xsdata_elem, 'name') - - temperature = get_data(xsdata_elem, 'kT') - if temperature is not None: - temperature = float(temperature) / openmc.data.K_BOLTZMANN * 1.E6 - else: - temperature = 294. - temperatures = [temperature] - - awr = get_data(xsdata_elem, 'awr') - if awr is not None: - awr = float(awr) - - representation = get_data(xsdata_elem, 'representation') - if representation is None: - representation = 'isotropic' - if representation == 'angle': - n_azi = int(get_data(xsdata_elem, 'num_azimuthal')) - n_pol = int(get_data(xsdata_elem, 'num_polar')) - - scatter_format = get_data(xsdata_elem, 'scatt_type') - if scatter_format is None: - scatter_format = 'legendre' - - order = int(get_data(xsdata_elem, 'order')) - - tab_leg = get_data(xsdata_elem, 'tabular_legendre') - if tab_leg is not None: - warnings.warn('The tabular_legendre option has moved to the ' - 'settings.xml file and must be added manually') - - # Either add the data to a previously existing xsdata (if it is - # for the same 'name' but a different temperature), or create a - # new one. - try: - # It is in our list, so store that entry - i = names.index(name) - except ValueError: - # It is not in our list, so add it - i = -1 - xsd.append(openmc.XSdata(name, energy_groups, - temperatures=temperatures, - representation=representation)) - if awr is not None: - xsd[-1].atomic_weight_ratio = awr - if representation == 'angle': - xsd[-1].num_azimuthal = n_azi - xsd[-1].num_polar = n_pol - xsd[-1].scatter_format = scatter_format - xsd[-1].order = order - names.append(name) - - if scatter_format == 'legendre': - order_dim = order + 1 - else: - order_dim = order - - if i != -1: - xsd[i].add_temperature(temperature) - - total = get_data(xsdata_elem, 'total') - if total is not None: - total = np.array(total.split(), dtype=float) - total.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_total(total, temperature) - - if inverse_velocity is not None: - xsd[i].set_inverse_velocity(inverse_velocity, temperature) - - absorption = get_data(xsdata_elem, 'absorption') - absorption = np.array(absorption.split(), dtype=float) - absorption.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_absorption(absorption, temperature) - - scatter = get_data(xsdata_elem, 'scatter') - scatter = np.array(scatter.split(), dtype=float) - # This is now a flattened-array of something that started with a - # shape of [Order][G][G']; we need to unflatten and then switch the - # ordering - in_shape = (order_dim, energy_groups.num_groups, - energy_groups.num_groups) - if representation == 'angle': - in_shape = (n_pol, n_azi) + in_shape - scatter.shape = in_shape - scatter = np.swapaxes(scatter, 2, 3) - scatter = np.swapaxes(scatter, 3, 4) - else: - scatter.shape = in_shape - scatter = np.swapaxes(scatter, 0, 1) - scatter = np.swapaxes(scatter, 1, 2) - - xsd[i].set_scatter_matrix(scatter, temperature) - - multiplicity = get_data(xsdata_elem, 'multiplicity') - if multiplicity is not None: - multiplicity = np.array(multiplicity.split(), dtype=float) - multiplicity.shape = xsd[i].xs_shapes["[G][G']"] - xsd[i].set_multiplicity_matrix(multiplicity, temperature) - - fission = get_data(xsdata_elem, 'fission') - if fission is not None: - fission = np.array(fission.split(), dtype=float) - fission.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_fission(fission, temperature) - - kappa_fission = get_data(xsdata_elem, 'kappa_fission') - if kappa_fission is not None: - kappa_fission = np.array(kappa_fission.split(), dtype=float) - kappa_fission.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_kappa_fission(kappa_fission, temperature) - - chi = get_data(xsdata_elem, 'chi') - if chi is not None: - chi = np.array(chi.split(), dtype=float) - chi.shape = xsd[i].xs_shapes['[G]'] - xsd[i].set_chi(chi, temperature) - else: - chi = None - - nu_fission = get_data(xsdata_elem, 'nu_fission') - if nu_fission is not None: - nu_fission = np.array(nu_fission.split(), dtype=float) - if chi is not None: - nu_fission.shape = xsd[i].xs_shapes['[G]'] - else: - nu_fission.shape = xsd[i].xs_shapes["[G][G']"] - xsd[i].set_nu_fission(nu_fission, temperature) - - # Build library as we go, but first we have enough to initialize it - lib = openmc.MGXSLibrary(energy_groups) - lib.add_xsdatas(xsd) - lib.export_to_hdf5(args['output']) - - -if __name__ == '__main__': - main() diff --git a/scripts/openmc-validate-xml b/scripts/openmc-validate-xml deleted file mode 100755 index f36ba2b5d3..0000000000 --- a/scripts/openmc-validate-xml +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import glob -import lxml.etree as etree -from subprocess import call -from optparse import OptionParser - -# Command line parsing -parser = OptionParser() -parser.add_option('-r', '--relaxng-path', dest='relaxng', - help="Path to RelaxNG files.") -parser.add_option('-i', '--input-path', dest='inputs', default=os.getcwd(), - help="Path to OpenMC input files." ) -(options, args) = parser.parse_args() - -# Colored output -if sys.stdout.isatty(): - OK = '\033[92m' - FAIL = '\033[91m' - NOT_FOUND = '\033[93m' - ENDC = '\033[0m' - BOLD = '\033[1m' -else: - OK = '' - FAIL = '' - ENDC = '' - BOLD = '' - NOT_FOUND = '' - -# Get absolute paths -if options.relaxng is not None: - relaxng_path = os.path.abspath(options.relaxng) -if options.inputs is not None: - inputs_path = os.path.abspath(options.inputs) - -# Search for relaxng path if not set -if options.relaxng is None: - xml_validate_path = os.path.abspath(os.path.dirname(sys.argv[0])) - if "bin" in xml_validate_path: - relaxng_path = os.path.join(xml_validate_path, "..", "share", "relaxng") - elif os.path.join("src", "utils") in xml_validate_path: - relaxng_path = os.path.join(xml_validate_path, "..", "relaxng") - else: - raise Exception("Set RelaxNG path with -r command line option.") -if not os.path.exists(relaxng_path): - raise Exception("RelaxNG path: {0} does not exist, set with -r " - "command line option.".format(relaxng_path)) - -# Make sure there are .rng files in RelaxNG path -rng_files = glob.glob(os.path.join(relaxng_path, "*.rng")) -if len(rng_files) == 0: - raise Exception("No .rng files found in RelaxNG " - "path: {0}.".format(relaxng_path)) - -# Get list of xml input files -xml_files = glob.glob(os.path.join(inputs_path, "*.xml")) -if len(xml_files) == 0: - raise Exception("No .xml files found at input path: {0}" - ".".format(inputs_path)) - -# Begin loop around input files -for xml_file in xml_files: - - text = "Validating {0}".format(os.path.basename(xml_file)) - print(text + '.'*(30 - len(text)), end="") - - # Validate the XML file - try: - xml_tree = etree.parse(xml_file) - except etree.XMLSyntaxError as e: - print(BOLD + FAIL + '[XML ERROR]' + ENDC) - print(" {0}".format(e)) - continue - - # Get xml_filename prefix - xml_prefix = os.path.basename(xml_file) - xml_prefix = xml_prefix.split(".")[0] - - # Search for rng file - rng_file = os.path.join(relaxng_path, xml_prefix + ".rng") - if rng_file in rng_files: - - # read in RelaxNG - relaxng_doc = etree.parse(rng_file) - relaxng = etree.RelaxNG(relaxng_doc) - - # validate xml file again RelaxNG - try: - relaxng.assertValid(xml_tree) - print(BOLD + OK + '[VALID]' + ENDC) - except (etree.DocumentInvalid, TypeError) as e: - print(BOLD + FAIL + '[NOT VALID]' + ENDC) - print(" {0}".format(e)) - - # RNG file does not exist - else: - print(BOLD + NOT_FOUND + '[NO RELAXNG FOUND]' + ENDC) diff --git a/scripts/openmc-voxel-to-vtk b/scripts/openmc-voxel-to-vtk deleted file mode 100755 index 33251144fb..0000000000 --- a/scripts/openmc-voxel-to-vtk +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 - -import struct -import sys -from argparse import ArgumentParser - -import numpy as np -import h5py -import vtk - -_min_version = (2, 0) - - -def main(): - # Process command line arguments - parser = ArgumentParser() - parser.add_argument('voxel_file', help='Path to voxel file') - parser.add_argument('-o', '--output', action='store', - default='plot', help='Path to output VTK file.') - args = parser.parse_args() - - # Read data from voxel file - fh = h5py.File(args.voxel_file, 'r') - - # check version - version = tuple(fh.attrs['version']) - if version < _min_version: - old_version = ".".join(map(str,version)) - min_version = ".".join(map(str,_min_version)) - err_msg = "This voxel file's version is {}. This script " \ - "only supports voxel files with version {} or " \ - "higher. Please generate a new voxel file using " \ - "a newer version of OpenMC.".format(old_version, min_version) - raise ValueError(err_msg) - - dimension = fh.attrs['num_voxels'] - width = fh.attrs['voxel_width'] - lower_left = fh.attrs['lower_left'] - - nx, ny, nz = dimension - upper_right = lower_left + width*dimension - - grid = vtk.vtkImageData() - grid.SetDimensions(nx+1, ny+1, nz+1) - grid.SetOrigin(*lower_left) - grid.SetSpacing(*width) - - # transpose data from OpenMC ordering (zyx) to VTK ordering (xyz) - # and flatten to 1-D array - print("Reading and translating data...") - h5data = fh['data'][...] - - data = vtk.vtkIntArray() - data.SetName("id") - # set the array using the h5data array - data.SetArray(h5data, h5data.size, True) - # add data to image grid - grid.GetCellData().AddArray(data) - - writer = vtk.vtkXMLImageDataWriter() - if vtk.vtkVersion.GetVTKMajorVersion() > 5: - writer.SetInputData(grid) - else: - writer.SetInput(grid) - if not args.output.endswith(".vti"): - args.output += ".vti" - writer.SetFileName(args.output) - print("Writing VTK file {}...".format(args.output)) - writer.Write() - -if __name__ == '__main__': - main() diff --git a/setup.py b/setup.py deleted file mode 100755 index f2a9929aeb..0000000000 --- a/setup.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python - -import glob -import sys -import numpy as np - -from setuptools import setup, find_packages -try: - from Cython.Build import cythonize - have_cython = True -except ImportError: - have_cython = False - - -# Determine shared library suffix -if sys.platform == 'darwin': - suffix = 'dylib' -else: - suffix = 'so' - -# Get version information from __init__.py. This is ugly, but more reliable than -# using an import. -with open('openmc/__init__.py', 'r') as f: - version = f.readlines()[-1].split()[-1].strip("'") - -kwargs = { - 'name': 'openmc', - 'version': version, - 'packages': find_packages(exclude=['tests*']), - 'scripts': glob.glob('scripts/openmc-*'), - - # Data files and librarries - 'package_data': { - 'openmc.lib': ['libopenmc.{}'.format(suffix)], - 'openmc.data': ['mass16.txt', 'BREMX.DAT', '*.h5'], - 'openmc.data.effective_dose': ['*.txt'] - }, - - # Metadata - 'author': 'The OpenMC Development Team', - 'author_email': 'openmc@anl.gov', - 'description': 'OpenMC', - 'url': 'https://openmc.org', - 'download_url': 'https://github.com/openmc-dev/openmc/releases', - 'project_urls': { - 'Issue Tracker': 'https://github.com/openmc-dev/openmc/issues', - 'Documentation': 'https://docs.openmc.org', - 'Source Code': 'https://github.com/openmc-dev/openmc', - }, - 'classifiers': [ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Topic :: Scientific/Engineering' - 'Programming Language :: C++', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - ], - - # Dependencies - 'python_requires': '>=3.6', - 'install_requires': [ - 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib', - 'pandas', 'lxml', 'uncertainties' - ], - 'extras_require': { - 'depletion-mpi': ['mpi4py'], - 'docs': ['sphinx', 'sphinxcontrib-katex', 'sphinx-numfig', 'jupyter', - 'sphinxcontrib-svg2pdfconverter', 'sphinx-rtd-theme'], - 'test': ['pytest', 'pytest-cov', 'colorama'], - 'vtk': ['vtk'], - }, -} - -# If Cython is present, add resonance reconstruction and fast float_endf -if have_cython: - kwargs.update({ - 'ext_modules': cythonize('openmc/data/*.pyx'), - 'include_dirs': [np.get_include()] - }) - -setup(**kwargs) diff --git a/src/bank.cpp b/src/bank.cpp index 8d00d54409..3e806b3c0b 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -1,6 +1,7 @@ #include "openmc/bank.h" #include "openmc/capi.h" #include "openmc/error.h" +#include "openmc/ifp.h" #include "openmc/message_passing.h" #include "openmc/simulation.h" #include "openmc/vector.h" @@ -26,6 +27,14 @@ SharedArray surf_source_bank; // function. SharedArray fission_bank; +vector> ifp_source_delayed_group_bank; + +vector> ifp_source_lifetime_bank; + +vector> ifp_fission_delayed_group_bank; + +vector> ifp_fission_lifetime_bank; + // Each entry in this vector corresponds to the number of progeny produced // this generation for the particle located at that index. This vector is // used to efficiently sort the fission bank after each iteration. @@ -43,6 +52,10 @@ void free_memory_bank() simulation::surf_source_bank.clear(); simulation::fission_bank.clear(); simulation::progeny_per_particle.clear(); + simulation::ifp_source_delayed_group_bank.clear(); + simulation::ifp_source_lifetime_bank.clear(); + simulation::ifp_fission_delayed_group_bank.clear(); + simulation::ifp_fission_lifetime_bank.clear(); } void init_fission_bank(int64_t max) @@ -66,22 +79,17 @@ void sort_fission_bank() // Perform exclusive scan summation to determine starting indices in fission // bank for each parent particle id - int64_t tmp = simulation::progeny_per_particle[0]; - simulation::progeny_per_particle[0] = 0; - for (int64_t i = 1; i < simulation::progeny_per_particle.size(); i++) { - int64_t value = simulation::progeny_per_particle[i - 1] + tmp; - tmp = simulation::progeny_per_particle[i]; - simulation::progeny_per_particle[i] = value; - } - - // TODO: C++17 introduces the exclusive_scan() function which could be - // used to replace everything above this point in this function. + std::exclusive_scan(simulation::progeny_per_particle.begin(), + simulation::progeny_per_particle.end(), + simulation::progeny_per_particle.begin(), 0); // We need a scratch vector to make permutation of the fission bank into // sorted order easy. Under normal usage conditions, the fission bank is // over provisioned, so we can use that as scratch space. SourceSite* sorted_bank; vector sorted_bank_holder; + vector> sorted_ifp_delayed_group_bank; + vector> sorted_ifp_lifetime_bank; // If there is not enough space, allocate a temporary vector and point to it if (simulation::fission_bank.size() > @@ -92,6 +100,11 @@ void sort_fission_bank() sorted_bank = &simulation::fission_bank[simulation::fission_bank.size()]; } + if (settings::ifp_on) { + allocate_temporary_vector_ifp( + sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); + } + // Use parent and progeny indices to sort fission bank for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { const auto& site = simulation::fission_bank[i]; @@ -102,11 +115,19 @@ void sort_fission_bank() "shared fission bank size."); } sorted_bank[idx] = site; + if (settings::ifp_on) { + copy_ifp_data_from_fission_banks( + i, sorted_ifp_delayed_group_bank[idx], sorted_ifp_lifetime_bank[idx]); + } } // Copy sorted bank into the fission bank std::copy(sorted_bank, sorted_bank + simulation::fission_bank.size(), simulation::fission_bank.data()); + if (settings::ifp_on) { + copy_ifp_data_to_fission_banks( + sorted_ifp_delayed_group_bank.data(), sorted_ifp_lifetime_bank.data()); + } } //============================================================================== diff --git a/src/boundary_condition.cpp b/src/boundary_condition.cpp index 5b842399e8..7216ac8964 100644 --- a/src/boundary_condition.cpp +++ b/src/boundary_condition.cpp @@ -6,6 +6,7 @@ #include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/random_ray/random_ray.h" #include "openmc/surface.h" namespace openmc { @@ -16,7 +17,18 @@ namespace openmc { void VacuumBC::handle_particle(Particle& p, const Surface& surf) const { - p.cross_vacuum_bc(surf); + // Random ray and Monte Carlo need different treatments at vacuum BCs + if (settings::solver_type == SolverType::RANDOM_RAY) { + // Reflect ray off of the surface + ReflectiveBC().handle_particle(p, surf); + + // Set ray's angular flux spectrum to vacuum conditions (zero) + RandomRay* r = static_cast(&p); + std::fill(r->angular_flux_.begin(), r->angular_flux_.end(), 0.0); + + } else { + p.cross_vacuum_bc(surf); + } } //============================================================================== @@ -28,6 +40,9 @@ void ReflectiveBC::handle_particle(Particle& p, const Surface& surf) const Direction u = surf.reflect(p.r(), p.u(), &p); u /= u.norm(); + // Handle the effects of the surface albedo on the particle's weight. + BoundaryCondition::handle_albedo(p, surf); + p.cross_reflective_bc(surf, u); } @@ -40,6 +55,9 @@ void WhiteBC::handle_particle(Particle& p, const Surface& surf) const Direction u = surf.diffuse_reflect(p.r(), p.u(), p.current_seed()); u /= u.norm(); + // Handle the effects of the surface albedo on the particle's weight. + BoundaryCondition::handle_albedo(p, surf); + p.cross_reflective_bc(surf, u); } @@ -111,8 +129,7 @@ TranslationalPeriodicBC::TranslationalPeriodicBC(int i_surf, int j_surf) void TranslationalPeriodicBC::handle_particle( Particle& p, const Surface& surf) const { - // TODO: off-by-one on surface indices throughout this function. - int i_particle_surf = std::abs(p.surface()) - 1; + int i_particle_surf = p.surface_index(); // Figure out which of the two BC surfaces were struck then find the // particle's new location and surface. @@ -130,6 +147,9 @@ void TranslationalPeriodicBC::handle_particle( "hitting a surface, but that surface is not recognized by the BC."); } + // Handle the effects of the surface albedo on the particle's weight. + BoundaryCondition::handle_albedo(p, surf); + // Pass the new location and surface to the particle. p.cross_periodic_bc(surf, new_r, p.u(), new_surface); } @@ -234,8 +254,7 @@ RotationalPeriodicBC::RotationalPeriodicBC(int i_surf, int j_surf) void RotationalPeriodicBC::handle_particle( Particle& p, const Surface& surf) const { - // TODO: off-by-one on surface indices throughout this function. - int i_particle_surf = std::abs(p.surface()) - 1; + int i_particle_surf = p.surface_index(); // Figure out which of the two BC surfaces were struck to figure out if a // forward or backward rotation is required. Specify the other surface as @@ -264,6 +283,9 @@ void RotationalPeriodicBC::handle_particle( Direction new_u = { cos_theta * u.x - sin_theta * u.y, sin_theta * u.x + cos_theta * u.y, u.z}; + // Handle the effects of the surface albedo on the particle's weight. + BoundaryCondition::handle_albedo(p, surf); + // Pass the new location, direction, and surface to the particle. p.cross_periodic_bc(surf, new_r, new_u, new_surface); } diff --git a/src/bremsstrahlung.cpp b/src/bremsstrahlung.cpp index d22d6392ab..a2320e0b46 100644 --- a/src/bremsstrahlung.cpp +++ b/src/bremsstrahlung.cpp @@ -112,6 +112,12 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost) std::pow(a * (c - c_l) / (std::exp(w_l) * p_l) + 1.0, 1.0 / a); if (w > settings::energy_cutoff[photon]) { + // If the energy of the secondary photon is larger than the remaining + // energy of the primary particle, adjust it to the remaining energy + if (*E_lost + w > p.E()) { + w = p.E() - *E_lost; + } + // Create secondary photon p.create_secondary(p.wgt(), p.u(), w, ParticleType::photon); *E_lost += w; diff --git a/src/cell.cpp b/src/cell.cpp index b59047ffd4..030ffcbb41 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -2,6 +2,7 @@ #include "openmc/cell.h" #include +#include #include #include #include @@ -10,7 +11,6 @@ #include #include -#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -34,109 +34,39 @@ namespace model { std::unordered_map cell_map; vector> cells; -std::unordered_map universe_map; -vector> universes; } // namespace model -//============================================================================== -//! Convert region specification string to integer tokens. -//! -//! The characters (, ), |, and ~ count as separate tokens since they represent -//! operators. -//============================================================================== - -vector tokenize(const std::string region_spec) +vector> generate_triso_distribution(vector lattice_shape, + vector lattice_pitch, vector lattice_lower_left, + vector cell_rpn, int id) { - // Check for an empty region_spec first. - vector tokens; - if (region_spec.empty()) { - return tokens; - } - - // Parse all halfspaces and operators except for intersection (whitespace). - for (int i = 0; i < region_spec.size();) { - if (region_spec[i] == '(') { - tokens.push_back(OP_LEFT_PAREN); - i++; - - } else if (region_spec[i] == ')') { - tokens.push_back(OP_RIGHT_PAREN); - i++; - - } else if (region_spec[i] == '|') { - tokens.push_back(OP_UNION); - i++; - - } else if (region_spec[i] == '~') { - tokens.push_back(OP_COMPLEMENT); - i++; - - } else if (region_spec[i] == '-' || region_spec[i] == '+' || - std::isdigit(region_spec[i])) { - // This is the start of a halfspace specification. Iterate j until we - // find the end, then push-back everything between i and j. - int j = i + 1; - while (j < region_spec.size() && std::isdigit(region_spec[j])) { - j++; - } - tokens.push_back(std::stoi(region_spec.substr(i, j - i))); - i = j; - - } else if (std::isspace(region_spec[i])) { - i++; - - } else { - auto err_msg = - fmt::format("Region specification contains invalid character, \"{}\"", - region_spec[i]); - fatal_error(err_msg); - } - } - - // Add in intersection operators where a missing operator is needed. - int i = 0; - while (i < tokens.size() - 1) { - bool left_compat {(tokens[i] < OP_UNION) || (tokens[i] == OP_RIGHT_PAREN)}; - bool right_compat {(tokens[i + 1] < OP_UNION) || - (tokens[i + 1] == OP_LEFT_PAREN) || - (tokens[i + 1] == OP_COMPLEMENT)}; - if (left_compat && right_compat) { - tokens.insert(tokens.begin() + i + 1, OP_INTERSECTION); - } - i++; - } - - return tokens; -} - -vector> \ - generate_triso_distribution(\ - vector lattice_shape,\ - vector lattice_pitch,\ - vector lattice_lower_left,\ - vector cell_rpn, int id) -{ - vector> triso_distribution(lattice_shape[0]*lattice_shape[1]*lattice_shape[2]); + vector> triso_distribution( + lattice_shape[0] * lattice_shape[1] * lattice_shape[2]); vector mesh_center(3); vector mesh_ind(3); for (int32_t token : cell_rpn) { - if (token >= OP_UNION) continue; - vector triso_center=model::surfaces[abs(token) - 1]->get_center(); - for (int i=0; i<3; i++) { - mesh_ind[i]=floor((triso_center[i]-lattice_lower_left[i])/lattice_pitch[i]); + if (token >= OP_UNION) + continue; + vector triso_center = model::surfaces[abs(token) - 1]->get_center(); + for (int i = 0; i < 3; i++) { + mesh_ind[i] = + floor((triso_center[i] - lattice_lower_left[i]) / lattice_pitch[i]); } - for (int i=mesh_ind[0]-1; i<=mesh_ind[0]+1; i++) { - for (int j=mesh_ind[1]-1; j<=mesh_ind[1]+1; j++) { - for (int k=mesh_ind[2]-1; k<=mesh_ind[2]+1; k++) { - if (i < 0 || i >= lattice_shape[0] ||\ - j < 0 || j >= lattice_shape[1] ||\ - k < 0 || k >= lattice_shape[2]) continue; - mesh_center[0]=(i+0.5)*lattice_pitch[0]+lattice_lower_left[0]; - mesh_center[1]=(j+0.5)*lattice_pitch[1]+lattice_lower_left[1]; - mesh_center[2]=(k+0.5)*lattice_pitch[2]+lattice_lower_left[2]; - if (model::surfaces[abs(token) - 1]->triso_in_mesh(mesh_center, lattice_pitch)) { - triso_distribution[i+j*lattice_shape[0]+k*lattice_shape[0]*lattice_shape[1]].push_back(token); + for (int i = mesh_ind[0] - 1; i <= mesh_ind[0] + 1; i++) { + for (int j = mesh_ind[1] - 1; j <= mesh_ind[1] + 1; j++) { + for (int k = mesh_ind[2] - 1; k <= mesh_ind[2] + 1; k++) { + if (i < 0 || i >= lattice_shape[0] || j < 0 || + j >= lattice_shape[1] || k < 0 || k >= lattice_shape[2]) + continue; + mesh_center[0] = (i + 0.5) * lattice_pitch[0] + lattice_lower_left[0]; + mesh_center[1] = (j + 0.5) * lattice_pitch[1] + lattice_lower_left[1]; + mesh_center[2] = (k + 0.5) * lattice_pitch[2] + lattice_lower_left[2]; + if (model::surfaces[abs(token) - 1]->triso_in_mesh( + mesh_center, lattice_pitch)) { + triso_distribution[i + j * lattice_shape[0] + + k * lattice_shape[0] * lattice_shape[1]] + .push_back(token); model::surfaces[abs(token) - 1]->connect_to_triso_base(id, "base"); } } @@ -147,18 +77,925 @@ vector> \ return triso_distribution; } +//============================================================================== +// Cell implementation +//============================================================================== + +int32_t Cell::n_instances() const +{ + return model::universes[universe_]->n_instances_; +} + +void Cell::set_rotation(const vector& rot) +{ + if (fill_ == C_NONE) { + fatal_error(fmt::format("Cannot apply a rotation to cell {}" + " because it is not filled with another universe", + id_)); + } + + if (rot.size() != 3 && rot.size() != 9) { + fatal_error(fmt::format("Non-3D rotation vector applied to cell {}", id_)); + } + + // Compute and store the rotation matrix. + rotation_.clear(); + rotation_.reserve(rot.size() == 9 ? 9 : 12); + if (rot.size() == 3) { + double phi = -rot[0] * PI / 180.0; + double theta = -rot[1] * PI / 180.0; + double psi = -rot[2] * PI / 180.0; + rotation_.push_back(std::cos(theta) * std::cos(psi)); + rotation_.push_back(-std::cos(phi) * std::sin(psi) + + std::sin(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::sin(phi) * std::sin(psi) + + std::cos(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::cos(theta) * std::sin(psi)); + rotation_.push_back(std::cos(phi) * std::cos(psi) + + std::sin(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(phi) * std::cos(psi) + + std::cos(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(theta)); + rotation_.push_back(std::sin(phi) * std::cos(theta)); + rotation_.push_back(std::cos(phi) * std::cos(theta)); + + // When user specifies angles, write them at end of vector + rotation_.push_back(rot[0]); + rotation_.push_back(rot[1]); + rotation_.push_back(rot[2]); + } else { + std::copy(rot.begin(), rot.end(), std::back_inserter(rotation_)); + } +} + +double Cell::temperature(int32_t instance) const +{ + if (sqrtkT_.size() < 1) { + throw std::runtime_error {"Cell temperature has not yet been set."}; + } + + if (instance >= 0) { + double sqrtkT = sqrtkT_.size() == 1 ? sqrtkT_.at(0) : sqrtkT_.at(instance); + return sqrtkT * sqrtkT / K_BOLTZMANN; + } else { + return sqrtkT_[0] * sqrtkT_[0] / K_BOLTZMANN; + } +} + +double Cell::density_mult(int32_t instance) const +{ + if (instance >= 0) { + return density_mult_.size() == 1 ? density_mult_.at(0) + : density_mult_.at(instance); + } else { + return density_mult_[0]; + } +} + +double Cell::density(int32_t instance) const +{ + const int32_t mat_index = material(instance); + if (mat_index == MATERIAL_VOID) + return 0.0; + + return density_mult(instance) * model::materials[mat_index]->density_gpcc(); +} + +void Cell::set_temperature(double T, int32_t instance, bool set_contained) +{ + if (settings::temperature_method == TemperatureMethod::INTERPOLATION) { + if (T < (data::temperature_min - settings::temperature_tolerance)) { + throw std::runtime_error { + fmt::format("Temperature of {} K is below minimum temperature at " + "which data is available of {} K.", + T, data::temperature_min)}; + } else if (T > (data::temperature_max + settings::temperature_tolerance)) { + throw std::runtime_error { + fmt::format("Temperature of {} K is above maximum temperature at " + "which data is available of {} K.", + T, data::temperature_max)}; + } + } + + if (type_ == Fill::MATERIAL) { + if (instance >= 0) { + // If temperature vector is not big enough, resize it first + if (sqrtkT_.size() != n_instances()) + sqrtkT_.resize(n_instances(), sqrtkT_[0]); + + // Set temperature for the corresponding instance + sqrtkT_.at(instance) = std::sqrt(K_BOLTZMANN * T); + } else { + // Set temperature for all instances + for (auto& T_ : sqrtkT_) { + T_ = std::sqrt(K_BOLTZMANN * T); + } + } + } else { + if (!set_contained) { + throw std::runtime_error { + fmt::format("Attempted to set the temperature of cell {} " + "which is not filled by a material.", + id_)}; + } + + auto contained_cells = this->get_contained_cells(instance); + for (const auto& entry : contained_cells) { + auto& cell = model::cells[entry.first]; + assert(cell->type_ == Fill::MATERIAL); + auto& instances = entry.second; + for (auto instance : instances) { + cell->set_temperature(T, instance); + } + } + } +} + +void Cell::set_density(double density, int32_t instance, bool set_contained) +{ + if (type_ != Fill::MATERIAL && !set_contained) { + fatal_error( + fmt::format("Attempted to set the density multiplier of cell {} " + "which is not filled by a material.", + id_)); + } + + if (type_ == Fill::MATERIAL) { + const int32_t mat_index = material(instance); + if (mat_index == MATERIAL_VOID) + return; + + if (instance >= 0) { + // If density multiplier vector is not big enough, resize it first + if (density_mult_.size() != n_instances()) + density_mult_.resize(n_instances(), density_mult_[0]); + + // Set density multiplier for the corresponding instance + density_mult_.at(instance) = + density / model::materials[mat_index]->density_gpcc(); + } else { + // Set density multiplier for all instances + for (auto& x : density_mult_) { + x = density / model::materials[mat_index]->density_gpcc(); + } + } + } else { + auto contained_cells = this->get_contained_cells(instance); + for (const auto& entry : contained_cells) { + auto& cell = model::cells[entry.first]; + assert(cell->type_ == Fill::MATERIAL); + auto& instances = entry.second; + for (auto instance : instances) { + cell->set_density(density, instance); + } + } + } +} + +void Cell::export_properties_hdf5(hid_t group) const +{ + // Create a group for this cell. + auto cell_group = create_group(group, fmt::format("cell {}", id_)); + + // Write temperature in [K] for one or more cell instances + vector temps; + for (auto sqrtkT_val : sqrtkT_) + temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); + write_dataset(cell_group, "temperature", temps); + + // Write density for one or more cell instances + if (type_ == Fill::MATERIAL && material_.size() > 0) { + vector density; + for (int32_t i = 0; i < density_mult_.size(); ++i) + density.push_back(this->density(i)); + + write_dataset(cell_group, "density", density); + } + + close_group(cell_group); +} + +void Cell::import_properties_hdf5(hid_t group) +{ + auto cell_group = open_group(group, fmt::format("cell {}", id_)); + + // Read temperatures from file + vector temps; + read_dataset(cell_group, "temperature", temps); + + // Ensure number of temperatures makes sense + auto n_temps = temps.size(); + if (n_temps > 1 && n_temps != n_instances()) { + fatal_error(fmt::format( + "Number of temperatures for cell {} doesn't match number of instances", + id_)); + } + + // Modify temperatures for the cell + sqrtkT_.clear(); + sqrtkT_.resize(temps.size()); + for (int64_t i = 0; i < temps.size(); ++i) { + this->set_temperature(temps[i], i); + } + + // Read densities + if (object_exists(cell_group, "density")) { + vector density; + read_dataset(cell_group, "density", density); + + // Ensure number of densities makes sense + auto n_density = density.size(); + if (n_density > 1 && n_density != n_instances()) { + fatal_error(fmt::format("Number of densities for cell {} " + "doesn't match number of instances", + id_)); + } + + // Set densities. + for (int32_t i = 0; i < n_density; ++i) { + this->set_density(density[i], i); + } + } + + close_group(cell_group); +} + +void Cell::to_hdf5(hid_t cell_group) const +{ + + // Create a group for this cell. + auto group = create_group(cell_group, fmt::format("cell {}", id_)); + + if (!name_.empty()) { + write_string(group, "name", name_, false); + } + + write_dataset(group, "universe", model::universes[universe_]->id_); + + to_hdf5_inner(group); + + // Write fill information. + if (type_ == Fill::MATERIAL) { + write_dataset(group, "fill_type", "material"); + std::vector mat_ids; + for (auto i_mat : material_) { + if (i_mat != MATERIAL_VOID) { + mat_ids.push_back(model::materials[i_mat]->id_); + } else { + mat_ids.push_back(MATERIAL_VOID); + } + } + if (mat_ids.size() == 1) { + write_dataset(group, "material", mat_ids[0]); + } else { + write_dataset(group, "material", mat_ids); + } + + std::vector temps; + for (auto sqrtkT_val : sqrtkT_) + temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); + write_dataset(group, "temperature", temps); + + write_dataset(group, "density_mult", density_mult_); + + } else if (type_ == Fill::UNIVERSE) { + write_dataset(group, "fill_type", "universe"); + write_dataset(group, "fill", model::universes[fill_]->id_); + if (translation_ != Position(0, 0, 0)) { + write_dataset(group, "translation", translation_); + } + if (!rotation_.empty()) { + if (rotation_.size() == 12) { + std::array rot {rotation_[9], rotation_[10], rotation_[11]}; + write_dataset(group, "rotation", rot); + } else { + write_dataset(group, "rotation", rotation_); + } + } + + } else if (type_ == Fill::LATTICE) { + write_dataset(group, "fill_type", "lattice"); + write_dataset(group, "lattice", model::lattices[fill_]->id_); + } + + close_group(group); +} + +//============================================================================== +// CSGCell implementation +//============================================================================== + +CSGCell::CSGCell(pugi::xml_node cell_node) +{ + if (check_for_node(cell_node, "id")) { + id_ = std::stoi(get_node_value(cell_node, "id")); + } else { + fatal_error("Must specify id of cell in geometry XML file."); + } + + if (check_for_node(cell_node, "name")) { + name_ = get_node_value(cell_node, "name"); + } + + if (check_for_node(cell_node, "universe")) { + universe_ = std::stoi(get_node_value(cell_node, "universe")); + } else { + universe_ = 0; + } + + // Check if the cell is the base of a virtual triso lattice + bool virtual_lattice_present = check_for_node(cell_node, "virtual_lattice"); + if (virtual_lattice_present) { + virtual_lattice_ = get_node_value_bool(cell_node, "virtual_lattice"); + if (virtual_lattice_) { + if (check_for_node(cell_node, "lower_left") && + check_for_node(cell_node, "pitch") && + check_for_node(cell_node, "shape")) { + vl_lower_left_ = get_node_array(cell_node, "lower_left"); + vl_pitch_ = get_node_array(cell_node, "pitch"); + vl_shape_ = get_node_array(cell_node, "shape"); + } else { + fatal_error(fmt::format("Lower_left, pitch and shape of the virtual " + "lattice must be specified for cell {}", + id_)); + } + } + } else { + virtual_lattice_ = false; + } + + if (check_for_node(cell_node, "triso_particle")) { + triso_particle_ = get_node_value_bool(cell_node, "triso_particle"); + } else { + triso_particle_ = false; + } + + // Make sure that either material or fill was specified, but not both. + bool fill_present = check_for_node(cell_node, "fill"); + bool material_present = check_for_node(cell_node, "material"); + if (!(fill_present || material_present)) { + fatal_error( + fmt::format("Neither material nor fill was specified for cell {}", id_)); + } + if (fill_present && material_present) { + fatal_error(fmt::format("Cell {} has both a material and a fill specified; " + "only one can be specified per cell", + id_)); + } + + if (fill_present) { + fill_ = std::stoi(get_node_value(cell_node, "fill")); + if (fill_ == universe_) { + fatal_error(fmt::format("Cell {} is filled with the same universe that " + "it is contained in.", + id_)); + } + } else { + fill_ = C_NONE; + } + + // Read the material element. There can be zero materials (filled with a + // universe), more than one material (distribmats), and some materials may + // be "void". + if (material_present) { + vector mats { + get_node_array(cell_node, "material", true)}; + if (mats.size() > 0) { + material_.reserve(mats.size()); + for (std::string mat : mats) { + if (mat.compare("void") == 0) { + material_.push_back(MATERIAL_VOID); + } else { + material_.push_back(std::stoi(mat)); + } + } + } else { + fatal_error(fmt::format( + "An empty material element was specified for cell {}", id_)); + } + } + + // Read the temperature element which may be distributed like materials. + if (check_for_node(cell_node, "temperature")) { + sqrtkT_ = get_node_array(cell_node, "temperature"); + sqrtkT_.shrink_to_fit(); + + // Make sure this is a material-filled cell. + if (material_.size() == 0) { + fatal_error(fmt::format( + "Cell {} was specified with a temperature but no material. Temperature" + "specification is only valid for cells filled with a material.", + id_)); + } + + // Make sure all temperatures are non-negative. + for (auto T : sqrtkT_) { + if (T < 0) { + fatal_error(fmt::format( + "Cell {} was specified with a negative temperature", id_)); + } + } + + // Convert to sqrt(k*T). + for (auto& T : sqrtkT_) { + T = std::sqrt(K_BOLTZMANN * T); + } + } + + // Read the density element which can be distributed similar to temperature. + // These get assigned to the density multiplier, requiring a division by + // the material density. + // Note: calculating the actual density multiplier is deferred until materials + // are finalized. density_mult_ contains the true density in the meantime. + if (check_for_node(cell_node, "density")) { + density_mult_ = get_node_array(cell_node, "density"); + density_mult_.shrink_to_fit(); + + // Make sure this is a material-filled cell. + if (material_.size() == 0) { + fatal_error(fmt::format( + "Cell {} was specified with a density but no material. Density" + "specification is only valid for cells filled with a material.", + id_)); + } + + // Make sure this is a non-void material. + for (auto mat_id : material_) { + if (mat_id == MATERIAL_VOID) { + fatal_error(fmt::format( + "Cell {} was specified with a density, but contains a void " + "material. Density specification is only valid for cells " + "filled with a non-void material.", + id_)); + } + } + + // Make sure all densities are non-negative and greater than zero. + for (auto rho : density_mult_) { + if (rho <= 0) { + fatal_error(fmt::format( + "Cell {} was specified with a density less than or equal to zero", + id_)); + } + } + } + + // Read the region specification. + std::string region_spec; + if (check_for_node(cell_node, "region")) { + region_spec = get_node_value(cell_node, "region"); + } + + // Get a tokenized representation of the region specification and apply De + // Morgans law + Region region(region_spec, id_); + region_ = region; + vector rpn = region_.generate_postfix(id_); + + if (virtual_lattice_) { + vl_triso_distribution_ = generate_triso_distribution( + vl_shape_, vl_pitch_, vl_lower_left_, rpn, id_); + } + + if (triso_particle_) { + if (rpn.size() != 1) { + fatal_error( + fmt::format("Wrong surface definition of triso particle cell {}", id_)); + } else { + model::surfaces[abs(rpn[0]) - 1]->connect_to_triso_base(id_, "particle"); + } + } + + // Read the translation vector. + if (check_for_node(cell_node, "translation")) { + if (fill_ == C_NONE) { + fatal_error(fmt::format("Cannot apply a translation to cell {}" + " because it is not filled with another universe", + id_)); + } + + auto xyz {get_node_array(cell_node, "translation")}; + if (xyz.size() != 3) { + fatal_error( + fmt::format("Non-3D translation vector applied to cell {}", id_)); + } + translation_ = xyz; + } + + // Read the rotation transform. + if (check_for_node(cell_node, "rotation")) { + auto rot {get_node_array(cell_node, "rotation")}; + set_rotation(rot); + } +} + +//============================================================================== + +void CSGCell::to_hdf5_inner(hid_t group_id) const +{ + write_string(group_id, "geom_type", "csg", false); + write_string(group_id, "region", region_.str(), false); +} + +//============================================================================== + +vector::iterator CSGCell::find_left_parenthesis( + vector::iterator start, const vector& infix) +{ + // start search at zero + int parenthesis_level = 0; + auto it = start; + while (it != infix.begin()) { + // look at two tokens at a time + int32_t one = *it; + int32_t two = *(it - 1); + + // decrement parenthesis level if there are two adjacent surfaces + if (one < OP_UNION && two < OP_UNION) { + parenthesis_level--; + // increment if there are two adjacent operators + } else if (one >= OP_UNION && two >= OP_UNION) { + parenthesis_level++; + } + + // if the level gets to zero, return the position + if (parenthesis_level == 0) { + // move the iterator back one before leaving the loop + // so that all tokens in the parenthesis block are included + it--; + break; + } + + // continue loop, one token at a time + it--; + } + return it; +} +std::pair CSGCell::distance( + Position r, Direction u, int32_t on_surface, GeometryState* p) const +{ + if (virtual_lattice_) { + return distance_in_virtual_lattice(r, u, on_surface, p); + } else { + return region_.distance(r, u, on_surface); + } +} + +std::pair CSGCell::distance_in_virtual_lattice( + Position r, Direction u, int32_t on_surface, GeometryState* p) const +{ + double min_dist {INFTY}; + int32_t i_surf {std::numeric_limits::max()}; + double min_dis_vl; + int32_t i_surf_vl; + + double max_dis = p->collision_distance(); + double tol_dis = 0; + vector dis_to_bou(3), dis_to_bou_max(3); + double u_value = sqrt(pow(u.x, 2) + pow(u.y, 2) + + pow(u.z, 2)); // don't know if u has been normalized + vector norm_u = {u.x / u_value, u.y / u_value, u.z / u_value}; + vector lat_ind(3); + vector temp_pos = {r.x, r.y, r.z}; + int loop_time; + for (int i = 0; i < 3; i++) { + lat_ind[i] = floor((temp_pos[i] - vl_lower_left_[i]) / vl_pitch_[i]); + if (lat_ind[i] == vl_shape_[i] && norm_u[i] < 0) { + lat_ind[i] = vl_shape_[i] - 1; + } + if (lat_ind[i] == -1 && norm_u[i] > 0) { + lat_ind[i] = 0; + } + } + + dis_to_bou = {INFTY, INFTY, INFTY}; + for (int i = 0; i < 3; i++) { + if (norm_u[i] > 0) { + dis_to_bou[i] = std::abs( + ((lat_ind[i] + 1) * vl_pitch_[i] + vl_lower_left_[i] - temp_pos[i]) / + norm_u[i]); + dis_to_bou_max[i] = vl_pitch_[i] / norm_u[i]; + } else if (norm_u[i] < 0) { + dis_to_bou[i] = + std::abs((lat_ind[i] * vl_pitch_[i] + vl_lower_left_[i] - temp_pos[i]) / + norm_u[i]); + dis_to_bou_max[i] = -vl_pitch_[i] / norm_u[i]; + } + } + + while (true) { + if (lat_ind[0] < 0 || lat_ind[0] >= vl_shape_[0] || lat_ind[1] < 0 || + lat_ind[1] >= vl_shape_[1] || lat_ind[2] < 0 || + lat_ind[2] >= vl_shape_[2]) + break; + + for (int token : + vl_triso_distribution_[lat_ind[0] + lat_ind[1] * vl_shape_[0] + + lat_ind[2] * vl_shape_[0] * vl_shape_[1]]) { + bool coincident {std::abs(token) == std::abs(on_surface)}; + double d {model::surfaces[abs(token) - 1]->distance(r, u, coincident)}; + if (d < min_dist) { + if (min_dist - d >= FP_PRECISION * min_dist) { + min_dist = d; + i_surf = -token; + } + } + } + + int mes_bou_crossed = 0; + if (dis_to_bou[1] < dis_to_bou[0]) { + mes_bou_crossed = 1; + } + if (dis_to_bou[2] < dis_to_bou[mes_bou_crossed]) { + mes_bou_crossed = 2; + } + + tol_dis = dis_to_bou[mes_bou_crossed]; + + if (min_dist < tol_dis) { + break; + } + + if (norm_u[mes_bou_crossed] > 0) { + lat_ind[mes_bou_crossed] += 1; + } else { + lat_ind[mes_bou_crossed] += -1; + } + + dis_to_bou[mes_bou_crossed] += dis_to_bou_max[mes_bou_crossed]; + + if (tol_dis > max_dis) { + break; + } + } + return {min_dist, i_surf}; +} + +//============================================================================== +// Region implementation +//============================================================================== + +Region::Region(std::string region_spec, int32_t cell_id) +{ + // Check if region_spec is not empty. + if (!region_spec.empty()) { + // Parse all halfspaces and operators except for intersection (whitespace). + for (int i = 0; i < region_spec.size();) { + if (region_spec[i] == '(') { + expression_.push_back(OP_LEFT_PAREN); + i++; + + } else if (region_spec[i] == ')') { + expression_.push_back(OP_RIGHT_PAREN); + i++; + + } else if (region_spec[i] == '|') { + expression_.push_back(OP_UNION); + i++; + + } else if (region_spec[i] == '~') { + expression_.push_back(OP_COMPLEMENT); + i++; + + } else if (region_spec[i] == '-' || region_spec[i] == '+' || + std::isdigit(region_spec[i])) { + // This is the start of a halfspace specification. Iterate j until we + // find the end, then push-back everything between i and j. + int j = i + 1; + while (j < region_spec.size() && std::isdigit(region_spec[j])) { + j++; + } + expression_.push_back(std::stoi(region_spec.substr(i, j - i))); + i = j; + + } else if (std::isspace(region_spec[i])) { + i++; + + } else { + auto err_msg = + fmt::format("Region specification contains invalid character, \"{}\"", + region_spec[i]); + fatal_error(err_msg); + } + } + + // Add in intersection operators where a missing operator is needed. + int i = 0; + while (i < expression_.size() - 1) { + bool left_compat { + (expression_[i] < OP_UNION) || (expression_[i] == OP_RIGHT_PAREN)}; + bool right_compat {(expression_[i + 1] < OP_UNION) || + (expression_[i + 1] == OP_LEFT_PAREN) || + (expression_[i + 1] == OP_COMPLEMENT)}; + if (left_compat && right_compat) { + expression_.insert(expression_.begin() + i + 1, OP_INTERSECTION); + } + i++; + } + + // Remove complement operators using DeMorgan's laws + auto it = std::find(expression_.begin(), expression_.end(), OP_COMPLEMENT); + while (it != expression_.end()) { + // Erase complement + expression_.erase(it); + + // Define stop given left parenthesis or not + auto stop = it; + if (*it == OP_LEFT_PAREN) { + int depth = 1; + do { + stop++; + if (*stop > OP_COMPLEMENT) { + if (*stop == OP_RIGHT_PAREN) { + depth--; + } else { + depth++; + } + } + } while (depth > 0); + it++; + } + + // apply DeMorgan's law to any surfaces/operators between these + // positions in the RPN + apply_demorgan(it, stop); + // update iterator position + it = std::find(expression_.begin(), expression_.end(), OP_COMPLEMENT); + } + + // Convert user IDs to surface indices. + for (auto& r : expression_) { + if (r < OP_UNION) { + const auto& it {model::surface_map.find(abs(r))}; + if (it == model::surface_map.end()) { + throw std::runtime_error { + "Invalid surface ID " + std::to_string(abs(r)) + + " specified in region for cell " + std::to_string(cell_id) + "."}; + } + r = (r > 0) ? it->second + 1 : -(it->second + 1); + } + } + + // Check if this is a simple cell. + simple_ = true; + for (int32_t token : expression_) { + if (token == OP_UNION) { + simple_ = false; + // Ensure intersections have precedence over unions + add_precedence(); + break; + } + } + + // If this cell is simple, remove all the superfluous operator tokens. + if (simple_) { + for (auto it = expression_.begin(); it != expression_.end(); it++) { + if (*it == OP_INTERSECTION || *it > OP_COMPLEMENT) { + expression_.erase(it); + it--; + } + } + } + expression_.shrink_to_fit(); + + } else { + simple_ = true; + } +} + +//============================================================================== + +void Region::apply_demorgan( + vector::iterator start, vector::iterator stop) +{ + do { + if (*start < OP_UNION) { + *start *= -1; + } else if (*start == OP_UNION) { + *start = OP_INTERSECTION; + } else if (*start == OP_INTERSECTION) { + *start = OP_UNION; + } + start++; + } while (start < stop); +} + +//============================================================================== +//! Add precedence for infix regions so intersections have higher +//! precedence than unions using parentheses. +//============================================================================== + +int64_t Region::add_parentheses(int64_t start) +{ + int32_t start_token = expression_[start]; + // Add left parenthesis and set new position to be after parenthesis + if (start_token == OP_UNION) { + start += 2; + } + expression_.insert(expression_.begin() + start - 1, OP_LEFT_PAREN); + + // Keep track of return iterator distance. If we don't encounter a left + // parenthesis, we return an iterator corresponding to wherever the right + // parenthesis is inserted. If a left parenthesis is encountered, an iterator + // corresponding to the left parenthesis is returned. Also note that we keep + // track of a *distance* instead of an iterator because the underlying memory + // allocation may change. + std::size_t return_it_dist = 0; + + // Add right parenthesis + // While the start iterator is within the bounds of infix + while (start + 1 < expression_.size()) { + start++; + + // If the current token is an operator and is different than the start token + if (expression_[start] >= OP_UNION && expression_[start] != start_token) { + // Skip wrapped regions but save iterator position to check precedence and + // add right parenthesis, right parenthesis position depends on the + // operator, when the operator is a union then do not include the operator + // in the region, when the operator is an intersection then include the + // operator and next surface + if (expression_[start] == OP_LEFT_PAREN) { + return_it_dist = start; + int depth = 1; + do { + start++; + if (expression_[start] > OP_COMPLEMENT) { + if (expression_[start] == OP_RIGHT_PAREN) { + depth--; + } else { + depth++; + } + } + } while (depth > 0); + } else { + if (start_token == OP_UNION) { + --start; + } + expression_.insert(expression_.begin() + start, OP_RIGHT_PAREN); + if (return_it_dist > 0) { + return return_it_dist; + } else { + return start - 1; + } + } + } + } + // If we get here a right parenthesis hasn't been placed, + // return iterator + expression_.push_back(OP_RIGHT_PAREN); + if (return_it_dist > 0) { + return return_it_dist; + } else { + return start - 1; + } +} + +//============================================================================== + +void Region::add_precedence() +{ + int32_t current_op = 0; + std::size_t current_dist = 0; + + for (int64_t i = 0; i < expression_.size(); i++) { + int32_t token = expression_[i]; + + if (token == OP_UNION || token == OP_INTERSECTION) { + if (current_op == 0) { + // Set the current operator if is hasn't been set + current_op = token; + current_dist = i; + } else if (token != current_op) { + // If the current operator doesn't match the token, add parenthesis to + // assert precedence + if (current_op == OP_INTERSECTION) { + i = add_parentheses(current_dist); + } else { + i = add_parentheses(i); + } + current_op = 0; + current_dist = 0; + } + } else if (token > OP_COMPLEMENT) { + // If the token is a parenthesis reset the current operator + current_op = 0; + current_dist = 0; + } + } +} + //============================================================================== //! Convert infix region specification to Reverse Polish Notation (RPN) //! //! This function uses the shunting-yard algorithm. //============================================================================== -vector generate_rpn(int32_t cell_id, vector infix) +vector Region::generate_postfix(int32_t cell_id) const { vector rpn; vector stack; - for (int32_t token : infix) { + for (int32_t token : expression_) { if (token < OP_UNION) { // If token is not an operator, add it to output rpn.push_back(token); @@ -224,643 +1061,13 @@ vector generate_rpn(int32_t cell_id, vector infix) return rpn; } -//============================================================================== -// Universe implementation //============================================================================== -void Universe::to_hdf5(hid_t universes_group) const +std::string Region::str() const { - // Create a group for this universe. - auto group = create_group(universes_group, fmt::format("universe {}", id_)); - - // Write the geometry representation type. - write_string(group, "geom_type", "csg", false); - - // Write the contained cells. - if (cells_.size() > 0) { - vector cell_ids; - for (auto i_cell : cells_) - cell_ids.push_back(model::cells[i_cell]->id_); - write_dataset(group, "cells", cell_ids); - } - - close_group(group); -} - -bool Universe::find_cell(Particle& p) const -{ - if (filled_with_triso_base_ != -1) { - Cell& c {*model::cells[model::cell_map[filled_with_triso_base_]]}; - vector lat_ind(3); - Position r {p.r_local()}; - lat_ind[0]=std::max(std::min(floor((r.x-c.vl_lower_left_[0])/c.vl_pitch_[0]),c.vl_shape_[0]-1),0); - lat_ind[1]=std::max(std::min(floor((r.y-c.vl_lower_left_[1])/c.vl_pitch_[1]),c.vl_shape_[1]-1),0); - lat_ind[2]=std::max(std::min(floor((r.z-c.vl_lower_left_[2])/c.vl_pitch_[2]),c.vl_shape_[2]-1),0); - - - int32_t i_univ = p.coord(p.n_coord() - 1).universe; - for (int token : c.vl_triso_distribution_[lat_ind[0]+lat_ind[1]*c.vl_shape_[0]+lat_ind[2]*c.vl_shape_[0]*c.vl_shape_[1]]) { - vector triso_center=model::surfaces[abs(token) - 1]->get_center(); - double triso_radius=model::surfaces[abs(token) - 1]->get_radius(); - if (model::cells[model::cell_map[model::surfaces[abs(token) - 1]->triso_base_index_]]->universe_!= i_univ) continue; - if (abs(token)==abs(p.surface())) { - if (p.surface() < 0) { - p.coord(p.n_coord() - 1).cell = model::cell_map[model::surfaces[abs(token) - 1]->triso_particle_index_]; - return true; - } else { - p.coord(p.n_coord() - 1).cell = model::cell_map[filled_with_triso_base_]; - return true; - } - } - if (pow(r.x-triso_center[0],2)+pow(r.y-triso_center[1],2)+pow(r.z-triso_center[2],2) < pow(triso_radius,2)) { - p.coord(p.n_coord() - 1).cell = model::cell_map[model::surfaces[abs(token) - 1]->triso_particle_index_]; - return true; - } - } - if (model::cells[model::cell_map[filled_with_triso_base_]]->universe_== i_univ) { - p.coord(p.n_coord() - 1).cell = model::cell_map[filled_with_triso_base_]; - return true; - } - } - const auto& cells { - !partitioner_ ? cells_ : partitioner_->get_cells(p.r_local(), p.u_local())}; - - for (auto it = cells.begin(); it != cells.end(); it++) { - int32_t i_cell = *it; - int32_t i_univ = p.coord(p.n_coord() - 1).universe; - if (model::cells[i_cell]->universe_ != i_univ) - continue; - - // Check if this cell contains the particle; - Position r {p.r_local()}; - Direction u {p.u_local()}; - auto surf = p.surface(); - if (model::cells[i_cell]->contains(r, u, surf)) { - p.coord(p.n_coord() - 1).cell = i_cell; - return true; - } - } - return false; -} - -BoundingBox Universe::bounding_box() const -{ - BoundingBox bbox = {INFTY, -INFTY, INFTY, -INFTY, INFTY, -INFTY}; - if (cells_.size() == 0) { - return {}; - } else { - for (const auto& cell : cells_) { - auto& c = model::cells[cell]; - bbox |= c->bounding_box(); - } - } - return bbox; -} - -//============================================================================== -// Cell implementation -//============================================================================== - -void Cell::set_rotation(const vector& rot) -{ - if (fill_ == C_NONE) { - fatal_error(fmt::format("Cannot apply a rotation to cell {}" - " because it is not filled with another universe", - id_)); - } - - if (rot.size() != 3 && rot.size() != 9) { - fatal_error(fmt::format("Non-3D rotation vector applied to cell {}", id_)); - } - - // Compute and store the rotation matrix. - rotation_.clear(); - rotation_.reserve(rot.size() == 9 ? 9 : 12); - if (rot.size() == 3) { - double phi = -rot[0] * PI / 180.0; - double theta = -rot[1] * PI / 180.0; - double psi = -rot[2] * PI / 180.0; - rotation_.push_back(std::cos(theta) * std::cos(psi)); - rotation_.push_back(-std::cos(phi) * std::sin(psi) + - std::sin(phi) * std::sin(theta) * std::cos(psi)); - rotation_.push_back(std::sin(phi) * std::sin(psi) + - std::cos(phi) * std::sin(theta) * std::cos(psi)); - rotation_.push_back(std::cos(theta) * std::sin(psi)); - rotation_.push_back(std::cos(phi) * std::cos(psi) + - std::sin(phi) * std::sin(theta) * std::sin(psi)); - rotation_.push_back(-std::sin(phi) * std::cos(psi) + - std::cos(phi) * std::sin(theta) * std::sin(psi)); - rotation_.push_back(-std::sin(theta)); - rotation_.push_back(std::sin(phi) * std::cos(theta)); - rotation_.push_back(std::cos(phi) * std::cos(theta)); - - // When user specifies angles, write them at end of vector - rotation_.push_back(rot[0]); - rotation_.push_back(rot[1]); - rotation_.push_back(rot[2]); - } else { - std::copy(rot.begin(), rot.end(), std::back_inserter(rotation_)); - } -} - -double Cell::temperature(int32_t instance) const -{ - if (sqrtkT_.size() < 1) { - throw std::runtime_error {"Cell temperature has not yet been set."}; - } - - if (instance >= 0) { - double sqrtkT = sqrtkT_.size() == 1 ? sqrtkT_.at(0) : sqrtkT_.at(instance); - return sqrtkT * sqrtkT / K_BOLTZMANN; - } else { - return sqrtkT_[0] * sqrtkT_[0] / K_BOLTZMANN; - } -} - -void Cell::set_temperature(double T, int32_t instance, bool set_contained) -{ - if (settings::temperature_method == TemperatureMethod::INTERPOLATION) { - if (T < data::temperature_min) { - throw std::runtime_error {"Temperature is below minimum temperature at " - "which data is available."}; - } else if (T > data::temperature_max) { - throw std::runtime_error {"Temperature is above maximum temperature at " - "which data is available."}; - } - } - - if (type_ == Fill::MATERIAL) { - if (instance >= 0) { - // If temperature vector is not big enough, resize it first - if (sqrtkT_.size() != n_instances_) - sqrtkT_.resize(n_instances_, sqrtkT_[0]); - - // Set temperature for the corresponding instance - sqrtkT_.at(instance) = std::sqrt(K_BOLTZMANN * T); - } else { - // Set temperature for all instances - for (auto& T_ : sqrtkT_) { - T_ = std::sqrt(K_BOLTZMANN * T); - } - } - } else { - if (!set_contained) { - throw std::runtime_error { - fmt::format("Attempted to set the temperature of cell {} " - "which is not filled by a material.", - id_)}; - } - - auto contained_cells = this->get_contained_cells(instance); - for (const auto& entry : contained_cells) { - auto& cell = model::cells[entry.first]; - Expects(cell->type_ == Fill::MATERIAL); - auto& instances = entry.second; - for (auto instance : instances) { - cell->set_temperature(T, instance); - } - } - } -} - -void Cell::export_properties_hdf5(hid_t group) const -{ - // Create a group for this cell. - auto cell_group = create_group(group, fmt::format("cell {}", id_)); - - // Write temperature in [K] for one or more cell instances - vector temps; - for (auto sqrtkT_val : sqrtkT_) - temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); - write_dataset(cell_group, "temperature", temps); - - close_group(cell_group); -} - -void Cell::import_properties_hdf5(hid_t group) -{ - auto cell_group = open_group(group, fmt::format("cell {}", id_)); - - // Read temperatures from file - vector temps; - read_dataset(cell_group, "temperature", temps); - - // Ensure number of temperatures makes sense - auto n_temps = temps.size(); - if (n_temps > 1 && n_temps != n_instances_) { - throw std::runtime_error(fmt::format( - "Number of temperatures for cell {} doesn't match number of instances", - id_)); - } - - // Modify temperatures for the cell - sqrtkT_.clear(); - sqrtkT_.resize(temps.size()); - for (gsl::index i = 0; i < temps.size(); ++i) { - this->set_temperature(temps[i], i); - } - - close_group(cell_group); -} - -void Cell::to_hdf5(hid_t cell_group) const -{ - - // Create a group for this cell. - auto group = create_group(cell_group, fmt::format("cell {}", id_)); - - if (!name_.empty()) { - write_string(group, "name", name_, false); - } - - write_dataset(group, "universe", model::universes[universe_]->id_); - - to_hdf5_inner(group); - - // Write fill information. - if (type_ == Fill::MATERIAL) { - write_dataset(group, "fill_type", "material"); - std::vector mat_ids; - for (auto i_mat : material_) { - if (i_mat != MATERIAL_VOID) { - mat_ids.push_back(model::materials[i_mat]->id_); - } else { - mat_ids.push_back(MATERIAL_VOID); - } - } - if (mat_ids.size() == 1) { - write_dataset(group, "material", mat_ids[0]); - } else { - write_dataset(group, "material", mat_ids); - } - - std::vector temps; - for (auto sqrtkT_val : sqrtkT_) - temps.push_back(sqrtkT_val * sqrtkT_val / K_BOLTZMANN); - write_dataset(group, "temperature", temps); - - } else if (type_ == Fill::UNIVERSE) { - write_dataset(group, "fill_type", "universe"); - write_dataset(group, "fill", model::universes[fill_]->id_); - if (translation_ != Position(0, 0, 0)) { - write_dataset(group, "translation", translation_); - } - if (!rotation_.empty()) { - if (rotation_.size() == 12) { - std::array rot {rotation_[9], rotation_[10], rotation_[11]}; - write_dataset(group, "rotation", rot); - } else { - write_dataset(group, "rotation", rotation_); - } - } - - } else if (type_ == Fill::LATTICE) { - write_dataset(group, "fill_type", "lattice"); - write_dataset(group, "lattice", model::lattices[fill_]->id_); - } - - close_group(group); -} - -//============================================================================== -// CSGCell implementation -//============================================================================== - -// default constructor -CSGCell::CSGCell() -{ - geom_type_ = GeometryType::CSG; -} - -CSGCell::CSGCell(pugi::xml_node cell_node) -{ - geom_type_ = GeometryType::CSG; - - if (check_for_node(cell_node, "id")) { - id_ = std::stoi(get_node_value(cell_node, "id")); - } else { - fatal_error("Must specify id of cell in geometry XML file."); - } - - if (check_for_node(cell_node, "name")) { - name_ = get_node_value(cell_node, "name"); - } - - if (check_for_node(cell_node, "universe")) { - universe_ = std::stoi(get_node_value(cell_node, "universe")); - } else { - universe_ = 0; - } - - // Check if the cell is the base of a virtual triso lattice - bool virtual_lattice_present = check_for_node(cell_node, "virtual_lattice"); - if (virtual_lattice_present) { - virtual_lattice_ = get_node_value_bool(cell_node, "virtual_lattice"); - if (virtual_lattice_) { - if (check_for_node(cell_node, "lower_left") && check_for_node(cell_node, "pitch") && check_for_node(cell_node, "shape")) { - vl_lower_left_ = get_node_array(cell_node, "lower_left"); - vl_pitch_ = get_node_array(cell_node, "pitch"); - vl_shape_ = get_node_array(cell_node, "shape"); - } else { - fatal_error(fmt::format("Lower_left, pitch and shape of the virtual lattice must be specified for cell {}", id_)); - } - } - } else { - virtual_lattice_ = false; - } - - if (check_for_node(cell_node, "triso_particle")) { - triso_particle_ = get_node_value_bool(cell_node, "triso_particle"); - } else { - triso_particle_ = false; - } - - // Make sure that either material or fill was specified, but not both. - bool fill_present = check_for_node(cell_node, "fill"); - bool material_present = check_for_node(cell_node, "material"); - if (!(fill_present || material_present)) { - fatal_error( - fmt::format("Neither material nor fill was specified for cell {}", id_)); - } - if (fill_present && material_present) { - fatal_error(fmt::format("Cell {} has both a material and a fill specified; " - "only one can be specified per cell", - id_)); - } - - if (fill_present) { - fill_ = std::stoi(get_node_value(cell_node, "fill")); - if (fill_ == universe_) { - fatal_error(fmt::format("Cell {} is filled with the same universe that" - "it is contained in.", - id_)); - } - } else { - fill_ = C_NONE; - } - - // Read the material element. There can be zero materials (filled with a - // universe), more than one material (distribmats), and some materials may - // be "void". - if (material_present) { - vector mats { - get_node_array(cell_node, "material", true)}; - if (mats.size() > 0) { - material_.reserve(mats.size()); - for (std::string mat : mats) { - if (mat.compare("void") == 0) { - material_.push_back(MATERIAL_VOID); - } else { - material_.push_back(std::stoi(mat)); - } - } - } else { - fatal_error(fmt::format( - "An empty material element was specified for cell {}", id_)); - } - } - - // Read the temperature element which may be distributed like materials. - if (check_for_node(cell_node, "temperature")) { - sqrtkT_ = get_node_array(cell_node, "temperature"); - sqrtkT_.shrink_to_fit(); - - // Make sure this is a material-filled cell. - if (material_.size() == 0) { - fatal_error(fmt::format( - "Cell {} was specified with a temperature but no material. Temperature" - "specification is only valid for cells filled with a material.", - id_)); - } - - // Make sure all temperatures are non-negative. - for (auto T : sqrtkT_) { - if (T < 0) { - fatal_error(fmt::format( - "Cell {} was specified with a negative temperature", id_)); - } - } - - // Convert to sqrt(k*T). - for (auto& T : sqrtkT_) { - T = std::sqrt(K_BOLTZMANN * T); - } - } - - // Read the region specification. - std::string region_spec; - if (check_for_node(cell_node, "region")) { - region_spec = get_node_value(cell_node, "region"); - } - - // Get a tokenized representation of the region specification. - region_ = tokenize(region_spec); - region_.shrink_to_fit(); - - // Convert user IDs to surface indices. - for (auto& r : region_) { - if (r < OP_UNION) { - const auto& it {model::surface_map.find(abs(r))}; - if (it == model::surface_map.end()) { - throw std::runtime_error { - "Invalid surface ID " + std::to_string(abs(r)) + - " specified in region for cell " + std::to_string(id_) + "."}; - } - r = (r > 0) ? it->second + 1 : -(it->second + 1); - } - } - - // Convert the infix region spec to RPN. - rpn_ = generate_rpn(id_, region_); - - // Check if this is a simple cell. - simple_ = true; - for (int32_t token : rpn_) { - if ((token == OP_COMPLEMENT) || (token == OP_UNION)) { - simple_ = false; - break; - } - } - - // If this cell is simple, remove all the superfluous operator tokens. - if (simple_) { - size_t i0 = 0; - size_t i1 = 0; - while (i1 < rpn_.size()) { - if (rpn_[i1] < OP_UNION) { - rpn_[i0] = rpn_[i1]; - ++i0; - } - ++i1; - } - rpn_.resize(i0); - } - rpn_.shrink_to_fit(); - - if (virtual_lattice_) { - vl_triso_distribution_ = generate_triso_distribution(vl_shape_, vl_pitch_, vl_lower_left_, rpn_, id_); - } - - if (triso_particle_) { - if (rpn_.size() != 1) { - fatal_error(fmt::format("Wrong surface definition of triso particle cell {}", id_)); - } else { - model::surfaces[abs(rpn_[0]) - 1]->connect_to_triso_base(id_, "particle"); - } - } - - // Read the translation vector. - if (check_for_node(cell_node, "translation")) { - if (fill_ == C_NONE) { - fatal_error(fmt::format("Cannot apply a translation to cell {}" - " because it is not filled with another universe", - id_)); - } - - auto xyz {get_node_array(cell_node, "translation")}; - if (xyz.size() != 3) { - fatal_error( - fmt::format("Non-3D translation vector applied to cell {}", id_)); - } - translation_ = xyz; - } - - // Read the rotation transform. - if (check_for_node(cell_node, "rotation")) { - auto rot {get_node_array(cell_node, "rotation")}; - set_rotation(rot); - } -} - -//============================================================================== - -bool CSGCell::contains(Position r, Direction u, int32_t on_surface) const -{ - if (simple_) { - return contains_simple(r, u, on_surface); - } else { - return contains_complex(r, u, on_surface); - } -} - -//============================================================================== - -std::pair CSGCell::distance( - Position r, Direction u, int32_t on_surface, Particle* p) const -{ - double min_dist {INFTY}; - int32_t i_surf {std::numeric_limits::max()}; - double min_dis_vl; - int32_t i_surf_vl; - if (virtual_lattice_) { - double max_dis = p->collision_distance(); - double tol_dis = 0; - vector dis_to_bou(3), dis_to_bou_max(3); - double u_value = sqrt(pow(u.x,2)+pow(u.y,2)+pow(u.z,2)); //don't know if u has been normalized - vector norm_u = {u.x/u_value, u.y/u_value, u.z/u_value}; - vector lat_ind(3); - vector temp_pos ={r.x,r.y,r.z}; - int loop_time; - for (int i=0; i<3; i++) { - lat_ind[i]=floor((temp_pos[i]-vl_lower_left_[i])/vl_pitch_[i]); - if (lat_ind[i]==vl_shape_[i] && norm_u[i]<0) { - lat_ind[i]=vl_shape_[i]-1; - } - if (lat_ind[i]==-1 && norm_u[i]>0) { - lat_ind[i]=0; - } - } - - dis_to_bou = {INFTY,INFTY,INFTY}; - for (int i=0; i<3; i++) { - if (norm_u[i] > 0) { - dis_to_bou[i]=std::abs(((lat_ind[i]+1)*vl_pitch_[i]+vl_lower_left_[i]-temp_pos[i])/norm_u[i]); - dis_to_bou_max[i]=vl_pitch_[i]/norm_u[i]; - } else if (norm_u[i] < 0){ - dis_to_bou[i]=std::abs((lat_ind[i]*vl_pitch_[i]+vl_lower_left_[i]-temp_pos[i])/norm_u[i]); - dis_to_bou_max[i]=-vl_pitch_[i]/norm_u[i]; - } - } - - while(true) { - if (lat_ind[0] < 0 || lat_ind[0] >= vl_shape_[0] ||\ - lat_ind[1] < 0 || lat_ind[1] >= vl_shape_[1] ||\ - lat_ind[2] < 0 || lat_ind[2] >= vl_shape_[2]) break; - - for (int token : vl_triso_distribution_[lat_ind[0]+lat_ind[1]*vl_shape_[0]+lat_ind[2]*vl_shape_[0]*vl_shape_[1]]) { - bool coincident {std::abs(token) == std::abs(on_surface)}; - double d {model::surfaces[abs(token) - 1]->distance(r, u, coincident)}; - if (d < min_dist) { - if (min_dist - d >= FP_PRECISION * min_dist) { - min_dist = d; - i_surf = -token; - } - } - } - - int mes_bou_crossed=0; - if (dis_to_bou[1] < dis_to_bou[0]) { - mes_bou_crossed=1; - } - if (dis_to_bou[2] < dis_to_bou[mes_bou_crossed]) { - mes_bou_crossed=2; - } - - tol_dis = dis_to_bou[mes_bou_crossed]; - - if (min_dist < tol_dis) { - break; - } - - if (norm_u[mes_bou_crossed] > 0) { - lat_ind[mes_bou_crossed] += 1; - } else { - lat_ind[mes_bou_crossed] += -1; - } - - dis_to_bou[mes_bou_crossed] += dis_to_bou_max[mes_bou_crossed]; - - if (tol_dis > max_dis) { - break; - } - } - - - } else { - for (int32_t token : rpn_) { - // Ignore this token if it corresponds to an operator rather than a region. - if (token >= OP_UNION) - continue; - - // Calculate the distance to this surface. - // Note the off-by-one indexing - bool coincident {std::abs(token) == std::abs(on_surface)}; - double d {model::surfaces[abs(token) - 1]->distance(r, u, coincident)}; - - // Check if this distance is the new minimum. - if (d < min_dist) { - if (min_dist - d >= FP_PRECISION * min_dist) { - min_dist = d; - i_surf = -token; - } - } - } - } - - return {min_dist, i_surf}; -} - -//============================================================================== - -void CSGCell::to_hdf5_inner(hid_t group_id) const -{ - - write_string(group_id, "geom_type", "csg", false); - - // Write the region specification. - if (!region_.empty()) { - std::stringstream region_spec {}; - for (int32_t token : region_) { + std::stringstream region_spec {}; + if (!expression_.empty()) { + for (int32_t token : expression_) { if (token == OP_LEFT_PAREN) { region_spec << " ("; } else if (token == OP_RIGHT_PAREN) { @@ -876,120 +1083,56 @@ void CSGCell::to_hdf5_inner(hid_t group_id) const region_spec << " " << ((token > 0) ? surf_id : -surf_id); } } - write_string(group_id, "region", region_spec.str(), false); } -} - -BoundingBox CSGCell::bounding_box_simple() const -{ - BoundingBox bbox; - for (int32_t token : rpn_) { - bbox &= model::surfaces[abs(token) - 1]->bounding_box(token > 0); - } - return bbox; -} - -void CSGCell::apply_demorgan( - vector::iterator start, vector::iterator stop) -{ - while (start < stop) { - if (*start < OP_UNION) { - *start *= -1; - } else if (*start == OP_UNION) { - *start = OP_INTERSECTION; - } else if (*start == OP_INTERSECTION) { - *start = OP_UNION; - } - start++; - } -} - -vector::iterator CSGCell::find_left_parenthesis( - vector::iterator start, const vector& rpn) -{ - // start search at zero - int parenthesis_level = 0; - auto it = start; - while (it != rpn.begin()) { - // look at two tokens at a time - int32_t one = *it; - int32_t two = *(it - 1); - - // decrement parenthesis level if there are two adjacent surfaces - if (one < OP_UNION && two < OP_UNION) { - parenthesis_level--; - // increment if there are two adjacent operators - } else if (one >= OP_UNION && two >= OP_UNION) { - parenthesis_level++; - } - - // if the level gets to zero, return the position - if (parenthesis_level == 0) { - // move the iterator back one before leaving the loop - // so that all tokens in the parenthesis block are included - it--; - break; - } - - // continue loop, one token at a time - it--; - } - return it; -} - -void CSGCell::remove_complement_ops(vector& rpn) -{ - auto it = std::find(rpn.begin(), rpn.end(), OP_COMPLEMENT); - while (it != rpn.end()) { - // find the opening parenthesis (if any) - auto left = find_left_parenthesis(it, rpn); - vector tmp(left, it + 1); - - // apply DeMorgan's law to any surfaces/operators between these - // positions in the RPN - apply_demorgan(left, it); - // remove complement operator - rpn.erase(it); - // update iterator position - it = std::find(rpn.begin(), rpn.end(), OP_COMPLEMENT); - } -} - -BoundingBox CSGCell::bounding_box_complex(vector rpn) -{ - // remove complements by adjusting surface signs and operators - remove_complement_ops(rpn); - - vector stack(rpn.size()); - int i_stack = -1; - - for (auto& token : rpn) { - if (token == OP_UNION) { - stack[i_stack - 1] = stack[i_stack - 1] | stack[i_stack]; - i_stack--; - } else if (token == OP_INTERSECTION) { - stack[i_stack - 1] = stack[i_stack - 1] & stack[i_stack]; - i_stack--; - } else { - i_stack++; - stack[i_stack] = model::surfaces[abs(token) - 1]->bounding_box(token > 0); - } - } - - Ensures(i_stack == 0); - return stack.front(); -} - -BoundingBox CSGCell::bounding_box() const -{ - return simple_ ? bounding_box_simple() : bounding_box_complex(rpn_); + return region_spec.str(); } //============================================================================== -bool CSGCell::contains_simple(Position r, Direction u, int32_t on_surface) const +std::pair Region::distance( + Position r, Direction u, int32_t on_surface) const { - for (int32_t token : rpn_) { + double min_dist {INFTY}; + int32_t i_surf {std::numeric_limits::max()}; + + for (int32_t token : expression_) { + // Ignore this token if it corresponds to an operator rather than a region. + if (token >= OP_UNION) + continue; + + // Calculate the distance to this surface. + // Note the off-by-one indexing + bool coincident {std::abs(token) == std::abs(on_surface)}; + double d {model::surfaces[abs(token) - 1]->distance(r, u, coincident)}; + + // Check if this distance is the new minimum. + if (d < min_dist) { + if (min_dist - d >= FP_PRECISION * min_dist) { + min_dist = d; + i_surf = -token; + } + } + } + + return {min_dist, i_surf}; +} + +//============================================================================== + +bool Region::contains(Position r, Direction u, int32_t on_surface) const +{ + if (simple_) { + return contains_simple(r, u, on_surface); + } else { + return contains_complex(r, u, on_surface); + } +} + +//============================================================================== + +bool Region::contains_simple(Position r, Direction u, int32_t on_surface) const +{ + for (int32_t token : expression_) { // Assume that no tokens are operators. Evaluate the sense of particle with // respect to the surface and see if the token matches the sense. If the // particle's surface attribute is set and matches the token, that @@ -1010,198 +1153,131 @@ bool CSGCell::contains_simple(Position r, Direction u, int32_t on_surface) const //============================================================================== -bool CSGCell::contains_complex( - Position r, Direction u, int32_t on_surface) const +bool Region::contains_complex(Position r, Direction u, int32_t on_surface) const { - // Make a stack of booleans. We don't know how big it needs to be, but we do - // know that rpn.size() is an upper-bound. - vector stack(rpn_.size()); - int i_stack = -1; + bool in_cell = true; + int total_depth = 0; - for (int32_t token : rpn_) { - // If the token is a binary operator (intersection/union), apply it to - // the last two items on the stack. If the token is a unary operator - // (complement), apply it to the last item on the stack. - if (token == OP_UNION) { - stack[i_stack - 1] = stack[i_stack - 1] || stack[i_stack]; - i_stack--; - } else if (token == OP_INTERSECTION) { - stack[i_stack - 1] = stack[i_stack - 1] && stack[i_stack]; - i_stack--; - } else if (token == OP_COMPLEMENT) { - stack[i_stack] = !stack[i_stack]; - } else { - // If the token is not an operator, evaluate the sense of particle with - // respect to the surface and see if the token matches the sense. If the - // particle's surface attribute is set and matches the token, that - // overrides the determination based on sense(). - i_stack++; + // For each token + for (auto it = expression_.begin(); it != expression_.end(); it++) { + int32_t token = *it; + + // If the token is a surface evaluate the sense + // If the token is a union or intersection check to + // short circuit + if (token < OP_UNION) { if (token == on_surface) { - stack[i_stack] = true; + in_cell = true; } else if (-token == on_surface) { - stack[i_stack] = false; + in_cell = false; } else { // Note the off-by-one indexing bool sense = model::surfaces[abs(token) - 1]->sense(r, u); - stack[i_stack] = (sense == (token > 0)); + in_cell = (sense == (token > 0)); } + } else if ((token == OP_UNION && in_cell == true) || + (token == OP_INTERSECTION && in_cell == false)) { + // If the total depth is zero return + if (total_depth == 0) { + return in_cell; + } + + total_depth--; + + // While the iterator is within the bounds of the vector + int depth = 1; + do { + // Get next token + it++; + int32_t next_token = *it; + + // If the token is an a parenthesis + if (next_token > OP_COMPLEMENT) { + // Adjust depth accordingly + if (next_token == OP_RIGHT_PAREN) { + depth--; + } else { + depth++; + } + } + } while (depth > 0); + } else if (token == OP_LEFT_PAREN) { + total_depth++; + } else if (token == OP_RIGHT_PAREN) { + total_depth--; } } + return in_cell; +} - if (i_stack == 0) { - // The one remaining bool on the stack indicates whether the particle is - // in the cell. - return stack[i_stack]; +//============================================================================== + +BoundingBox Region::bounding_box(int32_t cell_id) const +{ + if (simple_) { + return bounding_box_simple(); } else { - // This case occurs if there is no region specification since i_stack will - // still be -1. - return true; + auto postfix = generate_postfix(cell_id); + return bounding_box_complex(postfix); } } -//============================================================================== -// UniversePartitioner implementation //============================================================================== -UniversePartitioner::UniversePartitioner(const Universe& univ) +BoundingBox Region::bounding_box_simple() const { - // Define an ordered set of surface indices that point to z-planes. Use a - // functor to to order the set by the z0_ values of the corresponding planes. - struct compare_surfs { - bool operator()(const int32_t& i_surf, const int32_t& j_surf) const - { - const auto* surf = model::surfaces[i_surf].get(); - const auto* zplane = dynamic_cast(surf); - double zi = zplane->z0_; - surf = model::surfaces[j_surf].get(); - zplane = dynamic_cast(surf); - double zj = zplane->z0_; - return zi < zj; - } - }; - std::set surf_set; - - // Find all of the z-planes in this universe. A set is used here for the - // O(log(n)) insertions that will ensure entries are not repeated. - for (auto i_cell : univ.cells_) { - for (auto token : model::cells[i_cell]->rpn_) { - if (token < OP_UNION) { - auto i_surf = std::abs(token) - 1; - const auto* surf = model::surfaces[i_surf].get(); - if (const auto* zplane = dynamic_cast(surf)) - surf_set.insert(i_surf); - } - } - } - - // Populate the surfs_ vector from the ordered set. - surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end()); - - // Populate the partition lists. - partitions_.resize(surfs_.size() + 1); - for (auto i_cell : univ.cells_) { - // It is difficult to determine the bounds of a complex cell, so add complex - // cells to all partitions. - if (!model::cells[i_cell]->simple_) { - for (auto& p : partitions_) - p.push_back(i_cell); - continue; - } - - // Find the tokens for bounding z-planes. - int32_t lower_token = 0, upper_token = 0; - double min_z, max_z; - for (auto token : model::cells[i_cell]->rpn_) { - if (token < OP_UNION) { - const auto* surf = model::surfaces[std::abs(token) - 1].get(); - if (const auto* zplane = dynamic_cast(surf)) { - if (lower_token == 0 || zplane->z0_ < min_z) { - lower_token = token; - min_z = zplane->z0_; - } - if (upper_token == 0 || zplane->z0_ > max_z) { - upper_token = token; - max_z = zplane->z0_; - } - } - } - } - - // If there are no bounding z-planes, add this cell to all partitions. - if (lower_token == 0) { - for (auto& p : partitions_) - p.push_back(i_cell); - continue; - } - - // Find the first partition this cell lies in. If the lower_token indicates - // a negative halfspace, then the cell is unbounded in the lower direction - // and it lies in the first partition onward. Otherwise, it is bounded by - // the positive halfspace given by the lower_token. - int first_partition = 0; - if (lower_token > 0) { - for (int i = 0; i < surfs_.size(); ++i) { - if (lower_token == surfs_[i] + 1) { - first_partition = i + 1; - break; - } - } - } - - // Find the last partition this cell lies in. The logic is analogous to the - // logic for first_partition. - int last_partition = surfs_.size(); - if (upper_token < 0) { - for (int i = first_partition; i < surfs_.size(); ++i) { - if (upper_token == -(surfs_[i] + 1)) { - last_partition = i; - break; - } - } - } - - // Add the cell to all relevant partitions. - for (int i = first_partition; i <= last_partition; ++i) { - partitions_[i].push_back(i_cell); - } + BoundingBox bbox; + for (int32_t token : expression_) { + bbox &= model::surfaces[abs(token) - 1]->bounding_box(token > 0); } + return bbox; } -const vector& UniversePartitioner::get_cells( - Position r, Direction u) const -{ - // Perform a binary search for the partition containing the given coordinates. - int left = 0; - int middle = (surfs_.size() - 1) / 2; - int right = surfs_.size() - 1; - while (true) { - // Check the sense of the coordinates for the current surface. - const auto& surf = *model::surfaces[surfs_[middle]]; - if (surf.sense(r, u)) { - // The coordinates lie in the positive halfspace. Recurse if there are - // more surfaces to check. Otherwise, return the cells on the positive - // side of this surface. - int right_leaf = right - (right - middle) / 2; - if (right_leaf != middle) { - left = middle + 1; - middle = right_leaf; - } else { - return partitions_[middle + 1]; - } +//============================================================================== +BoundingBox Region::bounding_box_complex(vector postfix) const +{ + vector stack(postfix.size()); + int i_stack = -1; + + for (auto& token : postfix) { + if (token == OP_UNION) { + stack[i_stack - 1] = stack[i_stack - 1] | stack[i_stack]; + i_stack--; + } else if (token == OP_INTERSECTION) { + stack[i_stack - 1] = stack[i_stack - 1] & stack[i_stack]; + i_stack--; } else { - // The coordinates lie in the negative halfspace. Recurse if there are - // more surfaces to check. Otherwise, return the cells on the negative - // side of this surface. - int left_leaf = left + (middle - left) / 2; - if (left_leaf != middle) { - right = middle - 1; - middle = left_leaf; - } else { - return partitions_[middle]; - } + i_stack++; + stack[i_stack] = model::surfaces[abs(token) - 1]->bounding_box(token > 0); } } + + assert(i_stack == 0); + return stack.front(); +} + +//============================================================================== + +vector Region::surfaces() const +{ + if (simple_) { + return expression_; + } + + vector surfaces = expression_; + + auto it = std::find_if(surfaces.begin(), surfaces.end(), + [&](const auto& value) { return value >= OP_UNION; }); + + while (it != surfaces.end()) { + surfaces.erase(it); + + it = std::find_if(surfaces.begin(), surfaces.end(), + [&](const auto& value) { return value >= OP_UNION; }); + } + + return surfaces; } //============================================================================== @@ -1236,23 +1312,7 @@ void read_cells(pugi::xml_node node) read_dagmc_universes(node); - // Populate the Universe vector and map. - for (int i = 0; i < model::cells.size(); i++) { - int32_t uid = model::cells[i]->universe_; - auto it = model::universe_map.find(uid); - if (it == model::universe_map.end()) { - model::universes.push_back(make_unique()); - model::universes.back()->id_ = uid; - model::universes.back()->cells_.push_back(i); - model::universe_map[uid] = model::universes.size() - 1; - } else { - model::universes[it->second]->cells_.push_back(i); - } - if (model::cells[i]->virtual_lattice_) { - model::universes[it->second]->filled_with_triso_base_=model::cells[i]->id_; - } - } - model::universes.shrink_to_fit(); + populate_universes(); // Allocate the cell overlap count if necessary. if (settings::check_overlaps) { @@ -1264,6 +1324,50 @@ void read_cells(pugi::xml_node node) } } +void populate_universes() +{ + // Used to map universe index to the index of an implicit complement cell for + // DAGMC universes + std::unordered_map implicit_comp_cells; + + // Populate the Universe vector and map. + for (int index_cell = 0; index_cell < model::cells.size(); index_cell++) { + int32_t uid = model::cells[index_cell]->universe_; + auto it = model::universe_map.find(uid); + if (it == model::universe_map.end()) { + model::universes.push_back(make_unique()); + model::universes.back()->id_ = uid; + model::universes.back()->cells_.push_back(index_cell); + model::universe_map[uid] = model::universes.size() - 1; + } else { +#ifdef OPENMC_DAGMC_ENABLED + // Skip implicit complement cells for now + Universe* univ = model::universes[it->second].get(); + DAGUniverse* dag_univ = dynamic_cast(univ); + if (dag_univ && (dag_univ->implicit_complement_idx() == index_cell)) { + implicit_comp_cells[it->second] = index_cell; + continue; + } +#endif + + model::universes[it->second]->cells_.push_back(index_cell); + } + if (model::cells[index_cell]->virtual_lattice_) { + model::universes[it->second]->filled_with_triso_base_ = + model::cells[index_cell]->id_; + } + } + + // Add DAGUniverse implicit complement cells last + for (const auto& it : implicit_comp_cells) { + int index_univ = it.first; + int index_cell = it.second; + model::universes[index_univ]->cells_.push_back(index_cell); + } + + model::universes.shrink_to_fit(); +} + //============================================================================== // C-API functions //============================================================================== @@ -1339,6 +1443,24 @@ extern "C" int openmc_cell_set_temperature( return 0; } +extern "C" int openmc_cell_set_density( + int32_t index, double density, const int32_t* instance, bool set_contained) +{ + if (index < 0 || index >= model::cells.size()) { + strcpy(openmc_err_msg, "Index in cells array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + int32_t instance_index = instance ? *instance : -1; + try { + model::cells[index]->set_density(density, instance_index, set_contained); + } catch (const std::exception& e) { + set_errmsg(e.what()); + return OPENMC_E_UNASSIGNED; + } + return 0; +} + extern "C" int openmc_cell_get_temperature( int32_t index, const int32_t* instance, double* T) { @@ -1357,6 +1479,36 @@ extern "C" int openmc_cell_get_temperature( return 0; } +extern "C" int openmc_cell_get_density( + int32_t index, const int32_t* instance, double* density) +{ + if (index < 0 || index >= model::cells.size()) { + strcpy(openmc_err_msg, "Index in cells array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + int32_t instance_index = instance ? *instance : -1; + try { + if (model::cells[index]->type_ != Fill::MATERIAL) { + fatal_error( + fmt::format("Cell {}, instance {} is not filled with a material.", + model::cells[index]->id_, instance_index)); + } + + int32_t mat_index = model::cells[index]->material(instance_index); + if (mat_index == MATERIAL_VOID) { + *density = 0.0; + } else { + *density = model::cells[index]->density_mult(instance_index) * + model::materials[mat_index]->density_gpcc(); + } + } catch (const std::exception& e) { + set_errmsg(e.what()); + return OPENMC_E_UNASSIGNED; + } + return 0; +} + //! Get the bounding box of a cell extern "C" int openmc_cell_bounding_box( const int32_t index, double* llc, double* urc) @@ -1425,8 +1577,8 @@ struct ParentCell { lattice_index < other.lattice_index); } - gsl::index cell_index; - gsl::index lattice_index; + int64_t cell_index; + int64_t lattice_index; }; //! Structure used to insert ParentCell into hashed STL data structures @@ -1478,6 +1630,9 @@ struct ParentCellStack { //! compute an instance for the provided distribcell index int32_t compute_instance(int32_t distribcell_index) const { + if (distribcell_index == C_NONE) + return 0; + int32_t instance = 0; for (const auto& parent_cell : this->parent_cells_) { auto& cell = model::cells[parent_cell.cell_index]; @@ -1503,10 +1658,11 @@ struct ParentCellStack { }; vector Cell::find_parent_cells( - int32_t instance, const Position& r) const { + int32_t instance, const Position& r) const +{ // create a temporary particle - Particle dummy_particle {}; + GeometryState dummy_particle {}; dummy_particle.r() = r; dummy_particle.u() = {0., 0., 1.}; @@ -1514,7 +1670,8 @@ vector Cell::find_parent_cells( } vector Cell::find_parent_cells( - int32_t instance, Particle& p) const { + int32_t instance, GeometryState& p) const +{ // look up the particle's location exhaustive_find_cell(p); const auto& coords = p.coord(); @@ -1524,9 +1681,10 @@ vector Cell::find_parent_cells( bool cell_found = false; for (auto it = coords.begin(); it != coords.end(); it++) { const auto& coord = *it; - const auto& cell = model::cells[coord.cell]; - // if the cell at this level matches the current cell, stop adding to the stack - if (coord.cell == model::cell_map[this->id_]) { + const auto& cell = model::cells[coord.cell()]; + // if the cell at this level matches the current cell, stop adding to the + // stack + if (coord.cell() == model::cell_map[this->id_]) { cell_found = true; break; } @@ -1536,15 +1694,17 @@ vector Cell::find_parent_cells( int lattice_idx = C_NONE; if (cell->type_ == Fill::LATTICE) { const auto& next_coord = *(it + 1); - lattice_idx = model::lattices[next_coord.lattice]->get_flat_index(next_coord.lattice_i); + lattice_idx = model::lattices[next_coord.lattice()]->get_flat_index( + next_coord.lattice_index()); } - stack.push(coord.universe, {coord.cell, lattice_idx}); + stack.push(coord.universe(), {coord.cell(), lattice_idx}); } // if this loop finished because the cell was found and // the instance matches the one requested in the call // we have the correct path and can return the stack - if (cell_found && stack.compute_instance(this->distribcell_index_) == instance) { + if (cell_found && + stack.compute_instance(this->distribcell_index_) == instance) { return stack.parent_cells(); } @@ -1552,9 +1712,7 @@ vector Cell::find_parent_cells( return exhaustive_find_parent_cells(instance); } - -vector Cell::exhaustive_find_parent_cells( - int32_t instance) const +vector Cell::exhaustive_find_parent_cells(int32_t instance) const { ParentCellStack stack; // start with this cell's universe @@ -1827,7 +1985,7 @@ extern "C" int openmc_cell_get_num_instances( set_errmsg("Index in cells array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; } - *num_instances = model::cells[index]->n_instances_; + *num_instances = model::cells[index]->n_instances(); return 0; } diff --git a/src/chain.cpp b/src/chain.cpp new file mode 100644 index 0000000000..e279d1f591 --- /dev/null +++ b/src/chain.cpp @@ -0,0 +1,116 @@ +//! \file chain.cpp +//! \brief Depletion chain and associated information + +#include "openmc/chain.h" + +#include // for getenv +#include // for make_unique +#include // for stod + +#include +#include + +#include "openmc/distribution.h" // for distribution_from_xml +#include "openmc/error.h" +#include "openmc/reaction.h" +#include "openmc/xml_interface.h" // for get_node_value + +namespace openmc { + +//============================================================================== +// ChainNuclide implementation +//============================================================================== + +ChainNuclide::ChainNuclide(pugi::xml_node node) +{ + name_ = get_node_value(node, "name"); + if (check_for_node(node, "half_life")) { + half_life_ = std::stod(get_node_value(node, "half_life")); + } + if (check_for_node(node, "decay_energy")) { + decay_energy_ = std::stod(get_node_value(node, "decay_energy")); + } + + // Read reactions to store MT -> product map + for (pugi::xml_node reaction_node : node.children("reaction")) { + std::string rx_name = get_node_value(reaction_node, "type"); + if (!reaction_node.attribute("target")) + continue; + std::string rx_target = get_node_value(reaction_node, "target"); + double branching_ratio = 1.0; + if (reaction_node.attribute("branching_ratio")) { + branching_ratio = + std::stod(get_node_value(reaction_node, "branching_ratio")); + } + int mt = reaction_type(rx_name); + reaction_products_[mt].push_back({rx_target, branching_ratio}); + } + + for (pugi::xml_node source_node : node.children("source")) { + auto particle = get_node_value(source_node, "particle"); + if (particle == "photon") { + photon_energy_ = distribution_from_xml(source_node); + break; + } + } + + // Set entry in mapping + data::chain_nuclide_map[name_] = data::chain_nuclides.size(); +} + +ChainNuclide::~ChainNuclide() +{ + data::chain_nuclide_map.erase(name_); +} + +//============================================================================== +// DecayPhotonAngleEnergy implementation +//============================================================================== + +void DecayPhotonAngleEnergy::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + E_out = photon_energy_->sample(seed); + mu = Uniform(-1., 1.).sample(seed); +} + +//============================================================================== +// Global variables +//============================================================================== + +namespace data { + +std::unordered_map chain_nuclide_map; +vector> chain_nuclides; + +} // namespace data + +//============================================================================== +// Non-member functions +//============================================================================== + +void read_chain_file_xml() +{ + char* chain_file_path = std::getenv("OPENMC_CHAIN_FILE"); + if (!chain_file_path) { + return; + } + + write_message(5, "Reading chain file: {}...", chain_file_path); + + pugi::xml_document doc; + auto result = doc.load_file(chain_file_path); + if (!result) { + fatal_error( + fmt::format("Error processing chain file: {}", chain_file_path)); + } + + // Get root element + pugi::xml_node root = doc.document_element(); + + for (auto node : root.children("nuclide")) { + data::chain_nuclides.push_back(std::make_unique(node)); + } +} + +} // namespace openmc diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index afb0a1f36c..b1bfde03d1 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -23,6 +23,7 @@ #include "pugixml.hpp" #include // for getenv +#include #include namespace openmc { @@ -104,6 +105,11 @@ void read_cross_sections_xml() auto root = doc.document_element(); + read_cross_sections_xml(root); +} + +void read_cross_sections_xml(pugi::xml_node root) +{ // Find cross_sections.xml file -- the first place to look is the // materials.xml file. If no file is found there, then we check the // OPENMC_CROSS_SECTIONS environment variable @@ -277,10 +283,15 @@ void read_ce_cross_sections(const vector>& nuc_temps, void read_ce_cross_sections_xml() { // Check if cross_sections.xml exists - const auto& filename = settings::path_cross_sections; - if (!file_exists(filename)) { - // Could not find cross_sections.xml file - fatal_error("Cross sections XML file '" + filename + "' does not exist."); + std::filesystem::path filename(settings::path_cross_sections); + if (!std::filesystem::exists(filename)) { + fatal_error( + "Cross sections XML file '" + filename.string() + "' does not exist."); + } + + if (std::filesystem::is_directory(filename)) { + fatal_error("OPENMC_CROSS_SECTIONS is set to a directory. " + "It should be set to an XML file."); } write_message("Reading cross sections XML file...", 5); @@ -300,15 +311,10 @@ void read_ce_cross_sections_xml() } else { // If no directory is listed in cross_sections.xml, by default select the // directory in which the cross_sections.xml file resides - - // TODO: Use std::filesystem functionality when C++17 is adopted - auto pos = filename.rfind("/"); - if (pos == std::string::npos) { - // No '\\' found, so the file must be in the same directory as - // materials.xml - directory = settings::path_input; + if (filename.has_parent_path()) { + directory = filename.parent_path().string(); } else { - directory = filename.substr(0, pos); + directory = settings::path_input; } } diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 9b894d3bb9..b2ebe89c0b 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -1,5 +1,7 @@ #include "openmc/dagmc.h" +#include + #include "openmc/constants.h" #include "openmc/container_util.h" #include "openmc/error.h" @@ -11,28 +13,34 @@ #include "openmc/settings.h" #include "openmc/string_utils.h" -#ifdef DAGMC -#include "dagmcmetadata.hpp" +#ifdef OPENMC_UWUW_ENABLED #include "uwuw.hpp" #endif #include #include +#include #include #include #include namespace openmc { -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED const bool DAGMC_ENABLED = true; #else const bool DAGMC_ENABLED = false; #endif +#ifdef OPENMC_UWUW_ENABLED +const bool UWUW_ENABLED = true; +#else +const bool UWUW_ENABLED = false; +#endif + } // namespace openmc -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED namespace openmc { @@ -50,6 +58,10 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) if (check_for_node(node, "filename")) { filename_ = get_node_value(node, "filename"); + if (!starts_with(filename_, "/")) { + std::filesystem::path d(dir_name(settings::path_input)); + filename_ = (d / filename_).string(); + } } else { fatal_error("Must specify a file for the DAGMC universe"); } @@ -64,6 +76,23 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) adjust_material_ids_ = get_node_value_bool(node, "auto_mat_ids"); } + // get material assignment overloading + if (check_for_node(node, "material_overrides")) { + auto mat_node = node.child("material_overrides"); + // loop over all subelements (each subelement corresponds to a material) + for (pugi::xml_node cell_node : mat_node.children("cell_override")) { + // Store assignment reference name + int32_t ref_assignment = std::stoi(get_node_value(cell_node, "id")); + + // Get mat name for each assignement instances + vector instance_mats = + get_node_array(cell_node, "material_ids"); + + // Store mat name for each instances + material_overrides_.emplace(ref_assignment, instance_mats); + } + } + initialize(); } @@ -71,6 +100,22 @@ DAGUniverse::DAGUniverse( const std::string& filename, bool auto_geom_ids, bool auto_mat_ids) : filename_(filename), adjust_geometry_ids_(auto_geom_ids), adjust_material_ids_(auto_mat_ids) +{ + set_id(); + initialize(); +} + +DAGUniverse::DAGUniverse(std::shared_ptr dagmc_ptr, + const std::string& filename, bool auto_geom_ids, bool auto_mat_ids) + : dagmc_instance_(dagmc_ptr), filename_(filename), + adjust_geometry_ids_(auto_geom_ids), adjust_material_ids_(auto_mat_ids) +{ + set_id(); + init_metadata(); + init_geometry(); +} + +void DAGUniverse::set_id() { // determine the next universe id int32_t next_univ_id = 0; @@ -82,50 +127,29 @@ DAGUniverse::DAGUniverse( // set the universe id id_ = next_univ_id; - - initialize(); } void DAGUniverse::initialize() { - geom_type() = GeometryType::DAG; +#ifdef OPENMC_UWUW_ENABLED + // read uwuw materials from the .h5m file if present + read_uwuw_materials(); +#endif - // determine the next cell id - int32_t next_cell_id = 0; - for (const auto& c : model::cells) { - if (c->id_ > next_cell_id) - next_cell_id = c->id_; - } - cell_idx_offset_ = model::cells.size(); - next_cell_id++; + init_dagmc(); - // determine the next surface id - int32_t next_surf_id = 0; - for (const auto& s : model::surfaces) { - if (s->id_ > next_surf_id) - next_surf_id = s->id_; - } - surf_idx_offset_ = model::surfaces.size(); - next_surf_id++; + init_metadata(); + + init_geometry(); +} + +void DAGUniverse::init_dagmc() +{ // create a new DAGMC instance dagmc_instance_ = std::make_shared(); - // --- Materials --- - - // read any UWUW materials from the file - read_uwuw_materials(); - - // check for uwuw material definitions - bool using_uwuw = uses_uwuw(); - - // notify user if UWUW materials are going to be used - if (using_uwuw) { - write_message("Found UWUW Materials in the DAGMC geometry file.", 6); - } - // load the DAGMC geometry - filename_ = settings::path_input + filename_; if (!file_exists(filename_)) { fatal_error("Geometry DAGMC file '" + filename_ + "' does not exist!"); } @@ -135,18 +159,35 @@ void DAGUniverse::initialize() // initialize acceleration data structures rval = dagmc_instance_->init_OBBTree(); MB_CHK_ERR_CONT(rval); +} +void DAGUniverse::init_metadata() +{ // parse model metadata - dagmcMetaData DMD(dagmc_instance_.get(), false, false); - DMD.load_property_data(); + dmd_ptr = + std::make_unique(dagmc_instance_.get(), false, false); + dmd_ptr->load_property_data(); std::vector keywords {"temp"}; std::map dum; std::string delimiters = ":/"; + moab::ErrorCode rval; rval = dagmc_instance_->parse_properties(keywords, dum, delimiters.c_str()); MB_CHK_ERR_CONT(rval); +} - // --- Cells (Volumes) --- +void DAGUniverse::init_geometry() +{ + moab::ErrorCode rval; + + // determine the next cell id + int32_t next_cell_id = 0; + for (const auto& c : model::cells) { + if (c->id_ > next_cell_id) + next_cell_id = c->id_; + } + cell_idx_offset_ = model::cells.size(); + next_cell_id++; // initialize cell objects int n_cells = dagmc_instance_->num_entities(3); @@ -167,15 +208,18 @@ void DAGUniverse::initialize() model::cell_map[c->id_] = model::cells.size(); } else { warning(fmt::format("DAGMC Cell IDs: {}", dagmc_ids_for_dim(3))); - fatal_error(fmt::format("Cell ID {} exists in both DAGMC Universe {} " - "and the CSG geometry.", - c->id_, this->id_)); + fatal_error(fmt::format( + "DAGMC Universe {} contains a cell with ID {}, which " + "already exists elsewhere in the geometry. Setting auto_geom_ids " + "to True when initiating the DAGMC Universe may " + "resolve this issue", + this->id_, c->id_)); } // --- Materials --- // determine volume material assignment - std::string mat_str = DMD.get_volume_property("material", vol_handle); + std::string mat_str = dmd_ptr->get_volume_property("material", vol_handle); if (mat_str.empty()) { fatal_error(fmt::format("Volume {} has no material assignment.", c->id_)); @@ -186,25 +230,14 @@ void DAGUniverse::initialize() if (mat_str == "graveyard") { graveyard = vol_handle; } - // material void checks if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (using_uwuw) { - // lookup material in uwuw if present - std::string uwuw_mat = DMD.volume_material_property_data_eh[vol_handle]; - if (uwuw_->material_library.count(uwuw_mat) != 0) { - // Note: material numbers are set by UWUW - int mat_number = uwuw_->material_library.get_material(uwuw_mat) - .metadata["mat_number"] - .asInt(); - c->material_.push_back(mat_number); - } else { - fatal_error(fmt::format("Material with value '{}' not found in the " - "UWUW material library", - mat_str)); - } + if (material_overrides_.count(c->id_)) { + override_assign_material(c); + } else if (uses_uwuw()) { + uwuw_assign_material(vol_handle, c); } else { legacy_assign_material(mat_str, c); } @@ -243,7 +276,14 @@ void DAGUniverse::initialize() has_graveyard_ = graveyard; - // --- Surfaces --- + // determine the next surface id + int32_t next_surf_id = 0; + for (const auto& s : model::surfaces) { + if (s->id_ > next_surf_id) + next_surf_id = s->id_; + } + surf_idx_offset_ = model::surfaces.size(); + next_surf_id++; // initialize surface objects int n_surfaces = dagmc_instance_->num_entities(2); @@ -255,17 +295,24 @@ void DAGUniverse::initialize() s->id_ = adjust_geometry_ids_ ? next_surf_id++ : dagmc_instance_->id_by_index(2, i + 1); + // set surface source attribute if needed + if (contains(settings::source_write_surf_id, s->id_) || + settings::source_write_surf_id.empty()) { + s->surf_source_ = true; + } + // set BCs - std::string bc_value = DMD.get_surface_property("boundary", surf_handle); + std::string bc_value = + dmd_ptr->get_surface_property("boundary", surf_handle); to_lower(bc_value); if (bc_value.empty() || bc_value == "transmit" || bc_value == "transmission") { // set to transmission by default (nullptr) } else if (bc_value == "vacuum") { - s->bc_ = std::make_shared(); + s->bc_ = make_unique(); } else if (bc_value == "reflective" || bc_value == "reflect" || bc_value == "reflecting") { - s->bc_ = std::make_shared(); + s->bc_ = make_unique(); } else if (bc_value == "periodic") { fatal_error("Periodic boundary condition not supported in DAGMC."); } else { @@ -283,7 +330,7 @@ void DAGUniverse::initialize() // if this surface belongs to the graveyard if (graveyard && parent_vols.find(graveyard) != parent_vols.end()) { // set graveyard surface BC's to vacuum - s->bc_ = std::make_shared(); + s->bc_ = make_unique(); } // add to global array and map @@ -302,6 +349,20 @@ void DAGUniverse::initialize() } // end surface loop } +int32_t DAGUniverse::cell_index(moab::EntityHandle vol) const +{ + // return the index of the volume in the DAGMC instance and then + // adjust by the offset into the model cells for this DAGMC universe + return dagmc_ptr()->index_by_handle(vol) + cell_idx_offset_; +} + +int32_t DAGUniverse::surface_index(moab::EntityHandle surf) const +{ + // return the index of the surface in the DAGMC instance and then + // adjust by the offset into the model cells for this DAGMC universe + return dagmc_ptr()->index_by_handle(surf) + surf_idx_offset_; +} + std::string DAGUniverse::dagmc_ids_for_dim(int dim) const { // generate a vector of ids @@ -363,13 +424,13 @@ int32_t DAGUniverse::implicit_complement_idx() const return cell_idx_offset_ + dagmc_instance_->index_by_handle(ic) - 1; } -bool DAGUniverse::find_cell(Particle& p) const +bool DAGUniverse::find_cell(GeometryState& p) const { // if the particle isn't in any of the other DagMC // cells, place it in the implicit complement bool found = Universe::find_cell(p); if (!found && model::universe_map[this->id_] != model::root_universe) { - p.coord(p.n_coord() - 1).cell = implicit_complement_idx(); + p.lowest_coord().cell() = implicit_complement_idx(); found = true; } return found; @@ -395,11 +456,16 @@ void DAGUniverse::to_hdf5(hid_t universes_group) const bool DAGUniverse::uses_uwuw() const { - return !uwuw_->material_library.empty(); +#ifdef OPENMC_UWUW_ENABLED + return uwuw_ && !uwuw_->material_library.empty(); +#else + return false; +#endif // OPENMC_UWUW_ENABLED } std::string DAGUniverse::get_uwuw_materials_xml() const { +#ifdef OPENMC_UWUW_ENABLED if (!uses_uwuw()) { throw std::runtime_error("This DAGMC Universe does not use UWUW materials"); } @@ -417,10 +483,14 @@ std::string DAGUniverse::get_uwuw_materials_xml() const ss << ""; return ss.str(); +#else + fatal_error("DAGMC was not configured with UWUW."); +#endif // OPENMC_UWUW_ENABLED } void DAGUniverse::write_uwuw_materials_xml(const std::string& outfile) const { +#ifdef OPENMC_UWUW_ENABLED if (!uses_uwuw()) { throw std::runtime_error( "This DAGMC universe does not use UWUW materials."); @@ -431,6 +501,9 @@ void DAGUniverse::write_uwuw_materials_xml(const std::string& outfile) const std::ofstream mats_xml(outfile); mats_xml << xml_str; mats_xml.close(); +#else + fatal_error("DAGMC was not configured with UWUW."); +#endif // OPENMC_UWUW_ENABLED } void DAGUniverse::legacy_assign_material( @@ -460,13 +533,21 @@ void DAGUniverse::legacy_assign_material( // if no material was set using a name, assign by id if (!mat_found_by_name) { + bool found_by_id = true; try { auto id = std::stoi(mat_string); + if (model::material_map.find(id) == model::material_map.end()) + found_by_id = false; c->material_.emplace_back(id); } catch (const std::invalid_argument&) { - fatal_error(fmt::format( - "No material '{}' found for volume (cell) {}", mat_string, c->id_)); + found_by_id = false; } + + // report failure for failed int conversion or missing material + if (!found_by_id) + fatal_error( + fmt::format("Material with name/ID '{}' not found for volume (cell) {}", + mat_string, c->id_)); } if (settings::verbosity >= 10) { @@ -484,33 +565,33 @@ void DAGUniverse::legacy_assign_material( void DAGUniverse::read_uwuw_materials() { - - int32_t next_material_id = 0; - for (const auto& m : model::materials) { - next_material_id = std::max(m->id_, next_material_id); - } - next_material_id++; +#ifdef OPENMC_UWUW_ENABLED + // If no filename was provided, don't read UWUW materials + if (filename_ == "") + return; uwuw_ = std::make_shared(filename_.c_str()); - const auto& mat_lib = uwuw_->material_library; - if (mat_lib.size() == 0) + + if (!uses_uwuw()) return; + // Notify user if UWUW materials are going to be used + write_message("Found UWUW Materials in the DAGMC geometry file.", 6); + // if we're using automatic IDs, update the UWUW material metadata if (adjust_material_ids_) { + int32_t next_material_id = 0; + for (const auto& m : model::materials) { + next_material_id = std::max(m->id_, next_material_id); + } + next_material_id++; + for (auto& mat : uwuw_->material_library) { mat.second->metadata["mat_number"] = next_material_id++; } } - std::stringstream ss; - ss << "\n"; - ss << "\n"; - for (auto mat : mat_lib) { - ss << mat.second->openmc("atom"); - } - ss << ""; - std::string mat_xml_string = ss.str(); + std::string mat_xml_string = get_uwuw_materials_xml(); // create a pugi XML document from this string pugi::xml_document doc; @@ -522,6 +603,57 @@ void DAGUniverse::read_uwuw_materials() for (pugi::xml_node material_node : root.children("material")) { model::materials.push_back(std::make_unique(material_node)); } +#else + fatal_error("DAGMC was not configured with UWUW."); +#endif // OPENMC_UWUW_ENABLED +} + +void DAGUniverse::uwuw_assign_material( + moab::EntityHandle vol_handle, std::unique_ptr& c) const +{ +#ifdef OPENMC_UWUW_ENABLED + // lookup material in uwuw if present + std::string uwuw_mat = dmd_ptr->volume_material_property_data_eh[vol_handle]; + if (uwuw_->material_library.count(uwuw_mat) != 0) { + // Note: material numbers are set by UWUW + int mat_number = uwuw_->material_library.get_material(uwuw_mat) + .metadata["mat_number"] + .asInt(); + c->material_.push_back(mat_number); + } else { + fatal_error(fmt::format("Material with value '{}' not found in the " + "UWUW material library", + uwuw_mat)); + } +#else + fatal_error("DAGMC was not configured with UWUW."); +#endif // OPENMC_UWUW_ENABLED +} + +void DAGUniverse::override_assign_material(std::unique_ptr& c) const +{ + // if Cell ID matches an override key, use it to override the material + // assignment else if UWUW is used, get the material assignment from the DAGMC + // metadata + // Notify User that an override is being applied on a DAGMCCell + write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); + + if (settings::verbosity >= 10) { + auto msg = fmt::format("Assigning DAGMC cell {} material(s) based on " + "override information (see input XML).", + c->id_); + write_message(msg, 10); + } + + // Override the material assignment for each cell instance using the legacy + // assignement + for (auto mat_id : material_overrides_.at(c->id_)) { + if (model::material_map.find(mat_id) == model::material_map.end()) { + fatal_error(fmt::format( + "Material with ID '{}' not found for DAGMC cell {}", mat_id, c->id_)); + } + c->material_.push_back(mat_id); + } } //============================================================================== @@ -529,53 +661,63 @@ void DAGUniverse::read_uwuw_materials() //============================================================================== DAGCell::DAGCell(std::shared_ptr dag_ptr, int32_t dag_idx) - : Cell {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) -{ - geom_type_ = GeometryType::DAG; - simple_ = true; -}; + : Cell {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) {}; std::pair DAGCell::distance( - Position r, Direction u, int32_t on_surface, Particle* p) const + Position r, Direction u, int32_t on_surface, GeometryState* p) const { - Expects(p); // if we've changed direction or we're not on a surface, // reset the history and update last direction if (u != p->last_dir()) { p->last_dir() = u; p->history().reset(); } - if (on_surface == 0) { + if (on_surface == SURFACE_NONE) { p->history().reset(); } - const auto& univ = model::universes[p->coord(p->n_coord() - 1).universe]; + const auto& univ = model::universes[p->lowest_coord().universe()]; DAGUniverse* dag_univ = static_cast(univ.get()); if (!dag_univ) fatal_error("DAGMC call made for particle in a non-DAGMC universe"); - moab::ErrorCode rval; + // initialize to lost particle conditions + int surf_idx = -1; + double dist = INFINITY; + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); moab::EntityHandle hit_surf; - double dist; + + // create the ray double pnt[3] = {r.x, r.y, r.z}; double dir[3] = {u.x, u.y, u.z}; - rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &p->history()); - MB_CHK_ERR_CONT(rval); - int surf_idx; + MB_CHK_ERR_CONT( + dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &p->history())); if (hit_surf != 0) { surf_idx = dag_univ->surf_idx_offset_ + dagmc_ptr_->index_by_handle(hit_surf); - } else { - // indicate that particle is lost - surf_idx = -1; - dist = INFINITY; - if (!dagmc_ptr_->is_implicit_complement(vol) || - model::universe_map[dag_univ->id_] == model::root_universe) { - p->mark_as_lost( - fmt::format("No intersection found with DAGMC cell {}", id_)); - } + } else if (!dagmc_ptr_->is_implicit_complement(vol) || + is_root_universe(dag_univ->id_)) { + // surface boundary conditions are ignored for projection plotting, meaning + // that the particle may move through the graveyard (bounding) volume and + // into the implicit complement on the other side where no intersection will + // be found. Treating this as a lost particle is problematic when plotting. + // Instead, the infinite distance and invalid surface index are returned. + if (settings::run_mode == RunMode::PLOTTING) + return {INFTY, -1}; + + // the particle should be marked as lost immediately if an intersection + // isn't found in a volume that is not the implicit complement. In the case + // that the DAGMC model is the root universe of the geometry, even a missing + // intersection in the implicit complement should trigger this condition. + std::string material_id = + p->material() == MATERIAL_VOID + ? "-1 (VOID)" + : std::to_string(model::materials[p->material()]->id()); + p->mark_as_lost(fmt::format( + "No intersection found with DAGMC cell {}, filled with material {}", id_, + material_id)); } return {dist, surf_idx}; @@ -594,6 +736,11 @@ bool DAGCell::contains(Position r, Direction u, int32_t on_surface) const return result; } +moab::EntityHandle DAGCell::mesh_handle() const +{ + return dagmc_ptr()->entity_by_index(3, dag_index()); +} + void DAGCell::to_hdf5_inner(hid_t group_id) const { write_string(group_id, "geom_type", "dagmc", false); @@ -615,9 +762,12 @@ BoundingBox DAGCell::bounding_box() const DAGSurface::DAGSurface(std::shared_ptr dag_ptr, int32_t dag_idx) : Surface {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) +{} // empty constructor + +moab::EntityHandle DAGSurface::mesh_handle() const { - geom_type_ = GeometryType::DAG; -} // empty constructor + return dagmc_ptr()->entity_by_index(2, dag_index()); +} double DAGSurface::evaluate(Position r) const { @@ -650,18 +800,15 @@ Direction DAGSurface::normal(Position r) const return dir; } -Direction DAGSurface::reflect(Position r, Direction u, Particle* p) const +Direction DAGSurface::reflect(Position r, Direction u, GeometryState* p) const { - Expects(p); - p->history().reset_to_last_intersection(); - moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); + assert(p); double pnt[3] = {r.x, r.y, r.z}; double dir[3]; - rval = dagmc_ptr_->get_angle(surf, pnt, dir, &p->history()); + moab::ErrorCode rval = + dagmc_ptr_->get_angle(mesh_handle(), pnt, dir, &p->history()); MB_CHK_ERR_CONT(rval); - p->last_dir() = u.reflect(dir); - return p->last_dir(); + return u.reflect(dir); } //============================================================================== @@ -692,19 +839,58 @@ void check_dagmc_root_univ() } } -int32_t next_cell( - DAGUniverse* dag_univ, DAGCell* cur_cell, DAGSurface* surf_xed) +int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) { - moab::EntityHandle surf = - surf_xed->dagmc_ptr()->entity_by_index(2, surf_xed->dag_index()); - moab::EntityHandle vol = - cur_cell->dagmc_ptr()->entity_by_index(3, cur_cell->dag_index()); + auto surfp = dynamic_cast(model::surfaces[surf].get()); + auto cellp = dynamic_cast(model::cells[curr_cell].get()); + auto univp = static_cast(model::universes[univ].get()); + + moab::EntityHandle surf_handle = surfp->mesh_handle(); + moab::EntityHandle curr_vol = cellp->mesh_handle(); moab::EntityHandle new_vol; - cur_cell->dagmc_ptr()->next_vol(surf, vol, new_vol); + moab::ErrorCode rval = + cellp->dagmc_ptr()->next_vol(surf_handle, curr_vol, new_vol); + if (rval != moab::MB_SUCCESS) + return -1; - return cur_cell->dagmc_ptr()->index_by_handle(new_vol) + - dag_univ->cell_idx_offset_; + return univp->cell_index(new_vol); +} + +extern "C" int openmc_dagmc_universe_get_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n) +{ + // make sure the universe id is a DAGMC Universe + const auto& univ = model::universes[model::universe_map[univ_id]]; + if (univ->geom_type() != GeometryType::DAG) { + set_errmsg(fmt::format("Universe {} is not a DAGMC Universe", univ_id)); + return OPENMC_E_INVALID_TYPE; + } + + std::vector dag_cell_ids; + for (const auto& cell_index : univ->cells_) { + const auto& cell = model::cells[cell_index]; + if (cell->geom_type() == GeometryType::CSG) { + set_errmsg(fmt::format("Cell {} is not a DAGMC Cell", cell->id_)); + return OPENMC_E_INVALID_TYPE; + } + dag_cell_ids.push_back(cell->id_); + } + std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids); + *n = dag_cell_ids.size(); + return 0; +} + +extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) +{ + // make sure the universe id is a DAGMC Universe + const auto& univ = model::universes[model::universe_map[univ_id]]; + if (univ->geom_type() != GeometryType::DAG) { + set_errmsg(fmt::format("Universe {} is not a DAGMC universe", univ_id)); + return OPENMC_E_INVALID_TYPE; + } + *n = univ->cells_.size(); + return 0; } } // namespace openmc @@ -713,15 +899,31 @@ int32_t next_cell( namespace openmc { +extern "C" int openmc_dagmc_universe_get_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n) +{ + set_errmsg("OpenMC was not configured with DAGMC"); + return OPENMC_E_UNASSIGNED; +}; + +extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) +{ + set_errmsg("OpenMC was not configured with DAGMC"); + return OPENMC_E_UNASSIGNED; +}; + void read_dagmc_universes(pugi::xml_node node) { if (check_for_node(node, "dagmc_universe")) { - fatal_error("DAGMC Universes are present but OpenMC was not configured" + fatal_error("DAGMC Universes are present but OpenMC was not configured " "with DAGMC"); } }; + void check_dagmc_root_univ() {}; +int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ); + } // namespace openmc -#endif // DAGMC +#endif // OPENMC_DAGMC_ENABLED diff --git a/src/distribution.cpp b/src/distribution.cpp index 901852e0f5..ca00cbde4e 100644 --- a/src/distribution.cpp +++ b/src/distribution.cpp @@ -1,14 +1,13 @@ #include "openmc/distribution.h" #include // for copy +#include #include // for sqrt, floor, max #include // for back_inserter #include // for accumulate #include // for runtime_error #include // for string, stod -#include - #include "openmc/error.h" #include "openmc/math_functions.h" #include "openmc/random_dist.h" @@ -17,51 +16,123 @@ namespace openmc { +//============================================================================== +// DiscreteIndex implementation +//============================================================================== + +DiscreteIndex::DiscreteIndex(pugi::xml_node node) +{ + auto params = get_node_array(node, "parameters"); + std::size_t n = params.size() / 2; + + assign({params.data() + n, n}); +} + +DiscreteIndex::DiscreteIndex(span p) +{ + assign(p); +} + +void DiscreteIndex::assign(span p) +{ + prob_.assign(p.begin(), p.end()); + + this->init_alias(); +} + +void DiscreteIndex::init_alias() +{ + normalize(); + + // The initialization and sampling method is based on Vose + // (DOI: 10.1109/32.92917) + // Vectors for large and small probabilities based on 1/n + vector large; + vector small; + + size_t n = prob_.size(); + + // Set and allocate memory + alias_.assign(n, 0); + + // Fill large and small vectors based on 1/n + for (size_t i = 0; i < n; i++) { + prob_[i] *= n; + if (prob_[i] > 1.0) { + large.push_back(i); + } else { + small.push_back(i); + } + } + + while (!large.empty() && !small.empty()) { + int j = small.back(); + int k = large.back(); + + // Remove last element of small + small.pop_back(); + + // Update probability and alias based on Vose's algorithm + prob_[k] += prob_[j] - 1.0; + alias_[j] = k; + + // Move large index to small vector, if it is no longer large + if (prob_[k] < 1.0) { + small.push_back(k); + large.pop_back(); + } + } +} + +size_t DiscreteIndex::sample(uint64_t* seed) const +{ + // Alias sampling of discrete distribution + size_t n = prob_.size(); + if (n > 1) { + size_t u = prn(seed) * n; + if (prn(seed) < prob_[u]) { + return u; + } else { + return alias_[u]; + } + } else { + return 0; + } +} + +void DiscreteIndex::normalize() +{ + // Renormalize density function so that it sums to unity. Note that we save + // the integral of the distribution so that if it is used as part of another + // distribution (e.g., Mixture), we know its relative strength. + integral_ = std::accumulate(prob_.begin(), prob_.end(), 0.0); + for (auto& p_i : prob_) { + p_i /= integral_; + } +} + //============================================================================== // Discrete implementation //============================================================================== -Discrete::Discrete(pugi::xml_node node) +Discrete::Discrete(pugi::xml_node node) : di_(node) { auto params = get_node_array(node, "parameters"); - std::size_t n = params.size(); - std::copy(params.begin(), params.begin() + n / 2, std::back_inserter(x_)); - std::copy(params.begin() + n / 2, params.end(), std::back_inserter(p_)); + std::size_t n = params.size() / 2; - normalize(); + x_.assign(params.begin(), params.begin() + n); } -Discrete::Discrete(const double* x, const double* p, int n) - : x_ {x, x + n}, p_ {p, p + n} +Discrete::Discrete(const double* x, const double* p, size_t n) : di_({p, n}) { - normalize(); + + x_.assign(x, x + n); } double Discrete::sample(uint64_t* seed) const { - int n = x_.size(); - if (n > 1) { - double xi = prn(seed); - double c = 0.0; - for (int i = 0; i < n; ++i) { - c += p_[i]; - if (xi < c) - return x_[i]; - } - throw std::runtime_error {"Error when sampling probability mass function."}; - } else { - return x_[0]; - } -} - -void Discrete::normalize() -{ - // Renormalize density function so that it sums to unity - double norm = std::accumulate(p_.begin(), p_.end(), 0.0); - for (auto& p_i : p_) { - p_i /= norm; - } + return x_[di_.sample(seed)]; } //============================================================================== @@ -165,27 +236,6 @@ double Normal::sample(uint64_t* seed) const return normal_variate(mean_value_, std_dev_, seed); } -//============================================================================== -// Muir implementation -//============================================================================== -Muir::Muir(pugi::xml_node node) -{ - auto params = get_node_array(node, "parameters"); - if (params.size() != 3) { - openmc::fatal_error("Muir energy distribution must have three " - "parameters specified."); - } - - e0_ = params.at(0); - m_rat_ = params.at(1); - kt_ = params.at(2); -} - -double Muir::sample(uint64_t* seed) const -{ - return muir_spectrum(e0_, m_rat_, kt_, seed); -} - //============================================================================== // Tabular implementation //============================================================================== @@ -200,14 +250,18 @@ Tabular::Tabular(pugi::xml_node node) interp_ = Interpolation::lin_lin; } else { openmc::fatal_error( - "Unknown interpolation type for distribution: " + temp); + "Unsupported interpolation type for distribution: " + temp); } } else { interp_ = Interpolation::histogram; } - // Read and initialize tabular distribution + // Read and initialize tabular distribution. If number of parameters is odd, + // add an extra zero for the 'p' array. auto params = get_node_array(node, "parameters"); + if (params.size() % 2 != 0) { + params.push_back(0.0); + } std::size_t n = params.size() / 2; const double* x = params.data(); const double* p = x + n; @@ -250,10 +304,13 @@ void Tabular::init( } } - // Normalize density and distribution functions + // Normalize density and distribution functions. Note that we save the + // integral of the distribution so that if it is used as part of another + // distribution (e.g., Mixture), we know its relative strength. + integral_ = c_[n - 1]; for (int i = 0; i < n; ++i) { - p_[i] = p_[i] / c_[n - 1]; - c_[i] = c_[i] / c_[n - 1]; + p_[i] = p_[i] / integral_; + c_[i] = c_[i] / integral_; } } @@ -324,17 +381,24 @@ Mixture::Mixture(pugi::xml_node node) double cumsum = 0.0; for (pugi::xml_node pair : node.children("pair")) { // Check that required data exists - if (!pair.attribute("probability")) fatal_error("Mixture pair element does not have probability."); - if (!pair.child("dist")) fatal_error("Mixture pair element does not have a distribution."); + if (!pair.attribute("probability")) + fatal_error("Mixture pair element does not have probability."); + if (!pair.child("dist")) + fatal_error("Mixture pair element does not have a distribution."); - // cummulative sum of probybilities - cumsum += std::stod(pair.attribute("probability").value()); + // cummulative sum of probabilities + double p = std::stod(pair.attribute("probability").value()); - // Save cummulative probybility and distrubution - distribution_.push_back( - std::make_pair(cumsum, distribution_from_xml(pair.child("dist")))); + // Save cummulative probability and distribution + auto dist = distribution_from_xml(pair.child("dist")); + cumsum += p * dist->integral(); + + distribution_.push_back(std::make_pair(cumsum, std::move(dist))); } + // Save integral of distribution + integral_ = cumsum; + // Normalize cummulative probabilities to 1 for (auto& pair : distribution_) { pair.first /= cumsum; @@ -351,7 +415,7 @@ double Mixture::sample(uint64_t* seed) const p, [](const DistPair& pair, double p) { return pair.first < p; }); // This should not happen. Catch it - Ensures(it != distribution_.cend()); + assert(it != distribution_.cend()); // Sample the chosen distribution return it->second->sample(seed); @@ -381,14 +445,16 @@ UPtrDist distribution_from_xml(pugi::xml_node node) dist = UPtrDist {new Watt(node)}; } else if (type == "normal") { dist = UPtrDist {new Normal(node)}; - } else if (type == "muir") { - dist = UPtrDist {new Muir(node)}; } else if (type == "discrete") { dist = UPtrDist {new Discrete(node)}; } else if (type == "tabular") { dist = UPtrDist {new Tabular(node)}; } else if (type == "mixture") { dist = UPtrDist {new Mixture(node)}; + } else if (type == "muir") { + openmc::fatal_error( + "'muir' distributions are now specified using the openmc.stats.muir() " + "function in Python. Please regenerate your XML files."); } else { openmc::fatal_error("Invalid distribution type: " + type); } diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index 0735f0994e..b7b3efe526 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -12,6 +12,25 @@ namespace openmc { +unique_ptr UnitSphereDistribution::create( + pugi::xml_node node) +{ + // Check for type of angular distribution + std::string type; + if (check_for_node(node, "type")) + type = get_node_value(node, "type", true, true); + if (type == "isotropic") { + return UPtrAngle {new Isotropic()}; + } else if (type == "monodirectional") { + return UPtrAngle {new Monodirectional(node)}; + } else if (type == "mu-phi") { + return UPtrAngle {new PolarAzimuthal(node)}; + } else { + fatal_error(fmt::format( + "Invalid angular distribution for external source: {}", type)); + } +} + //============================================================================== // UnitSphereDistribution implementation //============================================================================== diff --git a/src/distribution_spatial.cpp b/src/distribution_spatial.cpp index 66430ddffe..d2b0f413bd 100644 --- a/src/distribution_spatial.cpp +++ b/src/distribution_spatial.cpp @@ -1,11 +1,45 @@ #include "openmc/distribution_spatial.h" #include "openmc/error.h" +#include "openmc/mesh.h" #include "openmc/random_lcg.h" +#include "openmc/search.h" #include "openmc/xml_interface.h" namespace openmc { +//============================================================================== +// SpatialDistribution implementation +//============================================================================== + +unique_ptr SpatialDistribution::create(pugi::xml_node node) +{ + // Check for type of spatial distribution and read + std::string type; + if (check_for_node(node, "type")) + type = get_node_value(node, "type", true, true); + if (type == "cartesian") { + return UPtrSpace {new CartesianIndependent(node)}; + } else if (type == "cylindrical") { + return UPtrSpace {new CylindricalIndependent(node)}; + } else if (type == "spherical") { + return UPtrSpace {new SphericalIndependent(node)}; + } else if (type == "mesh") { + return UPtrSpace {new MeshSpatial(node)}; + } else if (type == "cloud") { + return UPtrSpace {new PointCloud(node)}; + } else if (type == "box") { + return UPtrSpace {new SpatialBox(node)}; + } else if (type == "fission") { + return UPtrSpace {new SpatialBox(node, true)}; + } else if (type == "point") { + return UPtrSpace {new SpatialPoint(node)}; + } else { + fatal_error(fmt::format( + "Invalid spatial distribution for external source: {}", type)); + } +} + //============================================================================== // CartesianIndependent implementation //============================================================================== @@ -132,15 +166,16 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node) r_ = make_unique(x, p, 1); } - // Read distribution for theta-coordinate - if (check_for_node(node, "theta")) { - pugi::xml_node node_dist = node.child("theta"); - theta_ = distribution_from_xml(node_dist); + // Read distribution for cos_theta-coordinate + if (check_for_node(node, "cos_theta")) { + pugi::xml_node node_dist = node.child("cos_theta"); + cos_theta_ = distribution_from_xml(node_dist); } else { - // If no distribution was specified, default to a single point at theta=0 + // If no distribution was specified, default to a single point at + // cos_theta=0 double x[] {0.0}; double p[] {1.0}; - theta_ = make_unique(x, p, 1); + cos_theta_ = make_unique(x, p, 1); } // Read distribution for phi-coordinate @@ -171,14 +206,143 @@ SphericalIndependent::SphericalIndependent(pugi::xml_node node) Position SphericalIndependent::sample(uint64_t* seed) const { double r = r_->sample(seed); - double theta = theta_->sample(seed); + double cos_theta = cos_theta_->sample(seed); double phi = phi_->sample(seed); - double x = r * sin(theta) * cos(phi) + origin_.x; - double y = r * sin(theta) * sin(phi) + origin_.y; - double z = r * cos(theta) + origin_.z; + // sin(theta) by sin**2 + cos**2 = 1 + double x = r * std::sqrt(1 - cos_theta * cos_theta) * cos(phi) + origin_.x; + double y = r * std::sqrt(1 - cos_theta * cos_theta) * sin(phi) + origin_.y; + double z = r * cos_theta + origin_.z; return {x, y, z}; } +//============================================================================== +// MeshSpatial implementation +//============================================================================== + +MeshSpatial::MeshSpatial(pugi::xml_node node) +{ + + if (get_node_value(node, "type", true, true) != "mesh") { + fatal_error(fmt::format( + "Incorrect spatial type '{}' for a MeshSpatial distribution")); + } + + // No in-tet distributions implemented, could include distributions for the + // barycentric coords Read in unstructured mesh from mesh_id value + int32_t mesh_id = std::stoi(get_node_value(node, "mesh_id")); + // Get pointer to spatial distribution + mesh_idx_ = model::mesh_map.at(mesh_id); + + const auto mesh_ptr = model::meshes.at(mesh_idx_).get(); + + check_element_types(); + + size_t n_bins = this->n_sources(); + std::vector strengths(n_bins, 1.0); + + // Create cdfs for sampling for an element over a mesh + // Volume scheme is weighted by the volume of each tet + // File scheme is weighted by an array given in the xml file + if (check_for_node(node, "strengths")) { + strengths = get_node_array(node, "strengths"); + if (strengths.size() != n_bins) { + fatal_error( + fmt::format("Number of entries in the source strengths array {} does " + "not match the number of entities in mesh {} ({}).", + strengths.size(), mesh_id, n_bins)); + } + } + + if (get_node_value_bool(node, "volume_normalized")) { + for (int i = 0; i < n_bins; i++) { + strengths[i] *= this->mesh()->volume(i); + } + } + + elem_idx_dist_.assign(strengths); +} + +MeshSpatial::MeshSpatial(int32_t mesh_idx, span strengths) + : mesh_idx_(mesh_idx) +{ + check_element_types(); + elem_idx_dist_.assign(strengths); +} + +void MeshSpatial::check_element_types() const +{ + const auto umesh_ptr = dynamic_cast(this->mesh()); + if (umesh_ptr) { + // ensure that the unstructured mesh contains only linear tets + for (int bin = 0; bin < umesh_ptr->n_bins(); bin++) { + if (umesh_ptr->element_type(bin) != ElementType::LINEAR_TET) { + fatal_error( + "Mesh specified for source must contain only linear tetrahedra."); + } + } + } +} + +int32_t MeshSpatial::sample_element_index(uint64_t* seed) const +{ + return elem_idx_dist_.sample(seed); +} + +std::pair MeshSpatial::sample_mesh(uint64_t* seed) const +{ + // Sample the CDF defined in initialization above + int32_t elem_idx = this->sample_element_index(seed); + return {elem_idx, mesh()->sample_element(elem_idx, seed)}; +} + +Position MeshSpatial::sample(uint64_t* seed) const +{ + return this->sample_mesh(seed).second; +} + +//============================================================================== +// PointCloud implementation +//============================================================================== + +PointCloud::PointCloud(pugi::xml_node node) +{ + if (check_for_node(node, "coords")) { + point_cloud_ = get_node_position_array(node, "coords"); + } else { + fatal_error("No coordinates were provided for the PointCloud " + "spatial distribution"); + } + + std::vector strengths; + + if (check_for_node(node, "strengths")) + strengths = get_node_array(node, "strengths"); + else + strengths.resize(point_cloud_.size(), 1.0); + + if (strengths.size() != point_cloud_.size()) { + fatal_error( + fmt::format("Number of entries for the strengths array {} does " + "not match the number of spatial points provided {}.", + strengths.size(), point_cloud_.size())); + } + + point_idx_dist_.assign(strengths); +} + +PointCloud::PointCloud( + std::vector point_cloud, span strengths) +{ + point_cloud_.assign(point_cloud.begin(), point_cloud.end()); + point_idx_dist_.assign(strengths); +} + +Position PointCloud::sample(uint64_t* seed) const +{ + int32_t index = point_idx_dist_.sample(seed); + return point_cloud_[index]; +} + //============================================================================== // SpatialBox implementation //============================================================================== diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 5584210a1f..a2120a006d 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -11,6 +11,7 @@ #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/hdf5_interface.h" +#include "openmc/ifp.h" #include "openmc/math_functions.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" @@ -57,16 +58,28 @@ void calculate_generation_keff() double keff_reduced; #ifdef OPENMC_MPI - // Combine values across all processors - MPI_Allreduce(&simulation::keff_generation, &keff_reduced, 1, MPI_DOUBLE, - MPI_SUM, mpi::intracomm); + if (settings::solver_type != SolverType::RANDOM_RAY) { + // Combine values across all processors + MPI_Allreduce(&simulation::keff_generation, &keff_reduced, 1, MPI_DOUBLE, + MPI_SUM, mpi::intracomm); + } else { + // If using random ray, MPI parallelism is provided by domain replication. + // As such, all fluxes will be reduced at the end of each transport sweep, + // such that all ranks have identical scalar flux vectors, and will all + // independently compute the same value of k. Thus, there is no need to + // perform any additional MPI reduction here. + keff_reduced = simulation::keff_generation; + } #else keff_reduced = simulation::keff_generation; #endif // Normalize single batch estimate of k // TODO: This should be normalized by total_weight, not by n_particles - keff_reduced /= settings::n_particles; + if (settings::solver_type != SolverType::RANDOM_RAY) { + keff_reduced /= settings::n_particles; + } + simulation::k_generation.push_back(keff_reduced); } @@ -114,54 +127,54 @@ void synchronize_bank() "No fission sites banked on MPI rank " + std::to_string(mpi::rank)); } - // Make sure all processors start at the same point for random sampling. Then - // skip ahead in the sequence using the starting index in the 'global' - // fission bank for each processor. - - int64_t id = simulation::total_gen + overall_generation(); - uint64_t seed = init_seed(id, STREAM_TRACKING); - advance_prn_seed(start, &seed); - - // Determine how many fission sites we need to sample from the source bank - // and the probability for selecting a site. - - int64_t sites_needed; - if (total < settings::n_particles) { - sites_needed = settings::n_particles % total; - } else { - sites_needed = settings::n_particles; - } - double p_sample = static_cast(sites_needed) / total; - simulation::time_bank_sample.start(); - // ========================================================================== - // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES - // Allocate temporary source bank -- we don't really know how many fission // sites were created, so overallocate by a factor of 3 int64_t index_temp = 0; + vector temp_sites(3 * simulation::work_per_rank); - for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { - const auto& site = simulation::fission_bank[i]; + // Temporary banks for IFP + vector> temp_delayed_groups; + vector> temp_lifetimes; + if (settings::ifp_on) { + resize_ifp_data( + temp_delayed_groups, temp_lifetimes, 3 * simulation::work_per_rank); + } - // If there are less than n_particles particles banked, automatically add - // int(n_particles/total) sites to temp_sites. For example, if you need - // 1000 and 300 were banked, this would add 3 source sites per banked site - // and the remaining 100 would be randomly sampled. - if (total < settings::n_particles) { - for (int64_t j = 1; j <= settings::n_particles / total; ++j) { - temp_sites[index_temp] = site; - ++index_temp; - } - } + // ========================================================================== + // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES - // Randomly sample sites needed - if (prn(&seed) < p_sample) { - temp_sites[index_temp] = site; - ++index_temp; + // We use Uniform Combing method to exactly get the targeted particle size + // [https://doi.org/10.1080/00295639.2022.2091906] + + // Make sure all processors use the same random number seed. + int64_t id = simulation::total_gen + overall_generation(); + uint64_t seed = init_seed(id, STREAM_TRACKING); + + // Comb specification + double teeth_distance = static_cast(total) / settings::n_particles; + double teeth_offset = prn(&seed) * teeth_distance; + + // First and last hitting tooth + int64_t end = start + simulation::fission_bank.size(); + int64_t tooth_start = std::ceil((start - teeth_offset) / teeth_distance); + int64_t tooth_end = std::floor((end - teeth_offset) / teeth_distance) + 1; + + // Locally comb particles in fission_bank + double tooth = tooth_start * teeth_distance + teeth_offset; + for (int64_t i = tooth_start; i < tooth_end; i++) { + int64_t idx = std::floor(tooth) - start; + temp_sites[index_temp] = simulation::fission_bank[idx]; + if (settings::ifp_on) { + copy_ifp_data_from_fission_banks( + idx, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]); } + ++index_temp; + + // Next tooth + tooth += teeth_distance; } // At this point, the sampling of source sites is done and now we need to @@ -176,6 +189,8 @@ void synchronize_bank() MPI_Exscan(&index_temp, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); finish = start + index_temp; + // TODO: protect for MPI_Exscan at rank 0 + // Allocate space for bank_position if this hasn't been done yet int64_t bank_position[mpi::n_procs]; MPI_Allgather( @@ -185,31 +200,6 @@ void synchronize_bank() finish = index_temp; #endif - // Now that the sampling is complete, we need to ensure that we have exactly - // n_particles source sites. The way this is done in a reproducible manner is - // to adjust only the source sites on the last processor. - - if (mpi::rank == mpi::n_procs - 1) { - if (finish > settings::n_particles) { - // If we have extra sites sampled, we will simply discard the extra - // ones on the last processor - index_temp = settings::n_particles - start; - - } else if (finish < settings::n_particles) { - // If we have too few sites, repeat sites from the very end of the - // fission bank - sites_needed = settings::n_particles - finish; - for (int i = 0; i < sites_needed; ++i) { - int i_bank = simulation::fission_bank.size() - sites_needed + i; - temp_sites[index_temp] = simulation::fission_bank[i_bank]; - ++index_temp; - } - } - - // the last processor should not be sending sites to right - finish = simulation::work_index[mpi::rank + 1]; - } - simulation::time_bank_sample.stop(); simulation::time_bank_sendrecv.start(); @@ -217,15 +207,32 @@ void synchronize_bank() // ========================================================================== // SEND BANK SITES TO NEIGHBORS + // IFP number of generation + int ifp_n_generation; + if (settings::ifp_on) { + broadcast_ifp_n_generation( + ifp_n_generation, temp_delayed_groups, temp_lifetimes); + } + int64_t index_local = 0; vector requests; + // IFP send buffers + vector send_delayed_groups; + vector send_lifetimes; + if (start < settings::n_particles) { // Determine the index of the processor which has the first part of the // source_bank for the local processor int neighbor = upper_bound_index( simulation::work_index.begin(), simulation::work_index.end(), start); + // Resize IFP send buffers + if (settings::ifp_on && mpi::n_procs > 1) { + resize_ifp_data(send_delayed_groups, send_lifetimes, + ifp_n_generation * 3 * simulation::work_per_rank); + } + while (start < finish) { // Determine the number of sites to send int64_t n = @@ -238,6 +245,13 @@ void synchronize_bank() MPI_Isend(&temp_sites[index_local], static_cast(n), mpi::source_site, neighbor, mpi::rank, mpi::intracomm, &requests.back()); + + if (settings::ifp_on) { + // Send IFP data + send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, + temp_delayed_groups, send_delayed_groups, temp_lifetimes, + send_lifetimes); + } } // Increment all indices @@ -259,6 +273,11 @@ void synchronize_bank() start = simulation::work_index[mpi::rank]; index_local = 0; + // IFP receive buffers + vector recv_delayed_groups; + vector recv_lifetimes; + vector deserialization_info; + // Determine what process has the source sites that will need to be stored at // the beginning of this processor's source bank. @@ -270,6 +289,12 @@ void synchronize_bank() upper_bound_index(bank_position, bank_position + mpi::n_procs, start); } + // Resize IFP receive buffers + if (settings::ifp_on && mpi::n_procs > 1) { + resize_ifp_data(recv_delayed_groups, recv_lifetimes, + ifp_n_generation * simulation::work_per_rank); + } + while (start < simulation::work_index[mpi::rank + 1]) { // Determine how many sites need to be received int64_t n; @@ -289,13 +314,24 @@ void synchronize_bank() MPI_Irecv(&simulation::source_bank[index_local], static_cast(n), mpi::source_site, neighbor, neighbor, mpi::intracomm, &requests.back()); + if (settings::ifp_on) { + // Receive IFP data + receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, + recv_delayed_groups, recv_lifetimes, deserialization_info); + } + } else { - // If the source sites are on this procesor, we can simply copy them + // If the source sites are on this processor, we can simply copy them // from the temp_sites bank index_temp = start - bank_position[mpi::rank]; std::copy(&temp_sites[index_temp], &temp_sites[index_temp + n], &simulation::source_bank[index_local]); + + if (settings::ifp_on) { + copy_partial_ifp_data_to_source_banks( + index_temp, n, index_local, temp_delayed_groups, temp_lifetimes); + } } // Increment all indices @@ -311,9 +347,17 @@ void synchronize_bank() int n_request = requests.size(); MPI_Waitall(n_request, requests.data(), MPI_STATUSES_IGNORE); + if (settings::ifp_on) { + deserialize_ifp_info(ifp_n_generation, deserialization_info, + recv_delayed_groups, recv_lifetimes); + } + #else std::copy(temp_sites.data(), temp_sites.data() + settings::n_particles, simulation::source_bank.begin()); + if (settings::ifp_on) { + copy_complete_ifp_data_to_source_banks(temp_delayed_groups, temp_lifetimes); + } #endif simulation::time_bank_sendrecv.stop(); @@ -370,7 +414,8 @@ int openmc_get_keff(double* k_combined) // Special case for n <=3. Notice that at the end, // there is a N-3 term in a denominator. - if (simulation::n_realizations <= 3) { + if (simulation::n_realizations <= 3 || + settings::solver_type == SolverType::RANDOM_RAY) { k_combined[0] = simulation::keff; k_combined[1] = simulation::keff_std; if (simulation::n_realizations <= 1) { @@ -543,7 +588,7 @@ void shannon_entropy() double H = 0.0; for (auto p_i : p) { if (p_i > 0.0) { - H -= p_i * std::log(p_i) / std::log(2.0); + H -= p_i * std::log2(p_i); } } diff --git a/src/endf.cpp b/src/endf.cpp index b42c8641df..c0c1d2e7e8 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -90,23 +90,25 @@ bool is_inelastic_scatter(int mt) unique_ptr read_function(hid_t group, const char* name) { - hid_t dset = open_dataset(group, name); + hid_t obj_id = open_object(group, name); std::string func_type; - read_attribute(dset, "type", func_type); + read_attribute(obj_id, "type", func_type); unique_ptr func; if (func_type == "Tabulated1D") { - func = make_unique(dset); + func = make_unique(obj_id); } else if (func_type == "Polynomial") { - func = make_unique(dset); + func = make_unique(obj_id); } else if (func_type == "CoherentElastic") { - func = make_unique(dset); + func = make_unique(obj_id); } else if (func_type == "IncoherentElastic") { - func = make_unique(dset); + func = make_unique(obj_id); + } else if (func_type == "Sum") { + func = make_unique(obj_id); } else { throw std::runtime_error {"Unknown function type " + func_type + - " for dataset " + object_name(dset)}; + " for dataset " + object_name(obj_id)}; } - close_dataset(dset); + close_object(obj_id); return func; } @@ -271,4 +273,30 @@ double IncoherentElasticXS::operator()(double E) const return bound_xs_ / 2.0 * ((1 - std::exp(-4.0 * E * W)) / (2.0 * E * W)); } +//============================================================================== +// Sum1D implementation +//============================================================================== + +Sum1D::Sum1D(hid_t group) +{ + // Get number of functions + int n; + read_attribute(group, "n", n); + + // Get each function + for (int i = 0; i < n; ++i) { + auto dset_name = fmt::format("func_{}", i + 1); + functions_.push_back(read_function(group, dset_name.c_str())); + } +} + +double Sum1D::operator()(double x) const +{ + double result = 0.0; + for (auto& func : functions_) { + result += (*func)(x); + } + return result; +} + } // namespace openmc diff --git a/src/error.cpp b/src/error.cpp index 566950a973..f99f5935f0 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -110,23 +110,26 @@ void write_message(const std::string& message, int level) void fatal_error(const std::string& message, int err) { +#pragma omp critical(FatalError) + { #ifdef _POSIX_VERSION - // Make output red if user is in a terminal - if (isatty(STDERR_FILENO)) { - std::cerr << "\033[0;31m"; - } + // Make output red if user is in a terminal + if (isatty(STDERR_FILENO)) { + std::cerr << "\033[0;31m"; + } #endif - // Write error message - std::cerr << " ERROR: "; - output(message, std::cerr, 8); + // Write error message + std::cerr << " ERROR: "; + output(message, std::cerr, 8); #ifdef _POSIX_VERSION - // Reset color for terminal - if (isatty(STDERR_FILENO)) { - std::cerr << "\033[0m"; - } + // Reset color for terminal + if (isatty(STDERR_FILENO)) { + std::cerr << "\033[0m"; + } #endif + } #ifdef OPENMC_MPI MPI_Abort(mpi::intracomm, err); diff --git a/src/event.cpp b/src/event.cpp index 1db5c99d78..f33e132d0a 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,4 +1,5 @@ #include "openmc/event.h" + #include "openmc/material.h" #include "openmc/simulation.h" #include "openmc/timer.h" @@ -51,7 +52,7 @@ void dispatch_xs_event(int64_t buffer_idx) { Particle& p = simulation::particles[buffer_idx]; if (p.material() == MATERIAL_VOID || - !model::materials[p.material()]->fissionable_) { + !model::materials[p.material()]->fissionable()) { simulation::calculate_nonfuel_xs_queue.thread_safe_append({p, buffer_idx}); } else { simulation::calculate_fuel_xs_queue.thread_safe_append({p, buffer_idx}); @@ -73,17 +74,17 @@ void process_calculate_xs_events(SharedArray& queue) { simulation::time_event_calculate_xs.start(); - // TODO: If using C++17, perform a parallel sort of the queue - // by particle type, material type, and then energy, in order to - // improve cache locality and reduce thread divergence on GPU. Prior - // to C++17, std::sort is a serial only operation, which in this case - // makes it too slow to be practical for most test problems. + // TODO: If using C++17, we could perform a parallel sort of the queue by + // particle type, material type, and then energy, in order to improve cache + // locality and reduce thread divergence on GPU. However, the parallel + // algorithms typically require linking against an additional library (Intel + // TBB). Prior to C++17, std::sort is a serial only operation, which in this + // case makes it too slow to be practical for most test problems. // // std::sort(std::execution::par_unseq, queue.data(), queue.data() + // queue.size()); int64_t offset = simulation::advance_particle_queue.size(); - ; #pragma omp parallel for schedule(runtime) for (int64_t i = 0; i < queue.size(); i++) { @@ -112,7 +113,9 @@ void process_advance_particle_events() int64_t buffer_idx = simulation::advance_particle_queue[i].idx; Particle& p = simulation::particles[buffer_idx]; p.event_advance(); - if (p.collision_distance() > p.boundary().distance) { + if (!p.alive()) + continue; + if (p.collision_distance() > p.boundary().distance()) { simulation::surface_crossing_queue.thread_safe_append({p, buffer_idx}); } else { simulation::collision_queue.thread_safe_append({p, buffer_idx}); diff --git a/vendor/faddeeva/Faddeeva.cc b/src/external/Faddeeva.cc similarity index 99% rename from vendor/faddeeva/Faddeeva.cc rename to src/external/Faddeeva.cc index 80e42228c1..6a7051e249 100644 --- a/vendor/faddeeva/Faddeeva.cc +++ b/src/external/Faddeeva.cc @@ -157,7 +157,7 @@ #ifdef __cplusplus -# include "Faddeeva.hh" +# include "openmc/external/Faddeeva.hh" # include # include diff --git a/src/external/quartic_solver.cpp b/src/external/quartic_solver.cpp index 0b280e83c2..915020ffaa 100644 --- a/src/external/quartic_solver.cpp +++ b/src/external/quartic_solver.cpp @@ -1,4 +1,5 @@ #include +#define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers #include #include #include diff --git a/src/file_utils.cpp b/src/file_utils.cpp new file mode 100644 index 0000000000..517f82d157 --- /dev/null +++ b/src/file_utils.cpp @@ -0,0 +1,42 @@ +#include "openmc/file_utils.h" + +#include + +namespace openmc { + +bool dir_exists(const std::string& path) +{ + std::filesystem::path d(path); + return std::filesystem::is_directory(d); +} + +bool file_exists(const std::string& filename) +{ + std::filesystem::path p(filename); + if (!std::filesystem::exists(p)) { + return false; + } + if (std::filesystem::is_directory(p)) { + return false; + } + return true; +} + +std::string dir_name(const std::string& filename) +{ + std::filesystem::path p(filename); + return (p.parent_path()).string(); +} + +std::string get_file_extension(const std::string& filename) +{ + std::filesystem::path p(filename); + auto ext = p.extension(); + if (!ext.empty()) { + // path::extension includes the period + return ext.string().substr(1); + } + return ""; +} + +} // namespace openmc diff --git a/src/finalize.cpp b/src/finalize.cpp index bfcd77dda9..659f390b39 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -17,6 +17,7 @@ #include "openmc/photon.h" #include "openmc/plot.h" #include "openmc/random_lcg.h" +#include "openmc/random_ray/random_ray_simulation.h" #include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/source.h" @@ -63,6 +64,9 @@ using namespace openmc; int openmc_finalize() { + if (simulation::initialized) + openmc_simulation_finalize(); + // Clear results openmc_reset(); @@ -74,24 +78,41 @@ int openmc_finalize() settings::check_overlaps = false; settings::confidence_intervals = false; settings::create_fission_neutrons = true; + settings::create_delayed_neutrons = true; settings::electron_treatment = ElectronTreatment::LED; settings::delayed_photon_scaling = true; settings::energy_cutoff = {0.0, 1000.0, 0.0, 0.0}; + settings::time_cutoff = {INFTY, INFTY, INFTY, INFTY}; settings::entropy_on = false; settings::event_based = false; + settings::free_gas_threshold = 400.0; settings::gen_per_batch = 1; settings::legendre_to_tabular = true; settings::legendre_to_tabular_points = -1; settings::material_cell_offsets = true; + settings::max_lost_particles = 10; + settings::max_order = 0; settings::max_particles_in_flight = 100000; - settings::max_splits = 1000; + settings::max_secondaries = 10000; + settings::max_particle_events = 1'000'000; + settings::max_history_splits = 10'000'000; + settings::max_tracks = 1000; + settings::max_write_lost_particles = -1; + settings::n_log_bins = 8000; settings::n_inactive = 0; settings::n_particles = -1; settings::output_summary = true; settings::output_tallies = true; settings::particle_restart_run = false; + settings::path_cross_sections.clear(); + settings::path_input.clear(); + settings::path_output.clear(); + settings::path_particle_restart.clear(); + settings::path_sourcepoint.clear(); + settings::path_statepoint.clear(); settings::photon_transport = false; settings::reduce_tallies = true; + settings::rel_max_lost_particles = 1.0e-6; settings::res_scat_on = false; settings::res_scat_method = ResScatMethod::rvs; settings::res_scat_energy_min = 0.01; @@ -100,8 +121,13 @@ int openmc_finalize() settings::run_CE = true; settings::run_mode = RunMode::UNSET; settings::source_latest = false; + settings::source_rejection_fraction = 0.05; settings::source_separate = false; settings::source_write = true; + settings::ssw_cell_id = C_NONE; + settings::ssw_cell_type = SSWCellType::None; + settings::ssw_max_particles = 0; + settings::ssw_max_files = 1; settings::survival_biasing = false; settings::temperature_default = 293.6; settings::temperature_method = TemperatureMethod::NEAREST; @@ -111,51 +137,56 @@ int openmc_finalize() settings::trigger_on = false; settings::trigger_predict = false; settings::trigger_batch_interval = 1; + settings::uniform_source_sampling = false; settings::ufs_on = false; settings::urr_ptables_on = true; settings::verbosity = 7; settings::weight_cutoff = 0.25; settings::weight_survive = 1.0; + settings::weight_windows_file.clear(); settings::weight_windows_on = false; settings::write_all_tracks = false; settings::write_initial_source = false; simulation::keff = 1.0; - simulation::n_lost_particles = 0; simulation::need_depletion_rx = false; + simulation::ssw_current_file = 1; simulation::total_gen = 0; simulation::entropy_mesh = nullptr; simulation::ufs_mesh = nullptr; - data::energy_max = {INFTY, INFTY}; - data::energy_min = {0.0, 0.0}; + data::energy_max = {INFTY, INFTY, INFTY, INFTY}; + data::energy_min = {0.0, 0.0, 0.0, 0.0}; data::temperature_min = 0.0; data::temperature_max = INFTY; model::root_universe = -1; model::plotter_seed = 1; openmc::openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_stride(DEFAULT_STRIDE); // Deallocate arrays free_memory(); -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED settings::libmesh_init.reset(); #endif // Free all MPI types #ifdef OPENMC_MPI - if (mpi::source_site != MPI_DATATYPE_NULL) + if (mpi::source_site != MPI_DATATYPE_NULL) { MPI_Type_free(&mpi::source_site); + } #endif + openmc_reset_random_ray(); + return 0; } int openmc_reset() { - model::universe_cell_counts.clear(); model::universe_level_counts.clear(); for (auto& t : model::tallies) { @@ -174,6 +205,8 @@ int openmc_reset() settings::cmfd_run = false; + simulation::n_lost_particles = 0; + return 0; } @@ -195,5 +228,6 @@ int openmc_hard_reset() // Reset the random number generator state openmc::openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_stride(DEFAULT_STRIDE); return 0; } diff --git a/src/geometry.cpp b/src/geometry.cpp index acce3fc78e..e8c306ecf2 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -32,23 +32,23 @@ vector overlap_check_count; // Non-member functions //============================================================================== -bool check_cell_overlap(Particle& p, bool error) +bool check_cell_overlap(GeometryState& p, bool error) { int n_coord = p.n_coord(); // Loop through each coordinate level for (int j = 0; j < n_coord; j++) { - Universe& univ = *model::universes[p.coord(j).universe]; + Universe& univ = *model::universes[p.coord(j).universe()]; // Loop through each cell on this level for (auto index_cell : univ.cells_) { Cell& c = *model::cells[index_cell]; - if (c.contains(p.coord(j).r, p.coord(j).u, p.surface())) { - if (index_cell != p.coord(j).cell) { + if (c.contains(p.coord(j).r(), p.coord(j).u(), p.surface())) { + if (index_cell != p.coord(j).cell()) { if (error) { fatal_error( fmt::format("Overlapping cells detected: {}, {} on universe {}", - c.id_, model::cells[p.coord(j).cell]->id_, univ.id_)); + c.id_, model::cells[p.coord(j).cell()]->id_, univ.id_)); } return true; } @@ -63,7 +63,7 @@ bool check_cell_overlap(Particle& p, bool error) //============================================================================== -int cell_instance_at_level(const Particle& p, int level) +int cell_instance_at_level(const GeometryState& p, int level) { // throw error if the requested level is too deep for the geometry if (level > model::n_coord_levels) { @@ -73,7 +73,7 @@ int cell_instance_at_level(const Particle& p, int level) } // determine the cell instance - Cell& c {*model::cells[p.coord(level).cell]}; + Cell& c {*model::cells[p.coord(level).cell()]}; // quick exit if this cell doesn't have distribcell instances if (c.distribcell_index_ == C_NONE) @@ -82,12 +82,13 @@ int cell_instance_at_level(const Particle& p, int level) // compute the cell's instance int instance = 0; for (int i = 0; i < level; i++) { - const auto& c_i {*model::cells[p.coord(i).cell]}; + const auto& c_i {*model::cells[p.coord(i).cell()]}; if (c_i.type_ == Fill::UNIVERSE) { instance += c_i.offset_[c.distribcell_index_]; } else if (c_i.type_ == Fill::LATTICE) { - auto& lat {*model::lattices[p.coord(i + 1).lattice]}; - const auto& i_xyz {p.coord(i + 1).lattice_i}; + instance += c_i.offset_[c.distribcell_index_]; + auto& lat {*model::lattices[p.coord(i + 1).lattice()]}; + const auto& i_xyz {p.coord(i + 1).lattice_index()}; if (lat.are_valid_indices(i_xyz)) { instance += lat.offset(c.distribcell_index_, i_xyz); } @@ -98,7 +99,8 @@ int cell_instance_at_level(const Particle& p, int level) //============================================================================== -bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) +bool find_cell_inner( + GeometryState& p, const NeighborList* neighbor_list, bool verbose) { // Find which cell of this universe the particle is in. Use the neighbor list // to shorten the search if one was provided. @@ -109,7 +111,7 @@ bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) i_cell = *it; // Make sure the search cell is in the same universe. - int i_universe = p.coord(p.n_coord() - 1).universe; + int i_universe = p.lowest_coord().universe(); if (model::cells[i_cell]->universe_ != i_universe) continue; @@ -118,7 +120,7 @@ bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) Direction u {p.u_local()}; auto surf = p.surface(); if (model::cells[i_cell]->contains(r, u, surf)) { - p.coord(p.n_coord() - 1).cell = i_cell; + p.lowest_coord().cell() = i_cell; found = true; break; } @@ -144,7 +146,7 @@ bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) // code below this conditional, we set i_cell back to C_NONE to indicate // that. if (i_cell == C_NONE) { - int i_universe = p.coord(p.n_coord() - 1).universe; + int i_universe = p.lowest_coord().universe(); const auto& univ {model::universes[i_universe]}; found = univ->find_cell(p); } @@ -152,14 +154,191 @@ bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) if (!found) { return found; } - i_cell = p.coord(p.n_coord() - 1).cell; + i_cell = p.lowest_coord().cell(); // Announce the cell that the particle is entering. - if (found && (settings::verbosity >= 10 || p.trace())) { + if (found && verbose) { auto msg = fmt::format(" Entering cell {}", model::cells[i_cell]->id_); write_message(msg, 1); } + Cell& c {*model::cells[i_cell]}; + if (c.type_ == Fill::MATERIAL) { + // Found a material cell which means this is the lowest coord level. + + p.cell_instance() = 0; + // Find the distribcell instance number. + if (c.distribcell_index_ >= 0) { + p.cell_instance() = cell_instance_at_level(p, p.n_coord() - 1); + } + + // Set the material, temperature and density multiplier. + p.material_last() = p.material(); + p.material() = c.material(p.cell_instance()); + p.sqrtkT_last() = p.sqrtkT(); + p.sqrtkT() = c.sqrtkT(p.cell_instance()); + p.density_mult_last() = p.density_mult(); + p.density_mult() = c.density_mult(p.cell_instance()); + + return true; + + } else if (c.type_ == Fill::UNIVERSE) { + //======================================================================== + //! Found a lower universe, update this coord level then search the next. + + // Set the lower coordinate level universe. + auto& coord {p.coord(p.n_coord())}; + coord.universe() = c.fill_; + + // Set the position and direction. + coord.r() = p.r_local(); + coord.u() = p.u_local(); + + // Apply translation. + coord.r() -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + } else if (c.type_ == Fill::LATTICE) { + //======================================================================== + //! Found a lower lattice, update this coord level then search the next. + + Lattice& lat {*model::lattices[c.fill_]}; + + // Set the position and direction. + auto& coord {p.coord(p.n_coord())}; + coord.r() = p.r_local(); + coord.u() = p.u_local(); + + // Apply translation. + coord.r() -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + // Determine lattice indices. + auto& i_xyz {coord.lattice_index()}; + lat.get_indices(coord.r(), coord.u(), i_xyz); + + // Get local position in appropriate lattice cell + coord.r() = lat.get_local_position(coord.r(), i_xyz); + + // Set lattice indices. + coord.lattice() = c.fill_; + + // Set the lower coordinate level universe. + if (lat.are_valid_indices(i_xyz)) { + coord.universe() = lat[i_xyz]; + } else { + if (lat.outer_ != NO_OUTER_UNIVERSE) { + coord.universe() = lat.outer_; + } else { + p.mark_as_lost(fmt::format( + "Particle {} left lattice {}, but it has no outer definition.", + p.id(), lat.id_)); + } + } + } + i_cell = C_NONE; // trip non-neighbor cell search at next iteration + found = false; + } + + return found; +} + +//============================================================================== + +bool neighbor_list_find_cell(GeometryState& p, bool verbose) +{ + + // Reset all the deeper coordinate levels. + for (int i = p.n_coord(); i < model::n_coord_levels; i++) { + p.coord(i).reset(); + } + + // Get the cell this particle was in previously. + auto coord_lvl = p.n_coord() - 1; + auto i_cell = p.coord(coord_lvl).cell(); + Cell& c {*model::cells[i_cell]}; + + // Search for the particle in that cell's neighbor list. Return if we + // found the particle. + bool found = find_cell_inner(p, &c.neighbors_, verbose); + if (found) + return found; + + // The particle could not be found in the neighbor list. Try searching all + // cells in this universe, and update the neighbor list if we find a new + // neighboring cell. + found = find_cell_inner(p, nullptr, verbose); + if (found) + c.neighbors_.push_back(p.coord(coord_lvl).cell()); + return found; +} + +bool exhaustive_find_cell(GeometryState& p, bool verbose) +{ + int i_universe = p.lowest_coord().universe(); + if (i_universe == C_NONE) { + p.coord(0).universe() = model::root_universe; + p.n_coord() = 1; + i_universe = model::root_universe; + } + // Reset all the deeper coordinate levels. + for (int i = p.n_coord(); i < model::n_coord_levels; i++) { + p.coord(i).reset(); + } + return find_cell_inner(p, nullptr, verbose); +} + +bool find_cell_in_virtual_lattice(GeometryState& p, bool verbose) +{ + int i_surface = std::abs(p.surface()); + if (p.surface() > 0) { + for (int i = p.n_coord(); i < model::n_coord_levels; i++) { + p.coord(i).reset(); + } + p.coord(p.n_coord() - 1).cell() = + model::cell_map[model::surfaces[i_surface - 1]->triso_base_index_]; + } else if (p.surface() < 0) { + for (int i = p.n_coord(); i < model::n_coord_levels; i++) { + p.coord(i).reset(); + } + if (model::surfaces[i_surface - 1]->triso_particle_index_ == -1) { + fatal_error(fmt::format("Particle cell of surface {} is not defined", + model::surfaces[i_surface - 1]->id_)); + } + p.lowest_coord().cell() = + model::cell_map[model::surfaces[i_surface - 1]->triso_particle_index_]; + } + + // find material + bool found = true; + int i_cell = p.lowest_coord().cell(); + for (;; ++p.n_coord()) { + if (i_cell == C_NONE) { + int i_universe = p.lowest_coord().universe(); + const auto& univ {model::universes[i_universe]}; + + if (univ->filled_with_triso_base_ != -1) { + p.lowest_coord().cell() = + model::cell_map[univ->filled_with_triso_base_]; + found = true; + } else { + found = univ->find_cell(p); + } + if (!found) { + return found; + } + } + + i_cell = p.lowest_coord().cell(); + Cell& c {*model::cells[i_cell]}; if (c.type_ == Fill::MATERIAL) { // Found a material cell which means this is the lowest coord level. @@ -183,167 +362,76 @@ bool find_cell_inner(Particle& p, const NeighborList* neighbor_list) } else { p.sqrtkT() = c.sqrtkT_[0]; } - - return true; + return found; } else if (c.type_ == Fill::UNIVERSE) { //======================================================================== - //! Found a lower universe, update this coord level then search the next. + //! Found a lower universe, update this coord level then search the + //! next. // Set the lower coordinate level universe. - auto& coord {p.coord(p.n_coord())}; - coord.universe = c.fill_; + auto& coor {p.coord(p.n_coord())}; + coor.universe() = c.fill_; // Set the position and direction. - coord.r = p.r_local(); - coord.u = p.u_local(); + coor.r() = p.r_local(); + coor.u() = p.u_local(); // Apply translation. - coord.r -= c.translation_; + coor.r() -= c.translation_; // Apply rotation. if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - } else if (c.type_ == Fill::LATTICE) { - //======================================================================== - //! Found a lower lattice, update this coord level then search the next. - - Lattice& lat {*model::lattices[c.fill_]}; - - // Set the position and direction. - auto& coord {p.coord(p.n_coord())}; - coord.r = p.r_local(); - coord.u = p.u_local(); - - // Apply translation. - coord.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - // Determine lattice indices. - auto& i_xyz {coord.lattice_i}; - lat.get_indices(coord.r, coord.u, i_xyz); - - // Get local position in appropriate lattice cell - coord.r = lat.get_local_position(coord.r, i_xyz); - - // Set lattice indices. - coord.lattice = c.fill_; - - // Set the lower coordinate level universe. - if (lat.are_valid_indices(i_xyz)) { - coord.universe = lat[i_xyz]; - } else { - if (lat.outer_ != NO_OUTER_UNIVERSE) { - coord.universe = lat.outer_; - } else { - warning(fmt::format("Particle {} is outside lattice {} but the " - "lattice has no defined outer universe.", - p.id(), lat.id_)); - return false; - } + coor.rotate(c.rotation_); } + i_cell = C_NONE; } - i_cell = C_NONE; // trip non-neighbor cell search at next iteration - found = false; } - - return found; } - //============================================================================== -bool neighbor_list_find_cell(Particle& p) +void cross_lattice(GeometryState& p, const BoundaryInfo& boundary, bool verbose) { + auto& coord {p.lowest_coord()}; + auto& lat {*model::lattices[coord.lattice()]}; - // Reset all the deeper coordinate levels. - for (int i = p.n_coord(); i < model::n_coord_levels; i++) { - p.coord(i).reset(); - } - - // Get the cell this particle was in previously. - auto coord_lvl = p.n_coord() - 1; - auto i_cell = p.coord(coord_lvl).cell; - Cell& c {*model::cells[i_cell]}; - - // Search for the particle in that cell's neighbor list. Return if we - // found the particle. - bool found = find_cell_inner(p, &c.neighbors_); - if (found) - return found; - - // The particle could not be found in the neighbor list. Try searching all - // cells in this universe, and update the neighbor list if we find a new - // neighboring cell. - found = find_cell_inner(p, nullptr); - if (found) - c.neighbors_.push_back(p.coord(coord_lvl).cell); - return found; -} - -bool exhaustive_find_cell(Particle& p) -{ - int i_universe = p.coord(p.n_coord() - 1).universe; - if (i_universe == C_NONE) { - p.coord(0).universe = model::root_universe; - p.n_coord() = 1; - i_universe = model::root_universe; - } - // Reset all the deeper coordinate levels. - for (int i = p.n_coord(); i < model::n_coord_levels; i++) { - p.coord(i).reset(); - } - return find_cell_inner(p, nullptr); -} - -//============================================================================== - -void cross_lattice(Particle& p, const BoundaryInfo& boundary) -{ - auto& coord {p.coord(p.n_coord() - 1)}; - auto& lat {*model::lattices[coord.lattice]}; - - if (settings::verbosity >= 10 || p.trace()) { + if (verbose) { write_message( fmt::format(" Crossing lattice {}. Current position ({},{},{}). r={}", - lat.id_, coord.lattice_i[0], coord.lattice_i[1], coord.lattice_i[2], - p.r()), + lat.id_, coord.lattice_index()[0], coord.lattice_index()[1], + coord.lattice_index()[2], p.r()), 1); } // Set the lattice indices. - coord.lattice_i[0] += boundary.lattice_translation[0]; - coord.lattice_i[1] += boundary.lattice_translation[1]; - coord.lattice_i[2] += boundary.lattice_translation[2]; + coord.lattice_index()[0] += boundary.lattice_translation()[0]; + coord.lattice_index()[1] += boundary.lattice_translation()[1]; + coord.lattice_index()[2] += boundary.lattice_translation()[2]; // Set the new coordinate position. const auto& upper_coord {p.coord(p.n_coord() - 2)}; - const auto& cell {model::cells[upper_coord.cell]}; - Position r = upper_coord.r; + const auto& cell {model::cells[upper_coord.cell()]}; + Position r = upper_coord.r(); r -= cell->translation_; if (!cell->rotation_.empty()) { r = r.rotate(cell->rotation_); } - p.r_local() = lat.get_local_position(r, coord.lattice_i); + p.r_local() = lat.get_local_position(r, coord.lattice_index()); - if (!lat.are_valid_indices(coord.lattice_i)) { + if (!lat.are_valid_indices(coord.lattice_index())) { // The particle is outside the lattice. Search for it from the base coords. p.n_coord() = 1; bool found = exhaustive_find_cell(p); - if (!found && p.alive()) { - p.mark_as_lost(fmt::format("Could not locate particle {} after " - "crossing a lattice boundary", - p.id())); + + if (!found) { + p.mark_as_lost(fmt::format("Particle {} could not be located after " + "crossing a boundary of lattice {}", + p.id(), lat.id_)); } } else { // Find cell in next lattice element. - p.coord(p.n_coord() - 1).universe = lat[coord.lattice_i]; + p.lowest_coord().universe() = lat[coord.lattice_index()]; bool found = exhaustive_find_cell(p); if (!found) { @@ -351,10 +439,10 @@ void cross_lattice(Particle& p, const BoundaryInfo& boundary) // this case, search for it from the base coords. p.n_coord() = 1; bool found = exhaustive_find_cell(p); - if (!found && p.alive()) { - p.mark_as_lost(fmt::format("Could not locate particle {} after " - "crossing a lattice boundary", - p.id())); + if (!found) { + p.mark_as_lost(fmt::format("Particle {} could not be located after " + "crossing a boundary of lattice {}", + p.id(), lat.id_)); } } } @@ -362,7 +450,7 @@ void cross_lattice(Particle& p, const BoundaryInfo& boundary) //============================================================================== -BoundaryInfo distance_to_boundary(Particle& p) +BoundaryInfo distance_to_boundary(GeometryState& p) { BoundaryInfo info; double d_lat = INFINITY; @@ -373,9 +461,9 @@ BoundaryInfo distance_to_boundary(Particle& p) // Loop over each coordinate level. for (int i = 0; i < p.n_coord(); i++) { const auto& coord {p.coord(i)}; - const Position& r {coord.r}; - const Direction& u {coord.u}; - Cell& c {*model::cells[coord.cell]}; + const Position& r {coord.r()}; + const Direction& u {coord.u()}; + Cell& c {*model::cells[coord.cell()]}; // Find the oncoming surface in this cell and the distance to it. auto surface_distance = c.distance(r, u, p.surface(), &p); @@ -383,71 +471,73 @@ BoundaryInfo distance_to_boundary(Particle& p) level_surf_cross = surface_distance.second; // Find the distance to the next lattice tile crossing. - if (coord.lattice != C_NONE) { - auto& lat {*model::lattices[coord.lattice]}; + if (coord.lattice() != C_NONE) { + auto& lat {*model::lattices[coord.lattice()]}; // TODO: refactor so both lattice use the same position argument (which // also means the lat.type attribute can be removed) std::pair> lattice_distance; switch (lat.type_) { case LatticeType::rect: - lattice_distance = lat.distance(r, u, coord.lattice_i); + lattice_distance = lat.distance(r, u, coord.lattice_index()); break; case LatticeType::hex: - auto& cell_above {model::cells[p.coord(i - 1).cell]}; - Position r_hex {p.coord(i - 1).r}; + auto& cell_above {model::cells[p.coord(i - 1).cell()]}; + Position r_hex {p.coord(i - 1).r()}; r_hex -= cell_above->translation_; - if (coord.rotated) { + if (coord.rotated()) { r_hex = r_hex.rotate(cell_above->rotation_); } - r_hex.z = coord.r.z; - lattice_distance = lat.distance(r_hex, u, coord.lattice_i); + r_hex.z = coord.r().z; + lattice_distance = lat.distance(r_hex, u, coord.lattice_index()); break; } d_lat = lattice_distance.first; level_lat_trans = lattice_distance.second; if (d_lat < 0) { - p.mark_as_lost(fmt::format( - "Particle {} had a negative distance to a lattice boundary", p.id())); + p.mark_as_lost(fmt::format("Particle {} had a negative distance " + "to a lattice boundary.", + p.id())); } } // If the boundary on this coordinate level is coincident with a boundary on // a higher level then we need to make sure that the higher level boundary // is selected. This logic must consider floating point precision. - double& d = info.distance; + double& d = info.distance(); if (d_surf < d_lat - FP_COINCIDENT) { if (d == INFINITY || (d - d_surf) / d >= FP_REL_PRECISION) { + // Update closest distance d = d_surf; // If the cell is not simple, it is possible that both the negative and // positive half-space were given in the region specification. Thus, we // have to explicitly check which half-space the particle would be // traveling into if the surface is crossed - if (c.simple_) { - info.surface_index = level_surf_cross; + if (c.is_simple() || d == INFTY) { + info.surface() = level_surf_cross; } else { Position r_hit = r + d_surf * u; Surface& surf {*model::surfaces[std::abs(level_surf_cross) - 1]}; Direction norm = surf.normal(r_hit); if (u.dot(norm) > 0) { - info.surface_index = std::abs(level_surf_cross); + info.surface() = std::abs(level_surf_cross); } else { - info.surface_index = -std::abs(level_surf_cross); + info.surface() = -std::abs(level_surf_cross); } } - info.lattice_translation[0] = 0; - info.lattice_translation[1] = 0; - info.lattice_translation[2] = 0; - info.coord_level = i + 1; + info.lattice_translation()[0] = 0; + info.lattice_translation()[1] = 0; + info.lattice_translation()[2] = 0; + info.coord_level() = i + 1; } } else { if (d == INFINITY || (d - d_lat) / d >= FP_REL_PRECISION) { d = d_lat; - info.surface_index = 0; - info.lattice_translation = level_lat_trans; - info.coord_level = i + 1; + info.surface() = SURFACE_NONE; + info.lattice_translation() = level_lat_trans; + info.coord_level() = i + 1; } } } @@ -461,18 +551,19 @@ BoundaryInfo distance_to_boundary(Particle& p) extern "C" int openmc_find_cell( const double* xyz, int32_t* index, int32_t* instance) { - Particle p; + GeometryState geom_state; - p.r() = Position {xyz}; - p.u() = {0.0, 0.0, 1.0}; + geom_state.r() = Position {xyz}; + geom_state.u() = {0.0, 0.0, 1.0}; - if (!exhaustive_find_cell(p)) { - set_errmsg(fmt::format("Could not find cell at position {}.", p.r())); + if (!exhaustive_find_cell(geom_state)) { + set_errmsg( + fmt::format("Could not find cell at position {}.", geom_state.r())); return OPENMC_E_GEOMETRY; } - *index = p.coord(p.n_coord() - 1).cell; - *instance = p.cell_instance(); + *index = geom_state.lowest_coord().cell(); + *instance = geom_state.cell_instance(); return 0; } diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 842822d05a..8a145fb1f1 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -25,21 +25,9 @@ namespace openmc { namespace model { -std::unordered_map> - universe_cell_counts; std::unordered_map universe_level_counts; } // namespace model -// adds the cell counts of universe b to universe a -void update_universe_cell_count(int32_t a, int32_t b) -{ - auto& universe_a_counts = model::universe_cell_counts[a]; - const auto& universe_b_counts = model::universe_cell_counts[b]; - for (const auto& it : universe_b_counts) { - universe_a_counts[it.first] += it.second; - } -} - void read_geometry_xml() { // Display output message @@ -61,6 +49,11 @@ void read_geometry_xml() // Get root element pugi::xml_node root = doc.document_element(); + read_geometry_xml(root); +} + +void read_geometry_xml(pugi::xml_node root) +{ // Read surfaces, cells, lattice read_surfaces(root); read_cells(root); @@ -76,7 +69,8 @@ void read_geometry_xml() } } - if (settings::run_mode != RunMode::PLOTTING && !boundary_exists) { + if (settings::run_mode != RunMode::PLOTTING && + settings::run_mode != RunMode::VOLUME && !boundary_exists) { fatal_error("No boundary conditions were applied to any surfaces!"); } @@ -154,9 +148,8 @@ void partition_universes() // Collect the set of surfaces in this universe. std::unordered_set surf_inds; for (auto i_cell : univ->cells_) { - for (auto token : model::cells[i_cell]->rpn_) { - if (token < OP_UNION) - surf_inds.insert(std::abs(token) - 1); + for (auto token : model::cells[i_cell]->surfaces()) { + surf_inds.insert(std::abs(token) - 1); } } @@ -202,6 +195,24 @@ void assign_temperatures() //============================================================================== +void finalize_cell_densities() +{ + for (auto& c : model::cells) { + // Convert to density multipliers. + if (!c->density_mult_.empty()) { + for (int32_t instance = 0; instance < c->density_mult_.size(); + ++instance) { + c->density_mult_[instance] /= + model::materials[c->material(instance)]->density_gpcc(); + } + } else { + c->density_mult_ = {1.0}; + } + } +} + +//============================================================================== + void get_temperatures( vector>& nuc_temps, vector>& thermal_temps) { @@ -258,7 +269,7 @@ void finalize_geometry() { // Perform some final operations to set up the geometry adjust_indices(); - count_cell_instances(model::root_universe); + count_universe_instances(); partition_universes(); // Assign temperatures to cells that don't have temperatures already assigned @@ -339,7 +350,7 @@ void prepare_distribcell(const std::vector* user_distribcells) // By default, add material cells to the list of distributed cells if (settings::material_cell_offsets) { - for (gsl::index i = 0; i < model::cells.size(); ++i) { + for (int64_t i = 0; i < model::cells.size(); ++i) { if (model::cells[i]->type_ == Fill::MATERIAL) distribcells.insert(i); } @@ -351,35 +362,49 @@ void prepare_distribcell(const std::vector* user_distribcells) Cell& c {*model::cells[i]}; if (c.material_.size() > 1) { - if (c.material_.size() != c.n_instances_) { + if (c.material_.size() != c.n_instances()) { fatal_error(fmt::format( "Cell {} was specified with {} materials but has {} distributed " "instances. The number of materials must equal one or the number " "of instances.", - c.id_, c.material_.size(), c.n_instances_)); + c.id_, c.material_.size(), c.n_instances())); } } if (c.sqrtkT_.size() > 1) { - if (c.sqrtkT_.size() != c.n_instances_) { + if (c.sqrtkT_.size() != c.n_instances()) { fatal_error(fmt::format( "Cell {} was specified with {} temperatures but has {} distributed " "instances. The number of temperatures must equal one or the number " "of instances.", - c.id_, c.sqrtkT_.size(), c.n_instances_)); + c.id_, c.sqrtkT_.size(), c.n_instances())); + } + } + + if (c.density_mult_.size() > 1) { + if (c.density_mult_.size() != c.n_instances()) { + fatal_error(fmt::format("Cell {} was specified with {} density " + "multipliers but has {} distributed " + "instances. The number of density multipliers " + "must equal one or the number " + "of instances.", + c.id_, c.density_mult_.size(), c.n_instances())); } } } // Search through universes for material cells and assign each one a - // unique distribcell array index. - int distribcell_index = 0; + // distribcell array index according to the containing universe. vector target_univ_ids; for (const auto& u : model::universes) { for (auto idx : u->cells_) { if (distribcells.find(idx) != distribcells.end()) { - model::cells[idx]->distribcell_index_ = distribcell_index++; - target_univ_ids.push_back(u->id_); + if (!contains(target_univ_ids, u->id_)) { + target_univ_ids.push_back(u->id_); + } + model::cells[idx]->distribcell_index_ = + std::find(target_univ_ids.begin(), target_univ_ids.end(), u->id_) - + target_univ_ids.begin(); } } } @@ -412,9 +437,9 @@ void prepare_distribcell(const std::vector* user_distribcells) search_univ, target_univ_id, univ_count_memo); } else if (c.type_ == Fill::LATTICE) { + c.offset_[map] = offset; Lattice& lat = *model::lattices[c.fill_]; - offset = - lat.fill_offset_table(offset, target_univ_id, map, univ_count_memo); + offset += lat.fill_offset_table(target_univ_id, map, univ_count_memo); } } } @@ -423,32 +448,12 @@ void prepare_distribcell(const std::vector* user_distribcells) //============================================================================== -void count_cell_instances(int32_t univ_indx) +void count_universe_instances() { - const auto univ_counts = model::universe_cell_counts.find(univ_indx); - if (univ_counts != model::universe_cell_counts.end()) { - for (const auto& it : univ_counts->second) { - model::cells[it.first]->n_instances_ += it.second; - } - } else { - for (int32_t cell_indx : model::universes[univ_indx]->cells_) { - Cell& c = *model::cells[cell_indx]; - ++c.n_instances_; - model::universe_cell_counts[univ_indx][cell_indx] += 1; - - if (c.type_ == Fill::UNIVERSE) { - // This cell contains another universe. Recurse into that universe. - count_cell_instances(c.fill_); - update_universe_cell_count(univ_indx, c.fill_); - } else if (c.type_ == Fill::LATTICE) { - // This cell contains a lattice. Recurse into the lattice universes. - Lattice& lat = *model::lattices[c.fill_]; - for (auto it = lat.begin(); it != lat.end(); ++it) { - count_cell_instances(*it); - update_universe_cell_count(univ_indx, *it); - } - } - } + for (auto& univ : model::universes) { + std::unordered_map univ_count_memo; + univ->n_instances_ = count_universe_instances( + model::root_universe, univ->id_, univ_count_memo); } } @@ -522,13 +527,11 @@ std::string distribcell_path_inner(int32_t target_cell, int32_t map, // Material cells don't contain other cells so ignore them. if (c.type_ != Fill::MATERIAL) { - int32_t temp_offset; - if (c.type_ == Fill::UNIVERSE) { - temp_offset = offset + c.offset_[map]; - } else { + int32_t temp_offset = offset + c.offset_[map]; + if (c.type_ == Fill::LATTICE) { Lattice& lat = *model::lattices[c.fill_]; int32_t indx = lat.universes_.size() * map + lat.begin().indx_; - temp_offset = offset + lat.offsets_[indx]; + temp_offset += lat.offsets_[indx]; } // The desired cell is the first cell that gives an offset smaller or @@ -538,6 +541,15 @@ std::string distribcell_path_inner(int32_t target_cell, int32_t map, } } + // if we get through the loop without finding an appropriate entry, throw + // an error + if (cell_it == search_univ.cells_.crend()) { + fatal_error( + fmt::format("Failed to generate a text label for distribcell with ID {}." + "The current label is: '{}'", + model::cells[target_cell]->id_, path.str())); + } + // Add the cell to the path string. Cell& c = *model::cells[*cell_it]; path << "c" << c.id_ << "->"; @@ -554,7 +566,7 @@ std::string distribcell_path_inner(int32_t target_cell, int32_t map, path << "l" << lat.id_; for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) { int32_t indx = lat.universes_.size() * map + it.indx_; - int32_t temp_offset = offset + lat.offsets_[indx]; + int32_t temp_offset = offset + lat.offsets_[indx] + c.offset_[map]; if (temp_offset <= target_offset) { offset = temp_offset; path << "(" << lat.index_to_string(it.indx_) << ")->"; @@ -605,6 +617,11 @@ int maximum_levels(int32_t univ) return levels_below; } +bool is_root_universe(int32_t univ_id) +{ + return model::universe_map[univ_id] == model::root_universe; +} + //============================================================================== void free_memory_geometry() diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index d53ad38790..bf1f79549e 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -92,7 +92,7 @@ void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims) H5Aclose(attr); } -hid_t create_group(hid_t parent_id, char const* name) +hid_t create_group(hid_t parent_id, const char* name) { hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); if (out < 0) { @@ -118,6 +118,12 @@ void close_group(hid_t group_id) fatal_error("Failed to close group"); } +void close_object(hid_t obj_id) +{ + if (H5Oclose(obj_id) < 0) + fatal_error("Failed to close object"); +} + int dataset_ndims(hid_t dset) { hid_t dspace = H5Dget_space(dset); @@ -219,8 +225,7 @@ void get_name(hid_t obj_id, std::string& name) { size_t size = 1 + H5Iget_name(obj_id, nullptr, 0); name.resize(size); - // TODO: switch to name.data() when using C++17 - H5Iget_name(obj_id, &name[0], size); + H5Iget_name(obj_id, name.data(), size); } int get_num_datasets(hid_t group_id) @@ -394,6 +399,12 @@ hid_t open_group(hid_t group_id, const char* name) return H5Gopen(group_id, name, H5P_DEFAULT); } +hid_t open_object(hid_t group_id, const std::string& name) +{ + ensure_exists(group_id, name.c_str()); + return H5Oopen(group_id, name.c_str(), H5P_DEFAULT); +} + void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer) { hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT); diff --git a/src/ifp.cpp b/src/ifp.cpp new file mode 100644 index 0000000000..cc4a76538b --- /dev/null +++ b/src/ifp.cpp @@ -0,0 +1,216 @@ +#include "openmc/ifp.h" + +#include "openmc/bank.h" +#include "openmc/message_passing.h" +#include "openmc/particle.h" +#include "openmc/particle_data.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/vector.h" + +namespace openmc { + +bool is_beta_effective_or_both() +{ + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + return true; + } + return false; +} + +bool is_generation_time_or_both() +{ + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + return true; + } + return false; +} + +void ifp(const Particle& p, int64_t idx) +{ + if (is_beta_effective_or_both()) { + const auto& delayed_groups = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; + simulation::ifp_fission_delayed_group_bank[idx] = + _ifp(p.delayed_group(), delayed_groups); + } + if (is_generation_time_or_both()) { + const auto& lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + simulation::ifp_fission_lifetime_bank[idx] = _ifp(p.lifetime(), lifetimes); + } +} + +void resize_simulation_ifp_banks() +{ + resize_ifp_data(simulation::ifp_source_delayed_group_bank, + simulation::ifp_source_lifetime_bank, simulation::work_per_rank); + resize_ifp_data(simulation::ifp_fission_delayed_group_bank, + simulation::ifp_fission_lifetime_bank, 3 * simulation::work_per_rank); +} + +void copy_ifp_data_from_fission_banks( + int i_bank, vector& delayed_groups, vector& lifetimes) +{ + if (is_beta_effective_or_both()) { + delayed_groups = simulation::ifp_fission_delayed_group_bank[i_bank]; + } + if (is_generation_time_or_both()) { + lifetimes = simulation::ifp_fission_lifetime_bank[i_bank]; + } +} + +#ifdef OPENMC_MPI + +void broadcast_ifp_n_generation(int& n_generation, + const vector>& delayed_groups, + const vector>& lifetimes) +{ + if (mpi::rank == 0) { + if (is_beta_effective_or_both()) { + n_generation = static_cast(delayed_groups[0].size()); + } else { + n_generation = static_cast(lifetimes[0].size()); + } + } + MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); +} + +void send_ifp_info(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, const vector>& delayed_groups, + vector& send_delayed_groups, const vector>& lifetimes, + vector& send_lifetimes) +{ + // Copy data in send buffers + for (int i = idx; i < idx + n; i++) { + if (is_beta_effective_or_both()) { + std::copy(delayed_groups[i].begin(), delayed_groups[i].end(), + send_delayed_groups.begin() + i * n_generation); + } + if (is_generation_time_or_both()) { + std::copy(lifetimes[i].begin(), lifetimes[i].end(), + send_lifetimes.begin() + i * n_generation); + } + } + // Send delayed groups + if (is_beta_effective_or_both()) { + requests.emplace_back(); + MPI_Isend(&send_delayed_groups[n_generation * idx], + n_generation * static_cast(n), MPI_INT, neighbor, mpi::rank, + mpi::intracomm, &requests.back()); + } + // Send lifetimes + if (is_generation_time_or_both()) { + requests.emplace_back(); + MPI_Isend(&send_lifetimes[n_generation * idx], + n_generation * static_cast(n), MPI_DOUBLE, neighbor, mpi::rank, + mpi::intracomm, &requests.back()); + } +} + +void receive_ifp_data(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, vector& delayed_groups, + vector& lifetimes, vector& deserialization) +{ + // Receive delayed groups + if (is_beta_effective_or_both()) { + requests.emplace_back(); + MPI_Irecv(&delayed_groups[n_generation * idx], + n_generation * static_cast(n), MPI_INT, neighbor, neighbor, + mpi::intracomm, &requests.back()); + } + // Receive lifetimes + if (is_generation_time_or_both()) { + requests.emplace_back(); + MPI_Irecv(&lifetimes[n_generation * idx], + n_generation * static_cast(n), MPI_DOUBLE, neighbor, neighbor, + mpi::intracomm, &requests.back()); + } + // Deserialization info to reconstruct data later + DeserializationInfo info = {idx, n}; + deserialization.push_back(info); +} + +void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank, + const vector>& delayed_groups, + const vector>& lifetimes) +{ + if (is_beta_effective_or_both()) { + std::copy(&delayed_groups[idx], &delayed_groups[idx + n], + &simulation::ifp_source_delayed_group_bank[i_bank]); + } + if (is_generation_time_or_both()) { + std::copy(&lifetimes[idx], &lifetimes[idx + n], + &simulation::ifp_source_lifetime_bank[i_bank]); + } +} + +void deserialize_ifp_info(int n_generation, + const vector& deserialization, + const vector& delayed_groups, const vector& lifetimes) +{ + for (auto info : deserialization) { + int64_t index_local = info.index_local; + int64_t n = info.n; + + for (int i = index_local; i < index_local + n; i++) { + if (is_beta_effective_or_both()) { + vector delayed_groups_received( + delayed_groups.begin() + n_generation * i, + delayed_groups.begin() + n_generation * (i + 1)); + simulation::ifp_source_delayed_group_bank[i] = delayed_groups_received; + } + if (is_generation_time_or_both()) { + vector lifetimes_received(lifetimes.begin() + n_generation * i, + lifetimes.begin() + n_generation * (i + 1)); + simulation::ifp_source_lifetime_bank[i] = lifetimes_received; + } + } + } +} + +#endif + +void copy_complete_ifp_data_to_source_banks( + const vector>& delayed_groups, + const vector>& lifetimes) +{ + if (is_beta_effective_or_both()) { + std::copy(delayed_groups.data(), + delayed_groups.data() + settings::n_particles, + simulation::ifp_source_delayed_group_bank.begin()); + } + if (is_generation_time_or_both()) { + std::copy(lifetimes.data(), lifetimes.data() + settings::n_particles, + simulation::ifp_source_lifetime_bank.begin()); + } +} + +void allocate_temporary_vector_ifp( + vector>& delayed_groups, vector>& lifetimes) +{ + if (is_beta_effective_or_both()) { + delayed_groups.resize(simulation::fission_bank.size()); + } + if (is_generation_time_or_both()) { + lifetimes.resize(simulation::fission_bank.size()); + } +} + +void copy_ifp_data_to_fission_banks(const vector* const delayed_groups_ptr, + const vector* lifetimes_ptr) +{ + if (is_beta_effective_or_both()) { + std::copy(delayed_groups_ptr, + delayed_groups_ptr + simulation::fission_bank.size(), + simulation::ifp_fission_delayed_group_bank.data()); + } + if (is_generation_time_or_both()) { + std::copy(lifetimes_ptr, lifetimes_ptr + simulation::fission_bank.size(), + simulation::ifp_fission_lifetime_bank.data()); + } +} + +} // namespace openmc diff --git a/src/initialize.cpp b/src/initialize.cpp index 9c20a1f3cb..36f3261169 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -1,5 +1,6 @@ #include "openmc/initialize.h" +#include #include #include // for getenv #include @@ -11,9 +12,11 @@ #include #include "openmc/capi.h" +#include "openmc/chain.h" #include "openmc/constants.h" #include "openmc/cross_sections.h" #include "openmc/error.h" +#include "openmc/file_utils.h" #include "openmc/geometry_aux.h" #include "openmc/hdf5_interface.h" #include "openmc/material.h" @@ -21,6 +24,7 @@ #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" +#include "openmc/openmp_interface.h" #include "openmc/output.h" #include "openmc/plot.h" #include "openmc/random_lcg.h" @@ -32,8 +36,9 @@ #include "openmc/thermal.h" #include "openmc/timer.h" #include "openmc/vector.h" +#include "openmc/weight_windows.h" -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED #include "libmesh/libmesh.h" #endif @@ -59,14 +64,8 @@ int openmc_init(int argc, char* argv[], const void* intracomm) if (err) return err; -#ifdef LIBMESH - -#ifdef _OPENMP - int n_threads = omp_get_max_threads(); -#else - int n_threads = 1; -#endif - +#ifdef OPENMC_LIBMESH_ENABLED + const int n_threads = num_threads(); // initialize libMesh if it hasn't been initialized already // (if initialized externally, the libmesh_init object needs to be provided // also) @@ -100,12 +99,32 @@ int openmc_init(int argc, char* argv[], const void* intracomm) } #endif - // Initialize random number generator -- if the user specifies a seed, it - // will be re-initialized later + // Initialize random number generator -- if the user specifies a seed and/or + // stride, it will be re-initialized later openmc::openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_stride(DEFAULT_STRIDE); + + // Copy previous locale and set locale to C. This is a workaround for an issue + // whereby when openmc_init is called from the plotter, the Qt application + // framework first calls std::setlocale, which affects how pugixml reads + // floating point numbers due to a bug: + // https://github.com/zeux/pugixml/issues/469 + std::string prev_locale = std::setlocale(LC_ALL, nullptr); + if (std::setlocale(LC_ALL, "C") == NULL) { + fatal_error("Cannot set locale to C."); + } // Read XML input files - read_input_xml(); + if (!read_model_xml()) + read_separate_xml_files(); + + // Reset locale to previous state + if (std::setlocale(LC_ALL, prev_locale.c_str()) == NULL) { + fatal_error("Cannot reset locale."); + } + + // Write some initial output under the header if needed + initial_output(); // Check for particle restart run if (settings::particle_restart_run) @@ -138,7 +157,7 @@ void initialize_mpi(MPI_Comm intracomm) // Create bank datatype SourceSite b; - MPI_Aint disp[10]; + MPI_Aint disp[11]; MPI_Get_address(&b.r, &disp[0]); MPI_Get_address(&b.u, &disp[1]); MPI_Get_address(&b.E, &disp[2]); @@ -147,16 +166,17 @@ void initialize_mpi(MPI_Comm intracomm) MPI_Get_address(&b.delayed_group, &disp[5]); MPI_Get_address(&b.surf_id, &disp[6]); MPI_Get_address(&b.particle, &disp[7]); - MPI_Get_address(&b.parent_id, &disp[8]); - MPI_Get_address(&b.progeny_id, &disp[9]); - for (int i = 9; i >= 0; --i) { + MPI_Get_address(&b.parent_nuclide, &disp[8]); + MPI_Get_address(&b.parent_id, &disp[9]); + MPI_Get_address(&b.progeny_id, &disp[10]); + for (int i = 10; i >= 0; --i) { disp[i] -= disp[0]; } - int blocks[] {3, 3, 1, 1, 1, 1, 1, 1, 1, 1}; + int blocks[] {3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1}; MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, - MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_LONG, MPI_LONG}; - MPI_Type_create_struct(10, blocks, disp, types, &mpi::source_site); + MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_LONG, MPI_LONG}; + MPI_Type_create_struct(11, blocks, disp, types, &mpi::source_site); MPI_Type_commit(&mpi::source_site); } #endif // OPENMC_MPI @@ -177,10 +197,8 @@ int parse_command_line(int argc, char* argv[]) } else if (arg == "-e" || arg == "--event") { settings::event_based = true; - } else if (arg == "-r" || arg == "--restart") { i += 1; - // Check what type of file this is hid_t file_id = file_open(argv[i], 'r', true); std::string filetype; @@ -190,6 +208,7 @@ int parse_command_line(int argc, char* argv[]) // Set path and flag for type of run if (filetype == "statepoint") { settings::path_statepoint = argv[i]; + settings::path_statepoint_c = settings::path_statepoint.c_str(); settings::restart_run = true; } else if (filetype == "particle restart") { settings::path_particle_restart = argv[i]; @@ -260,6 +279,7 @@ int parse_command_line(int argc, char* argv[]) } else if (arg == "-v" || arg == "--version") { print_version(); + print_build_info(); return OPENMC_E_UNASSIGNED; } else if (arg == "-t" || arg == "--track") { @@ -279,8 +299,17 @@ int parse_command_line(int argc, char* argv[]) if (argc > 1 && last_flag < argc - 1) { settings::path_input = std::string(argv[last_flag + 1]); + // check that the path is either a valid directory or file + if (!dir_exists(settings::path_input) && + !file_exists(settings::path_input)) { + fatal_error(fmt::format( + "The path specified to the OpenMC executable '{}' does not exist.", + settings::path_input)); + } + // Add slash at end of directory if it isn't there - if (!ends_with(settings::path_input, "/")) { + if (!ends_with(settings::path_input, "/") && + dir_exists(settings::path_input)) { settings::path_input += "/"; } } @@ -288,10 +317,126 @@ int parse_command_line(int argc, char* argv[]) return 0; } -void read_input_xml() +bool read_model_xml() +{ + std::string model_filename = settings::path_input; + + // if the current filename is a directory, append the default model filename + if (model_filename.empty() || dir_exists(model_filename)) + model_filename += "model.xml"; + + // if this file doesn't exist, stop here + if (!file_exists(model_filename)) + return false; + + // try to process the path input as an XML file + pugi::xml_document doc; + if (!doc.load_file(model_filename.c_str())) { + fatal_error(fmt::format( + "Error reading from single XML input file '{}'", model_filename)); + } + + pugi::xml_node root = doc.document_element(); + + // Read settings + if (!check_for_node(root, "settings")) { + fatal_error("No node present in the model.xml file."); + } + auto settings_root = root.child("settings"); + + // Verbosity + if (check_for_node(settings_root, "verbosity")) { + settings::verbosity = std::stoi(get_node_value(settings_root, "verbosity")); + } + + // To this point, we haven't displayed any output since we didn't know what + // the verbosity is. Now that we checked for it, show the title if necessary + if (mpi::master) { + if (settings::verbosity >= 2) + title(); + } + + write_message( + fmt::format("Reading model XML file '{}' ...", model_filename), 5); + + read_settings_xml(settings_root); + + // If other XML files are present, display warning + // that they will be ignored + auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml", + "tallies.xml", "plots.xml"}; + for (const auto& input : other_inputs) { + if (file_exists(settings::path_input + input)) { + warning((fmt::format("Other XML file input(s) are present. These files " + "may be ignored in favor of the {} file.", + model_filename))); + break; + } + } + + // Read data from chain file + read_chain_file_xml(); + + // Read materials and cross sections + if (!check_for_node(root, "materials")) { + fatal_error(fmt::format( + "No node present in the {} file.", model_filename)); + } + + if (settings::run_mode != RunMode::PLOTTING) { + read_cross_sections_xml(root.child("materials")); + } + read_materials_xml(root.child("materials")); + + // Read geometry + if (!check_for_node(root, "geometry")) { + fatal_error(fmt::format( + "No node present in the {} file.", model_filename)); + } + read_geometry_xml(root.child("geometry")); + + // Final geometry setup and assign temperatures + finalize_geometry(); + + // Finalize cross sections having assigned temperatures + finalize_cross_sections(); + + // Compute cell density multipliers now that material densities + // have been finalized (from geometry_aux.h) + finalize_cell_densities(); + + if (check_for_node(root, "tallies")) + read_tallies_xml(root.child("tallies")); + + // Initialize distribcell_filters + prepare_distribcell(); + + if (check_for_node(root, "plots")) { + read_plots_xml(root.child("plots")); + } else { + // When no element is present in the model.xml file, check for a + // regular plots.xml file + std::string filename = settings::path_input + "plots.xml"; + if (file_exists(filename)) { + read_plots_xml(); + } + } + + finalize_variance_reduction(); + + return true; +} + +void read_separate_xml_files() { read_settings_xml(); - read_cross_sections_xml(); + if (settings::run_mode != RunMode::PLOTTING) { + read_cross_sections_xml(); + } + + // Read data from chain file + read_chain_file_xml(); + read_materials_xml(); read_geometry_xml(); @@ -301,6 +446,10 @@ void read_input_xml() // Finalize cross sections having assigned temperatures finalize_cross_sections(); + // Compute cell density multipliers now that material densities + // have been finalized (from geometry_aux.h) + finalize_cell_densities(); + read_tallies_xml(); // Initialize distribcell_filters @@ -309,6 +458,13 @@ void read_input_xml() // Read the plots.xml regardless of plot mode in case plots are requested // via the API read_plots_xml(); + + finalize_variance_reduction(); +} + +void initial_output() +{ + // write initial output if (settings::run_mode == RunMode::PLOTTING) { // Read plots.xml if it exists if (mpi::master && settings::verbosity >= 5) diff --git a/src/lattice.cpp b/src/lattice.cpp index 3875b2fa72..efbfcb2163 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -58,6 +58,11 @@ LatticeIter Lattice::end() return LatticeIter(*this, universes_.size()); } +int32_t& Lattice::back() +{ + return universes_.back(); +} + ReverseLatticeIter Lattice::rbegin() { return ReverseLatticeIter(*this, universes_.size() - 1); @@ -98,13 +103,28 @@ void Lattice::adjust_indices() //============================================================================== -int32_t Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id, - int map, std::unordered_map& univ_count_memo) +int32_t Lattice::fill_offset_table(int32_t target_univ_id, int map, + std::unordered_map& univ_count_memo) { + // If the offsets have already been determined for this "map", don't bother + // recalculating all of them and just return the total offset. Note that the + // offsets_ array doesn't actually include the offset accounting for the last + // universe, so we get the before-last offset for the given map and then + // explicitly add the count for the last universe. + if (offsets_[map * universes_.size() + this->begin().indx_] != C_NONE) { + int last_offset = + offsets_[(map + 1) * universes_.size() - this->begin().indx_ - 1]; + int last_univ = this->back(); + return last_offset + + count_universe_instances(last_univ, target_univ_id, univ_count_memo); + } + + int32_t offset = 0; for (LatticeIter it = begin(); it != end(); ++it) { offsets_[map * universes_.size() + it.indx_] = offset; offset += count_universe_instances(*it, target_univ_id, univ_count_memo); } + return offset; } @@ -213,14 +233,14 @@ RectLattice::RectLattice(pugi::xml_node lat_node) : Lattice {lat_node} //============================================================================== -int32_t const& RectLattice::operator[](array const& i_xyz) +const int32_t& RectLattice::operator[](const array& i_xyz) { return universes_[get_flat_index(i_xyz)]; } //============================================================================== -bool RectLattice::are_valid_indices(array const& i_xyz) const +bool RectLattice::are_valid_indices(const array& i_xyz) const { return ((i_xyz[0] >= 0) && (i_xyz[0] < n_cells_[0]) && (i_xyz[1] >= 0) && (i_xyz[1] < n_cells_[1]) && (i_xyz[2] >= 0) && @@ -342,7 +362,7 @@ Position RectLattice::get_local_position( //============================================================================== -int32_t& RectLattice::offset(int map, array const& i_xyz) +int32_t& RectLattice::offset(int map, const array& i_xyz) { return offsets_[n_cells_[0] * n_cells_[1] * n_cells_[2] * map + n_cells_[0] * n_cells_[1] * i_xyz[2] + @@ -664,13 +684,19 @@ void HexLattice::fill_lattice_y(const vector& univ_words) //============================================================================== -int32_t const& HexLattice::operator[](array const& i_xyz) +const int32_t& HexLattice::operator[](const array& i_xyz) { return universes_[get_flat_index(i_xyz)]; } //============================================================================== +// The HexLattice iterators need their own versions b/c the universes array is +// "square", meaning that it is allocated with entries that are intentionally +// left empty. As such, the iterator indices need to skip the empty entries to +// get cell instances and geometry paths correct. See the image in the Theory +// and Methodology section on "Hexagonal Lattice Indexing" for a visual of where +// the empty positions are. LatticeIter HexLattice::begin() { return LatticeIter(*this, n_rings_ - 1); @@ -681,9 +707,24 @@ ReverseLatticeIter HexLattice::rbegin() return ReverseLatticeIter(*this, universes_.size() - n_rings_); } +int32_t& HexLattice::back() +{ + return universes_[universes_.size() - n_rings_]; +} + +LatticeIter HexLattice::end() +{ + return LatticeIter(*this, universes_.size() - n_rings_ + 1); +} + +ReverseLatticeIter HexLattice::rend() +{ + return ReverseLatticeIter(*this, n_rings_ - 2); +} + //============================================================================== -bool HexLattice::are_valid_indices(array const& i_xyz) const +bool HexLattice::are_valid_indices(const array& i_xyz) const { // Check if (x, alpha, z) indices are valid, accounting for number of rings return ((i_xyz[0] >= 0) && (i_xyz[1] >= 0) && (i_xyz[2] >= 0) && @@ -980,7 +1021,7 @@ bool HexLattice::is_valid_index(int indx) const //============================================================================== -int32_t& HexLattice::offset(int map, array const& i_xyz) +int32_t& HexLattice::offset(int map, const array& i_xyz) { int nx {2 * n_rings_ - 1}; int ny {2 * n_rings_ - 1}; diff --git a/src/main.cpp b/src/main.cpp index 02a850ead8..88251ac723 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "openmc/error.h" #include "openmc/message_passing.h" #include "openmc/particle_restart.h" +#include "openmc/random_ray/random_ray_simulation.h" #include "openmc/settings.h" int main(int argc, char* argv[]) @@ -31,7 +32,15 @@ int main(int argc, char* argv[]) switch (settings::run_mode) { case RunMode::FIXED_SOURCE: case RunMode::EIGENVALUE: - err = openmc_run(); + switch (settings::solver_type) { + case SolverType::MONTE_CARLO: + err = openmc_run(); + break; + case SolverType::RANDOM_RAY: + openmc_run_random_ray(); + err = 0; + break; + } break; case RunMode::PLOTTING: err = openmc_plot_geometry(); diff --git a/src/material.cpp b/src/material.cpp index 30dfa5ed50..54caa38409 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,6 +1,7 @@ #include "openmc/material.h" #include // for min, max, sort, fill +#include #include #include #include @@ -60,6 +61,13 @@ Material::Material(pugi::xml_node node) name_ = get_node_value(node, "name"); } + if (check_for_node(node, "cfg")) { + auto cfg = get_node_value(node, "cfg"); + write_message( + 5, "NCrystal config string for material #{}: '{}'", this->id(), cfg); + ncrystal_mat_ = NCrystalMat(cfg); + } + if (check_for_node(node, "depletable")) { depletable_ = get_node_value_bool(node, "depletable"); } @@ -217,11 +225,13 @@ Material::Material(pugi::xml_node node) // Check that this nuclide is listed in the nuclear data library // (cross_sections.xml for CE and the MGXS HDF5 for MG) - LibraryKey key {Library::Type::neutron, name}; - if (data::library_map.find(key) == data::library_map.end()) { - fatal_error("Could not find nuclide " + name + - " in the " - "nuclear data library."); + if (settings::run_mode != RunMode::PLOTTING) { + LibraryKey key {Library::Type::neutron, name}; + if (data::library_map.find(key) == data::library_map.end()) { + fatal_error("Could not find nuclide " + name + + " in the " + "nuclear data library."); + } } // If this nuclide hasn't been encountered yet, we need to add its name @@ -240,10 +250,12 @@ Material::Material(pugi::xml_node node) std::string element = to_element(name); // Make sure photon cross section data is available - LibraryKey key {Library::Type::photon, element}; - if (data::library_map.find(key) == data::library_map.end()) { - fatal_error( - "Could not find element " + element + " in cross_sections.xml."); + if (settings::run_mode != RunMode::PLOTTING) { + LibraryKey key {Library::Type::photon, element}; + if (data::library_map.find(key) == data::library_map.end()) { + fatal_error( + "Could not find element " + element + " in cross_sections.xml."); + } } if (data::element_map.find(element) == data::element_map.end()) { @@ -317,10 +329,12 @@ Material::Material(pugi::xml_node node) // Check that the thermal scattering table is listed in the // cross_sections.xml file - LibraryKey key {Library::Type::thermal, name}; - if (data::library_map.find(key) == data::library_map.end()) { - fatal_error("Could not find thermal scattering data " + name + - " in cross_sections.xml file."); + if (settings::run_mode != RunMode::PLOTTING) { + LibraryKey key {Library::Type::thermal, name}; + if (data::library_map.find(key) == data::library_map.end()) { + fatal_error("Could not find thermal scattering data " + name + + " in cross_sections.xml file."); + } } // Determine index of thermal scattering data in global @@ -346,6 +360,35 @@ Material::~Material() model::material_map.erase(id_); } +Material& Material::clone() +{ + std::unique_ptr mat = std::make_unique(); + + // set all other parameters to whatever the calling Material has + mat->name_ = name_; + mat->nuclide_ = nuclide_; + mat->element_ = element_; + mat->ncrystal_mat_ = ncrystal_mat_.clone(); + mat->atom_density_ = atom_density_; + mat->density_ = density_; + mat->density_gpcc_ = density_gpcc_; + mat->volume_ = volume_; + mat->fissionable() = fissionable_; + mat->depletable() = depletable_; + mat->p0_ = p0_; + mat->mat_nuclide_index_ = mat_nuclide_index_; + mat->thermal_tables_ = thermal_tables_; + mat->temperature_ = temperature_; + + if (ttb_) + mat->ttb_ = std::make_unique(*ttb_); + + mat->index_ = model::materials.size(); + mat->set_id(C_NONE); + model::materials.push_back(std::move(mat)); + return *model::materials.back(); +} + void Material::finalize() { // Set fissionable if any nuclide is fissionable @@ -367,8 +410,8 @@ void Material::finalize() this->init_thermal(); } -// Normalize density -this->normalize_density(); + // Normalize density + this->normalize_density(); } void Material::normalize_density() @@ -411,12 +454,15 @@ void Material::normalize_density() // Calculate nuclide atom densities atom_density_ *= density_; - // Calculate density in g/cm^3. + // Calculate density in [g/cm^3] and charge density in [e/b-cm] density_gpcc_ = 0.0; + charge_density_ = 0.0; for (int i = 0; i < nuclide_.size(); ++i) { int i_nuc = nuclide_[i]; double awr = settings::run_CE ? data::nuclides[i_nuc]->awr_ : 1.0; + int z = settings::run_CE ? data::nuclides[i_nuc]->Z_ : 0.0; density_gpcc_ += atom_density_(i) * awr * MASS_NEUTRON / N_AVOGADRO; + charge_density_ += atom_density_(i) * z; } } @@ -597,7 +643,7 @@ void Material::init_bremsstrahlung() // Allocate arrays for TTB data ttb->pdf = xt::zeros({n_e, n_e}); ttb->cdf = xt::zeros({n_e, n_e}); - ttb->yield = xt::empty({n_e}); + ttb->yield = xt::zeros({n_e}); // Allocate temporary arrays xt::xtensor stopping_power_collision({n_e}, 0.0); @@ -734,14 +780,15 @@ void Material::init_bremsstrahlung() // Loop over photon energies double c = 0.0; for (int i = 0; i < j; ++i) { - // Integrate the CDF from the PDF using the trapezoidal rule in log-log - // space + // Integrate the CDF from the PDF using the fact that the PDF is linear + // in log-log space double w_l = std::log(data::ttb_e_grid(i)); double w_r = std::log(data::ttb_e_grid(i + 1)); double x_l = std::log(ttb->pdf(j, i)); double x_r = std::log(ttb->pdf(j, i + 1)); - - c += 0.5 * (w_r - w_l) * (std::exp(w_l + x_l) + std::exp(w_r + x_r)); + double beta = (x_r - x_l) / (w_r - w_l); + double a = beta + 1.0; + c += std::exp(w_l + x_l) / a * std::expm1(a * (w_r - w_l)); ttb->cdf(j, i + 1) = c; } @@ -792,6 +839,12 @@ void Material::calculate_neutron_xs(Particle& p) const // Initialize position in i_sab_nuclides int j = 0; + // Calculate NCrystal cross section + double ncrystal_xs = -1.0; + if (ncrystal_mat_ && p.E() < NCRYSTAL_MAX_ENERGY) { + ncrystal_xs = ncrystal_mat_.xs(p); + } + // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { // ====================================================================== @@ -826,21 +879,18 @@ void Material::calculate_neutron_xs(Particle& p) const // ====================================================================== // CALCULATE MICROSCOPIC CROSS SECTION - // Determine microscopic cross sections for this nuclide + // Get nuclide index int i_nuclide = nuclide_[i]; - // Calculate microscopic cross section for this nuclide - const auto& micro {p.neutron_xs(i_nuclide)}; - if (p.E() != micro.last_E || p.sqrtkT() != micro.last_sqrtkT || - i_sab != micro.index_sab || sab_frac != micro.sab_frac) { - data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); - } + // Update microscopic cross section for this nuclide + p.update_neutron_xs(i_nuclide, i_grid, i_sab, sab_frac, ncrystal_xs); + auto& micro = p.neutron_xs(i_nuclide); // ====================================================================== // ADD TO MACROSCOPIC CROSS SECTION // Copy atom density of nuclide in material - double atom_density = atom_density_(i); + double atom_density = this->atom_density(i, p.density_mult()); // Add contributions to cross sections p.macro_xs().total += atom_density * micro.total; @@ -875,7 +925,7 @@ void Material::calculate_photon_xs(Particle& p) const // ADD TO MACROSCOPIC CROSS SECTION // Copy atom density of nuclide in material - double atom_density = atom_density_(i); + double atom_density = this->atom_density(i, p.density_mult()); // Add contributions to material macroscopic cross sections p.macro_xs().total += atom_density * micro.total; @@ -888,7 +938,7 @@ void Material::calculate_photon_xs(Particle& p) const void Material::set_id(int32_t id) { - Expects(id >= 0 || id == C_NONE); + assert(id >= 0 || id == C_NONE); // Clear entry in material map if an ID was already assigned before if (id_ != C_NONE) { @@ -916,9 +966,9 @@ void Material::set_id(int32_t id) model::material_map[id] = index_; } -void Material::set_density(double density, gsl::cstring_span units) +void Material::set_density(double density, const std::string& units) { - Expects(density >= 0.0); + assert(density >= 0.0); if (nuclide_.empty()) { throw std::runtime_error {"No nuclides exist in material yet."}; @@ -935,12 +985,15 @@ void Material::set_density(double density, gsl::cstring_span units) // Recalculate nuclide atom densities based on given density atom_density_ *= density; - // Calculate density in g/cm^3. + // Calculate density in g/cm^3 and charge density in [e/b-cm] density_gpcc_ = 0.0; + charge_density_ = 0.0; for (int i = 0; i < nuclide_.size(); ++i) { int i_nuc = nuclide_[i]; double awr = data::nuclides[i_nuc]->awr_; + int z = settings::run_CE ? data::nuclides[i_nuc]->Z_ : 0.0; density_gpcc_ += atom_density_(i) * awr * MASS_NEUTRON / N_AVOGADRO; + charge_density_ += atom_density_(i) * z; } } else if (units == "g/cm3" || units == "g/cc") { // Determine factor by which to change densities @@ -951,6 +1004,7 @@ void Material::set_density(double density, gsl::cstring_span units) density_gpcc_ = density; density_ *= f; atom_density_ *= f; + charge_density_ *= f; } else { throw std::invalid_argument { "Invalid units '" + std::string(units.data()) + "' specified."}; @@ -961,8 +1015,8 @@ void Material::set_densities( const vector& name, const vector& density) { auto n = name.size(); - Expects(n > 0); - Expects(n == density.size()); + assert(n > 0); + assert(n == density.size()); if (n != nuclide_.size()) { nuclide_.resize(n); @@ -972,7 +1026,7 @@ void Material::set_densities( } double sum_density = 0.0; - for (gsl::index i = 0; i < n; ++i) { + for (int64_t i = 0; i < n; ++i) { const auto& nuc {name[i]}; if (data::nuclide_map.find(nuc) == data::nuclide_map.end()) { int err = openmc_load_nuclide(nuc.c_str(), nullptr, 0); @@ -981,7 +1035,7 @@ void Material::set_densities( } nuclide_[i] = data::nuclide_map.at(nuc); - Expects(density[i] > 0.0); + assert(density[i] > 0.0); atom_density_(i) = density[i]; sum_density += density[i]; @@ -1023,7 +1077,7 @@ void Material::to_hdf5(hid_t group) const { hid_t material_group = create_group(group, "material " + std::to_string(id_)); - write_attribute(material_group, "depletable", static_cast(depletable_)); + write_attribute(material_group, "depletable", static_cast(depletable())); if (volume_ > 0.0) { write_attribute(material_group, "volume", volume_); } @@ -1281,6 +1335,12 @@ void read_materials_xml() // Loop over XML material elements and populate the array. pugi::xml_node root = doc.document_element(); + + read_materials_xml(root); +} + +void read_materials_xml(pugi::xml_node root) +{ for (pugi::xml_node material_node : root.children("material")) { model::materials.push_back(make_unique(material_node)); } @@ -1499,6 +1559,30 @@ extern "C" int openmc_material_set_volume(int32_t index, double volume) } } +extern "C" int openmc_material_get_depletable(int32_t index, bool* depletable) +{ + if (index < 0 || index >= model::materials.size()) { + set_errmsg("Index in materials array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + *depletable = model::materials[index]->depletable(); + + return 0; +} + +extern "C" int openmc_material_set_depletable(int32_t index, bool depletable) +{ + if (index < 0 || index >= model::materials.size()) { + set_errmsg("Index in materials array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + model::materials[index]->depletable() = depletable; + + return 0; +} + extern "C" int openmc_extend_materials( int32_t n, int32_t* index_start, int32_t* index_end) { diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 32228bcdd5..5469b56c87 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -1,6 +1,6 @@ #include "openmc/math_functions.h" -#include "Faddeeva.hh" +#include "openmc/external/Faddeeva.hh" #include "openmc/constants.h" #include "openmc/random_lcg.h" @@ -650,7 +650,7 @@ void calc_zn(int n, double rho, double phi, double zn[]) // =========================================================================== // Determine vector of sin(n*phi) and cos(n*phi). This takes advantage of the // following recurrence relations so that only a single sin/cos have to be - // evaluated (http://mathworld.wolfram.com/Multiple-AngleFormulas.html) + // evaluated (https://mathworld.wolfram.com/Multiple-AngleFormulas.html) // // sin(nx) = 2 cos(x) sin((n-1)x) - sin((n-2)x) // cos(nx) = 2 cos(x) cos((n-1)x) - cos((n-2)x) diff --git a/src/mcpl_interface.cpp b/src/mcpl_interface.cpp new file mode 100644 index 0000000000..b8e7807100 --- /dev/null +++ b/src/mcpl_interface.cpp @@ -0,0 +1,548 @@ +#include "openmc/mcpl_interface.h" + +#include "openmc/bank.h" +#include "openmc/error.h" +#include "openmc/file_utils.h" +#include "openmc/message_passing.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/state_point.h" +#include "openmc/vector.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +// WARNING: These declarations MUST EXACTLY MATCH the structure and function +// signatures of the libmcpl being loaded at runtime. Any discrepancy will +// likely lead to crashes or incorrect behavior. This is a maintenance risk. +// MCPL 2.2.0 + +#pragma pack(push, 1) +struct mcpl_particle_repr_t { + double ekin; + double polarisation[3]; + double position[3]; + double direction[3]; + double time; + double weight; + int32_t pdgcode; + uint32_t userflags; +}; +#pragma pack(pop) + +// Opaque struct definitions replicating the MCPL C-API to ensure ABI +// compatibility without including mcpl.h. These must be kept in sync. +struct mcpl_file_t { + void* internal; +}; +struct mcpl_outfile_t { + void* internal; +}; + +// Function pointer types for the dynamically loaded MCPL library +using mcpl_open_file_fpt = mcpl_file_t* (*)(const char* filename); +using mcpl_hdr_nparticles_fpt = uint64_t (*)(mcpl_file_t* file_handle); +using mcpl_read_fpt = const mcpl_particle_repr_t* (*)(mcpl_file_t* file_handle); +using mcpl_close_file_fpt = void (*)(mcpl_file_t* file_handle); + +using mcpl_create_outfile_fpt = mcpl_outfile_t* (*)(const char* filename); +using mcpl_hdr_set_srcname_fpt = void (*)( + mcpl_outfile_t* outfile_handle, const char* srcname); +using mcpl_add_particle_fpt = void (*)( + mcpl_outfile_t* outfile_handle, const mcpl_particle_repr_t* particle); +using mcpl_close_outfile_fpt = void (*)(mcpl_outfile_t* outfile_handle); +using mcpl_hdr_add_stat_sum_fpt = void (*)( + mcpl_outfile_t* outfile_handle, const char* key, double value); + +namespace openmc { + +#ifdef _WIN32 +using LibraryHandleType = HMODULE; +#else +using LibraryHandleType = void*; +#endif + +std::string get_last_library_error() +{ +#ifdef _WIN32 + DWORD error_code = GetLastError(); + if (error_code == 0) + return "No error reported by system."; // More accurate than "No error." + LPSTR message_buffer = nullptr; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message_buffer, 0, NULL); + std::string message(message_buffer, size); + LocalFree(message_buffer); + while ( + !message.empty() && (message.back() == '\n' || message.back() == '\r')) { + message.pop_back(); + } + return message; +#else + const char* err = dlerror(); + return err ? std::string(err) : "No error reported by dlerror."; +#endif +} + +struct McplApi { + mcpl_open_file_fpt open_file; + mcpl_hdr_nparticles_fpt hdr_nparticles; + mcpl_read_fpt read; + mcpl_close_file_fpt close_file; + mcpl_create_outfile_fpt create_outfile; + mcpl_hdr_set_srcname_fpt hdr_set_srcname; + mcpl_add_particle_fpt add_particle; + mcpl_close_outfile_fpt close_outfile; + mcpl_hdr_add_stat_sum_fpt hdr_add_stat_sum; + + explicit McplApi(LibraryHandleType lib_handle) + { + if (!lib_handle) + throw std::runtime_error( + "MCPL library handle is null during API binding."); + + auto load_symbol_platform = [lib_handle](const char* name) { + void* sym = nullptr; +#ifdef _WIN32 + sym = (void*)GetProcAddress(lib_handle, name); +#else + sym = dlsym(lib_handle, name); +#endif + if (!sym) { + throw std::runtime_error( + fmt::format("Failed to load MCPL symbol '{}': {}", name, + get_last_library_error())); + } + return sym; + }; + + open_file = reinterpret_cast( + load_symbol_platform("mcpl_open_file")); + hdr_nparticles = reinterpret_cast( + load_symbol_platform("mcpl_hdr_nparticles")); + read = reinterpret_cast(load_symbol_platform("mcpl_read")); + close_file = reinterpret_cast( + load_symbol_platform("mcpl_close_file")); + create_outfile = reinterpret_cast( + load_symbol_platform("mcpl_create_outfile")); + hdr_set_srcname = reinterpret_cast( + load_symbol_platform("mcpl_hdr_set_srcname")); + add_particle = reinterpret_cast( + load_symbol_platform("mcpl_add_particle")); + close_outfile = reinterpret_cast( + load_symbol_platform("mcpl_close_outfile")); + + // Try to load mcpl_hdr_add_stat_sum (available in MCPL >= 2.1.0) + // Set to nullptr if not available for graceful fallback + try { + hdr_add_stat_sum = reinterpret_cast( + load_symbol_platform("mcpl_hdr_add_stat_sum")); + } catch (const std::runtime_error&) { + hdr_add_stat_sum = nullptr; + } + } +}; + +static LibraryHandleType g_mcpl_lib_handle = nullptr; +static std::unique_ptr g_mcpl_api; +static bool g_mcpl_init_attempted = false; +static bool g_mcpl_successfully_loaded = false; +static std::string g_mcpl_load_error_msg; +static std::once_flag g_mcpl_init_flag; + +void append_error(std::string& existing_msg, const std::string& new_error) +{ + if (!existing_msg.empty()) { + existing_msg += "; "; + } + existing_msg += new_error; +} + +void initialize_mcpl_interface_impl() +{ + g_mcpl_init_attempted = true; + g_mcpl_load_error_msg.clear(); + + // Try mcpl-config + if (!g_mcpl_lib_handle) { + FILE* pipe = nullptr; +#ifdef _WIN32 + pipe = _popen("mcpl-config --show libpath", "r"); +#else + pipe = popen("mcpl-config --show libpath 2>/dev/null", "r"); +#endif + if (pipe) { + char buffer[512]; + if (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + std::string shlibpath = buffer; + // Remove trailing whitespace + while (!shlibpath.empty() && + std::isspace(static_cast(shlibpath.back()))) { + shlibpath.pop_back(); + } + + if (!shlibpath.empty()) { +#ifdef _WIN32 + g_mcpl_lib_handle = LoadLibraryA(shlibpath.c_str()); +#else + g_mcpl_lib_handle = dlopen(shlibpath.c_str(), RTLD_LAZY); +#endif + if (!g_mcpl_lib_handle) { + append_error( + g_mcpl_load_error_msg, fmt::format("From mcpl-config ({}): {}", + shlibpath, get_last_library_error())); + } + } + } +#ifdef _WIN32 + _pclose(pipe); +#else + pclose(pipe); +#endif + } else { // pipe failed to open + append_error(g_mcpl_load_error_msg, + "mcpl-config command not found or failed to execute"); + } + } + + // Try standard library names + if (!g_mcpl_lib_handle) { +#ifdef _WIN32 + const char* standard_names[] = {"mcpl.dll", "libmcpl.dll"}; +#else + const char* standard_names[] = {"libmcpl.so", "libmcpl.dylib"}; +#endif + for (const char* name : standard_names) { +#ifdef _WIN32 + g_mcpl_lib_handle = LoadLibraryA(name); +#else + g_mcpl_lib_handle = dlopen(name, RTLD_LAZY); +#endif + if (g_mcpl_lib_handle) + break; + } + if (!g_mcpl_lib_handle) { + append_error( + g_mcpl_load_error_msg, fmt::format("Using standard names (e.g. {}): {}", + standard_names[0], get_last_library_error())); + } + } + + if (!g_mcpl_lib_handle) { + if (mpi::master) { + warning(fmt::format("MCPL library could not be loaded. MCPL-dependent " + "features will be unavailable. Load attempts: {}", + g_mcpl_load_error_msg.empty() + ? "No specific error during load attempts." + : g_mcpl_load_error_msg)); + } + g_mcpl_successfully_loaded = false; + return; + } + + try { + g_mcpl_api = std::make_unique(g_mcpl_lib_handle); + g_mcpl_successfully_loaded = true; + // Do not call dlclose/FreeLibrary at exit. Leaking the handle is safer + // and standard practice for libraries used for the application's lifetime. + } catch (const std::runtime_error& e) { + append_error(g_mcpl_load_error_msg, + fmt::format( + "MCPL library loaded, but failed to bind symbols: {}", e.what())); + if (mpi::master) { + warning(g_mcpl_load_error_msg); + } +#ifdef _WIN32 + FreeLibrary(g_mcpl_lib_handle); +#else + dlclose(g_mcpl_lib_handle); +#endif + g_mcpl_lib_handle = nullptr; + g_mcpl_successfully_loaded = false; + } +} + +void initialize_mcpl_interface_if_needed() +{ + std::call_once(g_mcpl_init_flag, initialize_mcpl_interface_impl); +} + +bool is_mcpl_interface_available() +{ + initialize_mcpl_interface_if_needed(); + return g_mcpl_successfully_loaded; +} + +inline void ensure_mcpl_ready_or_fatal() +{ + initialize_mcpl_interface_if_needed(); + if (!g_mcpl_successfully_loaded) { + fatal_error("MCPL functionality is required, but the MCPL library is not " + "available or failed to initialize. Please ensure MCPL is " + "installed and its library can be found (e.g., via PATH on " + "Windows, LD_LIBRARY_PATH on Linux, or DYLD_LIBRARY_PATH on " + "macOS). You can often install MCPL with 'pip install mcpl' or " + "'conda install mcpl'."); + } +} + +SourceSite mcpl_particle_to_site(const mcpl_particle_repr_t* particle_repr) +{ + SourceSite site; + switch (particle_repr->pdgcode) { + case 2112: + site.particle = ParticleType::neutron; + break; + case 22: + site.particle = ParticleType::photon; + break; + case 11: + site.particle = ParticleType::electron; + break; + case -11: + site.particle = ParticleType::positron; + break; + default: + fatal_error(fmt::format( + "MCPL: Encountered unexpected PDG code {} when converting to SourceSite.", + particle_repr->pdgcode)); + break; + } + + // Copy position and direction + site.r.x = particle_repr->position[0]; + site.r.y = particle_repr->position[1]; + site.r.z = particle_repr->position[2]; + site.u.x = particle_repr->direction[0]; + site.u.y = particle_repr->direction[1]; + site.u.z = particle_repr->direction[2]; + // MCPL stores kinetic energy in [MeV], time in [ms] + site.E = particle_repr->ekin * 1e6; + site.time = particle_repr->time * 1e-3; + site.wgt = particle_repr->weight; + return site; +} + +vector mcpl_source_sites(std::string path) +{ + ensure_mcpl_ready_or_fatal(); + vector sites; + + mcpl_file_t* mcpl_file = g_mcpl_api->open_file(path.c_str()); + if (!mcpl_file) { + fatal_error(fmt::format("MCPL: Could not open file '{}'. It might be " + "missing, inaccessible, or not a valid MCPL file.", + path)); + } + + size_t n_particles_in_file = g_mcpl_api->hdr_nparticles(mcpl_file); + size_t n_skipped = 0; + if (n_particles_in_file > 0) { + sites.reserve(n_particles_in_file); + } + + for (size_t i = 0; i < n_particles_in_file; ++i) { + const mcpl_particle_repr_t* p_repr = g_mcpl_api->read(mcpl_file); + if (!p_repr) { + warning(fmt::format("MCPL: Read error or unexpected end of file '{}' " + "after reading {} of {} expected particles.", + path, sites.size(), n_particles_in_file)); + break; + } + if (p_repr->pdgcode == 2112 || p_repr->pdgcode == 22 || + p_repr->pdgcode == 11 || p_repr->pdgcode == -11) { + sites.push_back(mcpl_particle_to_site(p_repr)); + } else { + n_skipped++; + } + } + + g_mcpl_api->close_file(mcpl_file); + + if (n_skipped > 0 && n_particles_in_file > 0) { + double percent_skipped = + 100.0 * static_cast(n_skipped) / n_particles_in_file; + warning(fmt::format( + "MCPL: Skipped {} of {} total particles ({:.1f}%) in file '{}' because " + "their type is not supported by OpenMC.", + n_skipped, n_particles_in_file, percent_skipped, path)); + } + + if (sites.empty()) { + if (n_particles_in_file > 0) { + fatal_error(fmt::format( + "MCPL file '{}' contained {} particles, but none were of the supported " + "types (neutron, photon, electron, positron). OpenMC cannot proceed " + "without source particles.", + path, n_particles_in_file)); + } else { + fatal_error(fmt::format( + "MCPL file '{}' is empty or contains no particle data.", path)); + } + } + return sites; +} + +void write_mcpl_source_bank_internal(mcpl_outfile_t* file_id, + span local_source_bank, + const vector& bank_index_all_ranks) +{ + if (mpi::master) { + if (!file_id) { + fatal_error("MCPL: Internal error - master rank called " + "write_mcpl_source_bank_internal with null file_id."); + } + vector receive_buffer; + + for (int rank_idx = 0; rank_idx < mpi::n_procs; ++rank_idx) { + size_t num_sites_on_rank = static_cast( + bank_index_all_ranks[rank_idx + 1] - bank_index_all_ranks[rank_idx]); + if (num_sites_on_rank == 0) + continue; + + span sites_to_write; +#ifdef OPENMC_MPI + if (rank_idx == mpi::rank) { + sites_to_write = openmc::span( + local_source_bank.data(), num_sites_on_rank); + } else { + if (receive_buffer.size() < num_sites_on_rank) { + receive_buffer.resize(num_sites_on_rank); + } + MPI_Recv(receive_buffer.data(), num_sites_on_rank, mpi::source_site, + rank_idx, rank_idx, mpi::intracomm, MPI_STATUS_IGNORE); + sites_to_write = openmc::span( + receive_buffer.data(), num_sites_on_rank); + } +#else + sites_to_write = openmc::span( + local_source_bank.data(), num_sites_on_rank); +#endif + for (const auto& site : sites_to_write) { + mcpl_particle_repr_t p_repr {}; + p_repr.position[0] = site.r.x; + p_repr.position[1] = site.r.y; + p_repr.position[2] = site.r.z; + p_repr.direction[0] = site.u.x; + p_repr.direction[1] = site.u.y; + p_repr.direction[2] = site.u.z; + p_repr.ekin = site.E * 1e-6; + p_repr.time = site.time * 1e3; + p_repr.weight = site.wgt; + switch (site.particle) { + case ParticleType::neutron: + p_repr.pdgcode = 2112; + break; + case ParticleType::photon: + p_repr.pdgcode = 22; + break; + case ParticleType::electron: + p_repr.pdgcode = 11; + break; + case ParticleType::positron: + p_repr.pdgcode = -11; + break; + default: + continue; + } + g_mcpl_api->add_particle(file_id, &p_repr); + } + } + } else { +#ifdef OPENMC_MPI + if (!local_source_bank.empty()) { + MPI_Send(local_source_bank.data(), local_source_bank.size(), + mpi::source_site, 0, mpi::rank, mpi::intracomm); + } +#endif + } +} + +void write_mcpl_source_point(const char* filename, span source_bank, + const vector& bank_index) +{ + ensure_mcpl_ready_or_fatal(); + + std::string filename_(filename); + const auto extension = get_file_extension(filename_); + if (extension.empty()) { + filename_.append(".mcpl"); + } else if (extension != "mcpl") { + warning(fmt::format("Specified filename '{}' has an extension '.{}', but " + "an MCPL file (.mcpl) will be written using this name.", + filename, extension)); + } + + mcpl_outfile_t* file_id = nullptr; + + if (mpi::master) { + file_id = g_mcpl_api->create_outfile(filename_.c_str()); + if (!file_id) { + fatal_error(fmt::format( + "MCPL: Failed to create output file '{}'. Check permissions and path.", + filename_)); + } + std::string src_line; + if (VERSION_DEV) { + src_line = fmt::format("OpenMC {}.{}.{}-dev{}", VERSION_MAJOR, + VERSION_MINOR, VERSION_RELEASE, VERSION_COMMIT_COUNT); + } else { + src_line = fmt::format( + "OpenMC {}.{}.{}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE); + } + g_mcpl_api->hdr_set_srcname(file_id, src_line.c_str()); + + // Initialize stat:sum with -1 to indicate incomplete file (issue #3514) + // This follows MCPL >= 2.1.0 convention for tracking simulation statistics + // The -1 value indicates "not available" if file creation is interrupted + if (g_mcpl_api->hdr_add_stat_sum) { + // Using key "openmc_np1" following tkittel's recommendation + // Initial value of -1 prevents misleading values in case of crashes + g_mcpl_api->hdr_add_stat_sum(file_id, "openmc_np1", -1.0); + } + } + + write_mcpl_source_bank_internal(file_id, source_bank, bank_index); + + if (mpi::master) { + if (file_id) { + // Update stat:sum with actual particle count before closing (issue #3514) + // This represents the original number of source particles in the + // simulation (not the number of particles in the file) + if (g_mcpl_api->hdr_add_stat_sum) { + // Calculate total source particles from active batches + // Per issue #3514: this should be the original number of source + // particles, not the number written to the file + int64_t total_source_particles = + static_cast(settings::n_batches - settings::n_inactive) * + settings::gen_per_batch * settings::n_particles; + // Update with actual count - this overwrites the initial -1 value + g_mcpl_api->hdr_add_stat_sum( + file_id, "openmc_np1", static_cast(total_source_particles)); + } + + g_mcpl_api->close_outfile(file_id); + } + } +} + +} // namespace openmc diff --git a/src/mesh.cpp b/src/mesh.cpp index 9310248c39..58d218b9ca 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -1,16 +1,20 @@ #include "openmc/mesh.h" #include // for copy, equal, min, min_element -#include // for ceil -#include // for size_t -#include +#include +#define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers +#include // for ceil +#include // for size_t #include +#ifdef _MSC_VER +#include // for _InterlockedCompareExchange +#endif + #ifdef OPENMC_MPI #include "mpi.h" #endif -#ifdef _OPENMP -#include -#endif + +#include "xtensor/xadapt.hpp" #include "xtensor/xbuilder.hpp" #include "xtensor/xeval.hpp" #include "xtensor/xmath.hpp" @@ -21,35 +25,54 @@ #include "openmc/capi.h" #include "openmc/constants.h" +#include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/file_utils.h" +#include "openmc/geometry.h" #include "openmc/hdf5_interface.h" +#include "openmc/material.h" #include "openmc/memory.h" #include "openmc/message_passing.h" +#include "openmc/openmp_interface.h" +#include "openmc/output.h" +#include "openmc/particle_data.h" +#include "openmc/plot.h" +#include "openmc/random_dist.h" #include "openmc/search.h" #include "openmc/settings.h" +#include "openmc/string_utils.h" #include "openmc/tallies/filter.h" #include "openmc/tallies/tally.h" +#include "openmc/timer.h" +#include "openmc/volume_calc.h" #include "openmc/xml_interface.h" -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED #include "libmesh/mesh_modification.h" #include "libmesh/mesh_tools.h" #include "libmesh/numeric_vector.h" #endif +#ifdef OPENMC_DAGMC_ENABLED +#include "moab/FileOptions.hpp" +#endif + namespace openmc { //============================================================================== // Global variables //============================================================================== -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED const bool LIBMESH_ENABLED = true; #else const bool LIBMESH_ENABLED = false; #endif +// Value used to indicate an empty slot in the hash table. We use -2 because +// the value -1 is used to indicate a void material. +constexpr int32_t EMPTY = -2; + namespace model { std::unordered_map mesh_map; @@ -57,7 +80,7 @@ vector> meshes; } // namespace model -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED namespace settings { unique_ptr libmesh_init; const libMesh::Parallel::Communicator* libmesh_comm {nullptr}; @@ -91,27 +114,133 @@ inline bool check_intersection_point(double x1, double x0, double y1, double y0, return false; } +//! Atomic compare-and-swap for signed 32-bit integer +// +//! \param[in,out] ptr Pointer to value to update +//! \param[in,out] expected Value to compare to +//! \param[in] desired If comparison is successful, value to update to +//! \return True if the comparison was successful and the value was updated +inline bool atomic_cas_int32(int32_t* ptr, int32_t& expected, int32_t desired) +{ +#if defined(__GNUC__) || defined(__clang__) + // For gcc/clang, use the __atomic_compare_exchange_n intrinsic + return __atomic_compare_exchange_n( + ptr, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + +#elif defined(_MSC_VER) + // For MSVC, use the _InterlockedCompareExchange intrinsic + int32_t old_val = + _InterlockedCompareExchange(reinterpret_cast(ptr), + static_cast(desired), static_cast(expected)); + return (old_val == expected); + +#else +#error "No compare-and-swap implementation available for this compiler." +#endif +} + +namespace detail { + +//============================================================================== +// MaterialVolumes implementation +//============================================================================== + +void MaterialVolumes::add_volume( + int index_elem, int index_material, double volume) +{ + // This method handles adding elements to the materials hash table, + // implementing open addressing with linear probing. Consistency across + // multiple threads is handled by with an atomic compare-and-swap operation. + // Ideally, we would use #pragma omp atomic compare, but it was introduced in + // OpenMP 5.1 and is not widely supported yet. + + // Loop for linear probing + for (int attempt = 0; attempt < table_size_; ++attempt) { + // Determine slot to check, making sure it is positive + int slot = (index_material + attempt) % table_size_; + if (slot < 0) + slot += table_size_; + int32_t* slot_ptr = &this->materials(index_elem, slot); + + // Non-atomic read of current material + int32_t current_val = *slot_ptr; + + // Found the desired material; accumulate volume + if (current_val == index_material) { +#pragma omp atomic + this->volumes(index_elem, slot) += volume; + return; + } + + // Slot appears to be empty; attempt to claim + if (current_val == EMPTY) { + // Attempt compare-and-swap from EMPTY to index_material + int32_t expected_val = EMPTY; + bool claimed_slot = + atomic_cas_int32(slot_ptr, expected_val, index_material); + + // If we claimed the slot or another thread claimed it but the same + // material was inserted, proceed to accumulate + if (claimed_slot || (expected_val == index_material)) { +#pragma omp atomic + this->volumes(index_elem, slot) += volume; + return; + } + } + } + + // If table is full, set a flag that can be checked later + table_full_ = true; +} + +void MaterialVolumes::add_volume_unsafe( + int index_elem, int index_material, double volume) +{ + // Linear probe + for (int attempt = 0; attempt < table_size_; ++attempt) { + // Determine slot to check, making sure it is positive + int slot = (index_material + attempt) % table_size_; + if (slot < 0) + slot += table_size_; + + // Read current material + int32_t current_val = this->materials(index_elem, slot); + + // Found the desired material; accumulate volume + if (current_val == index_material) { + this->volumes(index_elem, slot) += volume; + return; + } + + // Claim empty slot + if (current_val == EMPTY) { + this->materials(index_elem, slot) = index_material; + this->volumes(index_elem, slot) += volume; + return; + } + } + + // If table is full, set a flag that can be checked later + table_full_ = true; +} + +} // namespace detail + //============================================================================== // Mesh implementation //============================================================================== Mesh::Mesh(pugi::xml_node node) { - // Copy mesh id - if (check_for_node(node, "id")) { - id_ = std::stoi(get_node_value(node, "id")); - - // Check to make sure 'id' hasn't been used - if (model::mesh_map.find(id_) != model::mesh_map.end()) { - fatal_error( - "Two or more meshes use the same unique ID: " + std::to_string(id_)); - } - } + // Read mesh id + id_ = std::stoi(get_node_value(node, "id")); + if (check_for_node(node, "name")) + name_ = get_node_value(node, "name"); } void Mesh::set_id(int32_t id) { - Expects(id >= 0 || id == C_NONE); + assert(id >= 0 || id == C_NONE); // Clear entry in mesh map in case one was already assigned if (id_ != C_NONE) { @@ -139,6 +268,281 @@ void Mesh::set_id(int32_t id) model::mesh_map[id] = model::meshes.size() - 1; } +vector Mesh::volumes() const +{ + vector volumes(n_bins()); + for (int i = 0; i < n_bins(); i++) { + volumes[i] = this->volume(i); + } + return volumes; +} + +void Mesh::material_volumes(int nx, int ny, int nz, int table_size, + int32_t* materials, double* volumes) const +{ + if (mpi::master) { + header("MESH MATERIAL VOLUMES CALCULATION", 7); + } + write_message(7, "Number of mesh elements = {}", n_bins()); + write_message(7, "Number of rays (x) = {}", nx); + write_message(7, "Number of rays (y) = {}", ny); + write_message(7, "Number of rays (z) = {}", nz); + int64_t n_total = static_cast(nx) * ny + + static_cast(ny) * nz + + static_cast(nx) * nz; + write_message(7, "Total number of rays = {}", n_total); + write_message(7, "Table size per mesh element = {}", table_size); + + Timer timer; + timer.start(); + + // Create object for keeping track of materials/volumes + detail::MaterialVolumes result(materials, volumes, table_size); + + // Determine bounding box + auto bbox = this->bounding_box(); + + std::array n_rays = {nx, ny, nz}; + + // Determine effective width of rays + Position width((nx > 0) ? (bbox.xmax - bbox.xmin) / nx : 0.0, + (ny > 0) ? (bbox.ymax - bbox.ymin) / ny : 0.0, + (nz > 0) ? (bbox.zmax - bbox.zmin) / nz : 0.0); + + // Set flag for mesh being contained within model + bool out_of_model = false; + +#pragma omp parallel + { + // Preallocate vector for mesh indices and length fractions and particle + std::vector bins; + std::vector length_fractions; + Particle p; + + SourceSite site; + site.E = 1.0; + site.particle = ParticleType::neutron; + + for (int axis = 0; axis < 3; ++axis) { + // Set starting position and direction + site.r = {0.0, 0.0, 0.0}; + site.r[axis] = bbox.min()[axis]; + site.u = {0.0, 0.0, 0.0}; + site.u[axis] = 1.0; + + // Determine width of rays and number of rays in other directions + int ax1 = (axis + 1) % 3; + int ax2 = (axis + 2) % 3; + double min1 = bbox.min()[ax1]; + double min2 = bbox.min()[ax2]; + double d1 = width[ax1]; + double d2 = width[ax2]; + int n1 = n_rays[ax1]; + int n2 = n_rays[ax2]; + if (n1 == 0 || n2 == 0) { + continue; + } + + // Divide rays in first direction over MPI processes by computing starting + // and ending indices + int min_work = n1 / mpi::n_procs; + int remainder = n1 % mpi::n_procs; + int n1_local = (mpi::rank < remainder) ? min_work + 1 : min_work; + int i1_start = mpi::rank * min_work + std::min(mpi::rank, remainder); + int i1_end = i1_start + n1_local; + + // Loop over rays on face of bounding box +#pragma omp for collapse(2) + for (int i1 = i1_start; i1 < i1_end; ++i1) { + for (int i2 = 0; i2 < n2; ++i2) { + site.r[ax1] = min1 + (i1 + 0.5) * d1; + site.r[ax2] = min2 + (i2 + 0.5) * d2; + + p.from_source(&site); + + // Determine particle's location + if (!exhaustive_find_cell(p)) { + out_of_model = true; + continue; + } + + // Set birth cell attribute + if (p.cell_born() == C_NONE) + p.cell_born() = p.lowest_coord().cell(); + + // Initialize last cells from current cell + for (int j = 0; j < p.n_coord(); ++j) { + p.cell_last(j) = p.coord(j).cell(); + } + p.n_coord_last() = p.n_coord(); + + while (true) { + // Ray trace from r_start to r_end + Position r0 = p.r(); + double max_distance = bbox.max()[axis] - r0[axis]; + + // Find the distance to the nearest boundary + BoundaryInfo boundary = distance_to_boundary(p); + + // Advance particle forward + double distance = std::min(boundary.distance(), max_distance); + p.move_distance(distance); + + // Determine what mesh elements were crossed by particle + bins.clear(); + length_fractions.clear(); + this->bins_crossed(r0, p.r(), p.u(), bins, length_fractions); + + // Add volumes to any mesh elements that were crossed + int i_material = p.material(); + if (i_material != C_NONE) { + i_material = model::materials[i_material]->id(); + } + for (int i_bin = 0; i_bin < bins.size(); i_bin++) { + int mesh_index = bins[i_bin]; + double length = distance * length_fractions[i_bin]; + + // Add volume to result + result.add_volume(mesh_index, i_material, length * d1 * d2); + } + + if (distance == max_distance) + break; + + // cross next geometric surface + for (int j = 0; j < p.n_coord(); ++j) { + p.cell_last(j) = p.coord(j).cell(); + } + p.n_coord_last() = p.n_coord(); + + // Set surface that particle is on and adjust coordinate levels + p.surface() = boundary.surface(); + p.n_coord() = boundary.coord_level(); + + if (boundary.lattice_translation()[0] != 0 || + boundary.lattice_translation()[1] != 0 || + boundary.lattice_translation()[2] != 0) { + // Particle crosses lattice boundary + cross_lattice(p, boundary); + } else { + // Particle crosses surface + const auto& surf {model::surfaces[p.surface_index()].get()}; + p.cross_surface(*surf); + } + } + } + } + } + } + + // Check for errors + if (out_of_model) { + throw std::runtime_error("Mesh not fully contained in geometry."); + } else if (result.table_full()) { + throw std::runtime_error("Maximum number of materials for mesh material " + "volume calculation insufficient."); + } + + // Compute time for raytracing + double t_raytrace = timer.elapsed(); + +#ifdef OPENMC_MPI + // Combine results from multiple MPI processes + if (mpi::n_procs > 1) { + int total = this->n_bins() * table_size; + if (mpi::master) { + // Allocate temporary buffer for receiving data + std::vector mats(total); + std::vector vols(total); + + for (int i = 1; i < mpi::n_procs; ++i) { + // Receive material indices and volumes from process i + MPI_Recv(mats.data(), total, MPI_INT32_T, i, i, mpi::intracomm, + MPI_STATUS_IGNORE); + MPI_Recv(vols.data(), total, MPI_DOUBLE, i, i, mpi::intracomm, + MPI_STATUS_IGNORE); + + // Combine with existing results; we can call thread unsafe version of + // add_volume because each thread is operating on a different element +#pragma omp for + for (int index_elem = 0; index_elem < n_bins(); ++index_elem) { + for (int k = 0; k < table_size; ++k) { + int index = index_elem * table_size + k; + if (mats[index] != EMPTY) { + result.add_volume_unsafe(index_elem, mats[index], vols[index]); + } + } + } + } + } else { + // Send material indices and volumes to process 0 + MPI_Send(materials, total, MPI_INT32_T, 0, mpi::rank, mpi::intracomm); + MPI_Send(volumes, total, MPI_DOUBLE, 0, mpi::rank, mpi::intracomm); + } + } + + // Report time for MPI communication + double t_mpi = timer.elapsed() - t_raytrace; +#else + double t_mpi = 0.0; +#endif + + // Normalize based on known volumes of elements + for (int i = 0; i < this->n_bins(); ++i) { + // Estimated total volume in element i + double volume = 0.0; + for (int j = 0; j < table_size; ++j) { + volume += result.volumes(i, j); + } + // Renormalize volumes based on known volume of element i + double norm = this->volume(i) / volume; + for (int j = 0; j < table_size; ++j) { + result.volumes(i, j) *= norm; + } + } + + // Get total time and normalization time + timer.stop(); + double t_total = timer.elapsed(); + double t_norm = t_total - t_raytrace - t_mpi; + + // Show timing statistics + if (settings::verbosity < 7 || !mpi::master) + return; + header("Timing Statistics", 7); + fmt::print(" Total time elapsed = {:.4e} seconds\n", t_total); + fmt::print(" Ray tracing = {:.4e} seconds\n", t_raytrace); + fmt::print(" MPI communication = {:.4e} seconds\n", t_mpi); + fmt::print(" Normalization = {:.4e} seconds\n", t_norm); + fmt::print(" Calculation rate = {:.4e} rays/seconds\n", + n_total / t_raytrace); + fmt::print(" Calculation rate (per thread) = {:.4e} rays/seconds\n", + n_total / (t_raytrace * mpi::n_procs * num_threads())); + std::fflush(stdout); +} + +void Mesh::to_hdf5(hid_t group) const +{ + // Create group for mesh + std::string group_name = fmt::format("mesh {}", id_); + hid_t mesh_group = create_group(group, group_name.c_str()); + + // Write mesh type + write_dataset(mesh_group, "type", this->get_mesh_type()); + + // Write mesh ID + write_attribute(mesh_group, "id", id_); + + // Write mesh name + write_dataset(mesh_group, "name", name_); + + // Write mesh data + this->to_hdf5_inner(mesh_group); + + // Close group + close_group(mesh_group); +} + //============================================================================== // Structured Mesh implementation //============================================================================== @@ -163,12 +567,30 @@ xt::xtensor StructuredMesh::get_x_shape() const return xt::adapt(tmp_shape, {n_dimension_}); } +Position StructuredMesh::sample_element( + const MeshIndex& ijk, uint64_t* seed) const +{ + // lookup the lower/upper bounds for the mesh element + double x_min = negative_grid_boundary(ijk, 0); + double x_max = positive_grid_boundary(ijk, 0); + + double y_min = (n_dimension_ >= 2) ? negative_grid_boundary(ijk, 1) : 0.0; + double y_max = (n_dimension_ >= 2) ? positive_grid_boundary(ijk, 1) : 0.0; + + double z_min = (n_dimension_ == 3) ? negative_grid_boundary(ijk, 2) : 0.0; + double z_max = (n_dimension_ == 3) ? positive_grid_boundary(ijk, 2) : 0.0; + + return {x_min + (x_max - x_min) * prn(seed), + y_min + (y_max - y_min) * prn(seed), z_min + (z_max - z_min) * prn(seed)}; +} + //============================================================================== // Unstructured Mesh implementation //============================================================================== UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node) { + n_dimension_ = 3; // check the mesh type if (check_for_node(node, "type")) { @@ -181,7 +603,6 @@ UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node) // check if a length unit multiplier was specified if (check_for_node(node, "length_multiplier")) { length_multiplier_ = std::stod(get_node_value(node, "length_multiplier")); - specified_length_multiplier_ = true; } // get the filename of the unstructured mesh to load @@ -195,6 +616,10 @@ UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node) "No filename supplied for unstructured mesh with ID: {}", id_)); } + if (check_for_node(node, "options")) { + options_ = get_node_value(node, "options"); + } + // check if mesh tally data should be written with // statepoint files if (check_for_node(node, "output")) { @@ -202,6 +627,58 @@ UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node) } } +void UnstructuredMesh::determine_bounds() +{ + double xmin = INFTY; + double ymin = INFTY; + double zmin = INFTY; + double xmax = -INFTY; + double ymax = -INFTY; + double zmax = -INFTY; + int n = this->n_vertices(); + for (int i = 0; i < n; ++i) { + auto v = this->vertex(i); + xmin = std::min(v.x, xmin); + ymin = std::min(v.y, ymin); + zmin = std::min(v.z, zmin); + xmax = std::max(v.x, xmax); + ymax = std::max(v.y, ymax); + zmax = std::max(v.z, zmax); + } + lower_left_ = {xmin, ymin, zmin}; + upper_right_ = {xmax, ymax, zmax}; +} + +Position UnstructuredMesh::sample_tet( + std::array coords, uint64_t* seed) const +{ + // Uniform distribution + double s = prn(seed); + double t = prn(seed); + double u = prn(seed); + + // From PyNE implementation of moab tet sampling C. Rocchini & P. Cignoni + // (2000) Generating Random Points in a Tetrahedron, Journal of Graphics + // Tools, 5:4, 9-12, DOI: 10.1080/10867651.2000.10487528 + if (s + t > 1) { + s = 1.0 - s; + t = 1.0 - t; + } + if (s + t + u > 1) { + if (t + u > 1) { + double old_t = t; + t = 1.0 - u; + u = 1.0 - s - old_t; + } else if (t + u <= 1) { + double old_s = s; + s = 1.0 - t - u; + u = old_s + t + u - 1; + } + } + return s * (coords[1] - coords[0]) + t * (coords[2] - coords[0]) + + u * (coords[3] - coords[0]) + coords[0]; +} + const std::string UnstructuredMesh::mesh_type = "unstructured"; std::string UnstructuredMesh::get_mesh_type() const @@ -220,37 +697,84 @@ std::string UnstructuredMesh::bin_label(int bin) const return fmt::format("Mesh Index ({})", bin); }; -void UnstructuredMesh::to_hdf5(hid_t group) const +void UnstructuredMesh::to_hdf5_inner(hid_t mesh_group) const { - hid_t mesh_group = create_group(group, fmt::format("mesh {}", id_)); - - write_dataset(mesh_group, "type", mesh_type); write_dataset(mesh_group, "filename", filename_); write_dataset(mesh_group, "library", this->library()); - // write volume of each element - vector tet_vols; - xt::xtensor centroids({static_cast(this->n_bins()), 3}); - for (int i = 0; i < this->n_bins(); i++) { - tet_vols.emplace_back(this->volume(i)); - auto c = this->centroid(i); - xt::view(centroids, i, xt::all()) = xt::xarray({c.x, c.y, c.z}); + if (!options_.empty()) { + write_attribute(mesh_group, "options", options_); } - write_dataset(mesh_group, "volumes", tet_vols); - write_dataset(mesh_group, "centroids", centroids); - - if (specified_length_multiplier_) + if (length_multiplier_ > 0.0) write_dataset(mesh_group, "length_multiplier", length_multiplier_); - close_group(mesh_group); + // write vertex coordinates + xt::xtensor vertices({static_cast(this->n_vertices()), 3}); + for (int i = 0; i < this->n_vertices(); i++) { + auto v = this->vertex(i); + xt::view(vertices, i, xt::all()) = xt::xarray({v.x, v.y, v.z}); + } + write_dataset(mesh_group, "vertices", vertices); + + int num_elem_skipped = 0; + + // write element types and connectivity + vector volumes; + xt::xtensor connectivity({static_cast(this->n_bins()), 8}); + xt::xtensor elem_types({static_cast(this->n_bins()), 1}); + for (int i = 0; i < this->n_bins(); i++) { + auto conn = this->connectivity(i); + + volumes.emplace_back(this->volume(i)); + + // write linear tet element + if (conn.size() == 4) { + xt::view(elem_types, i, xt::all()) = + static_cast(ElementType::LINEAR_TET); + xt::view(connectivity, i, xt::all()) = + xt::xarray({conn[0], conn[1], conn[2], conn[3], -1, -1, -1, -1}); + // write linear hex element + } else if (conn.size() == 8) { + xt::view(elem_types, i, xt::all()) = + static_cast(ElementType::LINEAR_HEX); + xt::view(connectivity, i, xt::all()) = xt::xarray({conn[0], conn[1], + conn[2], conn[3], conn[4], conn[5], conn[6], conn[7]}); + } else { + num_elem_skipped++; + xt::view(elem_types, i, xt::all()) = + static_cast(ElementType::UNSUPPORTED); + xt::view(connectivity, i, xt::all()) = -1; + } + } + + // warn users that some elements were skipped + if (num_elem_skipped > 0) { + warning(fmt::format("The connectivity of {} elements " + "on mesh {} were not written " + "because they are not of type linear tet/hex.", + num_elem_skipped, this->id_)); + } + + write_dataset(mesh_group, "volumes", volumes); + write_dataset(mesh_group, "connectivity", connectivity); + write_dataset(mesh_group, "element_types", elem_types); } void UnstructuredMesh::set_length_multiplier(double length_multiplier) { length_multiplier_ = length_multiplier; +} - if (length_multiplier_ != 1.0) - specified_length_multiplier_ = true; +ElementType UnstructuredMesh::element_type(int bin) const +{ + auto conn = connectivity(bin); + + if (conn.size() == 4) + return ElementType::LINEAR_TET; + else if (conn.size() == 8) + return ElementType::LINEAR_HEX; + else + return ElementType::UNSUPPORTED; } StructuredMesh::MeshIndex StructuredMesh::get_indices( @@ -376,24 +900,28 @@ xt::xtensor StructuredMesh::count_sites( } // raytrace through the mesh. The template class T will do the tallying. -// A modern optimizing compiler can recognize the noop method of T and eleminate -// that call entirely. +// A modern optimizing compiler can recognize the noop method of T and +// eliminate that call entirely. template void StructuredMesh::raytrace_mesh( Position r0, Position r1, const Direction& u, T tally) const { - // TODO: when c++-17 is available, use "if constexpr ()" to compile-time // enable/disable tally calls for now, T template type needs to provide both // surface and track methods, which might be empty. modern optimizing - // compilers will (hopefully) eleminate the complete code (including + // compilers will (hopefully) eliminate the complete code (including // calculation of parameters) but for the future: be explicit // Compute the length of the entire track. double total_distance = (r1 - r0).norm(); - if (total_distance == 0.0) + if (total_distance == 0.0 && settings::solver_type != SolverType::RANDOM_RAY) return; + // keep a copy of the original global position to pass to get_indices, + // which performs its own transformation to local coordinates + Position global_r = r0; + Position local_r = local_coords(r0); + const int n = n_dimension_; // Flag if position is inside the mesh @@ -404,7 +932,7 @@ void StructuredMesh::raytrace_mesh( // Calculate index of current cell. Offset the position a tiny bit in // direction of flight - MeshIndex ijk = get_indices(r0 + TINY_BIT * u, in_mesh); + MeshIndex ijk = get_indices(global_r + TINY_BIT * u, in_mesh); // if track is very short, assume that it is completely inside one cell. // Only the current cell will score and no surfaces @@ -418,7 +946,7 @@ void StructuredMesh::raytrace_mesh( // Calculate initial distances to next surfaces in all three dimensions std::array distances; for (int k = 0; k < n; ++k) { - distances[k] = distance_to_grid_boundary(ijk, k, r0, u, 0.0); + distances[k] = distance_to_grid_boundary(ijk, k, local_r, u, 0.0); } // Loop until r = r1 is eventually reached @@ -440,28 +968,30 @@ void StructuredMesh::raytrace_mesh( if (traveled_distance >= total_distance) return; - // If we have not reached r1, we have hit a surface. Tally outward current + // If we have not reached r1, we have hit a surface. Tally outward + // current tally.surface(ijk, k, distances[k].max_surface, false); // Update cell and calculate distance to next surface in k-direction. // The two other directions are still valid! ijk[k] = distances[k].next_index; distances[k] = - distance_to_grid_boundary(ijk, k, r0, u, traveled_distance); + distance_to_grid_boundary(ijk, k, local_r, u, traveled_distance); // Check if we have left the interior of the mesh in_mesh = ((ijk[k] >= 1) && (ijk[k] <= shape_[k])); - // If we are still inside the mesh, tally inward current for the next cell + // If we are still inside the mesh, tally inward current for the next + // cell if (in_mesh) tally.surface(ijk, k, !distances[k].max_surface, true); } else { // not inside mesh - // For all directions outside the mesh, find the distance that we need to - // travel to reach the next surface. Use the largest distance, as only - // this will cross all outer surfaces. - int k_max {0}; + // For all directions outside the mesh, find the distance that we need + // to travel to reach the next surface. Use the largest distance, as + // only this will cross all outer surfaces. + int k_max {-1}; for (int k = 0; k < n; ++k) { if ((ijk[k] < 1 || ijk[k] > shape_[k]) && (distances[k].distance > traveled_distance)) { @@ -469,20 +999,25 @@ void StructuredMesh::raytrace_mesh( k_max = k; } } + // Assure some distance is traveled + if (k_max == -1) { + traveled_distance += TINY_BIT; + } // If r1 is not inside the mesh, exit here if (traveled_distance >= total_distance) return; - // Calculate the new cell index and update all distances to next surfaces. - ijk = get_indices(r0 + (traveled_distance + TINY_BIT) * u, in_mesh); + // Calculate the new cell index and update all distances to next + // surfaces. + ijk = get_indices(global_r + (traveled_distance + TINY_BIT) * u, in_mesh); for (int k = 0; k < n; ++k) { distances[k] = - distance_to_grid_boundary(ijk, k, r0, u, traveled_distance); + distance_to_grid_boundary(ijk, k, local_r, u, traveled_distance); } // If inside the mesh, Tally inward current - if (in_mesh) + if (in_mesh && k_max >= 0) tally.surface(ijk, k_max, !distances[k_max].max_surface, true); } } @@ -630,8 +1165,13 @@ RegularMesh::RegularMesh(pugi::xml_node node) : StructuredMesh {node} fatal_error("Must specify either or on a mesh."); } - // Set volume fraction + // Set material volumes volume_frac_ = 1.0 / xt::prod(shape)(); + + element_volume_ = 1.0; + for (int i = 0; i < n_dimension_; i++) { + element_volume_ *= width_[i]; + } } int RegularMesh::get_index_in_direction(double r, int i) const @@ -673,6 +1213,7 @@ StructuredMesh::MeshDistance RegularMesh::distance_to_grid_boundary( d.next_index--; d.distance = (negative_grid_boundary(ijk, i) - r0[i]) / u[i]; } + return d; } @@ -717,17 +1258,12 @@ std::pair, vector> RegularMesh::plot( return {axis_lines[0], axis_lines[1]}; } -void RegularMesh::to_hdf5(hid_t group) const +void RegularMesh::to_hdf5_inner(hid_t mesh_group) const { - hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - - write_dataset(mesh_group, "type", "regular"); write_dataset(mesh_group, "dimension", get_x_shape()); write_dataset(mesh_group, "lower_left", lower_left_); write_dataset(mesh_group, "upper_right", upper_right_); write_dataset(mesh_group, "width", width_); - - close_group(mesh_group); } xt::xtensor RegularMesh::count_sites( @@ -785,6 +1321,11 @@ xt::xtensor RegularMesh::count_sites( return counts; } +double RegularMesh::volume(const MeshIndex& ijk) const +{ + return element_volume_; +} + //============================================================================== // RectilinearMesh implementation //============================================================================== @@ -902,29 +1443,35 @@ std::pair, vector> RectilinearMesh::plot( return {axis_lines[0], axis_lines[1]}; } -void RectilinearMesh::to_hdf5(hid_t group) const +void RectilinearMesh::to_hdf5_inner(hid_t mesh_group) const { - hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - - write_dataset(mesh_group, "type", "rectilinear"); write_dataset(mesh_group, "x_grid", grid_[0]); write_dataset(mesh_group, "y_grid", grid_[1]); write_dataset(mesh_group, "z_grid", grid_[2]); +} - close_group(mesh_group); +double RectilinearMesh::volume(const MeshIndex& ijk) const +{ + double vol {1.0}; + + for (int i = 0; i < n_dimension_; i++) { + vol *= grid_[i][ijk[i]] - grid_[i][ijk[i] - 1]; + } + return vol; } //============================================================================== // CylindricalMesh implementation //============================================================================== -CylindricalMesh::CylindricalMesh(pugi::xml_node node) : StructuredMesh {node} +CylindricalMesh::CylindricalMesh(pugi::xml_node node) + : PeriodicStructuredMesh {node} { n_dimension_ = 3; - grid_[0] = get_node_array(node, "r_grid"); grid_[1] = get_node_array(node, "phi_grid"); grid_[2] = get_node_array(node, "z_grid"); + origin_ = get_node_position(node, "origin"); if (int err = set_grid()) { fatal_error(openmc_err_msg); @@ -941,10 +1488,12 @@ std::string CylindricalMesh::get_mesh_type() const StructuredMesh::MeshIndex CylindricalMesh::get_indices( Position r, bool& in_mesh) const { - Position mapped_r; + r = local_coords(r); + Position mapped_r; mapped_r[0] = std::hypot(r.x, r.y); mapped_r[2] = r[2]; + if (mapped_r[0] < FP_PRECISION) { mapped_r[1] = 0.0; } else { @@ -960,11 +1509,35 @@ StructuredMesh::MeshIndex CylindricalMesh::get_indices( return idx; } +Position CylindricalMesh::sample_element( + const MeshIndex& ijk, uint64_t* seed) const +{ + double r_min = this->r(ijk[0] - 1); + double r_max = this->r(ijk[0]); + + double phi_min = this->phi(ijk[1] - 1); + double phi_max = this->phi(ijk[1]); + + double z_min = this->z(ijk[2] - 1); + double z_max = this->z(ijk[2]); + + double r_min_sq = r_min * r_min; + double r_max_sq = r_max * r_max; + double r = std::sqrt(uniform_distribution(r_min_sq, r_max_sq, seed)); + double phi = uniform_distribution(phi_min, phi_max, seed); + double z = uniform_distribution(z_min, z_max, seed); + + double x = r * std::cos(phi); + double y = r * std::sin(phi); + + return origin_ + Position(x, y, z); +} + double CylindricalMesh::find_r_crossing( const Position& r, const Direction& u, double l, int shell) const { - if ((shell < 0) || (shell >= shape_[0])) + if ((shell < 0) || (shell > shape_[0])) return INFTY; // solve r.x^2 + r.y^2 == r0^2 @@ -972,6 +1545,9 @@ double CylindricalMesh::find_r_crossing( // s^2 * (u^2 + v^2) + 2*s*(u*x+v*y) + x^2+y^2-r0^2 = 0 const double r0 = grid_[0][shell]; + if (r0 == 0.0) + return INFTY; + const double denominator = u.x * u.x + u.y * u.y; // Direction of flight is in z-direction. Will never intersect r. @@ -982,7 +1558,8 @@ double CylindricalMesh::find_r_crossing( const double inv_denominator = 1.0 / denominator; const double p = (u.x * r.x + u.y * r.y) * inv_denominator; - double D = p * p + (r0 * r0 - r.x * r.x - r.y * r.y) * inv_denominator; + double c = r.x * r.x + r.y * r.y - r0 * r0; + double D = p * p - c * inv_denominator; if (D < 0.0) return INFTY; @@ -990,6 +1567,9 @@ double CylindricalMesh::find_r_crossing( D = std::sqrt(D); // the solution -p - D is always smaller as -p + D : Check this one first + if (std::abs(c) <= RADIAL_MESH_TOL) + return INFTY; + if (-p - D > l) return -p - D; if (-p + D > l) @@ -1056,7 +1636,6 @@ StructuredMesh::MeshDistance CylindricalMesh::distance_to_grid_boundary( const MeshIndex& ijk, int i, const Position& r0, const Direction& u, double l) const { - if (i == 0) { return std::min( @@ -1113,8 +1692,10 @@ int CylindricalMesh::set_grid() full_phi_ = (grid_[1].front() == 0.0) && (grid_[1].back() == 2.0 * PI); - lower_left_ = {grid_[0].front(), grid_[1].front(), grid_[2].front()}; - upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()}; + lower_left_ = {origin_[0] - grid_[0].back(), origin_[1] - grid_[0].back(), + origin_[2] + grid_[2].front()}; + upper_right_ = {origin_[0] + grid_[0].back(), origin_[1] + grid_[0].back(), + origin_[2] + grid_[2].back()}; return 0; } @@ -1134,29 +1715,41 @@ std::pair, vector> CylindricalMesh::plot( return {axis_lines[0], axis_lines[1]}; } -void CylindricalMesh::to_hdf5(hid_t group) const +void CylindricalMesh::to_hdf5_inner(hid_t mesh_group) const { - hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - - write_dataset(mesh_group, "type", "cylindrical"); write_dataset(mesh_group, "r_grid", grid_[0]); write_dataset(mesh_group, "phi_grid", grid_[1]); write_dataset(mesh_group, "z_grid", grid_[2]); + write_dataset(mesh_group, "origin", origin_); +} - close_group(mesh_group); +double CylindricalMesh::volume(const MeshIndex& ijk) const +{ + double r_i = grid_[0][ijk[0] - 1]; + double r_o = grid_[0][ijk[0]]; + + double phi_i = grid_[1][ijk[1] - 1]; + double phi_o = grid_[1][ijk[1]]; + + double z_i = grid_[2][ijk[2] - 1]; + double z_o = grid_[2][ijk[2]]; + + return 0.5 * (r_o * r_o - r_i * r_i) * (phi_o - phi_i) * (z_o - z_i); } //============================================================================== // SphericalMesh implementation //============================================================================== -SphericalMesh::SphericalMesh(pugi::xml_node node) : StructuredMesh {node} +SphericalMesh::SphericalMesh(pugi::xml_node node) + : PeriodicStructuredMesh {node} { n_dimension_ = 3; grid_[0] = get_node_array(node, "r_grid"); grid_[1] = get_node_array(node, "theta_grid"); grid_[2] = get_node_array(node, "phi_grid"); + origin_ = get_node_position(node, "origin"); if (int err = set_grid()) { fatal_error(openmc_err_msg); @@ -1173,9 +1766,11 @@ std::string SphericalMesh::get_mesh_type() const StructuredMesh::MeshIndex SphericalMesh::get_indices( Position r, bool& in_mesh) const { - Position mapped_r; + r = local_coords(r); + Position mapped_r; mapped_r[0] = r.norm(); + if (mapped_r[0] < FP_PRECISION) { mapped_r[1] = 0.0; mapped_r[2] = 0.0; @@ -1194,17 +1789,51 @@ StructuredMesh::MeshIndex SphericalMesh::get_indices( return idx; } +Position SphericalMesh::sample_element( + const MeshIndex& ijk, uint64_t* seed) const +{ + double r_min = this->r(ijk[0] - 1); + double r_max = this->r(ijk[0]); + + double theta_min = this->theta(ijk[1] - 1); + double theta_max = this->theta(ijk[1]); + + double phi_min = this->phi(ijk[2] - 1); + double phi_max = this->phi(ijk[2]); + + double cos_theta = + uniform_distribution(std::cos(theta_min), std::cos(theta_max), seed); + double sin_theta = std::sin(std::acos(cos_theta)); + double phi = uniform_distribution(phi_min, phi_max, seed); + double r_min_cub = std::pow(r_min, 3); + double r_max_cub = std::pow(r_max, 3); + // might be faster to do rejection here? + double r = std::cbrt(uniform_distribution(r_min_cub, r_max_cub, seed)); + + double x = r * std::cos(phi) * sin_theta; + double y = r * std::sin(phi) * sin_theta; + double z = r * cos_theta; + + return origin_ + Position(x, y, z); +} + double SphericalMesh::find_r_crossing( const Position& r, const Direction& u, double l, int shell) const { - if ((shell < 0) || (shell >= shape_[0])) + if ((shell < 0) || (shell > shape_[0])) return INFTY; // solve |r+s*u| = r0 // |r+s*u| = |r| + 2*s*r*u + s^2 (|u|==1 !) const double r0 = grid_[0][shell]; + if (r0 == 0.0) + return INFTY; const double p = r.dot(u); - double D = p * p - r.dot(r) + r0 * r0; + double c = r.dot(r) - r0 * r0; + double D = p * p - c; + + if (std::abs(c) <= RADIAL_MESH_TOL) + return INFTY; if (D >= 0.0) { D = std::sqrt(D); @@ -1242,7 +1871,8 @@ double SphericalMesh::find_theta_crossing( const double b = r.dot(u) * cos_t_2 - r.z * u.z; const double c = r.dot(r) * cos_t_2 - r.z * r.z; - // if factor of s^2 is zero, direction of flight is parallel to theta surface + // if factor of s^2 is zero, direction of flight is parallel to theta + // surface if (std::abs(a) < FP_PRECISION) { // if b vanishes, direction of flight is within theta surface and crossing // is not possible @@ -1250,7 +1880,8 @@ double SphericalMesh::find_theta_crossing( return INFTY; const double s = -0.5 * c / b; - // Check if solution is in positive direction of flight and has correct sign + // Check if solution is in positive direction of flight and has correct + // sign if ((s > l) && (std::signbit(r.z + s * u.z) == sgn)) return s; @@ -1319,20 +1950,17 @@ StructuredMesh::MeshDistance SphericalMesh::distance_to_grid_boundary( { if (i == 0) { - return std::min( MeshDistance(ijk[i] + 1, true, find_r_crossing(r0, u, l, ijk[i])), MeshDistance(ijk[i] - 1, false, find_r_crossing(r0, u, l, ijk[i] - 1))); } else if (i == 1) { - return std::min(MeshDistance(sanitize_theta(ijk[i] + 1), true, find_theta_crossing(r0, u, l, ijk[i])), MeshDistance(sanitize_theta(ijk[i] - 1), false, find_theta_crossing(r0, u, l, ijk[i] - 1))); } else { - return std::min(MeshDistance(sanitize_phi(ijk[i] + 1), true, find_phi_crossing(r0, u, l, ijk[i])), MeshDistance(sanitize_phi(ijk[i] - 1), false, @@ -1379,8 +2007,9 @@ int SphericalMesh::set_grid() full_theta_ = (grid_[1].front() == 0.0) && (grid_[1].back() == PI); full_phi_ = (grid_[2].front() == 0.0) && (grid_[2].back() == 2 * PI); - lower_left_ = {grid_[0].front(), grid_[1].front(), grid_[2].front()}; - upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()}; + double r = grid_[0].back(); + lower_left_ = {origin_[0] - r, origin_[1] - r, origin_[2] - r}; + upper_right_ = {origin_[0] + r, origin_[1] + r, origin_[2] + r}; return 0; } @@ -1400,16 +2029,27 @@ std::pair, vector> SphericalMesh::plot( return {axis_lines[0], axis_lines[1]}; } -void SphericalMesh::to_hdf5(hid_t group) const +void SphericalMesh::to_hdf5_inner(hid_t mesh_group) const { - hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - - write_dataset(mesh_group, "type", SphericalMesh::mesh_type); write_dataset(mesh_group, "r_grid", grid_[0]); write_dataset(mesh_group, "theta_grid", grid_[1]); write_dataset(mesh_group, "phi_grid", grid_[2]); + write_dataset(mesh_group, "origin", origin_); +} - close_group(mesh_group); +double SphericalMesh::volume(const MeshIndex& ijk) const +{ + double r_i = grid_[0][ijk[0] - 1]; + double r_o = grid_[0][ijk[0]]; + + double theta_i = grid_[1][ijk[1] - 1]; + double theta_o = grid_[1][ijk[1]]; + + double phi_i = grid_[2][ijk[2] - 1]; + double phi_o = grid_[2][ijk[2]]; + + return (1.0 / 3.0) * (r_o * r_o * r_o - r_i * r_i * r_i) * + (std::cos(theta_i) - std::cos(theta_o)) * (phi_o - phi_i); } //============================================================================== @@ -1496,14 +2136,14 @@ extern "C" int openmc_add_unstructured_mesh( std::string mesh_file(filename); bool valid_lib = false; -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED if (lib_name == MOABMesh::mesh_lib_type) { model::meshes.push_back(std::move(make_unique(mesh_file))); valid_lib = true; } #endif -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED if (lib_name == LibMesh::mesh_lib_type) { model::meshes.push_back(std::move(make_unique(mesh_file))); valid_lib = true; @@ -1555,6 +2195,124 @@ extern "C" int openmc_mesh_set_id(int32_t index, int32_t id) return 0; } +//! Get the number of elements in a mesh +extern "C" int openmc_mesh_get_n_elements(int32_t index, size_t* n) +{ + if (int err = check_mesh(index)) + return err; + *n = model::meshes[index]->n_bins(); + return 0; +} + +//! Get the volume of each element in the mesh +extern "C" int openmc_mesh_get_volumes(int32_t index, double* volumes) +{ + if (int err = check_mesh(index)) + return err; + for (int i = 0; i < model::meshes[index]->n_bins(); ++i) { + volumes[i] = model::meshes[index]->volume(i); + } + return 0; +} + +//! Get the bounding box of a mesh +extern "C" int openmc_mesh_bounding_box(int32_t index, double* ll, double* ur) +{ + if (int err = check_mesh(index)) + return err; + + BoundingBox bbox = model::meshes[index]->bounding_box(); + + // set lower left corner values + ll[0] = bbox.xmin; + ll[1] = bbox.ymin; + ll[2] = bbox.zmin; + + // set upper right corner values + ur[0] = bbox.xmax; + ur[1] = bbox.ymax; + ur[2] = bbox.zmax; + return 0; +} + +extern "C" int openmc_mesh_material_volumes(int32_t index, int nx, int ny, + int nz, int table_size, int32_t* materials, double* volumes) +{ + if (int err = check_mesh(index)) + return err; + + try { + model::meshes[index]->material_volumes( + nx, ny, nz, table_size, materials, volumes); + } catch (const std::exception& e) { + set_errmsg(e.what()); + if (starts_with(e.what(), "Mesh")) { + return OPENMC_E_GEOMETRY; + } else { + return OPENMC_E_ALLOCATE; + } + } + + return 0; +} + +extern "C" int openmc_mesh_get_plot_bins(int32_t index, Position origin, + Position width, int basis, int* pixels, int32_t* data) +{ + if (int err = check_mesh(index)) + return err; + const auto& mesh = model::meshes[index].get(); + + int pixel_width = pixels[0]; + int pixel_height = pixels[1]; + + // get pixel size + double in_pixel = (width[0]) / static_cast(pixel_width); + double out_pixel = (width[1]) / static_cast(pixel_height); + + // setup basis indices and initial position centered on pixel + int in_i, out_i; + Position xyz = origin; + enum class PlotBasis { xy = 1, xz = 2, yz = 3 }; + PlotBasis basis_enum = static_cast(basis); + switch (basis_enum) { + case PlotBasis::xy: + in_i = 0; + out_i = 1; + break; + case PlotBasis::xz: + in_i = 0; + out_i = 2; + break; + case PlotBasis::yz: + in_i = 1; + out_i = 2; + break; + default: + UNREACHABLE(); + } + + // set initial position + xyz[in_i] = origin[in_i] - width[0] / 2. + in_pixel / 2.; + xyz[out_i] = origin[out_i] + width[1] / 2. - out_pixel / 2.; + +#pragma omp parallel + { + Position r = xyz; + +#pragma omp for + for (int y = 0; y < pixel_height; y++) { + r[out_i] = xyz[out_i] - out_pixel * y; + for (int x = 0; x < pixel_width; x++) { + r[in_i] = xyz[in_i] + in_pixel * x; + data[pixel_width * y + x] = mesh->get_bin(r); + } + } + } + + return 0; +} + //! Get the dimension of a regular mesh extern "C" int openmc_regular_mesh_get_dimension( int32_t index, int** dims, int* n) @@ -1609,6 +2367,11 @@ extern "C" int openmc_regular_mesh_set_params( return err; RegularMesh* m = dynamic_cast(model::meshes[index].get()); + if (m->n_dimension_ == -1) { + set_errmsg("Need to set mesh dimension before setting parameters."); + return OPENMC_E_UNASSIGNED; + } + vector shape = {static_cast(n)}; if (ll && ur) { m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); @@ -1627,6 +2390,16 @@ extern "C" int openmc_regular_mesh_set_params( return OPENMC_E_INVALID_ARGUMENT; } + // Set material volumes + + // TODO: incorporate this into method in RegularMesh that can be called from + // here and from constructor + m->volume_frac_ = 1.0 / xt::prod(m->get_x_shape())(); + m->element_volume_ = 1.0; + for (int i = 0; i < m->n_dimension_; i++) { + m->element_volume_ *= m->width_[i]; + } + return 0; } @@ -1738,7 +2511,7 @@ extern "C" int openmc_spherical_mesh_set_grid(int32_t index, index, grid_x, nx, grid_y, ny, grid_z, nz); } -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED const std::string MOABMesh::mesh_lib_type = "moab"; @@ -1748,7 +2521,9 @@ MOABMesh::MOABMesh(pugi::xml_node node) : UnstructuredMesh(node) } MOABMesh::MOABMesh(const std::string& filename, double length_multiplier) + : UnstructuredMesh() { + n_dimension_ = 3; filename_ = filename; set_length_multiplier(length_multiplier); initialize(); @@ -1785,6 +2560,13 @@ void MOABMesh::initialize() filename_); } + // set member range of vertices + int vertex_dim = 0; + rval = mbi_->get_entities_by_dimension(0, vertex_dim, verts_); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to get all vertex handles"); + } + // make an entity set for all tetrahedra // this is used for convenience later in output rval = mbi_->create_meshset(moab::MESHSET_SET, tetset_); @@ -1797,7 +2579,7 @@ void MOABMesh::initialize() fatal_error("Failed to add tetrahedra to an entity set."); } - if (specified_length_multiplier_) { + if (length_multiplier_ > 0.0) { // get the connectivity of all tets moab::Range adj; rval = mbi_->get_adjacencies(ehs_, 0, true, adj, moab::Interface::UNION); @@ -1825,6 +2607,16 @@ void MOABMesh::initialize() } } + // Determine bounds of mesh + this->determine_bounds(); +} + +void MOABMesh::prepare_for_point_location() +{ + // if the KDTree has already been constructed, do nothing + if (kdtree_) + return; + // build acceleration data structures compute_barycentric_data(ehs_); build_kdtree(ehs_); @@ -1850,6 +2642,7 @@ void MOABMesh::build_kdtree(const moab::Range& all_tets) { moab::Range all_tris; int adj_dim = 2; + write_message("Getting tet adjacencies...", 7); moab::ErrorCode rval = mbi_->get_adjacencies( all_tets, adj_dim, true, all_tris, moab::Interface::UNION); if (rval != moab::MB_SUCCESS) { @@ -1868,10 +2661,21 @@ void MOABMesh::build_kdtree(const moab::Range& all_tets) all_tets_and_tris.merge(all_tris); // create a kd-tree instance + write_message( + 7, "Building adaptive k-d tree for tet mesh with ID {}...", id_); kdtree_ = make_unique(mbi_.get()); - // build the tree - rval = kdtree_->build_tree(all_tets_and_tris, &kdtree_root_); + // Determine what options to use + std::ostringstream options_stream; + if (options_.empty()) { + options_stream << "MAX_DEPTH=20;PLANE_SET=2;"; + } else { + options_stream << options_; + } + moab::FileOptions file_opts(options_stream.str().c_str()); + + // Build the k-d tree + rval = kdtree_->build_tree(all_tets_and_tris, &kdtree_root_, &file_opts); if (rval != moab::MB_SUCCESS) { fatal_error("Failed to construct KDTree for the " "unstructured mesh file: " + @@ -1887,7 +2691,8 @@ void MOABMesh::intersect_track(const moab::CartVect& start, moab::ErrorCode rval; vector tris; // get all intersections with triangles in the tet mesh - // (distances are relative to the start point, not the previous intersection) + // (distances are relative to the start point, not the previous + // intersection) rval = kdtree_->ray_intersect_triangles(kdtree_root_, FP_COINCIDENT, dir.array(), start.array(), tris, hits, 0, track_len); if (rval != moab::MB_SUCCESS) { @@ -2014,6 +2819,35 @@ std::string MOABMesh::library() const return mesh_lib_type; } +// Sample position within a tet for MOAB type tets +Position MOABMesh::sample_element(int32_t bin, uint64_t* seed) const +{ + + moab::EntityHandle tet_ent = get_ent_handle_from_bin(bin); + + // Get vertex coordinates for MOAB tet + const moab::EntityHandle* conn1; + int conn1_size; + moab::ErrorCode rval = mbi_->get_connectivity(tet_ent, conn1, conn1_size); + if (rval != moab::MB_SUCCESS || conn1_size != 4) { + fatal_error(fmt::format( + "Failed to get tet connectivity or connectivity size ({}) is invalid.", + conn1_size)); + } + moab::CartVect p[4]; + rval = mbi_->get_coords(conn1, conn1_size, p[0].array()); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to get tet coords"); + } + + std::array tet_verts; + for (int i = 0; i < 4; i++) { + tet_verts[i] = {p[i][0], p[i][1], p[i][2]}; + } + // Samples position within tet using Barycentric stuff + return this->sample_tet(tet_verts, seed); +} + double MOABMesh::tet_volume(moab::EntityHandle tet) const { vector conn; @@ -2134,6 +2968,16 @@ std::pair, vector> MOABMesh::plot( return {}; } +int MOABMesh::get_vert_idx_from_handle(moab::EntityHandle vert) const +{ + int idx = vert - verts_[0]; + if (idx >= n_vertices()) { + fatal_error( + fmt::format("Invalid vertex idx {} (# vertices {})", idx, n_vertices())); + } + return idx; +} + int MOABMesh::get_bin_from_ent_handle(moab::EntityHandle eh) const { int bin = eh - ehs_[0]; @@ -2201,6 +3045,49 @@ Position MOABMesh::centroid(int bin) const return {centroid[0], centroid[1], centroid[2]}; } +int MOABMesh::n_vertices() const +{ + return verts_.size(); +} + +Position MOABMesh::vertex(int id) const +{ + + moab::ErrorCode rval; + + moab::EntityHandle vert = verts_[id]; + + moab::CartVect coords; + rval = mbi_->get_coords(&vert, 1, coords.array()); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to get the coordinates of a vertex."); + } + + return {coords[0], coords[1], coords[2]}; +} + +std::vector MOABMesh::connectivity(int bin) const +{ + moab::ErrorCode rval; + + auto tet = get_ent_handle_from_bin(bin); + + // look up the tet connectivity + vector conn; + rval = mbi_->get_connectivity(&tet, 1, conn); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to get connectivity of a mesh element."); + return {}; + } + + std::vector verts(4); + for (int i = 0; i < verts.size(); i++) { + verts[i] = get_vert_idx_from_handle(conn[i]); + } + + return verts; +} + std::pair MOABMesh::get_score_tags( std::string score) const { @@ -2328,40 +3215,78 @@ void MOABMesh::write(const std::string& base_filename) const #endif -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED const std::string LibMesh::mesh_lib_type = "libmesh"; LibMesh::LibMesh(pugi::xml_node node) : UnstructuredMesh(node) { + // filename_ and length_multiplier_ will already be set by the + // UnstructuredMesh constructor + set_mesh_pointer_from_filename(filename_); + set_length_multiplier(length_multiplier_); initialize(); } -LibMesh::LibMesh(const std::string& filename, double length_multiplier) +// create the mesh from a pointer to a libMesh Mesh +LibMesh::LibMesh(libMesh::MeshBase& input_mesh, double length_multiplier) { - filename_ = filename; + if (!dynamic_cast(&input_mesh)) { + fatal_error("At present LibMesh tallies require a replicated mesh. Please " + "ensure 'input_mesh' is a libMesh::ReplicatedMesh."); + } + + m_ = &input_mesh; set_length_multiplier(length_multiplier); initialize(); } +// create the mesh from an input file +LibMesh::LibMesh(const std::string& filename, double length_multiplier) +{ + n_dimension_ = 3; + set_mesh_pointer_from_filename(filename); + set_length_multiplier(length_multiplier); + initialize(); +} + +void LibMesh::set_mesh_pointer_from_filename(const std::string& filename) +{ + filename_ = filename; + unique_m_ = + make_unique(*settings::libmesh_comm, n_dimension_); + m_ = unique_m_.get(); + m_->read(filename_); +} + +// build a libMesh equation system for storing values +void LibMesh::build_eqn_sys() +{ + eq_system_name_ = fmt::format("mesh_{}_system", id_); + equation_systems_ = make_unique(*m_); + libMesh::ExplicitSystem& eq_sys = + equation_systems_->add_system(eq_system_name_); +} + +// intialize from mesh file void LibMesh::initialize() { if (!settings::libmesh_comm) { - fatal_error( - "Attempting to use an unstructured mesh without a libMesh communicator."); + fatal_error("Attempting to use an unstructured mesh without a libMesh " + "communicator."); } // assuming that unstructured meshes used in OpenMC are 3D n_dimension_ = 3; - m_ = make_unique(*settings::libmesh_comm, n_dimension_); - m_->read(filename_); - - if (specified_length_multiplier_) { + if (length_multiplier_ > 0.0) { libMesh::MeshTools::Modification::scale(*m_, length_multiplier_); } - - m_->prepare_for_use(); + // if OpenMC is managing the libMesh::MeshBase instance, prepare the mesh. + // Otherwise assume that it is prepared by its owning application + if (unique_m_) { + m_->prepare_for_use(); + } // ensure that the loaded mesh is 3 dimensional if (m_->mesh_dimension() != n_dimension_) { @@ -2370,20 +3295,7 @@ void LibMesh::initialize() filename_)); } - // create an equation system for storing values - eq_system_name_ = fmt::format("mesh_{}_system", id_); - - equation_systems_ = make_unique(*m_); - libMesh::ExplicitSystem& eq_sys = - equation_systems_->add_system(eq_system_name_); - -#ifdef _OPENMP - int n_threads = omp_get_max_threads(); -#else - int n_threads = 1; -#endif - - for (int i = 0; i < n_threads; i++) { + for (int i = 0; i < num_threads(); i++) { pl_.emplace_back(m_->sub_point_locator()); pl_.back()->set_contains_point_tol(FP_COINCIDENT); pl_.back()->enable_out_of_mesh_mode(); @@ -2395,15 +3307,54 @@ void LibMesh::initialize() // bounding box for the mesh for quick rejection checks bbox_ = libMesh::MeshTools::create_bounding_box(*m_); + libMesh::Point ll = bbox_.min(); + libMesh::Point ur = bbox_.max(); + lower_left_ = {ll(0), ll(1), ll(2)}; + upper_right_ = {ur(0), ur(1), ur(2)}; +} + +// Sample position within a tet for LibMesh type tets +Position LibMesh::sample_element(int32_t bin, uint64_t* seed) const +{ + const auto& elem = get_element_from_bin(bin); + // Get tet vertex coordinates from LibMesh + std::array tet_verts; + for (int i = 0; i < elem.n_nodes(); i++) { + auto node_ref = elem.node_ref(i); + tet_verts[i] = {node_ref(0), node_ref(1), node_ref(2)}; + } + // Samples position within tet using Barycentric coordinates + return this->sample_tet(tet_verts, seed); } Position LibMesh::centroid(int bin) const { const auto& elem = this->get_element_from_bin(bin); - auto centroid = elem.centroid(); + auto centroid = elem.vertex_average(); return {centroid(0), centroid(1), centroid(2)}; } +int LibMesh::n_vertices() const +{ + return m_->n_nodes(); +} + +Position LibMesh::vertex(int vertex_id) const +{ + const auto node_ref = m_->node_ref(vertex_id); + return {node_ref(0), node_ref(1), node_ref(2)}; +} + +std::vector LibMesh::connectivity(int elem_id) const +{ + std::vector conn; + const auto* elem_ptr = m_->elem_ptr(elem_id); + for (int i = 0; i < elem_ptr->n_nodes(); i++) { + conn.push_back(elem_ptr->node_id(i)); + } + return conn; +} + std::string LibMesh::library() const { return mesh_lib_type; @@ -2434,6 +3385,10 @@ int LibMesh::n_surface_bins() const void LibMesh::add_score(const std::string& var_name) { + if (!equation_systems_) { + build_eqn_sys(); + } + // check if this is a new variable std::string value_name = var_name + "_mean"; if (!variable_map_.count(value_name)) { @@ -2455,14 +3410,20 @@ void LibMesh::add_score(const std::string& var_name) void LibMesh::remove_scores() { - auto& eqn_sys = equation_systems_->get_system(eq_system_name_); - eqn_sys.clear(); - variable_map_.clear(); + if (equation_systems_) { + auto& eqn_sys = equation_systems_->get_system(eq_system_name_); + eqn_sys.clear(); + variable_map_.clear(); + } } void LibMesh::set_score_data(const std::string& var_name, const vector& values, const vector& std_dev) { + if (!equation_systems_) { + build_eqn_sys(); + } + auto& eqn_sys = equation_systems_->get_system(eq_system_name_); if (!eqn_sys.is_initialized()) { @@ -2480,18 +3441,22 @@ void LibMesh::set_score_data(const std::string& var_name, for (auto it = m_->local_elements_begin(); it != m_->local_elements_end(); it++) { + if (!(*it)->active()) { + continue; + } + auto bin = get_bin_from_element(*it); // set value vector value_dof_indices; dof_map.dof_indices(*it, value_dof_indices, value_num); - Ensures(value_dof_indices.size() == 1); + assert(value_dof_indices.size() == 1); eqn_sys.solution->set(value_dof_indices[0], values.at(bin)); // set std dev vector std_dev_dof_indices; dof_map.dof_indices(*it, std_dev_dof_indices, std_dev_num); - Ensures(std_dev_dof_indices.size() == 1); + assert(std_dev_dof_indices.size() == 1); eqn_sys.solution->set(std_dev_dof_indices[0], std_dev.at(bin)); } } @@ -2523,13 +3488,7 @@ int LibMesh::get_bin(Position r) const return -1; } -#ifdef _OPENMP - int thread_num = omp_get_thread_num(); -#else - int thread_num = 0; -#endif - - const auto& point_locator = pl_.at(thread_num); + const auto& point_locator = pl_.at(thread_num()); const auto elem_ptr = (*point_locator)(p); return elem_ptr ? get_bin_from_element(elem_ptr) : -1; @@ -2557,10 +3516,69 @@ const libMesh::Elem& LibMesh::get_element_from_bin(int bin) const double LibMesh::volume(int bin) const { - return m_->elem_ref(bin).volume(); + return this->get_element_from_bin(bin).volume(); } -#endif // LIBMESH +AdaptiveLibMesh::AdaptiveLibMesh( + libMesh::MeshBase& input_mesh, double length_multiplier) + : LibMesh(input_mesh, length_multiplier), num_active_(m_->n_active_elem()) +{ + // if the mesh is adaptive elements aren't guaranteed by libMesh to be + // contiguous in ID space, so we need to map from bin indices (defined over + // active elements) to global dof ids + bin_to_elem_map_.reserve(num_active_); + elem_to_bin_map_.resize(m_->n_elem(), -1); + for (auto it = m_->active_elements_begin(); it != m_->active_elements_end(); + it++) { + auto elem = *it; + + bin_to_elem_map_.push_back(elem->id()); + elem_to_bin_map_[elem->id()] = bin_to_elem_map_.size() - 1; + } +} + +int AdaptiveLibMesh::n_bins() const +{ + return num_active_; +} + +void AdaptiveLibMesh::add_score(const std::string& var_name) +{ + warning(fmt::format( + "Exodus output cannot be provided as unstructured mesh {} is adaptive.", + this->id_)); +} + +void AdaptiveLibMesh::set_score_data(const std::string& var_name, + const vector& values, const vector& std_dev) +{ + warning(fmt::format( + "Exodus output cannot be provided as unstructured mesh {} is adaptive.", + this->id_)); +} + +void AdaptiveLibMesh::write(const std::string& filename) const +{ + warning(fmt::format( + "Exodus output cannot be provided as unstructured mesh {} is adaptive.", + this->id_)); +} + +int AdaptiveLibMesh::get_bin_from_element(const libMesh::Elem* elem) const +{ + int bin = elem_to_bin_map_[elem->id()]; + if (bin >= n_bins() || bin < 0) { + fatal_error(fmt::format("Invalid bin: {}", bin)); + } + return bin; +} + +const libMesh::Elem& AdaptiveLibMesh::get_element_from_bin(int bin) const +{ + return m_->elem_ref(bin_to_elem_map_.at(bin)); +} + +#endif // OPENMC_LIBMESH_ENABLED //============================================================================== // Non-member functions @@ -2568,7 +3586,25 @@ double LibMesh::volume(int bin) const void read_meshes(pugi::xml_node root) { + std::unordered_set mesh_ids; + for (auto node : root.children("mesh")) { + // Check to make sure multiple meshes in the same file don't share IDs + int id = std::stoi(get_node_value(node, "id")); + if (contains(mesh_ids, id)) { + fatal_error(fmt::format("Two or more meshes use the same unique ID " + "'{}' in the same input file", + id)); + } + mesh_ids.insert(id); + + // If we've already read a mesh with the same ID in a *different* file, + // assume it is the same here + if (model::mesh_map.find(id) != model::mesh_map.end()) { + warning(fmt::format("Mesh with ID={} appears in multiple files.", id)); + continue; + } + std::string mesh_type; if (check_for_node(node, "type")) { mesh_type = get_node_value(node, "type", true, true); @@ -2591,12 +3627,12 @@ void read_meshes(pugi::xml_node root) model::meshes.push_back(make_unique(node)); } else if (mesh_type == SphericalMesh::mesh_type) { model::meshes.push_back(make_unique(node)); -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED } else if (mesh_type == UnstructuredMesh::mesh_type && mesh_lib == MOABMesh::mesh_lib_type) { model::meshes.push_back(make_unique(node)); #endif -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED } else if (mesh_type == UnstructuredMesh::mesh_type && mesh_lib == LibMesh::mesh_lib_type) { model::meshes.push_back(make_unique(node)); diff --git a/src/message_passing.cpp b/src/message_passing.cpp index f877820831..374c1aa725 100644 --- a/src/message_passing.cpp +++ b/src/message_passing.cpp @@ -17,6 +17,27 @@ extern "C" bool openmc_master() return mpi::master; } +vector calculate_parallel_index_vector(int64_t size) +{ + vector result; + result.resize(n_procs + 1); + result[0] = 0; + +#ifdef OPENMC_MPI + + // Populate the result with cumulative sum of the number of + // surface source banks per process + int64_t scan_total; + MPI_Scan(&size, &scan_total, 1, MPI_INT64_T, MPI_SUM, intracomm); + MPI_Allgather( + &scan_total, 1, MPI_INT64_T, result.data() + 1, 1, MPI_INT64_T, intracomm); +#else + result[1] = size; +#endif + + return result; +} + } // namespace mpi } // namespace openmc diff --git a/src/mgxs.cpp b/src/mgxs.cpp index 472beda350..a2c479f215 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -5,10 +5,6 @@ #include #include -#ifdef _OPENMP -#include -#endif - #include "xtensor/xadapt.hpp" #include "xtensor/xmath.hpp" #include "xtensor/xsort.hpp" @@ -46,15 +42,6 @@ void Mgxs::init(const std::string& in_name, double in_awr, n_azi = in_azimuthal.size(); polar = in_polar; azimuthal = in_azimuthal; - - // Set the cross section index cache -#ifdef _OPENMP - int n_threads = omp_get_max_threads(); -#else - int n_threads = 1; -#endif - cache.resize(n_threads); - // vector.resize() will value-initialize the members of cache[:] } //============================================================================== @@ -85,18 +72,18 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, } get_datasets(kT_group, dset_names); vector shape = {num_temps}; - xt::xarray available_temps(shape); + xt::xarray temps_available(shape); for (int i = 0; i < num_temps; i++) { - read_double(kT_group, dset_names[i], &available_temps[i], true); + read_double(kT_group, dset_names[i], &temps_available[i], true); // convert eV to Kelvin - available_temps[i] /= K_BOLTZMANN; + temps_available[i] = std::round(temps_available[i] / K_BOLTZMANN); // Done with dset_names, so delete it delete[] dset_names[i]; } delete[] dset_names; - std::sort(available_temps.begin(), available_temps.end()); + std::sort(temps_available.begin(), temps_available.end()); // If only one temperature is available, lets just use nearest temperature // interpolation @@ -112,17 +99,34 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, case TemperatureMethod::NEAREST: // Determine actual temperatures to read for (const auto& T : temperature) { - auto i_closest = xt::argmin(xt::abs(available_temps - T))[0]; - double temp_actual = available_temps[i_closest]; + // Determine the closest temperature value + // NOTE: the below block could be replaced with the following line, + // though this gives a runtime error if using LLVM 20 or newer, + // likely due to a bug in xtensor. + // auto i_closest = xt::argmin(xt::abs(temps_available - T))[0]; + double closest = std::numeric_limits::max(); + int i_closest = 0; + for (int i = 0; i < temps_available.size(); i++) { + double diff = std::abs(temps_available[i] - T); + if (diff < closest) { + closest = diff; + i_closest = i; + } + } + + double temp_actual = temps_available[i_closest]; if (std::fabs(temp_actual - T) < settings::temperature_tolerance) { if (std::find(temps_to_read.begin(), temps_to_read.end(), std::round(temp_actual)) == temps_to_read.end()) { temps_to_read.push_back(std::round(temp_actual)); } } else { - fatal_error(fmt::format("MGXS library does not contain cross sections " - "for {} at or near {} K.", - in_name, std::round(T))); + fatal_error(fmt::format( + "MGXS library does not contain cross sections " + "for {} at or near {} K. Available temperatures " + "are {} K. Consider making use of openmc.Settings.temperature " + "to specify how intermediate temperatures are treated.", + in_name, std::round(T), concatenate(temps_available))); } } break; @@ -135,16 +139,16 @@ void Mgxs::metadata_from_hdf5(hid_t xs_id, const vector& temperature, in_name + " at temperatures that bound " + std::to_string(std::round(temperature[i]))); } - if ((available_temps[j] <= temperature[i]) && - (temperature[i] < available_temps[j + 1])) { + if ((temps_available[j] <= temperature[i]) && + (temperature[i] < temps_available[j + 1])) { if (std::find(temps_to_read.begin(), temps_to_read.end(), - std::round(available_temps[j])) == temps_to_read.end()) { - temps_to_read.push_back(std::round((int)available_temps[j])); + temps_available[j]) == temps_to_read.end()) { + temps_to_read.push_back(temps_available[j]); } if (std::find(temps_to_read.begin(), temps_to_read.end(), - std::round(available_temps[j + 1])) == temps_to_read.end()) { - temps_to_read.push_back(std::round((int)available_temps[j + 1])); + temps_available[j + 1]) == temps_to_read.end()) { + temps_to_read.push_back(temps_available[j + 1]); } break; } @@ -425,18 +429,10 @@ void Mgxs::combine(const vector& micros, const vector& scalars, //============================================================================== -double Mgxs::get_xs( - MgxsType xstype, int gin, const int* gout, const double* mu, const int* dg) +double Mgxs::get_xs(MgxsType xstype, int gin, const int* gout, const double* mu, + const int* dg, int t, int a) { - // This method assumes that the temperature and angle indices are set -#ifdef _OPENMP - int tid = omp_get_thread_num(); - XsData* xs_t = &xs[cache[tid].t]; - int a = cache[tid].a; -#else - XsData* xs_t = &xs[cache[0].t]; - int a = cache[0].a; -#endif + XsData* xs_t = &xs[t]; double val; switch (xstype) { case MgxsType::TOTAL: @@ -538,19 +534,14 @@ double Mgxs::get_xs( //============================================================================== -void Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed) +void Mgxs::sample_fission_energy( + int gin, int& dg, int& gout, uint64_t* seed, int t, int a) { - // This method assumes that the temperature and angle indices are set -#ifdef _OPENMP - int tid = omp_get_thread_num(); -#else - int tid = 0; -#endif - XsData* xs_t = &xs[cache[tid].t]; - double nu_fission = xs_t->nu_fission(cache[tid].a, gin); + XsData* xs_t = &xs[t]; + double nu_fission = xs_t->nu_fission(a, gin); // Find the probability of having a prompt neutron - double prob_prompt = xs_t->prompt_nu_fission(cache[tid].a, gin); + double prob_prompt = xs_t->prompt_nu_fission(a, gin); // sample random numbers double xi_pd = prn(seed) * nu_fission; @@ -566,7 +557,7 @@ void Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed) // sample the outgoing energy group double prob_gout = 0.; for (gout = 0; gout < num_groups; ++gout) { - prob_gout += xs_t->chi_prompt(cache[tid].a, gin, gout); + prob_gout += xs_t->chi_prompt(a, gin, gout); if (xi_gout < prob_gout) break; } @@ -576,7 +567,7 @@ void Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed) // get the delayed group for (dg = 0; dg < num_delayed_groups; ++dg) { - prob_prompt += xs_t->delayed_nu_fission(cache[tid].a, dg, gin); + prob_prompt += xs_t->delayed_nu_fission(a, dg, gin); if (xi_pd < prob_prompt) break; } @@ -587,7 +578,7 @@ void Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed) // sample the outgoing energy group double prob_gout = 0.; for (gout = 0; gout < num_groups; ++gout) { - prob_gout += xs_t->chi_delayed(cache[tid].a, dg, gin, gout); + prob_gout += xs_t->chi_delayed(a, dg, gin, gout); if (xi_gout < prob_gout) break; } @@ -597,35 +588,41 @@ void Mgxs::sample_fission_energy(int gin, int& dg, int& gout, uint64_t* seed) //============================================================================== void Mgxs::sample_scatter( - int gin, int& gout, double& mu, double& wgt, uint64_t* seed) + int gin, int& gout, double& mu, double& wgt, uint64_t* seed, int t, int a) { - // This method assumes that the temperature and angle indices are set // Sample the data -#ifdef _OPENMP - int tid = omp_get_thread_num(); -#else - int tid = 0; -#endif - xs[cache[tid].t].scatter[cache[tid].a]->sample(gin, gout, mu, wgt, seed); + xs[t].scatter[a]->sample(gin, gout, mu, wgt, seed); } //============================================================================== void Mgxs::calculate_xs(Particle& p) { - // Set our indices -#ifdef _OPENMP - int tid = omp_get_thread_num(); -#else - int tid = 0; -#endif - set_temperature_index(p.sqrtkT()); - set_angle_index(p.u_local()); - XsData* xs_t = &xs[cache[tid].t]; - p.macro_xs().total = xs_t->total(cache[tid].a, p.g()); - p.macro_xs().absorption = xs_t->absorption(cache[tid].a, p.g()); + // If the material is different, then we need to do a full lookup + if (p.material() != p.mg_xs_cache().material) { + set_temperature_index(p); + set_angle_index(p); + p.mg_xs_cache().material = p.material(); + } else { + // If material is the same, but temperature is different, need to + // find the new temperature index + if (p.sqrtkT() != p.mg_xs_cache().sqrtkT) { + set_temperature_index(p); + } + // If the material is the same, but angle is different, need to + // find the new angle index + if (p.u_local() != p.mg_xs_cache().u) { + set_angle_index(p); + } + } + int temperature = p.mg_xs_cache().t; + int angle = p.mg_xs_cache().a; + p.macro_xs().total = xs[temperature].total(angle, p.g()) * p.density_mult(); + p.macro_xs().absorption = + xs[temperature].absorption(angle, p.g()) * p.density_mult(); p.macro_xs().nu_fission = - fissionable ? xs_t->nu_fission(cache[tid].a, p.g()) : 0.; + fissionable ? xs[temperature].nu_fission(angle, p.g()) * p.density_mult() + : 0.; } //============================================================================== @@ -643,32 +640,26 @@ bool Mgxs::equiv(const Mgxs& that) //============================================================================== -void Mgxs::set_temperature_index(double sqrtkT) +int Mgxs::get_temperature_index(double sqrtkT) const { - // See if we need to find the new index -#ifdef _OPENMP - int tid = omp_get_thread_num(); -#else - int tid = 0; -#endif - if (sqrtkT != cache[tid].sqrtkT) { - cache[tid].t = xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0]; - cache[tid].sqrtkT = sqrtkT; - } + return xt::argmin(xt::abs(kTs - sqrtkT * sqrtkT))[0]; } //============================================================================== -void Mgxs::set_angle_index(Direction u) +void Mgxs::set_temperature_index(Particle& p) { - // See if we need to find the new index -#ifdef _OPENMP - int tid = omp_get_thread_num(); -#else - int tid = 0; -#endif - if (!is_isotropic && ((u.x != cache[tid].u) || (u.y != cache[tid].v) || - (u.z != cache[tid].w))) { + p.mg_xs_cache().t = get_temperature_index(p.sqrtkT()); + p.mg_xs_cache().sqrtkT = p.sqrtkT(); +} + +//============================================================================== + +int Mgxs::get_angle_index(const Direction& u) const +{ + if (is_isotropic) { + return 0; + } else { // convert direction to polar and azimuthal angles double my_pol = std::acos(u.z); double my_azi = std::atan2(u.y, u.x); @@ -679,12 +670,18 @@ void Mgxs::set_angle_index(Direction u) delta_angle = 2. * PI / n_azi; int a = std::floor((my_azi + PI) / delta_angle); - cache[tid].a = n_azi * p + a; + return n_azi * p + a; + } +} - // store this direction as the last one used - cache[tid].u = u.x; - cache[tid].v = u.y; - cache[tid].w = u.z; +//============================================================================== + +void Mgxs::set_angle_index(Particle& p) +{ + // See if we need to find the new index + if (!is_isotropic) { + p.mg_xs_cache().a = get_angle_index(p.u_local()); + p.mg_xs_cache().u = p.u_local(); } } diff --git a/src/mgxs_interface.cpp b/src/mgxs_interface.cpp index 77085d57c8..ed734d401e 100644 --- a/src/mgxs_interface.cpp +++ b/src/mgxs_interface.cpp @@ -15,6 +15,7 @@ #include "openmc/material.h" #include "openmc/math_functions.h" #include "openmc/nuclide.h" +#include "openmc/search.h" #include "openmc/settings.h" namespace openmc { @@ -145,7 +146,7 @@ void MgxsInterface::create_macro_xs() num_energy_groups_, num_delayed_groups_); } else { // Preserve the ordering of materials by including a blank entry - macro_xs_.emplace_back(); + macro_xs_.emplace_back(false); } } } @@ -183,6 +184,15 @@ vector> MgxsInterface::get_mat_kTs() //============================================================================== +int MgxsInterface::get_group_index(double E) +{ + int g = + lower_bound_index(rev_energy_bins_.begin(), rev_energy_bins_.end(), E); + return num_energy_groups_ - g - 1.; +} + +//============================================================================== + void MgxsInterface::read_header(const std::string& path_cross_sections) { // Save name of HDF5 file to be read to struct data @@ -284,7 +294,7 @@ void mark_fissionable_mgxs_materials() for (const auto& mat : model::materials) { for (int i_nuc : mat->nuclide_) { if (data::mg.nuclides_[i_nuc].fissionable) { - mat->fissionable_ = true; + mat->fissionable() = true; } } } diff --git a/src/ncrystal_interface.cpp b/src/ncrystal_interface.cpp new file mode 100644 index 0000000000..935d2b8850 --- /dev/null +++ b/src/ncrystal_interface.cpp @@ -0,0 +1,53 @@ +#include "openmc/ncrystal_interface.h" + +#include "openmc/error.h" +#include "openmc/material.h" +#include "openmc/random_lcg.h" + +namespace openmc { + +//============================================================================== +// NCrystalMat implementation +//============================================================================== + +NCrystalMat::NCrystalMat(const std::string& cfg) : cfg_(cfg), proc_(cfg.c_str()) +{} + +double NCrystalMat::xs(const Particle& p) const +{ + // Calculate scattering XS per atom with NCrystal, only once per material + double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z}; + return proc_.cross_section(neutron_state); +} + +void NCrystalMat::scatter(Particle& p) const +{ + // Scatter with NCrystal, using the OpenMC RNG stream: + uint64_t* seed = p.current_seed(); + std::function rng = [&seed]() { return prn(seed); }; + double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z}; + proc_.scatter(rng, neutron_state); + // Modify attributes of particle + p.E() = neutron_state[0]; + Direction u_old {p.u()}; + p.u() = Direction(neutron_state[1], neutron_state[2], neutron_state[3]); + p.mu() = u_old.dot(p.u()); + p.event_mt() = ELASTIC; +} + +//============================================================================== +// Functions +//============================================================================== + +void ncrystal_update_micro(double xs, NuclideMicroXS& micro) +{ + if (micro.thermal > 0 || micro.thermal_elastic > 0) { + fatal_error("S(a,b) treatment and NCrystal are not compatible."); + } + // remove free atom cross section + // and replace it by scattering cross section per atom from NCrystal + micro.total = micro.total - micro.elastic + xs; + micro.elastic = xs; +} + +} // namespace openmc diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp new file mode 100644 index 0000000000..b69f3a27f4 --- /dev/null +++ b/src/ncrystal_load.cpp @@ -0,0 +1,151 @@ +#include "openmc/ncrystal_load.h" + +#include // for isspace +#include // for strtoul +#include // for shared_ptr +#include // for mutex, lock_guard +#include + +#include +#include // for popen, pclose + +#include "openmc/error.h" + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // for LoadLibrary, GetProcAddress +#else +#include // for dlopen, dlsym, dlerror +#endif + +namespace openmc { +namespace { + +struct NCrystalConfig { + std::string shlibpath; + unsigned long intversion = 0; + std::string symbol_namespace; +}; + +NCrystalConfig query_ncrystal_config() +{ +#ifdef _WIN32 + FILE* pipe = _popen("ncrystal-config --show " + "intversion shlibpath namespace", + "r"); +#else + FILE* pipe = popen("ncrystal-config --show " + "intversion shlibpath namespace 2>/dev/null", + "r"); +#endif + if (!pipe) + return {}; // failure + auto readLine = [pipe](std::string& tgt) -> bool { + // Read line and discard trailing whitespace (including newline chars). + char buffer[4096]; + if (fgets(buffer, sizeof(buffer), pipe) == NULL) + return false; + tgt = buffer; + while (!tgt.empty() && std::isspace(tgt.back())) + tgt.pop_back(); + return true; + }; + auto parseIntVersion = [](const std::string& s) { + char* str_end = nullptr; + unsigned long v = std::strtoul(s.c_str(), &str_end, 10); + return (v >= 2002000 && v < 999999999 && str_end == s.c_str() + s.size()) + ? v + : 0; + }; + + NCrystalConfig res; + if (!readLine(res.shlibpath) || + !(res.intversion = parseIntVersion(res.shlibpath)) || + !readLine(res.shlibpath) || res.shlibpath.empty() || + !readLine(res.symbol_namespace)) { + res.intversion = 0; // failure + } + +#ifdef _WIN32 + auto returnCode = _pclose(pipe); +#else + auto returnCode = pclose(pipe); +#endif + if (returnCode == 0 && res.intversion >= 2002000) + return res; + return {}; // failure +} + +struct NCrystalAPIDB { + std::mutex mtx; + std::shared_ptr api; + using FctSignature = void* (*)(int); + FctSignature ncrystal_access_virtapi_fct = nullptr; +}; + +void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) +{ + if (!db.ncrystal_access_virtapi_fct) { + auto cfg = query_ncrystal_config(); + if (!(cfg.intversion >= 4001000)) { + // This is the most likely error message people will see: + fatal_error("Could not locate a functioning and recent enough" + " NCrystal installation (required since geometry" + " contains NCrystal materials)."); + } +#ifdef _WIN32 + auto handle = LoadLibrary(cfg.shlibpath.c_str()); +#else + dlerror(); // clear previous errors + void* handle = dlopen(cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY); +#endif + if (!handle) + fatal_error("Loading of the NCrystal library failed"); + + std::string symbol = + fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace); + +#ifdef _WIN32 + void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); + if (!addr) + fatal_error("GetProcAddress(" + "ncrystal_access_virtual_api) failed"); +#else + dlerror(); // clear previous errors + void* addr = dlsym(handle, symbol.c_str()); + if (!addr) + fatal_error("dlsym(ncrystal_access_virtual_api) failed"); +#endif + db.ncrystal_access_virtapi_fct = + reinterpret_cast(addr); + } + + void* result = (*db.ncrystal_access_virtapi_fct)(interface_id); + if (!result) + fatal_error("NCrystal installation does not support required interface."); + + return result; +} + +NCrystalAPIDB& get_ncrystal_api_db() +{ + static NCrystalAPIDB db; + return db; +} +} // namespace + +std::shared_ptr load_ncrystal_api() +{ + auto& db = get_ncrystal_api_db(); + std::lock_guard lock(db.mtx); + if (!db.api) { + void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db); + if (!raw_api) + fatal_error("Problems loading NCrystal."); + db.api = *reinterpret_cast*>(raw_api); + } + return db.api; +} +} // namespace openmc diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 564e27d446..5ae6e30ee2 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -21,7 +21,8 @@ #include "xtensor/xview.hpp" #include // for sort, min_element -#include // for to_string, stoi +#include +#include // for to_string, stoi namespace openmc { @@ -30,8 +31,8 @@ namespace openmc { //============================================================================== namespace data { -array energy_min {0.0, 0.0}; -array energy_max {INFTY, INFTY}; +array energy_min {0.0, 0.0, 0.0, 0.0}; +array energy_max {INFTY, INFTY, INFTY, INFTY}; double temperature_min {INFTY}; double temperature_max {0.0}; std::unordered_map nuclide_map; @@ -62,6 +63,23 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) read_attribute(group, "metastable", metastable_); read_attribute(group, "atomic_weight_ratio", awr_); + if (settings::run_mode == RunMode::VOLUME) { + // Determine whether nuclide is fissionable and then exit + int mt; + hid_t rxs_group = open_group(group, "reactions"); + for (auto name : group_names(rxs_group)) { + if (starts_with(name, "reaction_")) { + hid_t rx_group = open_group(rxs_group, name.c_str()); + read_attribute(rx_group, "mt", mt); + if (is_fission(mt)) { + fissionable_ = true; + break; + } + } + } + return; + } + // Determine temperatures available hid_t kT_group = open_group(group, "kTs"); auto dset_names = dataset_names(kT_group); @@ -69,7 +87,7 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) for (const auto& name : dset_names) { double T; read_dataset(kT_group, name.c_str(), T); - temps_available.push_back(T / K_BOLTZMANN); + temps_available.push_back(std::round(T / K_BOLTZMANN)); } std::sort(temps_available.begin(), temps_available.end()); @@ -141,9 +159,12 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) } } } else { - fatal_error( - "Nuclear data library does not contain cross sections for " + name_ + - " at or near " + std::to_string(T_desired) + " K."); + fatal_error(fmt::format( + "Nuclear data library does not contain cross sections " + "for {} at or near {} K. Available temperatures " + "are {} K. Consider making use of openmc.Settings.temperature " + "to specify how intermediate temperatures are treated.", + name_, std::to_string(T_desired), concatenate(temps_available))); } } break; @@ -156,8 +177,8 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) for (int j = 0; j < temps_available.size() - 1; ++j) { if (temps_available[j] <= T_desired && T_desired < temps_available[j + 1]) { - int T_j = std::round(temps_available[j]); - int T_j1 = std::round(temps_available[j + 1]); + int T_j = temps_available[j]; + int T_j1 = temps_available[j + 1]; if (!contains(temps_to_read, T_j)) { temps_to_read.push_back(T_j); } @@ -169,6 +190,22 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) } if (!found_pair) { + // If no pairs found, check if the desired temperature falls just + // outside of data + if (std::abs(T_desired - temps_available.front()) <= + settings::temperature_tolerance) { + if (!contains(temps_to_read, temps_available.front())) { + temps_to_read.push_back(temps_available.front()); + } + continue; + } + if (std::abs(T_desired - temps_available.back()) <= + settings::temperature_tolerance) { + if (!contains(temps_to_read, temps_available.back())) { + temps_to_read.push_back(temps_available.back()); + } + continue; + } fatal_error( "Nuclear data library does not contain cross sections for " + name_ + " at temperatures that bound " + std::to_string(T_desired) + " K."); @@ -211,7 +248,8 @@ Nuclide::Nuclide(hid_t group, const vector& temperature) for (auto name : group_names(rxs_group)) { if (starts_with(name, "reaction_")) { hid_t rx_group = open_group(rxs_group, name.c_str()); - reactions_.push_back(make_unique(rx_group, temps_to_read)); + reactions_.push_back( + make_unique(rx_group, temps_to_read, name_)); // Check for 0K elastic scattering const auto& rx = reactions_.back(); @@ -338,15 +376,15 @@ void Nuclide::create_derived( int j = rx->xs_[t].threshold; int n = rx->xs_[t].value.size(); auto xs = xt::adapt(rx->xs_[t].value); + auto pprod = xt::view(xs_[t], xt::range(j, j + n), XS_PHOTON_PROD); for (const auto& p : rx->products_) { if (p.particle_ == ParticleType::photon) { - auto pprod = xt::view(xs_[t], xt::range(j, j + n), XS_PHOTON_PROD); for (int k = 0; k < n; ++k) { double E = grid_[t].energy[k + j]; - // For fission, artificially increase the photon yield to account - // for delayed photons + // For fission, artificially increase the photon yield to + // account for delayed photons double f = 1.0; if (settings::delayed_photon_scaling) { if (is_fission(rx->mt_)) { @@ -432,8 +470,8 @@ void Nuclide::create_derived( } } } else { - // Otherwise, assume that any that have 0 K elastic scattering data are - // resonant + // Otherwise, assume that any that have 0 K elastic scattering data + // are resonant resonant_ = !energy_0K_.empty(); } @@ -455,7 +493,7 @@ void Nuclide::create_derived( xs_cdf_sum += (std::sqrt(E[i]) * xs[i] + std::sqrt(E[i + 1]) * xs[i + 1]) / 2.0 * (E[i + 1] - E[i]); - xs_cdf_[i+1] = xs_cdf_sum; + xs_cdf_[i + 1] = xs_cdf_sum; } } } @@ -503,7 +541,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const case EmissionMode::prompt: return (*fission_rx_[0]->products_[0].yield_)(E); case EmissionMode::delayed: - if (n_precursor_ > 0) { + if (n_precursor_ > 0 && settings::create_delayed_neutrons) { auto rx = fission_rx_[0]; if (group >= 1 && group < rx->products_.size()) { // If delayed group specified, determine yield immediately @@ -528,7 +566,7 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const return 0.0; } case EmissionMode::total: - if (total_nu_) { + if (total_nu_ && settings::create_delayed_neutrons) { return (*total_nu_)(E); } else { return (*fission_rx_[0]->products_[0].yield_)(E); @@ -614,16 +652,23 @@ void Nuclide::calculate_xs( } } - // Ensure these values are set - // Note, the only time either is used is in one of 4 places: - // 1. physics.cpp - scatter - For inelastic scatter. - // 2. physics.cpp - sample_fission - For partial fissions. - // 3. tally.F90 - score_general - For tallying on MTxxx reactions. - // 4. nuclide.cpp - calculate_urr_xs - For unresolved purposes. - // It is worth noting that none of these occur in the resolved - // resonance range, so the value here does not matter. index_temp is - // set to -1 to force a segfault in case a developer messes up and tries - // to use it with multipole. + /* + * index_temp, index_grid, and interp_factor are used only in the + * following places: + * 1. physics.cpp - scatter - For inelastic scatter. + * 2. physics.cpp - sample_fission - For partial fissions. + * 3. tallies/tally_scoring.cpp - score_general - + * For tallying on MTxxx reactions. + * 4. nuclide.cpp - calculate_urr_xs - For unresolved purposes. + * It is worth noting that none of these occur in the resolved resonance + * range, so the value here does not matter. index_temp is set to -1 to + * force a segfault in case a developer messes up and tries to use it with + * multipole. + * + * However, a segfault is not necessarily guaranteed with an out-of-bounds + * access, so this technique should be replaced by something more robust + * in the future. + */ micro.index_temp = -1; micro.index_grid = -1; micro.interp_factor = 0.0; @@ -646,6 +691,16 @@ void Nuclide::calculate_xs( } break; case TemperatureMethod::INTERPOLATION: + // If current kT outside of the bounds of available, snap to the bound + if (kT < kTs_.front()) { + i_temp = 0; + break; + } + if (kT > kTs_.back()) { + i_temp = kTs_.size() - 1; + break; + } + // Find temperatures that bound the actual temperature for (i_temp = 0; i_temp < kTs_.size() - 1; ++i_temp) { if (kTs_[i_temp] <= kT && kT < kTs_[i_temp + 1]) @@ -727,8 +782,8 @@ void Nuclide::calculate_xs( } for (int j = 0; j < DEPLETION_RX.size(); ++j) { - // If reaction is present and energy is greater than threshold, set the - // reaction xs appropriately + // If reaction is present and energy is greater than threshold, set + // the reaction xs appropriately int i_rx = reaction_index_[DEPLETION_RX[j]]; if (i_rx >= 0) { const auto& rx = reactions_[i_rx]; @@ -765,9 +820,9 @@ void Nuclide::calculate_xs( // Initialize URR probability table treatment to false micro.use_ptable = false; - // If there is S(a,b) data for this nuclide, we need to set the sab_scatter - // and sab_elastic cross sections and correct the total and elastic cross - // sections. + // If there is S(a,b) data for this nuclide, we need to set the + // sab_scatter and sab_elastic cross sections and correct the total and + // elastic cross sections. if (i_sab >= 0) this->calculate_sab_xs(i_sab, sab_frac, p); @@ -831,9 +886,8 @@ void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const // This guarantees the randomness and, at the same time, makes sure we // reuse random numbers for the same nuclide at different temperatures, // therefore preserving correlation of temperature in probability tables. - p.stream() = STREAM_URR_PTABLE; - double r = future_prn(static_cast(index_), *p.current_seed()); - p.stream() = STREAM_TRACKING; + double r = + future_prn(static_cast(index_), p.seeds(STREAM_URR_PTABLE)); // Warning: this assumes row-major order of cdf_values_ int i_low = upper_bound_index(&urr.cdf_values_(i_energy, 0), @@ -931,8 +985,8 @@ void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const } // Set elastic, absorption, fission, total, and capture x/s. Note that the - // total x/s is calculated as a sum of partials instead of the table-provided - // value + // total x/s is calculated as a sum of partials instead of the + // table-provided value micro.elastic = elastic; micro.absorption = capture + fission; micro.fission = fission; @@ -947,19 +1001,19 @@ void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const } } -std::pair Nuclide::find_temperature(double T) const +std::pair Nuclide::find_temperature(double T) const { - Expects(T >= 0.0); + assert(T >= 0.0); // Determine temperature index - gsl::index i_temp = 0; + int64_t i_temp = 0; double f = 0.0; double kT = K_BOLTZMANN * T; - gsl::index n = kTs_.size(); + int64_t n = kTs_.size(); switch (settings::temperature_method) { case TemperatureMethod::NEAREST: { double max_diff = INFTY; - for (gsl::index t = 0; t < n; ++t) { + for (int64_t t = 0; t < n; ++t) { double diff = std::abs(kTs_[t] - kT); if (diff < max_diff) { i_temp = t; @@ -969,6 +1023,15 @@ std::pair Nuclide::find_temperature(double T) const } break; case TemperatureMethod::INTERPOLATION: + // If current kT outside of the bounds of available, snap to the bound + if (kT < kTs_.front()) { + i_temp = 0; + break; + } + if (kT > kTs_.back()) { + i_temp = kTs_.size() - 1; + break; + } // Find temperatures that bound the actual temperature while (kTs_[i_temp + 1] < kT && i_temp + 1 < n - 1) ++i_temp; @@ -977,17 +1040,17 @@ std::pair Nuclide::find_temperature(double T) const f = (kT - kTs_[i_temp]) / (kTs_[i_temp + 1] - kTs_[i_temp]); } - Ensures(i_temp >= 0 && i_temp < n); + assert(i_temp >= 0 && i_temp < n); return {i_temp, f}; } double Nuclide::collapse_rate(int MT, double temperature, - gsl::span energy, gsl::span flux) const + span energy, span flux) const { - Expects(MT > 0); - Expects(energy.size() > 0); - Expects(energy.size() == flux.size() + 1); + assert(MT > 0); + assert(energy.size() > 0); + assert(energy.size() == flux.size() + 1); int i_rx = reaction_index_[MT]; if (i_rx < 0) @@ -995,7 +1058,7 @@ double Nuclide::collapse_rate(int MT, double temperature, const auto& rx = reactions_[i_rx]; // Determine temperature index - gsl::index i_temp; + int64_t i_temp; double f; std::tie(i_temp, f) = this->find_temperature(temperature); @@ -1051,7 +1114,7 @@ extern "C" size_t nuclides_size() extern "C" int openmc_load_nuclide(const char* name, const double* temps, int n) { if (data::nuclide_map.find(name) == data::nuclide_map.end() || - data::nuclide_map.at(name) >= data::elements.size()) { + data::nuclide_map.at(name) >= data::nuclides.size()) { LibraryKey key {Library::Type::neutron, name}; const auto& it = data::library_map.find(key); if (it == data::library_map.end()) { @@ -1152,7 +1215,6 @@ extern "C" int openmc_nuclide_collapse_rate(int index, int MT, *xs = data::nuclides[index]->collapse_rate( MT, temperature, {energy, energy + n + 1}, {flux, flux + n}); } catch (const std::out_of_range& e) { - fmt::print("Caught error\n"); set_errmsg(e.what()); return OPENMC_E_OUT_OF_BOUNDS; } diff --git a/src/output.cpp b/src/output.cpp index 401ad8338d..0a14e8843d 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,6 +1,7 @@ #include "openmc/output.h" #include // for transform, max +#include // for stdout #include // for strlen #include // for time, localtime #include @@ -30,6 +31,7 @@ #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" #include "openmc/plot.h" +#include "openmc/random_ray/flat_source_domain.h" #include "openmc/reaction.h" #include "openmc/settings.h" #include "openmc/simulation.h" @@ -73,13 +75,12 @@ void title() // Write version information fmt::print( " | The OpenMC Monte Carlo Code\n" - " Copyright | 2011-2022 MIT, UChicago Argonne LLC, and contributors\n" + " Copyright | 2011-2025 MIT, UChicago Argonne LLC, and contributors\n" " License | https://docs.openmc.org/en/latest/license.html\n" - " Version | {}.{}.{}{}\n", - VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_DEV ? "-dev" : ""); -#ifdef GIT_SHA1 - fmt::print(" Git SHA1 | {}\n", GIT_SHA1); -#endif + " Version | {}.{}.{}{}{}\n", + VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_DEV ? "-dev" : "", + VERSION_COMMIT_COUNT); + fmt::print(" Commit Hash | {}\n", VERSION_COMMIT_HASH); // Write the date and time fmt::print(" Date/Time | {}\n", time_stamp()); @@ -93,7 +94,8 @@ void title() // Write number of OpenMP threads fmt::print(" OpenMP Threads | {}\n", omp_get_max_threads()); #endif - std::cout << std::endl; + fmt::print("\n"); + std::fflush(stdout); } //============================================================================== @@ -132,8 +134,10 @@ void header(const char* msg, int level) auto out = header(msg); // Print header based on verbosity level. - if (settings::verbosity >= level) - std::cout << '\n' << out << "\n" << std::endl; + if (settings::verbosity >= level) { + fmt::print("\n{}\n\n", out); + std::fflush(stdout); + } } //============================================================================== @@ -173,31 +177,32 @@ void print_particle(Particle& p) for (auto i = 0; i < p.n_coord(); i++) { fmt::print(" Level {}\n", i); - if (p.coord(i).cell != C_NONE) { - const Cell& c {*model::cells[p.coord(i).cell]}; + if (p.coord(i).cell() != C_NONE) { + const Cell& c {*model::cells[p.coord(i).cell()]}; fmt::print(" Cell = {}\n", c.id_); } - if (p.coord(i).universe != C_NONE) { - const Universe& u {*model::universes[p.coord(i).universe]}; + if (p.coord(i).universe() != C_NONE) { + const Universe& u {*model::universes[p.coord(i).universe()]}; fmt::print(" Universe = {}\n", u.id_); } - if (p.coord(i).lattice != C_NONE) { - const Lattice& lat {*model::lattices[p.coord(i).lattice]}; + if (p.coord(i).lattice() != C_NONE) { + const Lattice& lat {*model::lattices[p.coord(i).lattice()]}; fmt::print(" Lattice = {}\n", lat.id_); - fmt::print(" Lattice position = ({},{},{})\n", p.coord(i).lattice_i[0], - p.coord(i).lattice_i[1], p.coord(i).lattice_i[2]); + fmt::print(" Lattice position = ({},{},{})\n", + p.coord(i).lattice_index()[0], p.coord(i).lattice_index()[1], + p.coord(i).lattice_index()[2]); } - fmt::print(" r = {}\n", p.coord(i).r); - fmt::print(" u = {}\n", p.coord(i).u); + fmt::print(" r = {}\n", p.coord(i).r()); + fmt::print(" u = {}\n", p.coord(i).u()); } // Display miscellaneous info. - if (p.surface() != 0) { + if (p.surface() != SURFACE_NONE) { // Surfaces identifiers are >= 1, but indices are >= 0 so we need -1 - const Surface& surf {*model::surfaces[std::abs(p.surface()) - 1]}; + const Surface& surf {*model::surfaces[p.surface_index()]}; fmt::print(" Surface = {}\n", (p.surface() > 0) ? surf.id_ : -surf.id_); } fmt::print(" Weight = {}\n", p.wgt()); @@ -217,56 +222,11 @@ void print_plot() if (settings::verbosity < 5) return; - for (auto pl : model::plots) { - // Plot id - fmt::print("Plot ID: {}\n", pl.id_); - // Plot filename - fmt::print("Plot file: {}\n", pl.path_plot_); - // Plot level - fmt::print("Universe depth: {}\n", pl.level_); - - // Plot type - if (PlotType::slice == pl.type_) { - fmt::print("Plot Type: Slice\n"); - } else if (PlotType::voxel == pl.type_) { - fmt::print("Plot Type: Voxel\n"); - } - - // Plot parameters - fmt::print( - "Origin: {} {} {}\n", pl.origin_[0], pl.origin_[1], pl.origin_[2]); - - if (PlotType::slice == pl.type_) { - fmt::print("Width: {:4} {:4}\n", pl.width_[0], pl.width_[1]); - } else if (PlotType::voxel == pl.type_) { - fmt::print( - "Width: {:4} {:4} {:4}\n", pl.width_[0], pl.width_[1], pl.width_[2]); - } - - if (PlotColorBy::cells == pl.color_by_) { - fmt::print("Coloring: Cells\n"); - } else if (PlotColorBy::mats == pl.color_by_) { - fmt::print("Coloring: Materials\n"); - } - - if (PlotType::slice == pl.type_) { - switch (pl.basis_) { - case PlotBasis::xy: - fmt::print("Basis: XY\n"); - break; - case PlotBasis::xz: - fmt::print("Basis: XZ\n"); - break; - case PlotBasis::yz: - fmt::print("Basis: YZ\n"); - break; - } - fmt::print("Pixels: {} {}\n", pl.pixels_[0], pl.pixels_[1]); - } else if (PlotType::voxel == pl.type_) { - fmt::print( - "Voxels: {} {} {}\n", pl.pixels_[0], pl.pixels_[1], pl.pixels_[2]); - } - + for (const auto& pl : model::plots) { + fmt::print("Plot ID: {}\n", pl->id()); + fmt::print("Plot file: {}\n", pl->path_plot()); + fmt::print("Universe depth: {}\n", pl->level()); + pl->print_info(); // prints type-specific plot info fmt::print("\n"); } } @@ -309,7 +269,7 @@ void print_usage() { if (mpi::master) { fmt::print( - "Usage: openmc [options] [directory]\n\n" + "Usage: openmc [options] [path]\n\n" "Options:\n" " -c, --volume Run in stochastic volume calculation mode\n" " -g, --geometry-debug Run with geometry debugging on\n" @@ -318,7 +278,8 @@ void print_usage() " -r, --restart Restart a previous run from a state point\n" " or a particle restart file\n" " -s, --threads Number of OpenMP threads\n" - " -t, --track Write tracks for all particles\n" + " -t, --track Write tracks for all particles (up to " + "max_tracks)\n" " -e, --event Run using event-based parallelism\n" " -v, --version Show version information\n" " -h, --help Show this message\n"); @@ -330,12 +291,10 @@ void print_usage() void print_version() { if (mpi::master) { - fmt::print("OpenMC version {}.{}.{}\n", VERSION_MAJOR, VERSION_MINOR, - VERSION_RELEASE); -#ifdef GIT_SHA1 - fmt::print("Git SHA1: {}\n", GIT_SHA1); -#endif - fmt::print("Copyright (c) 2011-2022 MIT, UChicago Argonne LLC, and " + fmt::print("OpenMC version {}.{}.{}{}{}\n", VERSION_MAJOR, VERSION_MINOR, + VERSION_RELEASE, VERSION_DEV ? "-dev" : "", VERSION_COMMIT_COUNT); + fmt::print("Commit hash: {}\n", VERSION_COMMIT_HASH); + fmt::print("Copyright (c) 2011-2025 MIT, UChicago Argonne LLC, and " "contributors\nMIT/X license at " "\n"); } @@ -343,6 +302,71 @@ void print_version() //============================================================================== +void print_build_info() +{ + const std::string n("no"); + const std::string y("yes"); + + std::string mpi(n); + std::string phdf5(n); + std::string dagmc(n); + std::string libmesh(n); + std::string png(n); + std::string profiling(n); + std::string coverage(n); + std::string mcpl(n); + std::string uwuw(n); + +#ifdef PHDF5 + phdf5 = y; +#endif +#ifdef OPENMC_MPI + mpi = y; +#endif +#ifdef OPENMC_DAGMC_ENABLED + dagmc = y; +#endif +#ifdef OPENMC_LIBMESH_ENABLED + libmesh = y; +#endif +#ifdef OPENMC_MCPL + mcpl = y; +#endif +#ifdef USE_LIBPNG + png = y; +#endif +#ifdef PROFILINGBUILD + profiling = y; +#endif +#ifdef COVERAGEBUILD + coverage = y; +#endif +#ifdef OPENMC_UWUW_ENABLED + uwuw = y; +#endif + + // Wraps macro variables in quotes +#define STRINGIFY(x) STRINGIFY2(x) +#define STRINGIFY2(x) #x + + if (mpi::master) { + fmt::print("Build type: {}\n", STRINGIFY(BUILD_TYPE)); + fmt::print("Compiler ID: {} {}\n", STRINGIFY(COMPILER_ID), + STRINGIFY(COMPILER_VERSION)); + fmt::print("MPI enabled: {}\n", mpi); + fmt::print("Parallel HDF5 enabled: {}\n", phdf5); + fmt::print("PNG support: {}\n", png); + fmt::print("DAGMC support: {}\n", dagmc); + fmt::print("libMesh support: {}\n", libmesh); + fmt::print("MCPL support: {}\n", mcpl); + fmt::print("Coverage testing: {}\n", coverage); + fmt::print("Profiling flags: {}\n", profiling); + fmt::print("UWUW support: {}\n", uwuw); + } +} + +//============================================================================== + void print_columns() { if (settings::entropy_on) { @@ -378,12 +402,13 @@ void print_generation() if (n > 1) { fmt::print(" {:8.5f} +/-{:8.5f}", simulation::keff, simulation::keff_std); } - std::cout << std::endl; + fmt::print("\n"); + std::fflush(stdout); } //============================================================================== -void show_time(const char* label, double secs, int indent_level = 0) +void show_time(const char* label, double secs, int indent_level) { int width = 33 - indent_level * 2; fmt::print("{0:{1}} {2:<{3}} = {4:>10.4e} seconds\n", "", 2 * indent_level, @@ -546,6 +571,7 @@ void print_results() gt(GlobalTally::LEAKAGE, TallyResult::SUM) / n); } fmt::print("\n"); + std::fflush(stdout); } //============================================================================== @@ -567,6 +593,10 @@ const std::unordered_map score_names = { {SCORE_FISS_Q_PROMPT, "Prompt fission power"}, {SCORE_FISS_Q_RECOV, "Recoverable fission power"}, {SCORE_CURRENT, "Current"}, + {SCORE_PULSE_HEIGHT, "pulse-height"}, + {SCORE_IFP_TIME_NUM, "IFP lifetime numerator"}, + {SCORE_IFP_BETA_NUM, "IFP delayed fraction numerator"}, + {SCORE_IFP_DENOM, "IFP common denominator"}, }; //! Create an ASCII output file showing all tally results. @@ -576,9 +606,12 @@ void write_tallies() if (model::tallies.empty()) return; + // Set filename for tallies_out + std::string filename = fmt::format("{}tallies.out", settings::path_output); + // Open the tallies.out file. std::ofstream tallies_out; - tallies_out.open("tallies.out", std::ios::out | std::ios::trunc); + tallies_out.open(filename, std::ios::out | std::ios::trunc); // Loop over each tally. for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) { diff --git a/src/particle.cpp b/src/particle.cpp index 98f0b60aa5..748c698fc3 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -17,6 +17,7 @@ #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" +#include "openmc/particle_data.h" #include "openmc/photon.h" #include "openmc/physics.h" #include "openmc/physics_mg.h" @@ -29,70 +30,97 @@ #include "openmc/tallies/tally.h" #include "openmc/tallies/tally_scoring.h" #include "openmc/track_output.h" +#include "openmc/weight_windows.h" -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED #include "DagMC.hpp" #endif namespace openmc { +//============================================================================== +// Particle implementation +//============================================================================== + double Particle::speed() const { - // Determine mass in eV/c^2 - double mass; - switch (this->type()) { - case ParticleType::neutron: - mass = MASS_NEUTRON_EV; - break; - case ParticleType::photon: - mass = 0.0; - break; - case ParticleType::electron: - case ParticleType::positron: - mass = MASS_ELECTRON_EV; - break; + if (settings::run_CE) { + // Determine mass in eV/c^2 + double mass; + switch (this->type()) { + case ParticleType::neutron: + mass = MASS_NEUTRON_EV; + break; + case ParticleType::photon: + mass = 0.0; + break; + case ParticleType::electron: + case ParticleType::positron: + mass = MASS_ELECTRON_EV; + break; + } + // Equivalent to C * sqrt(1-(m/(m+E))^2) without problem at E<E() * (this->E() + 2 * mass)) / + (this->E() + mass); + } else { + auto& macro_xs = data::mg.macro_xs_[this->material()]; + int macro_t = this->mg_xs_cache().t; + int macro_a = macro_xs.get_angle_index(this->u()); + return 1.0 / macro_xs.get_xs(MgxsType::INVERSE_VELOCITY, this->g(), nullptr, + nullptr, nullptr, macro_t, macro_a); } - - // Calculate inverse of Lorentz factor - const double inv_gamma = mass / (this->E() + mass); - - // Calculate speed via v = c * sqrt(1 - γ^-2) - return C_LIGHT * std::sqrt(1 - inv_gamma * inv_gamma); } -void Particle::create_secondary( +bool Particle::create_secondary( double wgt, Direction u, double E, ParticleType type) { // If energy is below cutoff for this particle, don't create secondary // particle if (E < settings::energy_cutoff[static_cast(type)]) { - return; + return false; } - secondary_bank().emplace_back(); - - auto& bank {secondary_bank().back()}; + auto& bank = secondary_bank().emplace_back(); bank.particle = type; bank.wgt = wgt; bank.r = r(); bank.u = u; bank.E = settings::run_CE ? E : g(); bank.time = time(); + bank_second_E() += bank.E; + return true; +} - n_bank_second() += 1; +void Particle::split(double wgt) +{ + auto& bank = secondary_bank().emplace_back(); + bank.particle = type(); + bank.wgt = wgt; + bank.r = r(); + bank.u = u(); + bank.E = settings::run_CE ? E() : g(); + bank.time = time(); + + // Convert signed index to a signed surface ID + if (surface() == SURFACE_NONE) { + bank.surf_id = SURFACE_NONE; + } else { + int surf_id = model::surfaces[surface_index()]->id_; + bank.surf_id = (surface() > 0) ? surf_id : -surf_id; + } } void Particle::from_source(const SourceSite* src) { // Reset some attributes clear(); - alive() = true; - surface() = 0; + surface() = SURFACE_NONE; cell_born() = C_NONE; material() = C_NONE; n_collision() = 0; fission() = false; zero_flux_derivs(); + lifetime() = 0.0; // Copy attributes from source bank site type() = src->particle; @@ -100,6 +128,7 @@ void Particle::from_source(const SourceSite* src) wgt_last() = src->wgt; r() = src->r; u() = src->u; + r_born() = src->r; r_last_current() = src->r; r_last() = src->r; u_last() = src->u; @@ -114,6 +143,14 @@ void Particle::from_source(const SourceSite* src) E_last() = E(); time() = src->time; time_last() = src->time; + parent_nuclide() = src->parent_nuclide; + delayed_group() = src->delayed_group; + + // Convert signed surface ID to signed index + if (src->surf_id != SURFACE_NONE) { + int index_plus_one = model::surface_map[std::abs(src->surf_id)] + 1; + surface() = (src->surf_id > 0) ? index_plus_one : -index_plus_one; + } } void Particle::event_calculate_xs() @@ -136,7 +173,7 @@ void Particle::event_calculate_xs() // If the cell hasn't been determined based on the particle's location, // initiate a search for the current cell. This generally happens at the // beginning of the history and again for any secondary particles - if (coord(n_coord() - 1).cell == C_NONE) { + if (lowest_coord().cell() == C_NONE) { if (!exhaustive_find_cell(*this)) { mark_as_lost( "Could not find the cell containing particle " + std::to_string(id())); @@ -145,7 +182,13 @@ void Particle::event_calculate_xs() // Set birth cell attribute if (cell_born() == C_NONE) - cell_born() = coord(n_coord() - 1).cell; + cell_born() = lowest_coord().cell(); + + // Initialize last cells from current cell + for (int j = 0; j < n_coord(); ++j) { + cell_last(j) = coord(j).cell(); + } + n_coord_last() = n_coord(); } // Write particle track. @@ -158,7 +201,8 @@ void Particle::event_calculate_xs() // Calculate microscopic and macroscopic cross sections if (material() != MATERIAL_VOID) { if (settings::run_CE) { - if (material() != material_last() || sqrtkT() != sqrtkT_last()) { + if (material() != material_last() || sqrtkT() != sqrtkT_last() || + density_mult() != density_mult_last()) { // If the material is the same as the last material and the // temperature hasn't changed, we don't need to lookup cross // sections again. @@ -183,27 +227,37 @@ void Particle::event_calculate_xs() void Particle::event_advance() { - // Find the distance to the nearest boundary - // Sample a distance to collision if (type() == ParticleType::electron || type() == ParticleType::positron) { - collision_distance() = 0.0; + collision_distance() = material() == MATERIAL_VOID ? INFINITY : 0.0; } else if (macro_xs().total == 0.0) { collision_distance() = INFINITY; } else { collision_distance() = -std::log(prn(current_seed())) / macro_xs().total; } + // Find the distance to the nearest boundary boundary() = distance_to_boundary(*this); - // Select smaller of the two distances - double distance = std::min(boundary().distance, collision_distance()); + double speed = this->speed(); + double time_cutoff = settings::time_cutoff[static_cast(type())]; + double distance_cutoff = + (time_cutoff < INFTY) ? (time_cutoff - time()) * speed : INFTY; + + // Select smaller of the three distances + double distance = + std::min({boundary().distance(), collision_distance(), distance_cutoff}); // Advance particle in space and time - for (int j = 0; j < n_coord(); ++j) { - coord(j).r += distance * coord(j).u; + this->move_distance(distance); + double dt = distance / speed; + this->time() += dt; + this->lifetime() += dt; + + // Score timed track-length tallies + if (!model::active_timed_tracklength_tallies.empty()) { + score_timed_tracklength_tally(*this, distance); } - this->time() += distance / this->speed(); // Score track-length tallies if (!model::active_tracklength_tallies.empty()) { @@ -220,29 +274,48 @@ void Particle::event_advance() if (!model::active_tallies.empty()) { score_track_derivative(*this, distance); } + + // Set particle weight to zero if it hit the time boundary + if (distance == distance_cutoff) { + wgt() = 0.0; + } } void Particle::event_cross_surface() { - // Set surface that particle is on and adjust coordinate levels - surface() = boundary().surface_index; - n_coord() = boundary().coord_level; - // Saving previous cell data for (int j = 0; j < n_coord(); ++j) { - cell_last(j) = coord(j).cell; + cell_last(j) = coord(j).cell(); } n_coord_last() = n_coord(); - if (boundary().lattice_translation[0] != 0 || - boundary().lattice_translation[1] != 0 || - boundary().lattice_translation[2] != 0) { + // Set surface that particle is on and adjust coordinate levels + surface() = boundary().surface(); + n_coord() = boundary().coord_level(); + + if (boundary().lattice_translation()[0] != 0 || + boundary().lattice_translation()[1] != 0 || + boundary().lattice_translation()[2] != 0) { // Particle crosses lattice boundary - cross_lattice(*this, boundary()); + + bool verbose = settings::verbosity >= 10 || trace(); + cross_lattice(*this, boundary(), verbose); event() = TallyEvent::LATTICE; } else { // Particle crosses surface - cross_surface(); + const auto& surf {model::surfaces[surface_index()].get()}; + // If BC, add particle to surface source before crossing surface + if (surf->surf_source_ && surf->bc_) { + add_surf_source_to_bank(*this, *surf); + } + this->cross_surface(*surf); + // If no BC, add particle to surface source after crossing surface + if (surf->surf_source_ && !surf->bc_) { + add_surf_source_to_bank(*this, *surf); + } + if (settings::weight_window_checkpoint_surface) { + apply_weight_windows(*this); + } event() = TallyEvent::SURFACE; } // Score cell to cell partial currents @@ -267,7 +340,7 @@ void Particle::event_collide() score_surface_tally(*this, model::active_meshsurf_tallies); // Clear surface component - surface() = 0; + surface() = SURFACE_NONE; if (settings::run_CE) { collision(*this); @@ -288,9 +361,14 @@ void Particle::event_collide() } } + if (!model::active_pulse_height_tallies.empty() && + type() == ParticleType::photon) { + pht_collision_energy(); + } + // Reset banked weight during collision n_bank() = 0; - n_bank_second() = 0; + bank_second_E() = 0.0; wgt_bank() = 0.0; zero_delayed_bank(); @@ -307,14 +385,14 @@ void Particle::event_collide() // Set all directions to base level -- right now, after a collision, only // the base level directions are changed for (int j = 0; j < n_coord() - 1; ++j) { - if (coord(j + 1).rotated) { + if (coord(j + 1).rotated()) { // If next level is rotated, apply rotation matrix - const auto& m {model::cells[coord(j).cell]->rotation_}; - const auto& u {coord(j).u}; - coord(j + 1).u = u.rotate(m); + const auto& m {model::cells[coord(j).cell()]->rotation_}; + const auto& u {coord(j).u()}; + coord(j + 1).u() = u.rotate(m); } else { // Otherwise, copy this level's direction - coord(j + 1).u = coord(j).u; + coord(j + 1).u() = coord(j).u(); } } @@ -322,7 +400,7 @@ void Particle::event_collide() if (!model::active_tallies.empty()) score_collision_derivative(*this); -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED history().reset(); #endif } @@ -331,14 +409,19 @@ void Particle::event_revive_from_secondary() { // If particle has too many events, display warning and kill it ++n_event(); - if (n_event() == MAX_EVENTS) { + if (n_event() == settings::max_particle_events) { warning("Particle " + std::to_string(id()) + " underwent maximum number of events."); - alive() = false; + wgt() = 0.0; } // Check for secondary particles if this particle is dead if (!alive()) { + // Write final position for this particle + if (write_track()) { + write_particle_track(*this); + } + // If no secondary particles, break out of event loop if (secondary_bank().empty()) return; @@ -346,6 +429,33 @@ void Particle::event_revive_from_secondary() from_source(&secondary_bank().back()); secondary_bank().pop_back(); n_event() = 0; + bank_second_E() = 0.0; + + // Subtract secondary particle energy from interim pulse-height results + if (!model::active_pulse_height_tallies.empty() && + this->type() == ParticleType::photon) { + // Since the birth cell of the particle has not been set we + // have to determine it before the energy of the secondary particle can be + // removed from the pulse-height of this cell. + if (lowest_coord().cell() == C_NONE) { + bool verbose = settings::verbosity >= 10 || trace(); + if (!exhaustive_find_cell(*this, verbose)) { + mark_as_lost("Could not find the cell containing particle " + + std::to_string(id())); + return; + } + // Set birth cell attribute + if (cell_born() == C_NONE) + cell_born() = lowest_coord().cell(); + + // Initialize last cells from current cell + for (int j = 0; j < n_coord(); ++j) { + cell_last(j) = coord(j).cell(); + } + n_coord_last() = n_coord(); + } + pht_secondary_particles(); + } // Enter new particle in particle track file if (write_track()) @@ -355,13 +465,12 @@ void Particle::event_revive_from_secondary() void Particle::event_death() { -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED history().reset(); #endif // Finish particle track output. if (write_track()) { - write_particle_track(*this); finalize_particle_track(*this); } @@ -381,6 +490,10 @@ void Particle::event_death() keff_tally_tracklength() = 0.0; keff_tally_leakage() = 0.0; + if (!model::active_pulse_height_tallies.empty()) { + score_pulse_height_tally(*this, model::active_pulse_height_tallies); + } + // Record the number of progeny created by this particle. // This data will be used to efficiently sort the fission bank. if (settings::run_mode == RunMode::EIGENVALUE) { @@ -389,163 +502,106 @@ void Particle::event_death() } } -void Particle::cross_surface() +void Particle::pht_collision_energy() { - int i_surface = std::abs(surface()); - // TODO: off-by-one - const auto& surf {model::surfaces[i_surface - 1].get()}; - if (settings::verbosity >= 10 || trace()) { - write_message(1, " Crossing surface {}", surf->id_); - } + // Adds the energy particles lose in a collision to the pulse-height - if (surf->surf_source_ && simulation::current_batch == settings::n_batches) { - SourceSite site; - site.r = r(); - site.u = u(); - site.E = E(); - site.time = time(); - site.wgt = wgt(); - site.delayed_group = delayed_group(); - site.surf_id = surf->id_; - site.particle = type(); - site.parent_id = id(); - site.progeny_id = n_progeny(); - int64_t idx = simulation::surf_source_bank.thread_safe_append(site); + // determine index of cell in pulse_height_cells + auto it = std::find(model::pulse_height_cells.begin(), + model::pulse_height_cells.end(), lowest_coord().cell()); + + if (it != model::pulse_height_cells.end()) { + int index = std::distance(model::pulse_height_cells.begin(), it); + pht_storage()[index] += E_last() - E(); + + // If the energy of the particle is below the cutoff, it will not be sampled + // so its energy is added to the pulse-height in the cell + int photon = static_cast(ParticleType::photon); + if (E() < settings::energy_cutoff[photon]) { + pht_storage()[index] += E(); + } + } +} + +void Particle::pht_secondary_particles() +{ + // Removes the energy of secondary produced particles from the pulse-height + + // determine index of cell in pulse_height_cells + auto it = std::find(model::pulse_height_cells.begin(), + model::pulse_height_cells.end(), cell_born()); + + if (it != model::pulse_height_cells.end()) { + int index = std::distance(model::pulse_height_cells.begin(), it); + pht_storage()[index] -= E(); + } +} + +void Particle::cross_surface(const Surface& surf) +{ + + if (settings::verbosity >= 10 || trace()) { + write_message(1, " Crossing surface {}", surf.id_); } // if we're crossing a CSG surface, make sure the DAG history is reset -#ifdef DAGMC - if (surf->geom_type_ == GeometryType::CSG) +#ifdef OPENMC_DAGMC_ENABLED + if (surf.geom_type() == GeometryType::CSG) history().reset(); #endif // Handle any applicable boundary conditions. - if (surf->bc_ && settings::run_mode != RunMode::PLOTTING) { - surf->bc_->handle_particle(*this, *surf); + if (surf.bc_ && settings::run_mode != RunMode::PLOTTING && + settings::run_mode != RunMode::VOLUME) { + surf.bc_->handle_particle(*this, surf); return; } // ========================================================================== // SEARCH NEIGHBOR LISTS FOR NEXT CELL -#ifdef DAGMC +#ifdef OPENMC_DAGMC_ENABLED // in DAGMC, we know what the next cell should be - if (surf->geom_type_ == GeometryType::DAG) { - auto surfp = dynamic_cast(surf); - auto cellp = - dynamic_cast(model::cells[cell_last(n_coord() - 1)].get()); - auto univp = static_cast( - model::universes[coord(n_coord() - 1).universe].get()); - // determine the next cell for this crossing - int32_t i_cell = next_cell(univp, cellp, surfp) - 1; - // save material and temp + if (surf.geom_type() == GeometryType::DAG) { + int32_t i_cell = next_cell(surface_index(), cell_last(n_coord() - 1), + lowest_coord().universe()) - + 1; + // save material, temperature, and density multiplier material_last() = material(); sqrtkT_last() = sqrtkT(); + density_mult_last() = density_mult(); // set new cell value - coord(n_coord() - 1).cell = i_cell; + lowest_coord().cell() = i_cell; + auto& cell = model::cells[i_cell]; + cell_instance() = 0; - material() = model::cells[i_cell]->material_[0]; - sqrtkT() = model::cells[i_cell]->sqrtkT_[0]; + if (cell->distribcell_index_ >= 0) + cell_instance() = cell_instance_at_level(*this, n_coord() - 1); + + material() = cell->material(cell_instance()); + sqrtkT() = cell->sqrtkT(cell_instance()); + density_mult() = cell->density_mult(cell_instance()); return; } #endif - - if (surf->is_triso_surface_) { - if (surface() > 0){ - for (int i = n_coord(); i < model::n_coord_levels; i++) { - coord(i).reset(); - } - coord(n_coord() - 1).cell = model::cell_map[model::surfaces[i_surface - 1]->triso_base_index_]; - } else if (surface() < 0) { - for (int i = n_coord(); i < model::n_coord_levels; i++) { - coord(i).reset(); - } - if (model::surfaces[i_surface - 1]->triso_particle_index_==-1) { - fatal_error(fmt::format("Particle cell of surface {} is not defined", model::surfaces[i_surface - 1]->id_)); - } - coord(n_coord() - 1).cell = model::cell_map[model::surfaces[i_surface - 1]->triso_particle_index_]; - } - - //find material - bool found=true; - int i_cell = coord(n_coord() - 1).cell; - for (;; ++n_coord()) { - if (i_cell == C_NONE) { - int i_universe = coord(n_coord() - 1).universe; - const auto& univ {model::universes[i_universe]}; - - if (univ->filled_with_triso_base_ != -1) { - coord(n_coord() - 1).cell = model::cell_map[univ->filled_with_triso_base_]; - found=true; - } else { - found = univ->find_cell(*this); - } - if (!found) { - break; - } - } - - i_cell = coord(n_coord() - 1).cell; - - Cell& c {*model::cells[i_cell]}; - if (c.type_ == Fill::MATERIAL) { - // Found a material cell which means this is the lowest coord level. - - cell_instance() = 0; - // Find the distribcell instance number. - if (c.distribcell_index_ >= 0) { - cell_instance() = cell_instance_at_level(*this, n_coord() - 1); - } - - // Set the material and temperature. - material_last() = material(); - if (c.material_.size() > 1) { - material() = c.material_[cell_instance()]; - } else { - material() = c.material_[0]; - } - sqrtkT_last() = sqrtkT(); - if (c.sqrtkT_.size() > 1) { - sqrtkT() = c.sqrtkT_[cell_instance()]; - } else { - sqrtkT() = c.sqrtkT_[0]; - } - return; - - } else if (c.type_ == Fill::UNIVERSE) { - //======================================================================== - //! Found a lower universe, update this coord level then search the next. - - // Set the lower coordinate level universe. - auto& coor {coord(n_coord())}; - coor.universe = c.fill_; - - // Set the position and direction. - coor.r = r_local(); - coor.u = u_local(); - - // Apply translation. - coor.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coor.rotate(c.rotation_); - } - i_cell = C_NONE; - } + int i_surface = std::abs(surface()); + bool verbose = settings::verbosity >= 10 || trace(); + if (surf.is_triso_surface_) { + if (find_cell_in_virtual_lattice(*this, verbose)) { + return; } } else { - if (neighbor_list_find_cell(*this)) + if (neighbor_list_find_cell(*this, verbose)) { return; + } } // ========================================================================== // COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS - // Remove lower coordinate levels and assignment of surface - surface() = 0; + // Remove lower coordinate levels n_coord() = 1; - bool found = exhaustive_find_cell(*this); + bool found = exhaustive_find_cell(*this, verbose); if (settings::run_mode != RunMode::PLOTTING && (!found)) { // If a cell is still not found, there are two possible causes: 1) there is @@ -553,15 +609,16 @@ void Particle::cross_surface() // the particle is really traveling tangent to a surface, if we move it // forward a tiny bit it should fix the problem. + surface() = SURFACE_NONE; n_coord() = 1; r() += TINY_BIT * u(); // Couldn't find next cell anywhere! This probably means there is an actual // undefined region in the geometry. - if (!exhaustive_find_cell(*this)) { + if (!exhaustive_find_cell(*this, verbose)) { mark_as_lost("After particle " + std::to_string(id()) + - " crossed surface " + std::to_string(surf->id_) + + " crossed surface " + std::to_string(surf.id_) + " it could not be located in any cell and it did not leak."); return; } @@ -570,9 +627,6 @@ void Particle::cross_surface() void Particle::cross_vacuum_bc(const Surface& surf) { - // Kill the particle - alive() = false; - // Score any surface current tallies -- note that the particle is moved // forward slightly so that if the mesh boundary is on the surface, it is // still processed @@ -588,6 +642,9 @@ void Particle::cross_vacuum_bc(const Surface& surf) // Score to global leakage tally keff_tally_leakage() += wgt(); + // Kill the particle + wgt() = 0.0; + // Display message if (settings::verbosity >= 10 || trace()) { write_message(1, " Leaked out of surface {}", surf.id_); @@ -625,7 +682,7 @@ void Particle::cross_reflective_bc(const Surface& surf, Direction new_u) u() = new_u; // Reassign particle's cell and surface - coord(0).cell = cell_last(n_coord_last() - 1); + coord(0).cell() = cell_last(0); surface() = -surface(); // If a reflective surface is coincident with a lattice or universe @@ -633,9 +690,10 @@ void Particle::cross_reflective_bc(const Surface& surf, Direction new_u) // the lower universes. // (unless we're using a dagmc model, which has exactly one universe) n_coord() = 1; - if (surf.geom_type_ != GeometryType::DAG && !neighbor_list_find_cell(*this)) { - this->mark_as_lost("Couldn't find particle after reflecting from surface " + - std::to_string(surf.id_) + "."); + if (surf.geom_type() != GeometryType::DAG && + !neighbor_list_find_cell(*this)) { + mark_as_lost("Couldn't find particle after reflecting from surface " + + std::to_string(surf.id_) + "."); return; } @@ -702,10 +760,12 @@ void Particle::mark_as_lost(const char* message) { // Print warning and write lost particle file warning(message); - write_restart(); - + if (settings::max_write_lost_particles < 0 || + simulation::n_lost_particles < settings::max_write_lost_particles) { + write_restart(); + } // Increment number of lost particles - alive() = false; + wgt() = 0.0; #pragma omp atomic simulation::n_lost_particles += 1; @@ -793,6 +853,31 @@ void Particle::write_restart() const } // #pragma omp critical } +void Particle::update_neutron_xs( + int i_nuclide, int i_grid, int i_sab, double sab_frac, double ncrystal_xs) +{ + // Get microscopic cross section cache + auto& micro = this->neutron_xs(i_nuclide); + + // If the cache doesn't match, recalculate micro xs + if (this->E() != micro.last_E || this->sqrtkT() != micro.last_sqrtkT || + i_sab != micro.index_sab || sab_frac != micro.sab_frac || + ncrystal_xs != micro.ncrystal_xs) { + data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, *this); + + // If NCrystal is being used, update micro cross section cache + micro.ncrystal_xs = ncrystal_xs; + if (ncrystal_xs >= 0.0) { + data::nuclides[i_nuclide]->calculate_elastic_xs(*this); + ncrystal_update_micro(ncrystal_xs, micro); + } + } +} + +//============================================================================== +// Non-method functions +//============================================================================== + std::string particle_type_to_str(ParticleType type) { switch (type) { @@ -823,4 +908,90 @@ ParticleType str_to_particle_type(std::string str) } } +void add_surf_source_to_bank(Particle& p, const Surface& surf) +{ + if (simulation::current_batch <= settings::n_inactive || + simulation::surf_source_bank.full()) { + return; + } + + // If a cell/cellfrom/cellto parameter is defined + if (settings::ssw_cell_id != C_NONE) { + + // Retrieve cell index and storage type + int cell_idx = model::cell_map[settings::ssw_cell_id]; + + if (surf.bc_) { + // Leave if cellto with vacuum boundary condition + if (surf.bc_->type() == "vacuum" && + settings::ssw_cell_type == SSWCellType::To) { + return; + } + + // Leave if other boundary condition than vacuum + if (surf.bc_->type() != "vacuum") { + return; + } + } + + // Check if the cell of interest has been exited + bool exited = false; + for (int i = 0; i < p.n_coord_last(); ++i) { + if (p.cell_last(i) == cell_idx) { + exited = true; + } + } + + // Check if the cell of interest has been entered + bool entered = false; + for (int i = 0; i < p.n_coord(); ++i) { + if (p.coord(i).cell() == cell_idx) { + entered = true; + } + } + + // Vacuum boundary conditions: return if cell is not exited + if (surf.bc_) { + if (surf.bc_->type() == "vacuum" && !exited) { + return; + } + } else { + + // If we both enter and exit the cell of interest + if (entered && exited) { + return; + } + + // If we did not enter nor exit the cell of interest + if (!entered && !exited) { + return; + } + + // If cellfrom and the cell before crossing is not the cell of + // interest + if (settings::ssw_cell_type == SSWCellType::From && !exited) { + return; + } + + // If cellto and the cell after crossing is not the cell of interest + if (settings::ssw_cell_type == SSWCellType::To && !entered) { + return; + } + } + } + + SourceSite site; + site.r = p.r(); + site.u = p.u(); + site.E = p.E(); + site.time = p.time(); + site.wgt = p.wgt(); + site.delayed_group = p.delayed_group(); + site.surf_id = surf.id_; + site.particle = p.type(); + site.parent_id = p.id(); + site.progeny_id = p.n_progeny(); + int64_t idx = simulation::surf_source_bank.thread_safe_append(site); +} + } // namespace openmc diff --git a/src/particle_data.cpp b/src/particle_data.cpp index 701e6cbc0e..370ca12e46 100644 --- a/src/particle_data.cpp +++ b/src/particle_data.cpp @@ -1,6 +1,11 @@ #include "openmc/particle_data.h" +#include + +#include "openmc/cell.h" +#include "openmc/error.h" #include "openmc/geometry.h" +#include "openmc/material.h" #include "openmc/nuclide.h" #include "openmc/photon.h" #include "openmc/settings.h" @@ -10,31 +15,83 @@ namespace openmc { +void GeometryState::mark_as_lost(const char* message) +{ + fatal_error(message); +} + +void GeometryState::mark_as_lost(const std::string& message) +{ + mark_as_lost(message.c_str()); +} + +void GeometryState::mark_as_lost(const std::stringstream& message) +{ + mark_as_lost(message.str()); +} + void LocalCoord::rotate(const vector& rotation) { - r = r.rotate(rotation); - u = u.rotate(rotation); - rotated = true; + r_ = r_.rotate(rotation); + u_ = u_.rotate(rotation); + rotated_ = true; } void LocalCoord::reset() { - cell = C_NONE; - universe = C_NONE; - lattice = C_NONE; - lattice_i[0] = 0; - lattice_i[1] = 0; - lattice_i[2] = 0; - rotated = false; + cell_ = C_NONE; + universe_ = C_NONE; + lattice_ = C_NONE; + lattice_index_[0] = 0; + lattice_index_[1] = 0; + lattice_index_[2] = 0; + rotated_ = false; } -ParticleData::ParticleData() +GeometryState::GeometryState() { // Create and clear coordinate levels coord_.resize(model::n_coord_levels); cell_last_.resize(model::n_coord_levels); clear(); +} +void GeometryState::advance_to_boundary_from_void() +{ + auto root_coord = this->coord(0); + const auto& root_universe = model::universes[model::root_universe]; + boundary().reset(); + + for (auto c_i : root_universe->cells_) { + auto dist = + model::cells.at(c_i)->distance(root_coord.r(), root_coord.u(), 0, this); + if (dist.first < boundary().distance()) { + boundary().distance() = dist.first; + boundary().surface() = dist.second; + } + } + + // if no intersection or near-infinite intersection, reset + // boundary information + if (boundary().distance() > 1e300) { + boundary().distance() = INFTY; + boundary().surface() = SURFACE_NONE; + return; + } + + // move the particle up to (and just past) the boundary + move_distance(boundary().distance() + TINY_BIT); +} + +void GeometryState::move_distance(double length) +{ + for (int j = 0; j < n_coord(); ++j) { + coord(j).r() += length * coord(j).u(); + } +} + +ParticleData::ParticleData() +{ zero_delayed_bank(); // Every particle starts with no accumulated flux derivative. Note that in @@ -51,6 +108,27 @@ ParticleData::ParticleData() // Create microscopic cross section caches neutron_xs_.resize(data::nuclides.size()); photon_xs_.resize(data::elements.size()); + + // Creates the pulse-height storage for the particle + if (!model::pulse_height_cells.empty()) { + pht_storage_.resize(model::pulse_height_cells.size(), 0.0); + } +} + +TrackState ParticleData::get_track_state() const +{ + TrackState state; + state.r = this->r(); + state.u = this->u(); + state.E = this->E(); + state.time = this->time(); + state.wgt = this->wgt(); + state.cell_id = model::cells[this->lowest_coord().cell()]->id_; + state.cell_instance = this->cell_instance(); + if (this->material() != MATERIAL_VOID) { + state.material_id = model::materials[material()]->id_; + } + return state; } } // namespace openmc diff --git a/src/particle_restart.cpp b/src/particle_restart.cpp index 32d187dd82..6b7778211a 100644 --- a/src/particle_restart.cpp +++ b/src/particle_restart.cpp @@ -87,8 +87,10 @@ void run_particle_restart() read_particle_restart(p, previous_run_mode); // write track if that was requested on command line - if (settings::write_all_tracks) + if (settings::write_all_tracks) { + open_track_file(); p.write_track() = true; + } // Set all tallies to 0 for now (just tracking errors) model::tallies.clear(); @@ -123,6 +125,10 @@ void run_particle_restart() // Write output if particle made it print_particle(p); + + if (settings::write_all_tracks) { + close_track_file(); + } } } // namespace openmc diff --git a/src/photon.cpp b/src/photon.cpp index b0cd8aa87d..4926e3eae6 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -14,7 +14,9 @@ #include "openmc/settings.h" #include "xtensor/xbuilder.hpp" +#include "xtensor/xmath.hpp" #include "xtensor/xoperation.hpp" +#include "xtensor/xslice.hpp" #include "xtensor/xview.hpp" #include @@ -44,6 +46,8 @@ vector> elements; PhotonInteraction::PhotonInteraction(hid_t group) { + using namespace xt::placeholders; + // Set index of element in global vector index_ = data::elements.size(); @@ -87,9 +91,13 @@ PhotonInteraction::PhotonInteraction(hid_t group) close_group(rgroup); // Read pair production - rgroup = open_group(group, "pair_production_electron"); - read_dataset(rgroup, "xs", pair_production_electron_); - close_group(rgroup); + if (object_exists(group, "pair_production_electron")) { + rgroup = open_group(group, "pair_production_electron"); + read_dataset(rgroup, "xs", pair_production_electron_); + close_group(rgroup); + } else { + pair_production_electron_ = xt::zeros_like(energy_); + } // Read pair production if (object_exists(group, "pair_production_nuclear")) { @@ -125,6 +133,7 @@ PhotonInteraction::PhotonInteraction(hid_t group) } shells_.resize(n_shell); + cross_sections_ = xt::zeros({energy_.size(), n_shell}); // Create mapping from designator to index std::unordered_map shell_map; @@ -149,19 +158,25 @@ PhotonInteraction::PhotonInteraction(hid_t group) // TODO: Move to ElectronSubshell constructor - // Read binding energy and number of electrons hid_t tgroup = open_group(rgroup, designator.c_str()); - read_attribute(tgroup, "binding_energy", shell.binding_energy); - read_attribute(tgroup, "num_electrons", shell.n_electrons); + + // Read binding energy energy and number of electrons if atomic relaxation + // data is present + if (attribute_exists(tgroup, "binding_energy")) { + has_atomic_relaxation_ = true; + read_attribute(tgroup, "binding_energy", shell.binding_energy); + } // Read subshell cross section + xt::xtensor xs; dset = open_dataset(tgroup, "xs"); read_attribute(dset, "threshold_idx", shell.threshold); close_dataset(dset); - read_dataset(tgroup, "xs", shell.cross_section); + read_dataset(tgroup, "xs", xs); - auto& xs = shell.cross_section; - xs = xt::where(xs > 0.0, xt::log(xs), -500.0); + auto cross_section = + xt::view(cross_sections_, xt::range(shell.threshold, _), i); + cross_section = xt::where(xs > 0, xt::log(xs), 0); if (object_exists(tgroup, "transitions")) { // Determine dimensions of transitions @@ -217,10 +232,33 @@ PhotonInteraction::PhotonInteraction(hid_t group) } close_group(rgroup); + // Map Compton subshell data to atomic relaxation data by finding the + // subshell with the equivalent binding energy + if (has_atomic_relaxation_) { + auto is_close = [](double a, double b) { + return std::abs(a - b) / a < FP_REL_PRECISION; + }; + subshell_map_ = xt::full_like(binding_energy_, -1); + for (int i = 0; i < binding_energy_.size(); ++i) { + double E_b = binding_energy_[i]; + if (i < n_shell && is_close(E_b, shells_[i].binding_energy)) { + subshell_map_[i] = i; + } else { + for (int j = 0; j < n_shell; ++j) { + if (is_close(E_b, shells_[j].binding_energy)) { + subshell_map_[i] = j; + break; + } + } + } + } + } + // Create Compton profile CDF auto n_profile = data::compton_profile_pz.size(); - profile_cdf_ = xt::empty({n_shell, n_profile}); - for (int i = 0; i < profile_pdf_.shape(0); ++i) { + auto n_shell_compton = profile_pdf_.shape(0); + profile_cdf_ = xt::empty({n_shell_compton, n_profile}); + for (int i = 0; i < n_shell_compton; ++i) { double c = 0.0; profile_cdf_(i, 0) = 0.0; for (int j = 0; j < n_profile - 1; ++j) { @@ -311,15 +349,19 @@ PhotonInteraction::PhotonInteraction(hid_t group) } // Take logarithm of energies and cross sections since they are log-log - // interpolated + // interpolated. Note that cross section libraries converted from ACE files + // represent zero as exp(-500) to avoid log-log interpolation errors. For + // values below exp(-499) we store the log as -900, for which exp(-900) + // evaluates to zero. + double limit = std::exp(-499.0); energy_ = xt::log(energy_); - coherent_ = xt::where(coherent_ > 0.0, xt::log(coherent_), -500.0); - incoherent_ = xt::where(incoherent_ > 0.0, xt::log(incoherent_), -500.0); + coherent_ = xt::where(coherent_ > limit, xt::log(coherent_), -900.0); + incoherent_ = xt::where(incoherent_ > limit, xt::log(incoherent_), -900.0); photoelectric_total_ = xt::where( - photoelectric_total_ > 0.0, xt::log(photoelectric_total_), -500.0); + photoelectric_total_ > limit, xt::log(photoelectric_total_), -900.0); pair_production_total_ = xt::where( - pair_production_total_ > 0.0, xt::log(pair_production_total_), -500.0); - heating_ = xt::where(heating_ > 0.0, xt::log(heating_), -500.0); + pair_production_total_ > limit, xt::log(pair_production_total_), -900.0); + heating_ = xt::where(heating_ > limit, xt::log(heating_), -900.0); } PhotonInteraction::~PhotonInteraction() @@ -566,18 +608,13 @@ void PhotonInteraction::calculate_xs(Particle& p) const // Calculate microscopic photoelectric cross section xs.photoelectric = 0.0; - for (const auto& shell : shells_) { - // Check threshold of reaction - int i_start = shell.threshold; - if (i_grid < i_start) - continue; + const auto& xs_lower = xt::row(cross_sections_, i_grid); + const auto& xs_upper = xt::row(cross_sections_, i_grid + 1); - // Evaluation subshell photoionization cross section - xs.photoelectric += - std::exp(shell.cross_section(i_grid - i_start) + - f * (shell.cross_section(i_grid + 1 - i_start) - - shell.cross_section(i_grid - i_start))); - } + for (int i = 0; i < xs_upper.size(); ++i) + if (xs_lower(i) != 0) + xs.photoelectric += + std::exp(xs_lower(i) + f * (xs_upper(i) - xs_lower(i))); // Calculate microscopic pair production cross section xs.pair_production = std::exp( @@ -747,6 +784,11 @@ void PhotonInteraction::pair_production(double alpha, double* E_electron, void PhotonInteraction::atomic_relaxation(int i_shell, Particle& p) const { + // Return if no atomic relaxation data is present or if the binding energy is + // larger than the incident particle energy + if (!has_atomic_relaxation_ || shells_[i_shell].binding_energy > p.E()) + return; + // Stack for unprocessed holes left by transitioning electrons int n_holes = 0; array holes; diff --git a/src/physics.cpp b/src/physics.cpp index 196c7dc3b8..3a17077b36 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -2,14 +2,17 @@ #include "openmc/bank.h" #include "openmc/bremsstrahlung.h" +#include "openmc/chain.h" #include "openmc/constants.h" #include "openmc/distribution_multi.h" #include "openmc/eigenvalue.h" #include "openmc/endf.h" #include "openmc/error.h" +#include "openmc/ifp.h" #include "openmc/material.h" #include "openmc/math_functions.h" #include "openmc/message_passing.h" +#include "openmc/ncrystal_interface.h" #include "openmc/nuclide.h" #include "openmc/photon.h" #include "openmc/physics_common.h" @@ -29,6 +32,7 @@ #include // for max, min, max_element #include // for sqrt, exp, log, abs, copysign +#include namespace openmc { @@ -45,15 +49,9 @@ void collision(Particle& p) switch (p.type()) { case ParticleType::neutron: sample_neutron_reaction(p); - if (settings::weight_windows_on) { - apply_weight_windows(p); - } break; case ParticleType::photon: sample_photon_reaction(p); - if (settings::weight_windows_on) { - apply_weight_windows(p); - } break; case ParticleType::electron: sample_electron_reaction(p); @@ -63,10 +61,12 @@ void collision(Particle& p) break; } + if (settings::weight_window_checkpoint_collision) + apply_weight_windows(p); + // Kill particle if energy falls below cutoff int type = static_cast(p.type()); if (p.E() < settings::energy_cutoff[type]) { - p.alive() = false; p.wgt() = 0.0; } @@ -105,7 +105,7 @@ void sample_neutron_reaction(Particle& p) const auto& nuc {data::nuclides[i_nuclide]}; - if (nuc->fissionable_) { + if (nuc->fissionable_ && p.neutron_xs(i_nuclide).fission > 0.0) { auto& rx = sample_fission(i_nuclide, p); if (settings::run_mode == RunMode::EIGENVALUE) { create_fission_sites(p, i_nuclide, rx); @@ -115,7 +115,7 @@ void sample_neutron_reaction(Particle& p) // Make sure particle population doesn't grow out of control for // subcritical multiplication problems. - if (p.secondary_bank().size() >= 10000) { + if (p.secondary_bank().size() >= settings::max_secondaries) { fatal_error( "The secondary particle bank appears to be growing without " "bound. You are likely running a subcritical multiplication problem " @@ -134,28 +134,35 @@ void sample_neutron_reaction(Particle& p) if (p.neutron_xs(i_nuclide).absorption > 0.0) { absorption(p, i_nuclide); - } else { - p.wgt_absorb() = 0.0; } if (!p.alive()) return; // Sample a scattering reaction and determine the secondary energy of the // exiting neutron - scatter(p, i_nuclide); + const auto& ncrystal_mat = model::materials[p.material()]->ncrystal_mat(); + if (ncrystal_mat && p.E() < NCRYSTAL_MAX_ENERGY) { + ncrystal_mat.scatter(p); + } else { + scatter(p, i_nuclide); + } // Advance URR seed stream 'N' times after energy changes if (p.E() != p.E_last()) { - p.stream() = STREAM_URR_PTABLE; - advance_prn_seed(data::nuclides.size(), p.current_seed()); - p.stream() = STREAM_TRACKING; + advance_prn_seed(data::nuclides.size(), &p.seeds(STREAM_URR_PTABLE)); } // Play russian roulette if survival biasing is turned on if (settings::survival_biasing) { - russian_roulette(p); - if (!p.alive()) - return; + // if survival normalization is on, use normalized weight cutoff and + // normalized weight survive + if (settings::survival_normalization) { + if (p.wgt() < settings::weight_cutoff * p.wgt_born()) { + russian_roulette(p, settings::weight_survive * p.wgt_born()); + } + } else if (p.wgt() < settings::weight_cutoff) { + russian_roulette(p, settings::weight_survive); + } } } @@ -203,12 +210,22 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) site.particle = ParticleType::neutron; site.time = p.time(); site.wgt = 1. / weight; - site.parent_id = p.id(); - site.progeny_id = p.n_progeny()++; site.surf_id = 0; // Sample delayed group and angle/energy for fission reaction - sample_fission_neutron(i_nuclide, rx, p.E(), &site, p.current_seed()); + sample_fission_neutron(i_nuclide, rx, &site, p); + + // Reject site if it exceeds time cutoff + if (site.delayed_group > 0) { + double t_cutoff = settings::time_cutoff[static_cast(site.particle)]; + if (site.time > t_cutoff) { + continue; + } + } + + // Set parent and progeny IDs + site.parent_id = p.id(); + site.progeny_id = p.n_progeny()++; // Store fission site in bank if (use_fission_bank) { @@ -227,24 +244,24 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Break out of loop as no more sites can be added to fission bank break; } + // Iterated Fission Probability (IFP) method + if (settings::ifp_on) { + ifp(p, idx); + } } else { p.secondary_bank().push_back(site); } - // Set the delayed group on the particle as well - p.delayed_group() = site.delayed_group; - // Increment the number of neutrons born delayed - if (p.delayed_group() > 0) { - nu_d[p.delayed_group() - 1]++; + if (site.delayed_group > 0) { + nu_d[site.delayed_group - 1]++; } // Write fission particles to nuBank - p.nu_bank().emplace_back(); - NuBank* nu_bank_entry = &p.nu_bank().back(); - nu_bank_entry->wgt = site.wgt; - nu_bank_entry->E = site.E; - nu_bank_entry->delayed_group = site.delayed_group; + NuBank& nu_bank_entry = p.nu_bank().emplace_back(); + nu_bank_entry.wgt = site.wgt; + nu_bank_entry.E = site.E; + nu_bank_entry.delayed_group = site.delayed_group; } // If shared fission bank was full, and no fissions could be added, @@ -274,7 +291,7 @@ void sample_photon_reaction(Particle& p) int photon = static_cast(ParticleType::photon); if (p.E() < settings::energy_cutoff[photon]) { p.E() = 0.0; - p.alive() = false; + p.wgt() = 0.0; return; } @@ -294,8 +311,8 @@ void sample_photon_reaction(Particle& p) // Coherent (Rayleigh) scattering prob += micro.coherent; if (prob > cutoff) { - double mu = element.rayleigh_scatter(alpha, p.current_seed()); - p.u() = rotate_angle(p.u(), mu, nullptr, p.current_seed()); + p.mu() = element.rayleigh_scatter(alpha, p.current_seed()); + p.u() = rotate_angle(p.u(), p.mu(), nullptr, p.current_seed()); p.event() = TallyEvent::SCATTER; p.event_mt() = COHERENT; return; @@ -304,10 +321,10 @@ void sample_photon_reaction(Particle& p) // Incoherent (Compton) scattering prob += micro.incoherent; if (prob > cutoff) { - double alpha_out, mu; + double alpha_out; int i_shell; element.compton_scatter( - alpha, true, &alpha_out, &mu, &i_shell, p.current_seed()); + alpha, true, &alpha_out, &p.mu(), &i_shell, p.current_seed()); // Determine binding energy of shell. The binding energy is 0.0 if // doppler broadening is not used. @@ -323,23 +340,23 @@ void sample_photon_reaction(Particle& p) double E_electron = (alpha - alpha_out) * MASS_ELECTRON_EV - e_b; int electron = static_cast(ParticleType::electron); if (E_electron >= settings::energy_cutoff[electron]) { - double mu_electron = (alpha - alpha_out * mu) / + double mu_electron = (alpha - alpha_out * p.mu()) / std::sqrt(alpha * alpha + alpha_out * alpha_out - - 2.0 * alpha * alpha_out * mu); + 2.0 * alpha * alpha_out * p.mu()); Direction u = rotate_angle(p.u(), mu_electron, &phi, p.current_seed()); p.create_secondary(p.wgt(), u, E_electron, ParticleType::electron); } - // TODO: Compton subshell data does not match atomic relaxation data - // Allow electrons to fill orbital and produce auger electrons - // and fluorescent photons - if (i_shell >= 0) { - element.atomic_relaxation(i_shell, p); + // Allow electrons to fill orbital and produce Auger electrons and + // fluorescent photons. Since Compton subshell data does not match atomic + // relaxation data, use the mapping between the data to find the subshell + if (i_shell >= 0 && element.subshell_map_[i_shell] >= 0) { + element.atomic_relaxation(element.subshell_map_[i_shell], p); } phi += PI; p.E() = alpha_out * MASS_ELECTRON_EV; - p.u() = rotate_angle(p.u(), mu, &phi, p.current_seed()); + p.u() = rotate_angle(p.u(), p.mu(), &phi, p.current_seed()); p.event() = TallyEvent::SCATTER; p.event_mt() = INCOHERENT; return; @@ -347,27 +364,35 @@ void sample_photon_reaction(Particle& p) // Photoelectric effect double prob_after = prob + micro.photoelectric; + if (prob_after > cutoff) { + // Get grid index, interpolation factor, and bounding subshell + // cross sections + int i_grid = micro.index_grid; + double f = micro.interp_factor; + const auto& xs_lower = xt::row(element.cross_sections_, i_grid); + const auto& xs_upper = xt::row(element.cross_sections_, i_grid + 1); + for (int i_shell = 0; i_shell < element.shells_.size(); ++i_shell) { const auto& shell {element.shells_[i_shell]}; - // Get grid index and interpolation factor - int i_grid = micro.index_grid; - double f = micro.interp_factor; - // Check threshold of reaction - int i_start = shell.threshold; - if (i_grid < i_start) + if (xs_lower(i_shell) == 0) continue; - // Evaluation subshell photoionization cross section - double xs = std::exp(shell.cross_section(i_grid - i_start) + - f * (shell.cross_section(i_grid + 1 - i_start) - - shell.cross_section(i_grid - i_start))); + // Evaluation subshell photoionization cross section + prob += std::exp( + xs_lower(i_shell) + f * (xs_upper(i_shell) - xs_lower(i_shell))); - prob += xs; if (prob > cutoff) { - double E_electron = p.E() - shell.binding_energy; + // Determine binding energy based on whether atomic relaxation data is + // present (if not, use value from Compton profile data) + double binding_energy = element.has_atomic_relaxation_ + ? shell.binding_energy + : element.binding_energy_[i_shell]; + + // Determine energy of secondary electron + double E_electron = p.E() - binding_energy; // Sample mu using non-relativistic Sauter distribution. // See Eqns 3.19 and 3.20 in "Implementing a photon physics @@ -399,7 +424,7 @@ void sample_photon_reaction(Particle& p) element.atomic_relaxation(i_shell, p); p.event() = TallyEvent::ABSORB; p.event_mt() = 533 + shell.index_subshell; - p.alive() = false; + p.wgt() = 0.0; p.E() = 0.0; return; } @@ -425,7 +450,7 @@ void sample_photon_reaction(Particle& p) p.event() = TallyEvent::ABSORB; p.event_mt() = PAIR_PROD; - p.alive() = false; + p.wgt() = 0.0; p.E() = 0.0; } } @@ -440,7 +465,7 @@ void sample_electron_reaction(Particle& p) } p.E() = 0.0; - p.alive() = false; + p.wgt() = 0.0; p.event() = TallyEvent::ABSORB; } @@ -461,7 +486,7 @@ void sample_positron_reaction(Particle& p) p.create_secondary(p.wgt(), -u, MASS_ELECTRON_EV, ParticleType::photon); p.E() = 0.0; - p.alive() = false; + p.wgt() = 0.0; p.event() = TallyEvent::ABSORB; } @@ -478,7 +503,7 @@ int sample_nuclide(Particle& p) for (int i = 0; i < n; ++i) { // Get atom density int i_nuclide = mat->nuclide_[i]; - double atom_density = mat->atom_density_[i]; + double atom_density = mat->atom_density(i, p.density_mult()); // Increment probability to compare to cutoff prob += atom_density * p.neutron_xs(i_nuclide).total; @@ -503,7 +528,7 @@ int sample_element(Particle& p) for (int i = 0; i < mat->element_.size(); ++i) { // Find atom density int i_element = mat->element_[i]; - double atom_density = mat->atom_density_[i]; + double atom_density = mat->atom_density(i, p.density_mult()); // Determine microscopic cross section double sigma = atom_density * p.photon_xs(i_element).total; @@ -543,23 +568,15 @@ Reaction& sample_fission(int i_nuclide, Particle& p) } } - // Get grid index and interpolatoin factor and sample fission cdf - int i_temp = p.neutron_xs(i_nuclide).index_temp; - int i_grid = p.neutron_xs(i_nuclide).index_grid; - double f = p.neutron_xs(i_nuclide).interp_factor; + // Get grid index and interpolation factor and sample fission cdf + const auto& micro = p.neutron_xs(i_nuclide); double cutoff = prn(p.current_seed()) * p.neutron_xs(i_nuclide).fission; double prob = 0.0; // Loop through each partial fission reaction type for (auto& rx : nuc->fission_rx_) { - // if energy is below threshold for this reaction, skip it - int threshold = rx->xs_[i_temp].threshold; - if (i_grid < threshold) - continue; - // add to cumulative probability - prob += (1.0 - f) * rx->xs_[i_temp].value[i_grid - threshold] + - f * rx->xs_[i_temp].value[i_grid - threshold + 1]; + prob += rx->xs(micro); // Create fission bank sites if fission occurs if (prob > cutoff) @@ -575,25 +592,20 @@ void sample_photon_product( int i_nuclide, Particle& p, int* i_rx, int* i_product) { // Get grid index and interpolation factor and sample photon production cdf - int i_temp = p.neutron_xs(i_nuclide).index_temp; - int i_grid = p.neutron_xs(i_nuclide).index_grid; - double f = p.neutron_xs(i_nuclide).interp_factor; - double cutoff = prn(p.current_seed()) * p.neutron_xs(i_nuclide).photon_prod; + const auto& micro = p.neutron_xs(i_nuclide); + double cutoff = prn(p.current_seed()) * micro.photon_prod; double prob = 0.0; // Loop through each reaction type const auto& nuc {data::nuclides[i_nuclide]}; for (int i = 0; i < nuc->reactions_.size(); ++i) { - const auto& rx = nuc->reactions_[i]; - int threshold = rx->xs_[i_temp].threshold; - - // if energy is below threshold for this reaction, skip it - if (i_grid < threshold) - continue; - // Evaluate neutron cross section - double xs = ((1.0 - f) * rx->xs_[i_temp].value[i_grid - threshold] + - f * (rx->xs_[i_temp].value[i_grid - threshold + 1])); + const auto& rx = nuc->reactions_[i]; + double xs = rx->xs(micro); + + // if cross section is zero for this reaction, skip it + if (xs == 0.0) + continue; for (int j = 0; j < rx->products_.size(); ++j) { if (rx->products_[j].particle_ == ParticleType::photon) { @@ -626,16 +638,15 @@ void absorption(Particle& p, int i_nuclide) { if (settings::survival_biasing) { // Determine weight absorbed in survival biasing - p.wgt_absorb() = p.wgt() * p.neutron_xs(i_nuclide).absorption / - p.neutron_xs(i_nuclide).total; + const double wgt_absorb = p.wgt() * p.neutron_xs(i_nuclide).absorption / + p.neutron_xs(i_nuclide).total; // Adjust weight of particle by probability of absorption - p.wgt() -= p.wgt_absorb(); - p.wgt_last() = p.wgt(); + p.wgt() -= wgt_absorb; // Score implicit absorption estimate of keff if (settings::run_mode == RunMode::EIGENVALUE) { - p.keff_tally_absorption() += p.wgt_absorb() * + p.keff_tally_absorption() += wgt_absorb * p.neutron_xs(i_nuclide).nu_fission / p.neutron_xs(i_nuclide).absorption; } @@ -650,7 +661,7 @@ void absorption(Particle& p, int i_nuclide) p.neutron_xs(i_nuclide).absorption; } - p.alive() = false; + p.wgt() = 0.0; p.event() = TallyEvent::ABSORB; p.event_mt() = N_DISAPPEAR; } @@ -666,8 +677,6 @@ void scatter(Particle& p, int i_nuclide) const auto& nuc {data::nuclides[i_nuclide]}; const auto& micro {p.neutron_xs(i_nuclide)}; int i_temp = micro.index_temp; - int i_grid = micro.index_grid; - double f = micro.interp_factor; // For tallying purposes, this routine might be called directly. In that // case, we need to sample a reaction via the cutoff variable @@ -709,26 +718,13 @@ void scatter(Particle& p, int i_nuclide) // ======================================================================= // INELASTIC SCATTERING - int j = 0; + int n = nuc->index_inelastic_scatter_.size(); int i = 0; - while (prob < cutoff) { + for (int j = 0; j < n && prob < cutoff; ++j) { i = nuc->index_inelastic_scatter_[j]; - ++j; - - // Check to make sure inelastic scattering reaction sampled - if (i >= nuc->reactions_.size()) { - p.write_restart(); - fatal_error("Did not sample any reaction for nuclide " + nuc->name_); - } - - // if energy is below threshold for this reaction, skip it - const auto& xs {nuc->reactions_[i]->xs_[i_temp]}; - if (i_grid < xs.threshold) - continue; // add to cumulative probability - prob += (1.0 - f) * xs.value[i_grid - xs.threshold] + - f * xs.value[i_grid - xs.threshold + 1]; + prob += nuc->reactions_[i]->xs(micro); } // Perform collision physics for inelastic scattering @@ -855,7 +851,7 @@ Direction sample_target_velocity(const Nuclide& nuc, double E, Direction u, // otherwise, use free gas model } else { - if (E >= FREE_GAS_THRESHOLD * kT && nuc.awr_ > 1.0) { + if (E >= settings::free_gas_threshold * kT && nuc.awr_ > 1.0) { return {}; } else { sampling_method = ResScatMethod::cxs; @@ -952,9 +948,8 @@ Direction sample_target_velocity(const Nuclide& nuc, double E, Direction u, // cdf value at upper bound attainable energy double m = (nuc.xs_cdf_[i_E_up + 1] - nuc.xs_cdf_[i_E_up]) / - (nuc.energy_0K_[i_E_up + 1] - nuc.energy_0K_[i_E_up]); - double cdf_up = - nuc.xs_cdf_[i_E_up] + m * (E_up - nuc.energy_0K_[i_E_up]); + (nuc.energy_0K_[i_E_up + 1] - nuc.energy_0K_[i_E_up]); + double cdf_up = nuc.xs_cdf_[i_E_up] + m * (E_up - nuc.energy_0K_[i_E_up]); while (true) { // directly sample Maxwellian @@ -962,9 +957,8 @@ Direction sample_target_velocity(const Nuclide& nuc, double E, Direction u, // sample a relative energy using the xs cdf double cdf_rel = cdf_low + prn(seed) * (cdf_up - cdf_low); - int i_E_rel = lower_bound_index(nuc.xs_cdf_.begin() + i_E_low, - nuc.xs_cdf_.begin() + i_E_up+2, - cdf_rel); + int i_E_rel = lower_bound_index(nuc.xs_cdf_.begin() + i_E_low, + nuc.xs_cdf_.begin() + i_E_up + 2, cdf_rel); double E_rel = nuc.energy_0K_[i_E_low + i_E_rel]; double m = (nuc.xs_cdf_[i_E_low + i_E_rel + 1] - nuc.xs_cdf_[i_E_low + i_E_rel]) / @@ -1043,9 +1037,13 @@ Direction sample_cxs_target_velocity( return vt * rotate_angle(u, mu, nullptr, seed); } -void sample_fission_neutron(int i_nuclide, const Reaction& rx, double E_in, - SourceSite* site, uint64_t* seed) +void sample_fission_neutron( + int i_nuclide, const Reaction& rx, SourceSite* site, Particle& p) { + // Get attributes of particle + double E_in = p.E(); + uint64_t* seed = p.current_seed(); + // Determine total nu, delayed nu, and delayed neutron fraction const auto& nuc {data::nuclides[i_nuclide]}; double nu_t = nuc->nu(E_in, Nuclide::EmissionMode::total); @@ -1078,6 +1076,10 @@ void sample_fission_neutron(int i_nuclide, const Reaction& rx, double E_in, // set the delayed group for the particle born from fission site->delayed_group = group; + // Sample time of emission based on decay constant of precursor + double decay_rate = rx.products_[site->delayed_group].decay_rate_; + site->time -= std::log(prn(p.current_seed())) / decay_rate; + } else { // ==================================================================== // PROMPT NEUTRON SAMPLED @@ -1108,9 +1110,7 @@ void sample_fission_neutron(int i_nuclide, const Reaction& rx, double E_in, } // Sample azimuthal angle uniformly in [0, 2*pi) and assign angle - // TODO: account for dependence on incident neutron? - Direction ref(1., 0., 0.); - site->u = rotate_angle(ref, mu, nullptr, seed); + site->u = rotate_angle(p.u(), mu, nullptr, seed); } void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p) @@ -1152,7 +1152,7 @@ void inelastic_scatter(const Nuclide& nuc, const Reaction& rx, Particle& p) // evaluate yield double yield = (*rx.products_[0].yield_)(E_in); - if (std::floor(yield) == yield) { + if (std::floor(yield) == yield && yield > 0) { // If yield is integral, create exactly that many secondary particles for (int i = 0; i < static_cast(std::round(yield)) - 1; ++i) { p.create_secondary(p.wgt(), p.u(), p.E(), ParticleType::neutron); @@ -1168,9 +1168,21 @@ void sample_secondary_photons(Particle& p, int i_nuclide) // Sample the number of photons produced double y_t = p.neutron_xs(i_nuclide).photon_prod / p.neutron_xs(i_nuclide).total; - int y = static_cast(y_t); - if (prn(p.current_seed()) <= y_t - y) - ++y; + double photon_wgt = p.wgt(); + int y = 1; + + if (settings::use_decay_photons) { + // For decay photons, sample a single photon and modify the weight + if (y_t <= 0.0) + return; + photon_wgt *= y_t; + } else { + // For prompt photons, sample an integral number of photons with weight + // equal to the neutron's weight + y = static_cast(y_t); + if (prn(p.current_seed()) <= y_t - y) + ++y; + } // Sample each secondary photon for (int i = 0; i < y; ++i) { @@ -1193,15 +1205,19 @@ void sample_secondary_photons(Particle& p, int i_nuclide) // release and deposition. See D. P. Griesheimer, S. J. Douglass, and M. H. // Stedry, "Self-consistent energy normalization for quasistatic reactor // calculations", Proc. PHYSOR, Cambridge, UK, Mar 29-Apr 2, 2020. - double wgt; + double wgt = photon_wgt; if (settings::run_mode == RunMode::EIGENVALUE && !is_fission(rx->mt_)) { - wgt = simulation::keff * p.wgt(); - } else { - wgt = p.wgt(); + wgt *= simulation::keff; } // Create the secondary photon - p.create_secondary(wgt, u, E, ParticleType::photon); + bool created_photon = p.create_secondary(wgt, u, E, ParticleType::photon); + + // Tag secondary particle with parent nuclide + if (created_photon && settings::use_decay_photons) { + p.secondary_bank().back().parent_nuclide = + rx->products_[i_product].parent_nuclide_; + } } } diff --git a/src/physics_common.cpp b/src/physics_common.cpp index f6820f81fd..e25ae6b97a 100644 --- a/src/physics_common.cpp +++ b/src/physics_common.cpp @@ -9,17 +9,12 @@ namespace openmc { // RUSSIAN_ROULETTE //============================================================================== -void russian_roulette(Particle& p) +void russian_roulette(Particle& p, double weight_survive) { - if (p.wgt() < settings::weight_cutoff) { - if (prn(p.current_seed()) < p.wgt() / settings::weight_survive) { - p.wgt() = settings::weight_survive; - p.wgt_last() = p.wgt(); - } else { - p.wgt() = 0.; - p.wgt_last() = 0.; - p.alive() = false; - } + if (weight_survive * prn(p.current_seed()) < p.wgt()) { + p.wgt() = weight_survive; + } else { + p.wgt() = 0.; } } diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp index eaf13b4413..4c28cb1795 100644 --- a/src/physics_mg.cpp +++ b/src/physics_mg.cpp @@ -19,6 +19,7 @@ #include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/tallies/tally.h" +#include "openmc/weight_windows.h" namespace openmc { @@ -30,6 +31,9 @@ void collision_mg(Particle& p) // Sample the reaction type sample_reaction(p); + if (settings::weight_window_checkpoint_collision) + apply_weight_windows(p); + // Display information about collision if ((settings::verbosity >= 10) || p.trace()) { write_message(fmt::format(" Energy Group = {}", p.g()), 1); @@ -43,7 +47,7 @@ void sample_reaction(Particle& p) // change when sampling fission sites. The following block handles all // absorption (including fission) - if (model::materials[p.material()]->fissionable_) { + if (model::materials[p.material()]->fissionable()) { if (settings::run_mode == RunMode::EIGENVALUE || (settings::run_mode == RunMode::FIXED_SOURCE && settings::create_fission_neutrons)) { @@ -55,8 +59,6 @@ void sample_reaction(Particle& p) // weight of the particle. Otherwise, it checks to see if absorption occurs. if (p.macro_xs().absorption > 0.) { absorption(p); - } else { - p.wgt_absorb() = 0.; } if (!p.alive()) return; @@ -66,16 +68,22 @@ void sample_reaction(Particle& p) // Play Russian roulette if survival biasing is turned on if (settings::survival_biasing) { - russian_roulette(p); - if (!p.alive()) - return; + // if survival normalization is applicable, use normalized weight cutoff and + // normalized weight survive + if (settings::survival_normalization) { + if (p.wgt() < settings::weight_cutoff * p.wgt_born()) { + russian_roulette(p, settings::weight_survive * p.wgt_born()); + } + } else if (p.wgt() < settings::weight_cutoff) { + russian_roulette(p, settings::weight_survive); + } } } void scatter(Particle& p) { - data::mg.macro_xs_[p.material()].sample_scatter( - p.g_last(), p.g(), p.mu(), p.wgt(), p.current_seed()); + data::mg.macro_xs_[p.material()].sample_scatter(p.g_last(), p.g(), p.mu(), + p.wgt(), p.current_seed(), p.mg_xs_cache().t, p.mg_xs_cache().a); // Rotate the angle p.u() = rotate_angle(p.u(), p.mu(), nullptr, p.current_seed()); @@ -129,9 +137,8 @@ void create_fission_sites(Particle& p) SourceSite site; site.r = p.r(); site.particle = ParticleType::neutron; + site.time = p.time(); site.wgt = 1. / weight; - site.parent_id = p.id(); - site.progeny_id = p.n_progeny()++; // Sample the cosine of the angle, assuming fission neutrons are emitted // isotropically @@ -147,7 +154,7 @@ void create_fission_sites(Particle& p) int dg; int gout; data::mg.macro_xs_[p.material()].sample_fission_energy( - p.g(), dg, gout, p.current_seed()); + p.g(), dg, gout, p.current_seed(), p.mg_xs_cache().t, p.mg_xs_cache().a); // Store the energy and delayed groups on the fission bank site.E = gout; @@ -156,6 +163,24 @@ void create_fission_sites(Particle& p) // of the code, 0 is prompt. site.delayed_group = dg + 1; + // If delayed product production, sample time of emission + if (dg != -1) { + auto& macro_xs = data::mg.macro_xs_[p.material()]; + double decay_rate = + macro_xs.get_xs(MgxsType::DECAY_RATE, 0, nullptr, nullptr, &dg, 0, 0); + site.time -= std::log(prn(p.current_seed())) / decay_rate; + + // Reject site if it exceeds time cutoff + double t_cutoff = settings::time_cutoff[static_cast(site.particle)]; + if (site.time > t_cutoff) { + continue; + } + } + + // Set parent and progeny ID + site.parent_id = p.id(); + site.progeny_id = p.n_progeny()++; + // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -186,11 +211,10 @@ void create_fission_sites(Particle& p) } // Write fission particles to nuBank - p.nu_bank().emplace_back(); - NuBank* nu_bank_entry = &p.nu_bank().back(); - nu_bank_entry->wgt = site.wgt; - nu_bank_entry->E = site.E; - nu_bank_entry->delayed_group = site.delayed_group; + NuBank& nu_bank_entry = p.nu_bank().emplace_back(); + nu_bank_entry.wgt = site.wgt; + nu_bank_entry.E = site.E; + nu_bank_entry.delayed_group = site.delayed_group; } // If shared fission bank was full, and no fissions could be added, @@ -216,20 +240,19 @@ void absorption(Particle& p) { if (settings::survival_biasing) { // Determine weight absorbed in survival biasing - p.wgt_absorb() = p.wgt() * p.macro_xs().absorption / p.macro_xs().total; + double wgt_absorb = p.wgt() * p.macro_xs().absorption / p.macro_xs().total; // Adjust weight of particle by the probability of absorption - p.wgt() -= p.wgt_absorb(); - p.wgt_last() = p.wgt(); + p.wgt() -= wgt_absorb; // Score implicit absorpion estimate of keff p.keff_tally_absorption() += - p.wgt_absorb() * p.macro_xs().nu_fission / p.macro_xs().absorption; + wgt_absorb * p.macro_xs().nu_fission / p.macro_xs().absorption; } else { if (p.macro_xs().absorption > prn(p.current_seed()) * p.macro_xs().total) { p.keff_tally_absorption() += p.wgt() * p.macro_xs().nu_fission / p.macro_xs().absorption; - p.alive() = false; + p.wgt() = 0.0; p.event() = TallyEvent::ABSORB; } } diff --git a/src/plot.cpp b/src/plot.cpp index c24a023f2e..2cadc48cef 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -1,10 +1,13 @@ #include "openmc/plot.h" #include +#define _USE_MATH_DEFINES // to make M_PI declared in Intel and MSVC compilers +#include #include #include #include +#include "xtensor/xmanipulation.hpp" #include "xtensor/xview.hpp" #include #include @@ -13,6 +16,8 @@ #endif #include "openmc/constants.h" +#include "openmc/container_util.h" +#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/file_utils.h" #include "openmc/geometry.h" @@ -20,6 +25,7 @@ #include "openmc/material.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" +#include "openmc/openmp_interface.h" #include "openmc/output.h" #include "openmc/particle.h" #include "openmc/progress_bar.h" @@ -41,21 +47,21 @@ constexpr int32_t OVERLAP {-3}; IdData::IdData(size_t h_res, size_t v_res) : data_({v_res, h_res, 3}, NOT_FOUND) {} -void IdData::set_value(size_t y, size_t x, const Particle& p, int level) +void IdData::set_value(size_t y, size_t x, const GeometryState& p, int level) { // set cell data if (p.n_coord() <= level) { data_(y, x, 0) = NOT_FOUND; data_(y, x, 1) = NOT_FOUND; } else { - data_(y, x, 0) = model::cells.at(p.coord(level).cell)->id_; + data_(y, x, 0) = model::cells.at(p.coord(level).cell())->id_; data_(y, x, 1) = level == p.n_coord() - 1 ? p.cell_instance() : cell_instance_at_level(p, level); } // set material data - Cell* c = model::cells.at(p.coord(p.n_coord() - 1).cell).get(); + Cell* c = model::cells.at(p.lowest_coord().cell()).get(); if (p.material() == MATERIAL_VOID) { data_(y, x, 2) = MATERIAL_VOID; return; @@ -74,9 +80,10 @@ PropertyData::PropertyData(size_t h_res, size_t v_res) : data_({v_res, h_res, 2}, NOT_FOUND) {} -void PropertyData::set_value(size_t y, size_t x, const Particle& p, int level) +void PropertyData::set_value( + size_t y, size_t x, const GeometryState& p, int level) { - Cell* c = model::cells.at(p.coord(p.n_coord() - 1).cell).get(); + Cell* c = model::cells.at(p.lowest_coord().cell()).get(); data_(y, x, 0) = (p.sqrtkT() * p.sqrtkT()) / K_BOLTZMANN; if (c->type_ != Fill::UNIVERSE && p.material() != MATERIAL_VOID) { Material* m = model::materials.at(p.material()).get(); @@ -96,7 +103,7 @@ void PropertyData::set_overlap(size_t y, size_t x) namespace model { std::unordered_map plot_map; -vector plots; +vector> plots; uint64_t plotter_seed = 1; } // namespace model @@ -109,20 +116,66 @@ extern "C" int openmc_plot_geometry() { for (auto& pl : model::plots) { - write_message(5, "Processing plot {}: {}...", pl.id_, pl.path_plot_); - - if (PlotType::slice == pl.type_) { - // create 2D image - create_image(pl); - } else if (PlotType::voxel == pl.type_) { - // create voxel file for 3D viewing - create_voxel(pl); - } + write_message(5, "Processing plot {}: {}...", pl->id(), pl->path_plot()); + pl->create_output(); } return 0; } +void Plot::create_output() const +{ + if (PlotType::slice == type_) { + // create 2D image + create_image(); + } else if (PlotType::voxel == type_) { + // create voxel file for 3D viewing + create_voxel(); + } +} + +void Plot::print_info() const +{ + // Plot type + if (PlotType::slice == type_) { + fmt::print("Plot Type: Slice\n"); + } else if (PlotType::voxel == type_) { + fmt::print("Plot Type: Voxel\n"); + } + + // Plot parameters + fmt::print("Origin: {} {} {}\n", origin_[0], origin_[1], origin_[2]); + + if (PlotType::slice == type_) { + fmt::print("Width: {:4} {:4}\n", width_[0], width_[1]); + } else if (PlotType::voxel == type_) { + fmt::print("Width: {:4} {:4} {:4}\n", width_[0], width_[1], width_[2]); + } + + if (PlotColorBy::cells == color_by_) { + fmt::print("Coloring: Cells\n"); + } else if (PlotColorBy::mats == color_by_) { + fmt::print("Coloring: Materials\n"); + } + + if (PlotType::slice == type_) { + switch (basis_) { + case PlotBasis::xy: + fmt::print("Basis: XY\n"); + break; + case PlotBasis::xz: + fmt::print("Basis: XZ\n"); + break; + case PlotBasis::yz: + fmt::print("Basis: YZ\n"); + break; + } + fmt::print("Pixels: {} {}\n", pixels_[0], pixels_[1]); + } else if (PlotType::voxel == type_) { + fmt::print("Voxels: {} {} {}\n", pixels_[0], pixels_[1], pixels_[2]); + } +} + void read_plots_xml() { // Check if plots.xml exists; this is only necessary when the plot runmode is @@ -140,9 +193,36 @@ void read_plots_xml() doc.load_file(filename.c_str()); pugi::xml_node root = doc.document_element(); + + read_plots_xml(root); +} + +void read_plots_xml(pugi::xml_node root) +{ for (auto node : root.children("plot")) { - model::plots.emplace_back(node); - model::plot_map[model::plots.back().id_] = model::plots.size() - 1; + std::string id_string = get_node_value(node, "id", true); + int id = std::stoi(id_string); + if (check_for_node(node, "type")) { + std::string type_str = get_node_value(node, "type", true); + if (type_str == "slice") { + model::plots.emplace_back( + std::make_unique(node, Plot::PlotType::slice)); + } else if (type_str == "voxel") { + model::plots.emplace_back( + std::make_unique(node, Plot::PlotType::voxel)); + } else if (type_str == "wireframe_raytrace") { + model::plots.emplace_back( + std::make_unique(node)); + } else if (type_str == "solid_raytrace") { + model::plots.emplace_back(std::make_unique(node)); + } else { + fatal_error( + fmt::format("Unsupported plot type '{}' in plot {}", type_str, id)); + } + model::plot_map[model::plots.back()->id()] = model::plots.size() - 1; + } else { + fatal_error(fmt::format("Must specify plot type in plot {}", id)); + } } } @@ -152,61 +232,58 @@ void free_memory_plot() model::plot_map.clear(); } -//============================================================================== -// CREATE_IMAGE creates an image based on user input from a plots.xml +// creates an image based on user input from a plots.xml // specification in the PNG/PPM format -//============================================================================== - -void create_image(Plot const& pl) +void Plot::create_image() const { - size_t width = pl.pixels_[0]; - size_t height = pl.pixels_[1]; + size_t width = pixels_[0]; + size_t height = pixels_[1]; - ImageData data({width, height}, pl.not_found_); + ImageData data({width, height}, not_found_); // generate ids for the plot - auto ids = pl.get_map(); + auto ids = get_map(); // assign colors for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { - int idx = pl.color_by_ == PlotColorBy::cells ? 0 : 2; + int idx = color_by_ == PlotColorBy::cells ? 0 : 2; auto id = ids.data_(y, x, idx); // no setting needed if not found if (id == NOT_FOUND) { continue; } if (id == OVERLAP) { - data(x, y) = pl.overlap_color_; + data(x, y) = overlap_color_; continue; } - if (PlotColorBy::cells == pl.color_by_) { - data(x, y) = pl.colors_[model::cell_map[id]]; - } else if (PlotColorBy::mats == pl.color_by_) { + if (PlotColorBy::cells == color_by_) { + data(x, y) = colors_[model::cell_map[id]]; + } else if (PlotColorBy::mats == color_by_) { if (id == MATERIAL_VOID) { data(x, y) = WHITE; continue; } - data(x, y) = pl.colors_[model::material_map[id]]; + data(x, y) = colors_[model::material_map[id]]; } // color_by if-else - } // x for loop - } // y for loop + } + } // draw mesh lines if present - if (pl.index_meshlines_mesh_ >= 0) { - draw_mesh_lines(pl, data); + if (index_meshlines_mesh_ >= 0) { + draw_mesh_lines(data); } // create image file #ifdef USE_LIBPNG - output_png(pl, data); + output_png(path_plot(), data); #else - output_ppm(pl, data); + output_ppm(path_plot(), data); #endif } -void Plot::set_id(pugi::xml_node plot_node) +void PlottableInterface::set_id(pugi::xml_node plot_node) { // Copy data into plots if (check_for_node(plot_node, "id")) { @@ -222,25 +299,15 @@ void Plot::set_id(pugi::xml_node plot_node) } } -void Plot::set_type(pugi::xml_node plot_node) +// Checks if png or ppm is already present +bool file_extension_present( + const std::string& filename, const std::string& extension) { - // Copy plot type - // Default is slice - type_ = PlotType::slice; - // check type specified on plot node - if (check_for_node(plot_node, "type")) { - std::string type_str = get_node_value(plot_node, "type", true); - // set type using node value - if (type_str == "slice") { - type_ = PlotType::slice; - } else if (type_str == "voxel") { - type_ = PlotType::voxel; - } else { - // if we're here, something is wrong - fatal_error( - fmt::format("Unsupported plot type '{}' in plot {}", type_str, id_)); - } - } + std::string file_extension_if_present = + filename.substr(filename.find_last_of(".") + 1); + if (file_extension_if_present == extension) + return true; + return false; } void Plot::set_output_path(pugi::xml_node plot_node) @@ -251,19 +318,27 @@ void Plot::set_output_path(pugi::xml_node plot_node) if (check_for_node(plot_node, "filename")) { filename = get_node_value(plot_node, "filename"); } else { - filename = fmt::format("plot_{}", id_); + filename = fmt::format("plot_{}", id()); + } + const std::string dir_if_present = + filename.substr(0, filename.find_last_of("/") + 1); + if (dir_if_present.size() > 0 && !dir_exists(dir_if_present)) { + fatal_error(fmt::format("Directory '{}' does not exist!", dir_if_present)); } // add appropriate file extension to name switch (type_) { case PlotType::slice: #ifdef USE_LIBPNG - filename.append(".png"); + if (!file_extension_present(filename, "png")) + filename.append(".png"); #else - filename.append(".ppm"); + if (!file_extension_present(filename, "ppm")) + filename.append(".ppm"); #endif break; case PlotType::voxel: - filename.append(".h5"); + if (!file_extension_present(filename, "h5")) + filename.append(".h5"); break; } @@ -277,7 +352,7 @@ void Plot::set_output_path(pugi::xml_node plot_node) pixels_[1] = pxls[1]; } else { fatal_error( - fmt::format(" must be length 2 in slice plot {}", id_)); + fmt::format(" must be length 2 in slice plot {}", id())); } } else if (PlotType::voxel == type_) { if (pxls.size() == 3) { @@ -286,25 +361,20 @@ void Plot::set_output_path(pugi::xml_node plot_node) pixels_[2] = pxls[2]; } else { fatal_error( - fmt::format(" must be length 3 in voxel plot {}", id_)); + fmt::format(" must be length 3 in voxel plot {}", id())); } } } -void Plot::set_bg_color(pugi::xml_node plot_node) +void PlottableInterface::set_bg_color(pugi::xml_node plot_node) { // Copy plot background color if (check_for_node(plot_node, "background")) { vector bg_rgb = get_node_array(plot_node, "background"); - if (PlotType::voxel == type_) { - if (mpi::master) { - warning(fmt::format("Background color ignored in voxel plot {}", id_)); - } - } if (bg_rgb.size() == 3) { not_found_ = bg_rgb; } else { - fatal_error(fmt::format("Bad background RGB in plot {}", id_)); + fatal_error(fmt::format("Bad background RGB in plot {}", id())); } } } @@ -325,7 +395,7 @@ void Plot::set_basis(pugi::xml_node plot_node) basis_ = PlotBasis::yz; } else { fatal_error( - fmt::format("Unsupported plot basis '{}' in plot {}", pl_basis, id_)); + fmt::format("Unsupported plot basis '{}' in plot {}", pl_basis, id())); } } } @@ -337,7 +407,7 @@ void Plot::set_origin(pugi::xml_node plot_node) if (pl_origin.size() == 3) { origin_ = pl_origin; } else { - fatal_error(fmt::format("Origin must be length 3 in plot {}", id_)); + fatal_error(fmt::format("Origin must be length 3 in plot {}", id())); } } @@ -351,7 +421,7 @@ void Plot::set_width(pugi::xml_node plot_node) width_.y = pl_width[1]; } else { fatal_error( - fmt::format(" must be length 2 in slice plot {}", id_)); + fmt::format(" must be length 2 in slice plot {}", id())); } } else if (PlotType::voxel == type_) { if (pl_width.size() == 3) { @@ -359,25 +429,25 @@ void Plot::set_width(pugi::xml_node plot_node) width_ = pl_width; } else { fatal_error( - fmt::format(" must be length 3 in voxel plot {}", id_)); + fmt::format(" must be length 3 in voxel plot {}", id())); } } } -void Plot::set_universe(pugi::xml_node plot_node) +void PlottableInterface::set_universe(pugi::xml_node plot_node) { // Copy plot universe level if (check_for_node(plot_node, "level")) { level_ = std::stoi(get_node_value(plot_node, "level")); if (level_ < 0) { - fatal_error(fmt::format("Bad universe level in plot {}", id_)); + fatal_error(fmt::format("Bad universe level in plot {}", id())); } } else { level_ = PLOT_LEVEL_LOWEST; } } -void Plot::set_default_colors(pugi::xml_node plot_node) +void PlottableInterface::set_default_colors(pugi::xml_node plot_node) { // Copy plot color type and initialize all colors randomly std::string pl_color_by = "cell"; @@ -392,7 +462,7 @@ void Plot::set_default_colors(pugi::xml_node plot_node) colors_.resize(model::materials.size()); } else { fatal_error(fmt::format( - "Unsupported plot color type '{}' in plot {}", pl_color_by, id_)); + "Unsupported plot color type '{}' in plot {}", pl_color_by, id())); } for (auto& c : colors_) { @@ -404,28 +474,21 @@ void Plot::set_default_colors(pugi::xml_node plot_node) } } -void Plot::set_user_colors(pugi::xml_node plot_node) +void PlottableInterface::set_user_colors(pugi::xml_node plot_node) { - if (!plot_node.select_nodes("color").empty() && PlotType::voxel == type_) { - if (mpi::master) { - warning( - fmt::format("Color specifications ignored in voxel plot {}", id_)); - } - } - for (auto cn : plot_node.children("color")) { // Make sure 3 values are specified for RGB vector user_rgb = get_node_array(cn, "rgb"); if (user_rgb.size() != 3) { - fatal_error(fmt::format("Bad RGB in plot {}", id_)); + fatal_error(fmt::format("Bad RGB in plot {}", id())); } // Ensure that there is an id for this color specification int col_id; if (check_for_node(cn, "id")) { col_id = std::stoi(get_node_value(cn, "id")); } else { - fatal_error( - fmt::format("Must specify id for color specification in plot {}", id_)); + fatal_error(fmt::format( + "Must specify id for color specification in plot {}", id())); } // Add RGB if (PlotColorBy::cells == color_by_) { @@ -434,7 +497,7 @@ void Plot::set_user_colors(pugi::xml_node plot_node) colors_[col_id] = user_rgb; } else { warning(fmt::format( - "Could not find cell {} specified in plot {}", col_id, id_)); + "Could not find cell {} specified in plot {}", col_id, id())); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { @@ -442,7 +505,7 @@ void Plot::set_user_colors(pugi::xml_node plot_node) colors_[col_id] = user_rgb; } else { warning(fmt::format( - "Could not find material {} specified in plot {}", col_id, id_)); + "Could not find material {} specified in plot {}", col_id, id())); } } } // color node loop @@ -455,7 +518,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) if (!mesh_line_nodes.empty()) { if (PlotType::voxel == type_) { - warning(fmt::format("Meshlines ignored in voxel plot {}", id_)); + warning(fmt::format("Meshlines ignored in voxel plot {}", id())); } if (mesh_line_nodes.size() == 1) { @@ -469,7 +532,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else { fatal_error(fmt::format( "Must specify a meshtype for meshlines specification in plot {}", - id_)); + id())); } // Ensure that there is a linewidth for this meshlines specification @@ -480,7 +543,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else { fatal_error(fmt::format( "Must specify a linewidth for meshlines specification in plot {}", - id_)); + id())); } // Check for color @@ -489,7 +552,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) vector ml_rgb = get_node_array(meshlines_node, "color"); if (ml_rgb.size() != 3) { fatal_error( - fmt::format("Bad RGB for meshlines color in plot {}", id_)); + fmt::format("Bad RGB for meshlines color in plot {}", id())); } meshlines_color_ = ml_rgb; } @@ -497,7 +560,8 @@ void Plot::set_meshlines(pugi::xml_node plot_node) // Set mesh based on type if ("ufs" == meshtype) { if (!simulation::ufs_mesh) { - fatal_error(fmt::format("No UFS mesh for meshlines on plot {}", id_)); + fatal_error( + fmt::format("No UFS mesh for meshlines on plot {}", id())); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m = @@ -513,7 +577,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) } else if ("entropy" == meshtype) { if (!simulation::entropy_mesh) { fatal_error( - fmt::format("No entropy mesh for meshlines on plot {}", id_)); + fmt::format("No entropy mesh for meshlines on plot {}", id())); } else { for (int i = 0; i < model::meshes.size(); ++i) { if (const auto* m = @@ -535,7 +599,7 @@ void Plot::set_meshlines(pugi::xml_node plot_node) std::stringstream err_msg; fatal_error(fmt::format("Must specify a mesh id for meshlines tally " "mesh specification in plot {}", - id_)); + id())); } // find the tally index int idx; @@ -543,30 +607,24 @@ void Plot::set_meshlines(pugi::xml_node plot_node) if (err != 0) { fatal_error(fmt::format("Could not find mesh {} specified in " "meshlines for plot {}", - tally_mesh_id, id_)); + tally_mesh_id, id())); } index_meshlines_mesh_ = idx; } else { - fatal_error(fmt::format("Invalid type for meshlines on plot {}", id_)); + fatal_error(fmt::format("Invalid type for meshlines on plot {}", id())); } } else { - fatal_error(fmt::format("Mutliple meshlines specified in plot {}", id_)); + fatal_error(fmt::format("Mutliple meshlines specified in plot {}", id())); } } } -void Plot::set_mask(pugi::xml_node plot_node) +void PlottableInterface::set_mask(pugi::xml_node plot_node) { // Deal with masks pugi::xpath_node_set mask_nodes = plot_node.select_nodes("mask"); if (!mask_nodes.empty()) { - if (PlotType::voxel == type_) { - if (mpi::master) { - warning(fmt::format("Mask ignored in voxel plot {}", id_)); - } - } - if (mask_nodes.size() == 1) { // Get pointer to mask pugi::xml_node mask_node = mask_nodes[0].node(); @@ -575,7 +633,7 @@ void Plot::set_mask(pugi::xml_node plot_node) vector iarray = get_node_array(mask_node, "components"); if (iarray.size() == 0) { fatal_error( - fmt::format("Missing in mask of plot {}", id_)); + fmt::format("Missing in mask of plot {}", id())); } // First we need to change the user-specified identifiers to indices @@ -587,7 +645,7 @@ void Plot::set_mask(pugi::xml_node plot_node) } else { fatal_error(fmt::format("Could not find cell {} specified in the " "mask in plot {}", - col_id, id_)); + col_id, id())); } } else if (PlotColorBy::mats == color_by_) { if (model::material_map.find(col_id) != model::material_map.end()) { @@ -595,14 +653,14 @@ void Plot::set_mask(pugi::xml_node plot_node) } else { fatal_error(fmt::format("Could not find material {} specified in " "the mask in plot {}", - col_id, id_)); + col_id, id())); } } } // Alter colors based on mask information for (int j = 0; j < colors_.size(); j++) { - if (std::find(iarray.begin(), iarray.end(), j) == iarray.end()) { + if (contains(iarray, j)) { if (check_for_node(mask_node, "background")) { vector bg_rgb = get_node_array(mask_node, "background"); colors_[j] = bg_rgb; @@ -613,12 +671,12 @@ void Plot::set_mask(pugi::xml_node plot_node) } } else { - fatal_error(fmt::format("Mutliple masks specified in plot {}", id_)); + fatal_error(fmt::format("Mutliple masks specified in plot {}", id())); } } } -void Plot::set_overlap_color(pugi::xml_node plot_node) +void PlottableInterface::set_overlap_color(pugi::xml_node plot_node) { color_overlaps_ = false; if (check_for_node(plot_node, "show_overlaps")) { @@ -628,13 +686,13 @@ void Plot::set_overlap_color(pugi::xml_node plot_node) if (!color_overlaps_) { warning(fmt::format( "Overlap color specified in plot {} but overlaps won't be shown.", - id_)); + id())); } vector olap_clr = get_node_array(plot_node, "overlap_color"); if (olap_clr.size() == 3) { overlap_color_ = olap_clr; } else { - fatal_error(fmt::format("Bad overlap RGB in plot {}", id_)); + fatal_error(fmt::format("Bad overlap RGB in plot {}", id())); } } } @@ -647,32 +705,37 @@ void Plot::set_overlap_color(pugi::xml_node plot_node) } } -Plot::Plot(pugi::xml_node plot_node) - : index_meshlines_mesh_ {-1}, overlap_color_ {RED} +PlottableInterface::PlottableInterface(pugi::xml_node plot_node) { set_id(plot_node); - set_type(plot_node); - set_output_path(plot_node); set_bg_color(plot_node); - set_basis(plot_node); - set_origin(plot_node); - set_width(plot_node); set_universe(plot_node); set_default_colors(plot_node); set_user_colors(plot_node); - set_meshlines(plot_node); set_mask(plot_node); set_overlap_color(plot_node); -} // End Plot constructor +} + +Plot::Plot(pugi::xml_node plot_node, PlotType type) + : PlottableInterface(plot_node), type_(type), index_meshlines_mesh_ {-1} +{ + set_output_path(plot_node); + set_basis(plot_node); + set_origin(plot_node); + set_width(plot_node); + set_meshlines(plot_node); + slice_level_ = level_; // Copy level employed in SlicePlotBase::get_map + slice_color_overlaps_ = color_overlaps_; +} //============================================================================== // OUTPUT_PPM writes out a previously generated image to a PPM file //============================================================================== -void output_ppm(Plot const& pl, const ImageData& data) +void output_ppm(const std::string& filename, const ImageData& data) { // Open PPM file for writing - std::string fname = pl.path_plot_; + std::string fname = filename; fname = strtrim(fname); std::ofstream of; @@ -680,14 +743,14 @@ void output_ppm(Plot const& pl, const ImageData& data) // Write header of << "P6\n"; - of << pl.pixels_[0] << " " << pl.pixels_[1] << "\n"; + of << data.shape()[0] << " " << data.shape()[1] << "\n"; of << "255\n"; of.close(); of.open(fname, std::ios::binary | std::ios::app); // Write color for each pixel - for (int y = 0; y < pl.pixels_[1]; y++) { - for (int x = 0; x < pl.pixels_[0]; x++) { + for (int y = 0; y < data.shape()[1]; y++) { + for (int x = 0; x < data.shape()[0]; x++) { RGBColor rgb = data(x, y); of << rgb.red << rgb.green << rgb.blue; } @@ -700,10 +763,10 @@ void output_ppm(Plot const& pl, const ImageData& data) //============================================================================== #ifdef USE_LIBPNG -void output_png(Plot const& pl, const ImageData& data) +void output_png(const std::string& filename, const ImageData& data) { // Open PNG file for writing - std::string fname = pl.path_plot_; + std::string fname = filename; fname = strtrim(fname); auto fp = std::fopen(fname.c_str(), "wb"); @@ -719,8 +782,8 @@ void output_png(Plot const& pl, const ImageData& data) png_init_io(png_ptr, fp); // Write header (8 bit colour depth) - int width = pl.pixels_[0]; - int height = pl.pixels_[1]; + int width = data.shape()[0]; + int height = data.shape()[1]; png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); @@ -753,13 +816,13 @@ void output_png(Plot const& pl, const ImageData& data) // DRAW_MESH_LINES draws mesh line boundaries on an image //============================================================================== -void draw_mesh_lines(Plot const& pl, ImageData& data) +void Plot::draw_mesh_lines(ImageData& data) const { RGBColor rgb; - rgb = pl.meshlines_color_; + rgb = meshlines_color_; int ax1, ax2; - switch (pl.basis_) { + switch (basis_) { case PlotBasis::xy: ax1 = 0; ax2 = 1; @@ -776,45 +839,45 @@ void draw_mesh_lines(Plot const& pl, ImageData& data) UNREACHABLE(); } - Position ll_plot {pl.origin_}; - Position ur_plot {pl.origin_}; + Position ll_plot {origin_}; + Position ur_plot {origin_}; - ll_plot[ax1] -= pl.width_[0] / 2.; - ll_plot[ax2] -= pl.width_[1] / 2.; - ur_plot[ax1] += pl.width_[0] / 2.; - ur_plot[ax2] += pl.width_[1] / 2.; + ll_plot[ax1] -= width_[0] / 2.; + ll_plot[ax2] -= width_[1] / 2.; + ur_plot[ax1] += width_[0] / 2.; + ur_plot[ax2] += width_[1] / 2.; Position width = ur_plot - ll_plot; // Find the (axis-aligned) lines of the mesh that intersect this plot. auto axis_lines = - model::meshes[pl.index_meshlines_mesh_]->plot(ll_plot, ur_plot); + model::meshes[index_meshlines_mesh_]->plot(ll_plot, ur_plot); // Find the bounds along the second axis (accounting for low-D meshes). int ax2_min, ax2_max; if (axis_lines.second.size() > 0) { double frac = (axis_lines.second.back() - ll_plot[ax2]) / width[ax2]; - ax2_min = (1.0 - frac) * pl.pixels_[1]; + ax2_min = (1.0 - frac) * pixels_[1]; if (ax2_min < 0) ax2_min = 0; frac = (axis_lines.second.front() - ll_plot[ax2]) / width[ax2]; - ax2_max = (1.0 - frac) * pl.pixels_[1]; - if (ax2_max > pl.pixels_[1]) - ax2_max = pl.pixels_[1]; + ax2_max = (1.0 - frac) * pixels_[1]; + if (ax2_max > pixels_[1]) + ax2_max = pixels_[1]; } else { ax2_min = 0; - ax2_max = pl.pixels_[1]; + ax2_max = pixels_[1]; } // Iterate across the first axis and draw lines. for (auto ax1_val : axis_lines.first) { double frac = (ax1_val - ll_plot[ax1]) / width[ax1]; - int ax1_ind = frac * pl.pixels_[0]; + int ax1_ind = frac * pixels_[0]; for (int ax2_ind = ax2_min; ax2_ind < ax2_max; ++ax2_ind) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - if (ax1_ind + plus >= 0 && ax1_ind + plus < pl.pixels_[0]) + for (int plus = 0; plus <= meshlines_width_; plus++) { + if (ax1_ind + plus >= 0 && ax1_ind + plus < pixels_[0]) data(ax1_ind + plus, ax2_ind) = rgb; - if (ax1_ind - plus >= 0 && ax1_ind - plus < pl.pixels_[0]) + if (ax1_ind - plus >= 0 && ax1_ind - plus < pixels_[0]) data(ax1_ind - plus, ax2_ind) = rgb; } } @@ -824,60 +887,57 @@ void draw_mesh_lines(Plot const& pl, ImageData& data) int ax1_min, ax1_max; if (axis_lines.first.size() > 0) { double frac = (axis_lines.first.front() - ll_plot[ax1]) / width[ax1]; - ax1_min = frac * pl.pixels_[0]; + ax1_min = frac * pixels_[0]; if (ax1_min < 0) ax1_min = 0; frac = (axis_lines.first.back() - ll_plot[ax1]) / width[ax1]; - ax1_max = frac * pl.pixels_[0]; - if (ax1_max > pl.pixels_[0]) - ax1_max = pl.pixels_[0]; + ax1_max = frac * pixels_[0]; + if (ax1_max > pixels_[0]) + ax1_max = pixels_[0]; } else { ax1_min = 0; - ax1_max = pl.pixels_[0]; + ax1_max = pixels_[0]; } // Iterate across the second axis and draw lines. for (auto ax2_val : axis_lines.second) { double frac = (ax2_val - ll_plot[ax2]) / width[ax2]; - int ax2_ind = (1.0 - frac) * pl.pixels_[1]; + int ax2_ind = (1.0 - frac) * pixels_[1]; for (int ax1_ind = ax1_min; ax1_ind < ax1_max; ++ax1_ind) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - if (ax2_ind + plus >= 0 && ax2_ind + plus < pl.pixels_[1]) + for (int plus = 0; plus <= meshlines_width_; plus++) { + if (ax2_ind + plus >= 0 && ax2_ind + plus < pixels_[1]) data(ax1_ind, ax2_ind + plus) = rgb; - if (ax2_ind - plus >= 0 && ax2_ind - plus < pl.pixels_[1]) + if (ax2_ind - plus >= 0 && ax2_ind - plus < pixels_[1]) data(ax1_ind, ax2_ind - plus) = rgb; } } } } -//============================================================================== -// CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D -// geometry visualization. It works the same way as create_image by dragging a -// particle across the geometry for the specified number of voxels. The first 3 -// int's in the binary are the number of x, y, and z voxels. The next 3 -// double's are the widths of the voxels in the x, y, and z directions. The -// next 3 double's are the x, y, and z coordinates of the lower left -// point. Finally the binary is filled with entries of four int's each. Each -// 'row' in the binary contains four int's: 3 for x,y,z position and 1 for -// cell or material id. For 1 million voxels this produces a file of -// approximately 15MB. -// ============================================================================= - -void create_voxel(Plot const& pl) +/* outputs a binary file that can be input into silomesh for 3D geometry + * visualization. It works the same way as create_image by dragging a particle + * across the geometry for the specified number of voxels. The first 3 int's in + * the binary are the number of x, y, and z voxels. The next 3 double's are + * the widths of the voxels in the x, y, and z directions. The next 3 double's + * are the x, y, and z coordinates of the lower left point. Finally the binary + * is filled with entries of four int's each. Each 'row' in the binary contains + * four int's: 3 for x,y,z position and 1 for cell or material id. For 1 + * million voxels this produces a file of approximately 15MB. + */ +void Plot::create_voxel() const { // compute voxel widths in each direction array vox; - vox[0] = pl.width_[0] / (double)pl.pixels_[0]; - vox[1] = pl.width_[1] / (double)pl.pixels_[1]; - vox[2] = pl.width_[2] / (double)pl.pixels_[2]; + vox[0] = width_[0] / static_cast(pixels_[0]); + vox[1] = width_[1] / static_cast(pixels_[1]); + vox[2] = width_[2] / static_cast(pixels_[2]); // initial particle position - Position ll = pl.origin_ - pl.width_ / 2.; + Position ll = origin_ - width_ / 2.; // Open binary plot file for writing std::ofstream of; - std::string fname = std::string(pl.path_plot_); + std::string fname = std::string(path_plot_); fname = strtrim(fname); hid_t file_id = file_open(fname, 'w'); @@ -893,7 +953,7 @@ void create_voxel(Plot const& pl) // Write current date and time write_attribute(file_id, "date_and_time", time_stamp().c_str()); array pixels; - std::copy(pl.pixels_.begin(), pl.pixels_.end(), pixels.begin()); + std::copy(pixels_.begin(), pixels_.end(), pixels.begin()); write_attribute(file_id, "num_voxels", pixels); write_attribute(file_id, "voxel_width", vox); write_attribute(file_id, "lower_left", ll); @@ -901,25 +961,21 @@ void create_voxel(Plot const& pl) // Create dataset for voxel data -- note that the dimensions are reversed // since we want the order in the file to be z, y, x hsize_t dims[3]; - dims[0] = pl.pixels_[2]; - dims[1] = pl.pixels_[1]; - dims[2] = pl.pixels_[0]; + dims[0] = pixels_[2]; + dims[1] = pixels_[1]; + dims[2] = pixels_[0]; hid_t dspace, dset, memspace; voxel_init(file_id, &(dims[0]), &dspace, &dset, &memspace); - PlotBase pltbase; - pltbase.width_ = pl.width_; - pltbase.origin_ = pl.origin_; + SlicePlotBase pltbase; + pltbase.width_ = width_; + pltbase.origin_ = origin_; pltbase.basis_ = PlotBasis::xy; - pltbase.pixels_ = pl.pixels_; - pltbase.level_ = -1; // all universes for voxel files - pltbase.color_overlaps_ = pl.color_overlaps_; + pltbase.pixels_ = pixels_; + pltbase.slice_color_overlaps_ = color_overlaps_; ProgressBar pb; - for (int z = 0; z < pl.pixels_[2]; z++) { - // update progress bar - pb.set_value(100. * (double)z / (double)(pl.pixels_[2] - 1)); - + for (int z = 0; z < pixels_[2]; z++) { // update z coordinate pltbase.origin_.z = ll.z + z * vox[2]; @@ -927,13 +983,17 @@ void create_voxel(Plot const& pl) IdData ids = pltbase.get_map(); // select only cell/material ID data and flip the y-axis - int idx = pl.color_by_ == PlotColorBy::cells ? 0 : 2; + int idx = color_by_ == PlotColorBy::cells ? 0 : 2; xt::xtensor data_slice = xt::view(ids.data_, xt::all(), xt::all(), idx); xt::xtensor data_flipped = xt::flip(data_slice, 0); // Write to HDF5 dataset voxel_write_slice(z, dspace, dset, memspace, data_flipped.data()); + + // update progress bar + pb.set_value( + 100. * static_cast(z + 1) / static_cast((pixels_[2]))); } voxel_finalize(dspace, dset, memspace); @@ -979,16 +1039,839 @@ RGBColor random_color(void) int(prn(&model::plotter_seed) * 255), int(prn(&model::plotter_seed) * 255)}; } +RayTracePlot::RayTracePlot(pugi::xml_node node) : PlottableInterface(node) +{ + set_look_at(node); + set_camera_position(node); + set_field_of_view(node); + set_pixels(node); + set_orthographic_width(node); + set_output_path(node); + + if (check_for_node(node, "orthographic_width") && + check_for_node(node, "field_of_view")) + fatal_error("orthographic_width and field_of_view are mutually exclusive " + "parameters."); + + // Get centerline vector for camera-to-model. We create vectors around this + // that form a pixel array, and then trace rays along that. + auto up = up_ / up_.norm(); + Direction looking_direction = look_at_ - camera_position_; + looking_direction /= looking_direction.norm(); + if (std::abs(std::abs(looking_direction.dot(up)) - 1.0) < 1e-9) + fatal_error("Up vector cannot align with vector between camera position " + "and look_at!"); + Direction cam_yaxis = looking_direction.cross(up); + cam_yaxis /= cam_yaxis.norm(); + Direction cam_zaxis = cam_yaxis.cross(looking_direction); + cam_zaxis /= cam_zaxis.norm(); + + // Cache the camera-to-model matrix + camera_to_model_ = {looking_direction.x, cam_yaxis.x, cam_zaxis.x, + looking_direction.y, cam_yaxis.y, cam_zaxis.y, looking_direction.z, + cam_yaxis.z, cam_zaxis.z}; +} + +WireframeRayTracePlot::WireframeRayTracePlot(pugi::xml_node node) + : RayTracePlot(node) +{ + set_opacities(node); + set_wireframe_thickness(node); + set_wireframe_ids(node); + set_wireframe_color(node); +} + +void WireframeRayTracePlot::set_wireframe_color(pugi::xml_node plot_node) +{ + // Copy plot wireframe color + if (check_for_node(plot_node, "wireframe_color")) { + vector w_rgb = get_node_array(plot_node, "wireframe_color"); + if (w_rgb.size() == 3) { + wireframe_color_ = w_rgb; + } else { + fatal_error(fmt::format("Bad wireframe RGB in plot {}", id())); + } + } +} + +void RayTracePlot::set_output_path(pugi::xml_node node) +{ + // Set output file path + std::string filename; + + if (check_for_node(node, "filename")) { + filename = get_node_value(node, "filename"); + } else { + filename = fmt::format("plot_{}", id()); + } + +#ifdef USE_LIBPNG + if (!file_extension_present(filename, "png")) + filename.append(".png"); +#else + if (!file_extension_present(filename, "ppm")) + filename.append(".ppm"); +#endif + path_plot_ = filename; +} + +bool WireframeRayTracePlot::trackstack_equivalent( + const std::vector& track1, + const std::vector& track2) const +{ + if (wireframe_ids_.empty()) { + // Draw wireframe for all surfaces/cells/materials + if (track1.size() != track2.size()) + return false; + for (int i = 0; i < track1.size(); ++i) { + if (track1[i].id != track2[i].id || + track1[i].surface_index != track2[i].surface_index) { + return false; + } + } + return true; + } else { + // This runs in O(nm) where n is the intersection stack size + // and m is the number of IDs we are wireframing. A simpler + // algorithm can likely be found. + for (const int id : wireframe_ids_) { + int t1_i = 0; + int t2_i = 0; + + // Advance to first instance of the ID + while (t1_i < track1.size() && t2_i < track2.size()) { + while (t1_i < track1.size() && track1[t1_i].id != id) + t1_i++; + while (t2_i < track2.size() && track2[t2_i].id != id) + t2_i++; + + // This one is really important! + if ((t1_i == track1.size() && t2_i != track2.size()) || + (t1_i != track1.size() && t2_i == track2.size())) + return false; + if (t1_i == track1.size() && t2_i == track2.size()) + break; + // Check if surface different + if (track1[t1_i].surface_index != track2[t2_i].surface_index) + return false; + + // Pretty sure this should not be used: + // if (t2_i != track2.size() - 1 && + // t1_i != track1.size() - 1 && + // track1[t1_i+1].id != track2[t2_i+1].id) return false; + if (t2_i != 0 && t1_i != 0 && + track1[t1_i - 1].surface_index != track2[t2_i - 1].surface_index) + return false; + + // Check if neighboring cells are different + // if (track1[t1_i ? t1_i - 1 : 0].id != track2[t2_i ? t2_i - 1 : 0].id) + // return false; if (track1[t1_i < track1.size() - 1 ? t1_i + 1 : t1_i + // ].id != + // track2[t2_i < track2.size() - 1 ? t2_i + 1 : t2_i].id) return + // false; + t1_i++, t2_i++; + } + } + return true; + } +} + +std::pair RayTracePlot::get_pixel_ray( + int horiz, int vert) const +{ + // Compute field of view in radians + constexpr double DEGREE_TO_RADIAN = M_PI / 180.0; + double horiz_fov_radians = horizontal_field_of_view_ * DEGREE_TO_RADIAN; + double p0 = static_cast(pixels_[0]); + double p1 = static_cast(pixels_[1]); + double vert_fov_radians = horiz_fov_radians * p1 / p0; + + // focal_plane_dist can be changed to alter the perspective distortion + // effect. This is in units of cm. This seems to look good most of the + // time. TODO let this variable be set through XML. + constexpr double focal_plane_dist = 10.0; + const double dx = 2.0 * focal_plane_dist * std::tan(0.5 * horiz_fov_radians); + const double dy = p1 / p0 * dx; + + std::pair result; + + // Generate the starting position/direction of the ray + if (orthographic_width_ == C_NONE) { // perspective projection + Direction camera_local_vec; + camera_local_vec.x = focal_plane_dist; + camera_local_vec.y = -0.5 * dx + horiz * dx / p0; + camera_local_vec.z = 0.5 * dy - vert * dy / p1; + camera_local_vec /= camera_local_vec.norm(); + + result.first = camera_position_; + result.second = camera_local_vec.rotate(camera_to_model_); + } else { // orthographic projection + + double x_pix_coord = (static_cast(horiz) - p0 / 2.0) / p0; + double y_pix_coord = (static_cast(vert) - p1 / 2.0) / p1; + + result.first = camera_position_ + + camera_y_axis() * x_pix_coord * orthographic_width_ + + camera_z_axis() * y_pix_coord * orthographic_width_; + result.second = camera_x_axis(); + } + + return result; +} + +void WireframeRayTracePlot::create_output() const +{ + size_t width = pixels_[0]; + size_t height = pixels_[1]; + ImageData data({width, height}, not_found_); + + // This array marks where the initial wireframe was drawn. We convolve it with + // a filter that gets adjusted with the wireframe thickness in order to + // thicken the lines. + xt::xtensor wireframe_initial({width, height}, 0); + + /* Holds all of the track segments for the current rendered line of pixels. + * old_segments holds a copy of this_line_segments from the previous line. + * By holding both we can check if the cell/material intersection stack + * differs from the left or upper neighbor. This allows a robustly drawn + * wireframe. If only checking the left pixel (which requires substantially + * less memory), the wireframe tends to be spotty and be disconnected for + * surface edges oriented horizontally in the rendering. + * + * Note that a vector of vectors is required rather than a 2-tensor, + * since the stack size varies within each column. + */ + const int n_threads = num_threads(); + std::vector>> this_line_segments( + n_threads); + for (int t = 0; t < n_threads; ++t) { + this_line_segments[t].resize(pixels_[0]); + } + + // The last thread writes to this, and the first thread reads from it. + std::vector> old_segments(pixels_[0]); + +#pragma omp parallel + { + const int n_threads = num_threads(); + const int tid = thread_num(); + + int vert = tid; + for (int iter = 0; iter <= pixels_[1] / n_threads; iter++) { + + // Save bottom line of current work chunk to compare against later. This + // used to be inside the below if block, but it causes a spurious line to + // be drawn at the bottom of the image. Not sure why, but moving it here + // fixes things. + if (tid == n_threads - 1) + old_segments = this_line_segments[n_threads - 1]; + + if (vert < pixels_[1]) { + + for (int horiz = 0; horiz < pixels_[0]; ++horiz) { + + // RayTracePlot implements camera ray generation + std::pair ru = get_pixel_ray(horiz, vert); + + this_line_segments[tid][horiz].clear(); + ProjectionRay ray( + ru.first, ru.second, *this, this_line_segments[tid][horiz]); + + ray.trace(); + + // Now color the pixel based on what we have intersected... + // Loops backwards over intersections. + Position current_color( + not_found_.red, not_found_.green, not_found_.blue); + const auto& segments = this_line_segments[tid][horiz]; + + // There must be at least two cell intersections to color, front and + // back of the cell. Maybe an infinitely thick cell could be present + // with no back, but why would you want to color that? It's easier to + // just skip that edge case and not even color it. + if (segments.size() <= 1) + continue; + + for (int i = segments.size() - 2; i >= 0; --i) { + int colormap_idx = segments[i].id; + RGBColor seg_color = colors_[colormap_idx]; + Position seg_color_vec( + seg_color.red, seg_color.green, seg_color.blue); + double mixing = + std::exp(-xs_[colormap_idx] * + (segments[i + 1].length - segments[i].length)); + current_color = + current_color * mixing + (1.0 - mixing) * seg_color_vec; + } + + // save result converting from double-precision color coordinates to + // byte-sized + RGBColor result; + result.red = static_cast(current_color.x); + result.green = static_cast(current_color.y); + result.blue = static_cast(current_color.z); + data(horiz, vert) = result; + + // Check to draw wireframe in horizontal direction. No inter-thread + // comm. + if (horiz > 0) { + if (!trackstack_equivalent(this_line_segments[tid][horiz], + this_line_segments[tid][horiz - 1])) { + wireframe_initial(horiz, vert) = 1; + } + } + } + } // end "if" vert in correct range + + // We require a barrier before comparing vertical neighbors' intersection + // stacks. i.e. all threads must be done with their line. +#pragma omp barrier + + // Now that the horizontal line has finished rendering, we can fill in + // wireframe entries that require comparison among all the threads. Hence + // the omp barrier being used. It has to be OUTSIDE any if blocks! + if (vert < pixels_[1]) { + // Loop over horizontal pixels, checking intersection stack of upper + // neighbor + + const std::vector>* top_cmp = nullptr; + if (tid == 0) + top_cmp = &old_segments; + else + top_cmp = &this_line_segments[tid - 1]; + + for (int horiz = 0; horiz < pixels_[0]; ++horiz) { + if (!trackstack_equivalent( + this_line_segments[tid][horiz], (*top_cmp)[horiz])) { + wireframe_initial(horiz, vert) = 1; + } + } + } + + // We need another barrier to ensure threads don't proceed to modify their + // intersection stacks on that horizontal line while others are + // potentially still working on the above. +#pragma omp barrier + vert += n_threads; + } + } // end omp parallel + + // Now thicken the wireframe lines and apply them to our image + for (int vert = 0; vert < pixels_[1]; ++vert) { + for (int horiz = 0; horiz < pixels_[0]; ++horiz) { + if (wireframe_initial(horiz, vert)) { + if (wireframe_thickness_ == 1) + data(horiz, vert) = wireframe_color_; + for (int i = -wireframe_thickness_ / 2; i < wireframe_thickness_ / 2; + ++i) + for (int j = -wireframe_thickness_ / 2; j < wireframe_thickness_ / 2; + ++j) + if (i * i + j * j < wireframe_thickness_ * wireframe_thickness_) { + + // Check if wireframe pixel is out of bounds + int w_i = std::max(std::min(horiz + i, pixels_[0] - 1), 0); + int w_j = std::max(std::min(vert + j, pixels_[1] - 1), 0); + data(w_i, w_j) = wireframe_color_; + } + } + } + } + +#ifdef USE_LIBPNG + output_png(path_plot(), data); +#else + output_ppm(path_plot(), data); +#endif +} + +void RayTracePlot::print_info() const +{ + fmt::print("Camera position: {} {} {}\n", camera_position_.x, + camera_position_.y, camera_position_.z); + fmt::print("Look at: {} {} {}\n", look_at_.x, look_at_.y, look_at_.z); + fmt::print( + "Horizontal field of view: {} degrees\n", horizontal_field_of_view_); + fmt::print("Pixels: {} {}\n", pixels_[0], pixels_[1]); +} + +void WireframeRayTracePlot::print_info() const +{ + fmt::print("Plot Type: Wireframe ray-traced\n"); + RayTracePlot::print_info(); +} + +void WireframeRayTracePlot::set_opacities(pugi::xml_node node) +{ + xs_.resize(colors_.size(), 1e6); // set to large value for opaque by default + + for (auto cn : node.children("color")) { + // Make sure 3 values are specified for RGB + double user_xs = std::stod(get_node_value(cn, "xs")); + int col_id = std::stoi(get_node_value(cn, "id")); + + // Add RGB + if (PlotColorBy::cells == color_by_) { + if (model::cell_map.find(col_id) != model::cell_map.end()) { + col_id = model::cell_map[col_id]; + xs_[col_id] = user_xs; + } else { + warning(fmt::format( + "Could not find cell {} specified in plot {}", col_id, id())); + } + } else if (PlotColorBy::mats == color_by_) { + if (model::material_map.find(col_id) != model::material_map.end()) { + col_id = model::material_map[col_id]; + xs_[col_id] = user_xs; + } else { + warning(fmt::format( + "Could not find material {} specified in plot {}", col_id, id())); + } + } + } +} + +void RayTracePlot::set_orthographic_width(pugi::xml_node node) +{ + if (check_for_node(node, "orthographic_width")) { + double orthographic_width = + std::stod(get_node_value(node, "orthographic_width", true)); + if (orthographic_width < 0.0) + fatal_error("Requires positive orthographic_width"); + orthographic_width_ = orthographic_width; + } +} + +void WireframeRayTracePlot::set_wireframe_thickness(pugi::xml_node node) +{ + if (check_for_node(node, "wireframe_thickness")) { + int wireframe_thickness = + std::stoi(get_node_value(node, "wireframe_thickness", true)); + if (wireframe_thickness < 0) + fatal_error("Requires non-negative wireframe thickness"); + wireframe_thickness_ = wireframe_thickness; + } +} + +void WireframeRayTracePlot::set_wireframe_ids(pugi::xml_node node) +{ + if (check_for_node(node, "wireframe_ids")) { + wireframe_ids_ = get_node_array(node, "wireframe_ids"); + // It is read in as actual ID values, but we have to convert to indices in + // mat/cell array + for (auto& x : wireframe_ids_) + x = color_by_ == PlotColorBy::mats ? model::material_map[x] + : model::cell_map[x]; + } + // We make sure the list is sorted in order to later use + // std::binary_search. + std::sort(wireframe_ids_.begin(), wireframe_ids_.end()); +} + +void RayTracePlot::set_pixels(pugi::xml_node node) +{ + vector pxls = get_node_array(node, "pixels"); + if (pxls.size() != 2) + fatal_error( + fmt::format(" must be length 2 in projection plot {}", id())); + pixels_[0] = pxls[0]; + pixels_[1] = pxls[1]; +} + +void RayTracePlot::set_camera_position(pugi::xml_node node) +{ + vector camera_pos = get_node_array(node, "camera_position"); + if (camera_pos.size() != 3) { + fatal_error(fmt::format( + "camera_position element must have three floating point values")); + } + camera_position_.x = camera_pos[0]; + camera_position_.y = camera_pos[1]; + camera_position_.z = camera_pos[2]; +} + +void RayTracePlot::set_look_at(pugi::xml_node node) +{ + vector look_at = get_node_array(node, "look_at"); + if (look_at.size() != 3) { + fatal_error("look_at element must have three floating point values"); + } + look_at_.x = look_at[0]; + look_at_.y = look_at[1]; + look_at_.z = look_at[2]; +} + +void RayTracePlot::set_field_of_view(pugi::xml_node node) +{ + // Defaults to 70 degree horizontal field of view (see .h file) + if (check_for_node(node, "horizontal_field_of_view")) { + double fov = + std::stod(get_node_value(node, "horizontal_field_of_view", true)); + if (fov < 180.0 && fov > 0.0) { + horizontal_field_of_view_ = fov; + } else { + fatal_error(fmt::format("Horizontal field of view for plot {} " + "out-of-range. Must be in (0, 180) degrees.", + id())); + } + } +} + +SolidRayTracePlot::SolidRayTracePlot(pugi::xml_node node) : RayTracePlot(node) +{ + set_opaque_ids(node); + set_diffuse_fraction(node); + set_light_position(node); +} + +void SolidRayTracePlot::print_info() const +{ + fmt::print("Plot Type: Solid ray-traced\n"); + RayTracePlot::print_info(); +} + +void SolidRayTracePlot::create_output() const +{ + size_t width = pixels_[0]; + size_t height = pixels_[1]; + ImageData data({width, height}, not_found_); + +#pragma omp parallel for schedule(dynamic) collapse(2) + for (int horiz = 0; horiz < pixels_[0]; ++horiz) { + for (int vert = 0; vert < pixels_[1]; ++vert) { + // RayTracePlot implements camera ray generation + std::pair ru = get_pixel_ray(horiz, vert); + PhongRay ray(ru.first, ru.second, *this); + ray.trace(); + data(horiz, vert) = ray.result_color(); + } + } + +#ifdef USE_LIBPNG + output_png(path_plot(), data); +#else + output_ppm(path_plot(), data); +#endif +} + +void SolidRayTracePlot::set_opaque_ids(pugi::xml_node node) +{ + if (check_for_node(node, "opaque_ids")) { + auto opaque_ids_tmp = get_node_array(node, "opaque_ids"); + + // It is read in as actual ID values, but we have to convert to indices in + // mat/cell array + for (auto& x : opaque_ids_tmp) + x = color_by_ == PlotColorBy::mats ? model::material_map[x] + : model::cell_map[x]; + + opaque_ids_.insert(opaque_ids_tmp.begin(), opaque_ids_tmp.end()); + } +} + +void SolidRayTracePlot::set_light_position(pugi::xml_node node) +{ + if (check_for_node(node, "light_position")) { + auto light_pos_tmp = get_node_array(node, "light_position"); + + if (light_pos_tmp.size() != 3) + fatal_error("Light position must be given as 3D coordinates"); + + light_location_.x = light_pos_tmp[0]; + light_location_.y = light_pos_tmp[1]; + light_location_.z = light_pos_tmp[2]; + } else { + light_location_ = camera_position(); + } +} + +void SolidRayTracePlot::set_diffuse_fraction(pugi::xml_node node) +{ + if (check_for_node(node, "diffuse_fraction")) { + diffuse_fraction_ = std::stod(get_node_value(node, "diffuse_fraction")); + if (diffuse_fraction_ < 0.0 || diffuse_fraction_ > 1.0) { + fatal_error("Must have 0 <= diffuse fraction <= 1"); + } + } +} + +void Ray::compute_distance() +{ + boundary() = distance_to_boundary(*this); +} + +void Ray::trace() +{ + // To trace the ray from its origin all the way through the model, we have + // to proceed in two phases. In the first, the ray may or may not be found + // inside the model. If the ray is already in the model, phase one can be + // skipped. Otherwise, the ray has to be advanced to the boundary of the + // model where all the cells are defined. Importantly, this is assuming that + // the model is convex, which is a very reasonable assumption for any + // radiation transport model. + // + // After phase one is done, we can starting tracing from cell to cell within + // the model. This step can use neighbor lists to accelerate the ray tracing. + + // Attempt to initialize the particle. We may have to enter a loop to move + // it up to the edge of the model. + bool inside_cell = exhaustive_find_cell(*this, settings::verbosity >= 10); + + // Advance to the boundary of the model + while (!inside_cell) { + advance_to_boundary_from_void(); + inside_cell = exhaustive_find_cell(*this, settings::verbosity >= 10); + + // If true this means no surface was intersected. See cell.cpp and search + // for numeric_limits to see where we return it. + if (surface() == std::numeric_limits::max()) { + warning(fmt::format("Lost a ray, r = {}, u = {}", r(), u())); + return; + } + + // Exit this loop and enter into cell-to-cell ray tracing (which uses + // neighbor lists) + if (inside_cell) + break; + + // if there is no intersection with the model, we're done + if (boundary().surface() == SURFACE_NONE) + return; + + event_counter_++; + if (event_counter_ > MAX_INTERSECTIONS) { + warning("Likely infinite loop in ray traced plot"); + return; + } + } + + // Call the specialized logic for this type of ray. This is for the + // intersection for the first intersection if we had one. + if (boundary().surface() != SURFACE_NONE) { + // set the geometry state's surface attribute to be used for + // surface normal computation + surface() = boundary().surface(); + on_intersection(); + if (stop_) + return; + } + + // reset surface attribute to zero after the first intersection so that it + // doesn't perturb surface crossing logic from here on out + surface() = 0; + + // This is the ray tracing loop within the model. It exits after exiting + // the model, which is equivalent to assuming that the model is convex. + // It would be nice to factor out the on_intersection at the end of this + // loop and then do "while (inside_cell)", but we can't guarantee it's + // on a surface in that case. There might be some other way to set it + // up that is perhaps a little more elegant, but this is what works just + // fine. + while (true) { + + compute_distance(); + + // There are no more intersections to process + // if we hit the edge of the model, so stop + // the particle in that case. Also, just exit + // if a negative distance was somehow computed. + if (boundary().distance() == INFTY || boundary().distance() == INFINITY || + boundary().distance() < 0) { + return; + } + + // See below comment where call_on_intersection is checked in an + // if statement for an explanation of this. + bool call_on_intersection {true}; + if (boundary().distance() < 10 * TINY_BIT) { + call_on_intersection = false; + } + + // DAGMC surfaces expect us to go a little bit further than the advance + // distance to properly check cell inclusion. + boundary().distance() += TINY_BIT; + + // Advance particle, prepare for next intersection + for (int lev = 0; lev < n_coord(); ++lev) { + coord(lev).r() += boundary().distance() * coord(lev).u(); + } + surface() = boundary().surface(); + n_coord_last() = n_coord(); + n_coord() = boundary().coord_level(); + if (boundary().lattice_translation()[0] != 0 || + boundary().lattice_translation()[1] != 0 || + boundary().lattice_translation()[2] != 0) { + cross_lattice(*this, boundary(), settings::verbosity >= 10); + } + + // Record how far the ray has traveled + traversal_distance_ += boundary().distance(); + inside_cell = neighbor_list_find_cell(*this, settings::verbosity >= 10); + + // Call the specialized logic for this type of ray. Note that we do not + // call this if the advance distance is very small. Unfortunately, it seems + // darn near impossible to get the particle advanced to the model boundary + // and through it without sometimes accidentally calling on_intersection + // twice. This incorrectly shades the region as occluded when it might not + // actually be. By screening out intersection distances smaller than a + // threshold 10x larger than the scoot distance used to advance up to the + // model boundary, we can avoid that situation. + if (call_on_intersection) { + on_intersection(); + if (stop_) + return; + } + + if (!inside_cell) + return; + + event_counter_++; + if (event_counter_ > MAX_INTERSECTIONS) { + warning("Likely infinite loop in ray traced plot"); + return; + } + } +} + +void ProjectionRay::on_intersection() +{ + // This records a tuple with the following info + // + // 1) ID (material or cell depending on color_by_) + // 2) Distance traveled by the ray through that ID + // 3) Index of the intersected surface (starting from 1) + + line_segments_.emplace_back( + plot_.color_by_ == PlottableInterface::PlotColorBy::mats + ? material() + : lowest_coord().cell(), + traversal_distance_, boundary().surface_index()); +} + +void PhongRay::on_intersection() +{ + // Check if we hit an opaque material or cell + int hit_id = plot_.color_by_ == PlottableInterface::PlotColorBy::mats + ? material() + : lowest_coord().cell(); + + // If we are reflected and have advanced beyond the camera, + // the ray is done. This is checked here because we should + // kill the ray even if the material is not opaque. + if (reflected_ && (r() - plot_.camera_position()).dot(u()) >= 0.0) { + stop(); + return; + } + + // Anything that's not opaque has zero impact on the plot. + if (plot_.opaque_ids_.find(hit_id) == plot_.opaque_ids_.end()) + return; + + if (!reflected_) { + // reflect the particle and set the color to be colored by + // the normal or the diffuse lighting contribution + reflected_ = true; + result_color_ = plot_.colors_[hit_id]; + Direction to_light = plot_.light_location_ - r(); + to_light /= to_light.norm(); + + // TODO + // Not sure what can cause a surface token to be invalid here, although it + // sometimes happens for a few pixels. It's very very rare, so proceed by + // coloring the pixel with the overlap color. It seems to happen only for a + // few pixels on the outer boundary of a hex lattice. + // + // We cannot detect it in the outer loop, and it only matters here, so + // that's why the error handling is a little different than for a lost + // ray. + if (surface() == 0) { + result_color_ = plot_.overlap_color_; + stop(); + return; + } + + // Get surface pointer + const auto& surf = model::surfaces.at(surface_index()); + + Direction normal = surf->normal(r_local()); + normal /= normal.norm(); + + // Need to apply translations to find the normal vector in + // the base level universe's coordinate system. + for (int lev = n_coord() - 2; lev >= 0; --lev) { + if (coord(lev + 1).rotated()) { + const Cell& c {*model::cells[coord(lev).cell()]}; + normal = normal.inverse_rotate(c.rotation_); + } + } + + // use the normal opposed to the ray direction + if (normal.dot(u()) > 0.0) { + normal *= -1.0; + } + + // Facing away from the light means no lighting + double dotprod = normal.dot(to_light); + dotprod = std::max(0.0, dotprod); + + double modulation = + plot_.diffuse_fraction_ + (1.0 - plot_.diffuse_fraction_) * dotprod; + result_color_ *= modulation; + + // Now point the particle to the camera. We now begin + // checking to see if it's occluded by another surface + u() = to_light; + + orig_hit_id_ = hit_id; + + // OpenMC native CSG and DAGMC surfaces have some slight differences + // in how they interpret particles that are sitting on a surface. + // I don't know exactly why, but this makes everything work beautifully. + if (surf->geom_type() == GeometryType::DAG) { + surface() = 0; + } else { + surface() = -surface(); // go to other side + } + + // Must fully restart coordinate search. Why? Not sure. + clear(); + + // Note this could likely be faster if we cached the previous + // cell we were in before the reflection. This is the easiest + // way to fully initialize all the sub-universe coordinates and + // directions though. + bool found = exhaustive_find_cell(*this); + if (!found) { + fatal_error("Lost particle after reflection."); + } + + // Must recalculate distance to boundary due to the + // direction change + compute_distance(); + + } else { + // If it's not facing the light, we color with the diffuse contribution, so + // next we check if we're going to occlude the last reflected surface. if + // so, color by the diffuse contribution instead + + if (orig_hit_id_ == -1) + fatal_error("somehow a ray got reflected but not original ID set?"); + + result_color_ = plot_.colors_[orig_hit_id_]; + result_color_ *= plot_.diffuse_fraction_; + stop(); + } +} + extern "C" int openmc_id_map(const void* plot, int32_t* data_out) { - auto plt = reinterpret_cast(plot); + auto plt = reinterpret_cast(plot); if (!plt) { set_errmsg("Invalid slice pointer passed to openmc_id_map"); return OPENMC_E_INVALID_ARGUMENT; } - if (plt->color_overlaps_ && model::overlap_check_count.size() == 0) { + if (plt->slice_color_overlaps_ && model::overlap_check_count.size() == 0) { model::overlap_check_count.resize(model::cells.size()); } @@ -1003,13 +1886,13 @@ extern "C" int openmc_id_map(const void* plot, int32_t* data_out) extern "C" int openmc_property_map(const void* plot, double* data_out) { - auto plt = reinterpret_cast(plot); + auto plt = reinterpret_cast(plot); if (!plt) { set_errmsg("Invalid slice pointer passed to openmc_id_map"); return OPENMC_E_INVALID_ARGUMENT; } - if (plt->color_overlaps_ && model::overlap_check_count.size() == 0) { + if (plt->slice_color_overlaps_ && model::overlap_check_count.size() == 0) { model::overlap_check_count.resize(model::cells.size()); } diff --git a/src/position.cpp b/src/position.cpp index 5b3613b289..0361e99a0a 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -75,13 +75,6 @@ Position Position::operator-() const return {-x, -y, -z}; } -Position Position::rotate(const vector& rotation) const -{ - return {x * rotation[0] + y * rotation[1] + z * rotation[2], - x * rotation[3] + y * rotation[4] + z * rotation[5], - x * rotation[6] + y * rotation[7] + z * rotation[8]}; -} - std::ostream& operator<<(std::ostream& os, Position r) { os << "(" << r.x << ", " << r.y << ", " << r.z << ")"; diff --git a/src/random_dist.cpp b/src/random_dist.cpp index c8d2380c36..b05b76f99e 100644 --- a/src/random_dist.cpp +++ b/src/random_dist.cpp @@ -12,6 +12,11 @@ double uniform_distribution(double a, double b, uint64_t* seed) return a + (b - a) * prn(seed); } +int64_t uniform_int_distribution(int64_t a, int64_t b, uint64_t* seed) +{ + return a + static_cast(prn(seed) * (b - a + 1)); +} + double maxwell_spectrum(double T, uint64_t* seed) { // Set the random numbers @@ -46,11 +51,4 @@ double normal_variate(double mean, double standard_deviation, uint64_t* seed) return mean + standard_deviation * z * x; } -double muir_spectrum(double e0, double m_rat, double kt, uint64_t* seed) -{ - // https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-05411-MS - double sigma = std::sqrt(4. * e0 * kt / m_rat); - return normal_variate(e0, sigma, seed); -} - } // namespace openmc diff --git a/src/random_lcg.cpp b/src/random_lcg.cpp index 581d696176..29457569b9 100644 --- a/src/random_lcg.cpp +++ b/src/random_lcg.cpp @@ -10,14 +10,15 @@ int64_t master_seed {1}; // LCG parameters constexpr uint64_t prn_mult {6364136223846793005ULL}; // multiplication constexpr uint64_t prn_add {1442695040888963407ULL}; // additive factor, c -constexpr uint64_t prn_stride {152917LL}; // stride between particles +uint64_t prn_stride {DEFAULT_STRIDE}; // stride between particles //============================================================================== // PRN //============================================================================== // 64 bit implementation of the PCG-RXS-M-XS 64-bit state / 64-bit output -// geneator Adapted from: https://github.com/imneme/pcg-c +// geneator Adapted from: https://github.com/imneme/pcg-c, in particular +// https://github.com/imneme/pcg-c/blob/83252d9c23df9c82ecb42210afed61a7b42402d7/include/pcg_variants.h#L188-L192 // @techreport{oneill:pcg2014, // title = "PCG: A Family of Simple Fast Space-Efficient Statistically Good // Algorithms for Random Number Generation", author = "Melissa E. O'Neill", @@ -132,4 +133,14 @@ extern "C" void openmc_set_seed(int64_t new_seed) master_seed = new_seed; } +extern "C" uint64_t openmc_get_stride() +{ + return prn_stride; +} + +extern "C" void openmc_set_stride(uint64_t new_stride) +{ + prn_stride = new_stride; +} + } // namespace openmc diff --git a/src/random_ray/flat_source_domain.cpp b/src/random_ray/flat_source_domain.cpp new file mode 100644 index 0000000000..1bf27e1eda --- /dev/null +++ b/src/random_ray/flat_source_domain.cpp @@ -0,0 +1,1656 @@ +#include "openmc/random_ray/flat_source_domain.h" + +#include "openmc/cell.h" +#include "openmc/constants.h" +#include "openmc/eigenvalue.h" +#include "openmc/geometry.h" +#include "openmc/material.h" +#include "openmc/message_passing.h" +#include "openmc/mgxs_interface.h" +#include "openmc/output.h" +#include "openmc/plot.h" +#include "openmc/random_ray/random_ray.h" +#include "openmc/simulation.h" +#include "openmc/tallies/filter.h" +#include "openmc/tallies/tally.h" +#include "openmc/tallies/tally_scoring.h" +#include "openmc/timer.h" +#include "openmc/weight_windows.h" + +#include + +namespace openmc { + +//============================================================================== +// FlatSourceDomain implementation +//============================================================================== + +// Static Variable Declarations +RandomRayVolumeEstimator FlatSourceDomain::volume_estimator_ { + RandomRayVolumeEstimator::HYBRID}; +bool FlatSourceDomain::volume_normalized_flux_tallies_ {false}; +bool FlatSourceDomain::adjoint_ {false}; +double FlatSourceDomain::diagonal_stabilization_rho_ {1.0}; +std::unordered_map>> + FlatSourceDomain::mesh_domain_map_; + +FlatSourceDomain::FlatSourceDomain() : negroups_(data::mg.num_energy_groups_) +{ + // Count the number of source regions, compute the cell offset + // indices, and store the material type The reason for the offsets is that + // some cell types may not have material fills, and therefore do not + // produce FSRs. Thus, we cannot index into the global arrays directly + int base_source_regions = 0; + for (const auto& c : model::cells) { + if (c->type_ != Fill::MATERIAL) { + source_region_offsets_.push_back(-1); + } else { + source_region_offsets_.push_back(base_source_regions); + base_source_regions += c->n_instances(); + } + } + + // Initialize source regions. + bool is_linear = RandomRay::source_shape_ != RandomRaySourceShape::FLAT; + source_regions_ = SourceRegionContainer(negroups_, is_linear); + + // Initialize tally volumes + if (volume_normalized_flux_tallies_) { + tally_volumes_.resize(model::tallies.size()); + for (int i = 0; i < model::tallies.size(); i++) { + // Get the shape of the 3D result tensor + auto shape = model::tallies[i]->results().shape(); + + // Create a new 2D tensor with the same size as the first + // two dimensions of the 3D tensor + tally_volumes_[i] = + xt::xtensor::from_shape({shape[0], shape[1]}); + } + } + + // Compute simulation domain volume based on ray source + auto* is = dynamic_cast(RandomRay::ray_source_.get()); + SpatialDistribution* space_dist = is->space(); + SpatialBox* sb = dynamic_cast(space_dist); + Position dims = sb->upper_right() - sb->lower_left(); + simulation_volume_ = dims.x * dims.y * dims.z; +} + +void FlatSourceDomain::batch_reset() +{ +// Reset scalar fluxes and iteration volume tallies to zero +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + source_regions_.volume(sr) = 0.0; + source_regions_.volume_sq(sr) = 0.0; + } + +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.scalar_flux_new(se) = 0.0; + } +} + +void FlatSourceDomain::accumulate_iteration_flux() +{ +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.scalar_flux_final(se) += + source_regions_.scalar_flux_new(se); + } +} + +void FlatSourceDomain::update_single_neutron_source(SourceRegionHandle& srh) +{ + // Reset all source regions to zero (important for void regions) + for (int g = 0; g < negroups_; g++) { + srh.source(g) = 0.0; + } + + // Add scattering + fission source + int material = srh.material(); + if (material != MATERIAL_VOID) { + double inverse_k_eff = 1.0 / k_eff_; + for (int g_out = 0; g_out < negroups_; g_out++) { + double sigma_t = sigma_t_[material * negroups_ + g_out]; + double scatter_source = 0.0; + double fission_source = 0.0; + + for (int g_in = 0; g_in < negroups_; g_in++) { + double scalar_flux = srh.scalar_flux_old(g_in); + double sigma_s = + sigma_s_[material * negroups_ * negroups_ + g_out * negroups_ + g_in]; + double nu_sigma_f = nu_sigma_f_[material * negroups_ + g_in]; + double chi = chi_[material * negroups_ + g_out]; + + scatter_source += sigma_s * scalar_flux; + fission_source += nu_sigma_f * scalar_flux * chi; + } + srh.source(g_out) = + (scatter_source + fission_source * inverse_k_eff) / sigma_t; + } + } + + // Add external source if in fixed source mode + if (settings::run_mode == RunMode::FIXED_SOURCE) { + for (int g = 0; g < negroups_; g++) { + srh.source(g) += srh.external_source(g); + } + } +} + +// Compute new estimate of scattering + fission sources in each source region +// based on the flux estimate from the previous iteration. +void FlatSourceDomain::update_all_neutron_sources() +{ + simulation::time_update_src.start(); + +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + SourceRegionHandle srh = source_regions_.get_source_region_handle(sr); + update_single_neutron_source(srh); + } + + simulation::time_update_src.stop(); +} + +// Normalizes flux and updates simulation-averaged volume estimate +void FlatSourceDomain::normalize_scalar_flux_and_volumes( + double total_active_distance_per_iteration) +{ + double normalization_factor = 1.0 / total_active_distance_per_iteration; + double volume_normalization_factor = + 1.0 / (total_active_distance_per_iteration * simulation::current_batch); + +// Normalize scalar flux to total distance travelled by all rays this +// iteration +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.scalar_flux_new(se) *= normalization_factor; + } + +// Accumulate cell-wise ray length tallies collected this iteration, then +// update the simulation-averaged cell-wise volume estimates +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + source_regions_.volume_t(sr) += source_regions_.volume(sr); + source_regions_.volume_sq_t(sr) += source_regions_.volume_sq(sr); + source_regions_.volume_naive(sr) = + source_regions_.volume(sr) * normalization_factor; + source_regions_.volume_sq(sr) = + source_regions_.volume_sq_t(sr) / source_regions_.volume_t(sr); + source_regions_.volume(sr) = + source_regions_.volume_t(sr) * volume_normalization_factor; + } +} + +void FlatSourceDomain::set_flux_to_flux_plus_source( + int64_t sr, double volume, int g) +{ + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + source_regions_.scalar_flux_new(sr, g) /= volume; + if (settings::run_mode == RunMode::FIXED_SOURCE) { + source_regions_.scalar_flux_new(sr, g) += + 0.5f * source_regions_.external_source(sr, g) * + source_regions_.volume_sq(sr); + } + } else { + double sigma_t = sigma_t_[source_regions_.material(sr) * negroups_ + g]; + source_regions_.scalar_flux_new(sr, g) /= (sigma_t * volume); + source_regions_.scalar_flux_new(sr, g) += source_regions_.source(sr, g); + } +} + +void FlatSourceDomain::set_flux_to_old_flux(int64_t sr, int g) +{ + source_regions_.scalar_flux_new(sr, g) = + source_regions_.scalar_flux_old(sr, g); +} + +void FlatSourceDomain::set_flux_to_source(int64_t sr, int g) +{ + source_regions_.scalar_flux_new(sr, g) = source_regions_.source(sr, g); +} + +// Combine transport flux contributions and flat source contributions from the +// previous iteration to generate this iteration's estimate of scalar flux. +int64_t FlatSourceDomain::add_source_to_scalar_flux() +{ + int64_t n_hits = 0; + double inverse_batch = 1.0 / simulation::current_batch; + +#pragma omp parallel for reduction(+ : n_hits) + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + + double volume_simulation_avg = source_regions_.volume(sr); + double volume_iteration = source_regions_.volume_naive(sr); + + // Increment the number of hits if cell was hit this iteration + if (volume_iteration) { + n_hits++; + } + + // Set the SR to small status if its expected number of hits + // per iteration is less than 1.5 + if (source_regions_.n_hits(sr) * inverse_batch < MIN_HITS_PER_BATCH) { + source_regions_.is_small(sr) = 1; + } else { + source_regions_.is_small(sr) = 0; + } + + // The volume treatment depends on the volume estimator type + // and whether or not an external source is present in the cell. + double volume; + switch (volume_estimator_) { + case RandomRayVolumeEstimator::NAIVE: + volume = volume_iteration; + break; + case RandomRayVolumeEstimator::SIMULATION_AVERAGED: + volume = volume_simulation_avg; + break; + case RandomRayVolumeEstimator::HYBRID: + if (source_regions_.external_source_present(sr) || + source_regions_.is_small(sr)) { + volume = volume_iteration; + } else { + volume = volume_simulation_avg; + } + break; + default: + fatal_error("Invalid volume estimator type"); + } + + for (int g = 0; g < negroups_; g++) { + // There are three scenarios we need to consider: + if (volume_iteration > 0.0) { + // 1. If the FSR was hit this iteration, then the new flux is equal to + // the flat source from the previous iteration plus the contributions + // from rays passing through the source region (computed during the + // transport sweep) + set_flux_to_flux_plus_source(sr, volume, g); + } else if (volume_simulation_avg > 0.0) { + // 2. If the FSR was not hit this iteration, but has been hit some + // previous iteration, then we need to make a choice about what + // to do. Naively we will usually want to set the flux to be equal + // to the reduced source. However, in fixed source problems where + // there is a strong external source present in the cell, and where + // the cell has a very low cross section, this approximation will + // cause a huge upward bias in the flux estimate of the cell (in these + // conditions, the flux estimate can be orders of magnitude too large). + // Thus, to avoid this bias, if any external source is present + // in the cell we will use the previous iteration's flux estimate. This + // injects a small degree of correlation into the simulation, but this + // is going to be trivial when the miss rate is a few percent or less. + if (source_regions_.external_source_present(sr)) { + set_flux_to_old_flux(sr, g); + } else { + set_flux_to_source(sr, g); + } + } + // Halt if NaN implosion is detected + if (!std::isfinite(source_regions_.scalar_flux_new(sr, g))) { + fatal_error("A source region scalar flux is not finite. " + "This indicates a numerical instability in the " + "simulation. Consider increasing ray density or adjusting " + "the source region mesh."); + } + } + } + + // Return the number of source regions that were hit this iteration + return n_hits; +} + +// Generates new estimate of k_eff based on the differences between this +// iteration's estimate of the scalar flux and the last iteration's estimate. +void FlatSourceDomain::compute_k_eff() +{ + double fission_rate_old = 0; + double fission_rate_new = 0; + + // Vector for gathering fission source terms for Shannon entropy calculation + vector p(n_source_regions(), 0.0f); + +#pragma omp parallel for reduction(+ : fission_rate_old, fission_rate_new) + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + + // If simulation averaged volume is zero, don't include this cell + double volume = source_regions_.volume(sr); + if (volume == 0.0) { + continue; + } + + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + continue; + } + + double sr_fission_source_old = 0; + double sr_fission_source_new = 0; + + for (int g = 0; g < negroups_; g++) { + double nu_sigma_f = nu_sigma_f_[material * negroups_ + g]; + sr_fission_source_old += + nu_sigma_f * source_regions_.scalar_flux_old(sr, g); + sr_fission_source_new += + nu_sigma_f * source_regions_.scalar_flux_new(sr, g); + } + + // Compute total fission rates in FSR + sr_fission_source_old *= volume; + sr_fission_source_new *= volume; + + // Accumulate totals + fission_rate_old += sr_fission_source_old; + fission_rate_new += sr_fission_source_new; + + // Store total fission rate in the FSR for Shannon calculation + p[sr] = sr_fission_source_new; + } + + double k_eff_new = k_eff_ * (fission_rate_new / fission_rate_old); + + double H = 0.0; + // defining an inverse sum for better performance + double inverse_sum = 1 / fission_rate_new; + +#pragma omp parallel for reduction(+ : H) + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + // Only if FSR has non-negative and non-zero fission source + if (p[sr] > 0.0f) { + // Normalize to total weight of bank sites. p_i for better performance + float p_i = p[sr] * inverse_sum; + // Sum values to obtain Shannon entropy. + H -= p_i * std::log2(p_i); + } + } + + // Adds entropy value to shared entropy vector in openmc namespace. + simulation::entropy.push_back(H); + + k_eff_ = k_eff_new; +} + +// This function is responsible for generating a mapping between random +// ray flat source regions (cell instances) and tally bins. The mapping +// takes the form of a "TallyTask" object, which accounts for one single +// score being applied to a single tally. Thus, a single source region +// may have anywhere from zero to many tally tasks associated with it --- +// meaning that the global "tally_task" data structure is in 2D. The outer +// dimension corresponds to the source element (i.e., each entry corresponds +// to a specific energy group within a specific source region), and the +// inner dimension corresponds to the tallying task itself. Mechanically, +// the mapping between FSRs and spatial filters is done by considering +// the location of a single known ray midpoint that passed through the +// FSR. I.e., during transport, the first ray to pass through a given FSR +// will write down its midpoint for use with this function. This is a cheap +// and easy way of mapping FSRs to spatial tally filters, but comes with +// the downside of adding the restriction that spatial tally filters must +// share boundaries with the physical geometry of the simulation (so as +// not to subdivide any FSR). It is acceptable for a spatial tally region +// to contain multiple FSRs, but not the other way around. + +// TODO: In future work, it would be preferable to offer a more general +// (but perhaps slightly more expensive) option for handling arbitrary +// spatial tallies that would be allowed to subdivide FSRs. + +// Besides generating the mapping structure, this function also keeps track +// of whether or not all flat source regions have been hit yet. This is +// required, as there is no guarantee that all flat source regions will +// be hit every iteration, such that in the first few iterations some FSRs +// may not have a known position within them yet to facilitate mapping to +// spatial tally filters. However, after several iterations, if all FSRs +// have been hit and have had a tally map generated, then this status will +// be passed back to the caller to alert them that this function doesn't +// need to be called for the remainder of the simulation. + +// It takes as an argument the starting index in the source region array, +// and it will operate from that index until the end of the array. This +// is useful as it can be called for both explicit user source regions or +// when a source region mesh is overlaid. + +void FlatSourceDomain::convert_source_regions_to_tallies(int64_t start_sr_id) +{ + openmc::simulation::time_tallies.start(); + + // Tracks if we've generated a mapping yet for all source regions. + bool all_source_regions_mapped = true; + +// Attempt to generate mapping for all source regions +#pragma omp parallel for + for (int64_t sr = start_sr_id; sr < n_source_regions(); sr++) { + + // If this source region has not been hit by a ray yet, then + // we aren't going to be able to map it, so skip it. + if (!source_regions_.position_recorded(sr)) { + all_source_regions_mapped = false; + continue; + } + + // A particle located at the recorded midpoint of a ray + // crossing through this source region is used to estabilish + // the spatial location of the source region + Particle p; + p.r() = source_regions_.position(sr); + p.r_last() = source_regions_.position(sr); + p.u() = {1.0, 0.0, 0.0}; + bool found = exhaustive_find_cell(p); + + // Loop over energy groups (so as to support energy filters) + for (int g = 0; g < negroups_; g++) { + + // Set particle to the current energy + p.g() = g; + p.g_last() = g; + p.E() = data::mg.energy_bin_avg_[p.g()]; + p.E_last() = p.E(); + + int64_t source_element = sr * negroups_ + g; + + // If this task has already been populated, we don't need to do + // it again. + if (source_regions_.tally_task(sr, g).size() > 0) { + continue; + } + + // Loop over all active tallies. This logic is essentially identical + // to what happens when scanning for applicable tallies during + // MC transport. + for (int i_tally = 0; i_tally < model::tallies.size(); i_tally++) { + Tally& tally {*model::tallies[i_tally]}; + + // Initialize an iterator over valid filter bin combinations. + // If there are no valid combinations, use a continue statement + // to ensure we skip the assume_separate break below. + auto filter_iter = FilterBinIter(tally, p); + auto end = FilterBinIter(tally, true, &p.filter_matches()); + if (filter_iter == end) + continue; + + // Loop over filter bins. + for (; filter_iter != end; ++filter_iter) { + auto filter_index = filter_iter.index_; + auto filter_weight = filter_iter.weight_; + + // Loop over scores + for (int score = 0; score < tally.scores_.size(); score++) { + auto score_bin = tally.scores_[score]; + // If a valid tally, filter, and score combination has been found, + // then add it to the list of tally tasks for this source element. + TallyTask task(i_tally, filter_index, score, score_bin); + source_regions_.tally_task(sr, g).push_back(task); + + // Also add this task to the list of volume tasks for this source + // region. + source_regions_.volume_task(sr).insert(task); + } + } + } + // Reset all the filter matches for the next tally event. + for (auto& match : p.filter_matches()) + match.bins_present_ = false; + } + } + openmc::simulation::time_tallies.stop(); + + mapped_all_tallies_ = all_source_regions_mapped; +} + +// Set the volume accumulators to zero for all tallies +void FlatSourceDomain::reset_tally_volumes() +{ + if (volume_normalized_flux_tallies_) { +#pragma omp parallel for + for (int i = 0; i < tally_volumes_.size(); i++) { + auto& tensor = tally_volumes_[i]; + tensor.fill(0.0); // Set all elements of the tensor to 0.0 + } + } +} + +// In fixed source mode, due to the way that volumetric fixed sources are +// converted and applied as volumetric sources in one or more source regions, +// we need to perform an additional normalization step to ensure that the +// reported scalar fluxes are in units per source neutron. This allows for +// direct comparison of reported tallies to Monte Carlo flux results. +// This factor needs to be computed at each iteration, as it is based on the +// volume estimate of each FSR, which improves over the course of the +// simulation +double FlatSourceDomain::compute_fixed_source_normalization_factor() const +{ + // If we are not in fixed source mode, then there are no external sources + // so no normalization is needed. + if (settings::run_mode != RunMode::FIXED_SOURCE || adjoint_) { + return 1.0; + } + + // Step 1 is to sum over all source regions and energy groups to get the + // total external source strength in the simulation. + double simulation_external_source_strength = 0.0; +#pragma omp parallel for reduction(+ : simulation_external_source_strength) + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + int material = source_regions_.material(sr); + double volume = source_regions_.volume(sr) * simulation_volume_; + for (int g = 0; g < negroups_; g++) { + // For non-void regions, we store the external source pre-divided by + // sigma_t. We need to multiply non-void regions back up by sigma_t + // to get the total source strength in the expected units. + double sigma_t = 1.0; + if (material != MATERIAL_VOID) { + sigma_t = sigma_t_[material * negroups_ + g]; + } + simulation_external_source_strength += + source_regions_.external_source(sr, g) * sigma_t * volume; + } + } + + // Step 2 is to determine the total user-specified external source strength + double user_external_source_strength = 0.0; + for (auto& ext_source : model::external_sources) { + user_external_source_strength += ext_source->strength(); + } + + // The correction factor is the ratio of the user-specified external source + // strength to the simulation external source strength. + double source_normalization_factor = + user_external_source_strength / simulation_external_source_strength; + + return source_normalization_factor; +} + +// Tallying in random ray is not done directly during transport, rather, +// it is done only once after each power iteration. This is made possible +// by way of a mapping data structure that relates spatial source regions +// (FSRs) to tally/filter/score combinations. The mechanism by which the +// mapping is done (and the limitations incurred) is documented in the +// "convert_source_regions_to_tallies()" function comments above. The present +// tally function simply traverses the mapping data structure and executes +// the scoring operations to OpenMC's native tally result arrays. + +void FlatSourceDomain::random_ray_tally() +{ + openmc::simulation::time_tallies.start(); + + // Reset our tally volumes to zero + reset_tally_volumes(); + + double source_normalization_factor = + compute_fixed_source_normalization_factor(); + +// We loop over all source regions and energy groups. For each +// element, we check if there are any scores needed and apply +// them. +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + // The fsr.volume_ is the unitless fractional simulation averaged volume + // (i.e., it is the FSR's fraction of the overall simulation volume). The + // simulation_volume_ is the total 3D physical volume in cm^3 of the + // entire global simulation domain (as defined by the ray source box). + // Thus, the FSR's true 3D spatial volume in cm^3 is found by multiplying + // its fraction of the total volume by the total volume. Not important in + // eigenvalue solves, but useful in fixed source solves for returning the + // flux shape with a magnitude that makes sense relative to the fixed + // source strength. + double volume = source_regions_.volume(sr) * simulation_volume_; + + double material = source_regions_.material(sr); + for (int g = 0; g < negroups_; g++) { + double flux = + source_regions_.scalar_flux_new(sr, g) * source_normalization_factor; + + // Determine numerical score value + for (auto& task : source_regions_.tally_task(sr, g)) { + double score = 0.0; + switch (task.score_type) { + + case SCORE_FLUX: + score = flux * volume; + break; + + case SCORE_TOTAL: + if (material != MATERIAL_VOID) { + score = flux * volume * sigma_t_[material * negroups_ + g]; + } + break; + + case SCORE_FISSION: + if (material != MATERIAL_VOID) { + score = flux * volume * sigma_f_[material * negroups_ + g]; + } + break; + + case SCORE_NU_FISSION: + if (material != MATERIAL_VOID) { + score = flux * volume * nu_sigma_f_[material * negroups_ + g]; + } + break; + + case SCORE_EVENTS: + score = 1.0; + break; + + default: + fatal_error("Invalid score specified in tallies.xml. Only flux, " + "total, fission, nu-fission, and events are supported in " + "random ray mode."); + break; + } + // Apply score to the appropriate tally bin + Tally& tally {*model::tallies[task.tally_idx]}; +#pragma omp atomic + tally.results_(task.filter_idx, task.score_idx, TallyResult::VALUE) += + score; + } + } + + // For flux tallies, the total volume of the spatial region is needed + // for normalizing the flux. We store this volume in a separate tensor. + // We only contribute to each volume tally bin once per FSR. + if (volume_normalized_flux_tallies_) { + for (const auto& task : source_regions_.volume_task(sr)) { + if (task.score_type == SCORE_FLUX) { +#pragma omp atomic + tally_volumes_[task.tally_idx](task.filter_idx, task.score_idx) += + volume; + } + } + } + } // end FSR loop + + // Normalize any flux scores by the total volume of the FSRs scoring to that + // bin. To do this, we loop over all tallies, and then all filter bins, + // and then scores. For each score, we check the tally data structure to + // see what index that score corresponds to. If that score is a flux score, + // then we divide it by volume. + if (volume_normalized_flux_tallies_) { + for (int i = 0; i < model::tallies.size(); i++) { + Tally& tally {*model::tallies[i]}; +#pragma omp parallel for + for (int bin = 0; bin < tally.n_filter_bins(); bin++) { + for (int score_idx = 0; score_idx < tally.n_scores(); score_idx++) { + auto score_type = tally.scores_[score_idx]; + if (score_type == SCORE_FLUX) { + double vol = tally_volumes_[i](bin, score_idx); + if (vol > 0.0) { + tally.results_(bin, score_idx, TallyResult::VALUE) /= vol; + } + } + } + } + } + } + + openmc::simulation::time_tallies.stop(); +} + +double FlatSourceDomain::evaluate_flux_at_point( + Position r, int64_t sr, int g) const +{ + return source_regions_.scalar_flux_final(sr, g) / + (settings::n_batches - settings::n_inactive); +} + +// Outputs all basic material, FSR ID, multigroup flux, and +// fission source data to .vtk file that can be directly +// loaded and displayed by Paraview. Note that .vtk binary +// files require big endian byte ordering, so endianness +// is checked and flipped if necessary. +void FlatSourceDomain::output_to_vtk() const +{ + // Rename .h5 plot filename(s) to .vtk filenames + for (int p = 0; p < model::plots.size(); p++) { + PlottableInterface* plot = model::plots[p].get(); + plot->path_plot() = + plot->path_plot().substr(0, plot->path_plot().find_last_of('.')) + ".vtk"; + } + + // Print header information + print_plot(); + + // Outer loop over plots + for (int plt = 0; plt < model::plots.size(); plt++) { + + // Get handle to OpenMC plot object and extract params + Plot* openmc_plot = dynamic_cast(model::plots[plt].get()); + + // Random ray plots only support voxel plots + if (!openmc_plot) { + warning(fmt::format("Plot {} is invalid plot type -- only voxel plotting " + "is allowed in random ray mode.", + plt)); + continue; + } else if (openmc_plot->type_ != Plot::PlotType::voxel) { + warning(fmt::format("Plot {} is invalid plot type -- only voxel plotting " + "is allowed in random ray mode.", + plt)); + continue; + } + + int Nx = openmc_plot->pixels_[0]; + int Ny = openmc_plot->pixels_[1]; + int Nz = openmc_plot->pixels_[2]; + Position origin = openmc_plot->origin_; + Position width = openmc_plot->width_; + Position ll = origin - width / 2.0; + double x_delta = width.x / Nx; + double y_delta = width.y / Ny; + double z_delta = width.z / Nz; + std::string filename = openmc_plot->path_plot(); + + // Perform sanity checks on file size + uint64_t bytes = Nx * Ny * Nz * (negroups_ + 1 + 1 + 1) * sizeof(float); + write_message(5, "Processing plot {}: {}... (Estimated size is {} MB)", + openmc_plot->id(), filename, bytes / 1.0e6); + if (bytes / 1.0e9 > 1.0) { + warning("Voxel plot specification is very large (>1 GB). Plotting may be " + "slow."); + } else if (bytes / 1.0e9 > 100.0) { + fatal_error("Voxel plot specification is too large (>100 GB). Exiting."); + } + + // Relate voxel spatial locations to random ray source regions + vector voxel_indices(Nx * Ny * Nz); + vector voxel_positions(Nx * Ny * Nz); + vector weight_windows(Nx * Ny * Nz); + float min_weight = 1e20; +#pragma omp parallel for collapse(3) reduction(min : min_weight) + for (int z = 0; z < Nz; z++) { + for (int y = 0; y < Ny; y++) { + for (int x = 0; x < Nx; x++) { + Position sample; + sample.z = ll.z + z_delta / 2.0 + z * z_delta; + sample.y = ll.y + y_delta / 2.0 + y * y_delta; + sample.x = ll.x + x_delta / 2.0 + x * x_delta; + Particle p; + p.r() = sample; + p.r_last() = sample; + p.E() = 1.0; + p.E_last() = 1.0; + p.u() = {1.0, 0.0, 0.0}; + + bool found = exhaustive_find_cell(p); + if (!found) { + voxel_indices[z * Ny * Nx + y * Nx + x] = -1; + voxel_positions[z * Ny * Nx + y * Nx + x] = sample; + weight_windows[z * Ny * Nx + y * Nx + x] = 0.0; + continue; + } + + SourceRegionKey sr_key = lookup_source_region_key(p); + int64_t sr = -1; + auto it = source_region_map_.find(sr_key); + if (it != source_region_map_.end()) { + sr = it->second; + } + + voxel_indices[z * Ny * Nx + y * Nx + x] = sr; + voxel_positions[z * Ny * Nx + y * Nx + x] = sample; + + if (variance_reduction::weight_windows.size() == 1) { + WeightWindow ww = + variance_reduction::weight_windows[0]->get_weight_window(p); + float weight = ww.lower_weight; + weight_windows[z * Ny * Nx + y * Nx + x] = weight; + if (weight < min_weight) + min_weight = weight; + } + } + } + } + + double source_normalization_factor = + compute_fixed_source_normalization_factor(); + + // Open file for writing + std::FILE* plot = std::fopen(filename.c_str(), "wb"); + + // Write vtk metadata + std::fprintf(plot, "# vtk DataFile Version 2.0\n"); + std::fprintf(plot, "Dataset File\n"); + std::fprintf(plot, "BINARY\n"); + std::fprintf(plot, "DATASET STRUCTURED_POINTS\n"); + std::fprintf(plot, "DIMENSIONS %d %d %d\n", Nx, Ny, Nz); + std::fprintf(plot, "ORIGIN %lf %lf %lf\n", ll.x, ll.y, ll.z); + std::fprintf(plot, "SPACING %lf %lf %lf\n", x_delta, y_delta, z_delta); + std::fprintf(plot, "POINT_DATA %d\n", Nx * Ny * Nz); + + int64_t num_neg = 0; + int64_t num_samples = 0; + float min_flux = 0.0; + float max_flux = -1.0e20; + // Plot multigroup flux data + for (int g = 0; g < negroups_; g++) { + std::fprintf(plot, "SCALARS flux_group_%d float\n", g); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int i = 0; i < Nx * Ny * Nz; i++) { + int64_t fsr = voxel_indices[i]; + int64_t source_element = fsr * negroups_ + g; + float flux = 0; + if (fsr >= 0) { + flux = evaluate_flux_at_point(voxel_positions[i], fsr, g); + if (flux < 0.0) + flux = FlatSourceDomain::evaluate_flux_at_point( + voxel_positions[i], fsr, g); + } + if (flux < 0.0) { + num_neg++; + if (flux < min_flux) { + min_flux = flux; + } + } + if (flux > max_flux) + max_flux = flux; + num_samples++; + flux = convert_to_big_endian(flux); + std::fwrite(&flux, sizeof(float), 1, plot); + } + } + + // Slightly negative fluxes can be normal when sampling corners of linear + // source regions. However, very common and high magnitude negative fluxes + // may indicate numerical instability. + if (num_neg > 0) { + warning(fmt::format("{} plot samples ({:.4f}%) contained negative fluxes " + "(minumum found = {:.2e} maximum_found = {:.2e})", + num_neg, (100.0 * num_neg) / num_samples, min_flux, max_flux)); + } + + // Plot FSRs + std::fprintf(plot, "SCALARS FSRs float\n"); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int fsr : voxel_indices) { + float value = future_prn(10, fsr); + value = convert_to_big_endian(value); + std::fwrite(&value, sizeof(float), 1, plot); + } + + // Plot Materials + std::fprintf(plot, "SCALARS Materials int\n"); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int fsr : voxel_indices) { + int mat = -1; + if (fsr >= 0) + mat = source_regions_.material(fsr); + mat = convert_to_big_endian(mat); + std::fwrite(&mat, sizeof(int), 1, plot); + } + + // Plot fission source + if (settings::run_mode == RunMode::EIGENVALUE) { + std::fprintf(plot, "SCALARS total_fission_source float\n"); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int i = 0; i < Nx * Ny * Nz; i++) { + int64_t fsr = voxel_indices[i]; + float total_fission = 0.0; + if (fsr >= 0) { + int mat = source_regions_.material(fsr); + if (mat != MATERIAL_VOID) { + for (int g = 0; g < negroups_; g++) { + int64_t source_element = fsr * negroups_ + g; + float flux = evaluate_flux_at_point(voxel_positions[i], fsr, g); + double sigma_f = sigma_f_[mat * negroups_ + g]; + total_fission += sigma_f * flux; + } + } + } + total_fission = convert_to_big_endian(total_fission); + std::fwrite(&total_fission, sizeof(float), 1, plot); + } + } else { + std::fprintf(plot, "SCALARS external_source float\n"); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int i = 0; i < Nx * Ny * Nz; i++) { + int64_t fsr = voxel_indices[i]; + int mat = source_regions_.material(fsr); + float total_external = 0.0f; + if (fsr >= 0) { + for (int g = 0; g < negroups_; g++) { + // External sources are already divided by sigma_t, so we need to + // multiply it back to get the true external source. + double sigma_t = 1.0; + if (mat != MATERIAL_VOID) { + sigma_t = sigma_t_[mat * negroups_ + g]; + } + total_external += source_regions_.external_source(fsr, g) * sigma_t; + } + } + total_external = convert_to_big_endian(total_external); + std::fwrite(&total_external, sizeof(float), 1, plot); + } + } + + // Plot weight window data + if (variance_reduction::weight_windows.size() == 1) { + std::fprintf(plot, "SCALARS weight_window_lower float\n"); + std::fprintf(plot, "LOOKUP_TABLE default\n"); + for (int i = 0; i < Nx * Ny * Nz; i++) { + float weight = weight_windows[i]; + if (weight == 0.0) + weight = min_weight; + weight = convert_to_big_endian(weight); + std::fwrite(&weight, sizeof(float), 1, plot); + } + } + + std::fclose(plot); + } +} + +void FlatSourceDomain::apply_external_source_to_source_region( + int src_idx, SourceRegionHandle& srh) +{ + auto s = model::external_sources[src_idx].get(); + auto is = dynamic_cast(s); + auto discrete = dynamic_cast(is->energy()); + double strength_factor = is->strength(); + const auto& discrete_energies = discrete->x(); + const auto& discrete_probs = discrete->prob(); + + srh.external_source_present() = 1; + + for (int i = 0; i < discrete_energies.size(); i++) { + int g = data::mg.get_group_index(discrete_energies[i]); + srh.external_source(g) += discrete_probs[i] * strength_factor; + } +} + +void FlatSourceDomain::apply_external_source_to_cell_instances(int32_t i_cell, + int src_idx, int target_material_id, const vector& instances) +{ + Cell& cell = *model::cells[i_cell]; + + if (cell.type_ != Fill::MATERIAL) + return; + + for (int j : instances) { + int cell_material_idx = cell.material(j); + int cell_material_id; + if (cell_material_idx == MATERIAL_VOID) { + cell_material_id = MATERIAL_VOID; + } else { + cell_material_id = model::materials[cell_material_idx]->id(); + } + if (target_material_id == C_NONE || + cell_material_id == target_material_id) { + int64_t source_region = source_region_offsets_[i_cell] + j; + external_volumetric_source_map_[source_region].push_back(src_idx); + } + } +} + +void FlatSourceDomain::apply_external_source_to_cell_and_children( + int32_t i_cell, int src_idx, int32_t target_material_id) +{ + Cell& cell = *model::cells[i_cell]; + + if (cell.type_ == Fill::MATERIAL) { + vector instances(cell.n_instances()); + std::iota(instances.begin(), instances.end(), 0); + apply_external_source_to_cell_instances( + i_cell, src_idx, target_material_id, instances); + } else if (target_material_id == C_NONE) { + std::unordered_map> cell_instance_list = + cell.get_contained_cells(0, nullptr); + for (const auto& pair : cell_instance_list) { + int32_t i_child_cell = pair.first; + apply_external_source_to_cell_instances( + i_child_cell, src_idx, target_material_id, pair.second); + } + } +} + +void FlatSourceDomain::count_external_source_regions() +{ + n_external_source_regions_ = 0; +#pragma omp parallel for reduction(+ : n_external_source_regions_) + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + if (source_regions_.external_source_present(sr)) { + n_external_source_regions_++; + } + } +} + +void FlatSourceDomain::convert_external_sources() +{ + // Loop over external sources + for (int es = 0; es < model::external_sources.size(); es++) { + + // Extract source information + Source* s = model::external_sources[es].get(); + IndependentSource* is = dynamic_cast(s); + Discrete* energy = dynamic_cast(is->energy()); + const std::unordered_set& domain_ids = is->domain_ids(); + double strength_factor = is->strength(); + + // If there is no domain constraint specified, then this must be a point + // source. In this case, we need to find the source region that contains the + // point source and apply or relate it to the external source. + if (is->domain_ids().size() == 0) { + + // Extract the point source coordinate and find the base source region at + // that point + auto sp = dynamic_cast(is->space()); + GeometryState gs; + gs.r() = sp->r(); + gs.r_last() = sp->r(); + gs.u() = {1.0, 0.0, 0.0}; + bool found = exhaustive_find_cell(gs); + if (!found) { + fatal_error(fmt::format("Could not find cell containing external " + "point source at {}", + sp->r())); + } + SourceRegionKey key = lookup_source_region_key(gs); + + // With the source region and mesh bin known, we can use the + // accompanying SourceRegionKey as a key into a map that stores the + // corresponding external source index for the point source. Notably, we + // do not actually apply the external source to any source regions here, + // as if mesh subdivision is enabled, they haven't actually been + // discovered & initilized yet. When discovered, they will read from the + // external_source_map to determine if there are any external source + // terms that should be applied. + external_point_source_map_[key].push_back(es); + + } else { + // If not a point source, then use the volumetric domain constraints to + // determine which source regions to apply the external source to. + if (is->domain_type() == Source::DomainType::MATERIAL) { + for (int32_t material_id : domain_ids) { + for (int i_cell = 0; i_cell < model::cells.size(); i_cell++) { + apply_external_source_to_cell_and_children(i_cell, es, material_id); + } + } + } else if (is->domain_type() == Source::DomainType::CELL) { + for (int32_t cell_id : domain_ids) { + int32_t i_cell = model::cell_map[cell_id]; + apply_external_source_to_cell_and_children(i_cell, es, C_NONE); + } + } else if (is->domain_type() == Source::DomainType::UNIVERSE) { + for (int32_t universe_id : domain_ids) { + int32_t i_universe = model::universe_map[universe_id]; + Universe& universe = *model::universes[i_universe]; + for (int32_t i_cell : universe.cells_) { + apply_external_source_to_cell_and_children(i_cell, es, C_NONE); + } + } + } + } + } // End loop over external sources +} + +void FlatSourceDomain::flux_swap() +{ + source_regions_.flux_swap(); +} + +void FlatSourceDomain::flatten_xs() +{ + // Temperature and angle indices, if using multiple temperature + // data sets and/or anisotropic data sets. + // TODO: Currently assumes we are only using single temp/single angle data. + const int t = 0; + const int a = 0; + + n_materials_ = data::mg.macro_xs_.size(); + for (int i = 0; i < n_materials_; i++) { + auto& m = data::mg.macro_xs_[i]; + for (int g_out = 0; g_out < negroups_; g_out++) { + if (m.exists_in_model) { + double sigma_t = + m.get_xs(MgxsType::TOTAL, g_out, NULL, NULL, NULL, t, a); + sigma_t_.push_back(sigma_t); + + if (sigma_t < MINIMUM_MACRO_XS) { + Material* mat = model::materials[i].get(); + warning(fmt::format( + "Material \"{}\" (id: {}) has a group {} total cross section " + "({:.3e}) below the minimum threshold " + "({:.3e}). Material will be treated as pure void.", + mat->name(), mat->id(), g_out, sigma_t, MINIMUM_MACRO_XS)); + } + + double nu_sigma_f = + m.get_xs(MgxsType::NU_FISSION, g_out, NULL, NULL, NULL, t, a); + nu_sigma_f_.push_back(nu_sigma_f); + + double sigma_f = + m.get_xs(MgxsType::FISSION, g_out, NULL, NULL, NULL, t, a); + sigma_f_.push_back(sigma_f); + + double chi = + m.get_xs(MgxsType::CHI_PROMPT, g_out, &g_out, NULL, NULL, t, a); + if (!std::isfinite(chi)) { + // MGXS interface may return NaN in some cases, such as when material + // is fissionable but has very small sigma_f. + chi = 0.0; + } + chi_.push_back(chi); + + for (int g_in = 0; g_in < negroups_; g_in++) { + double sigma_s = + m.get_xs(MgxsType::NU_SCATTER, g_in, &g_out, NULL, NULL, t, a); + sigma_s_.push_back(sigma_s); + // For transport corrected XS data, diagonal elements may be negative. + // In this case, set a flag to enable transport stabilization for the + // simulation. + if (g_out == g_in && sigma_s < 0.0) + is_transport_stabilization_needed_ = true; + } + } else { + sigma_t_.push_back(0); + nu_sigma_f_.push_back(0); + sigma_f_.push_back(0); + chi_.push_back(0); + for (int g_in = 0; g_in < negroups_; g_in++) { + sigma_s_.push_back(0); + } + } + } + } +} + +void FlatSourceDomain::set_adjoint_sources() +{ + // Set the adjoint external source to 1/forward_flux. If the forward flux is + // negative, zero, or extremely close to zero, set the adjoint source to zero, + // as this is likely a very small source region that we don't need to bother + // trying to vector particles towards. In the case of flux "being extremely + // close to zero", we define this as being a fixed fraction of the maximum + // forward flux, below which we assume the flux would be physically + // undetectable. + + // First, find the maximum forward flux value + double max_flux = 0.0; +#pragma omp parallel for reduction(max : max_flux) + for (int64_t se = 0; se < n_source_elements(); se++) { + double flux = source_regions_.scalar_flux_final(se); + if (flux > max_flux) { + max_flux = flux; + } + } + + // Then, compute the adjoint source for each source region +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + for (int g = 0; g < negroups_; g++) { + double flux = source_regions_.scalar_flux_final(sr, g); + if (flux <= ZERO_FLUX_CUTOFF * max_flux) { + source_regions_.external_source(sr, g) = 0.0; + } else { + source_regions_.external_source(sr, g) = 1.0 / flux; + } + if (flux > 0.0) { + source_regions_.external_source_present(sr) = 1; + } + source_regions_.scalar_flux_final(sr, g) = 0.0; + } + } + + // "Small" source regions in OpenMC are defined as those that are hit by + // MIN_HITS_PER_BATCH rays or fewer each batch. These regions typically have + // very small volumes combined with a low aspect ratio, and are often + // generated when applying a source region mesh that clips the edge of a + // curved surface. As perhaps only a few rays will visit these regions over + // the entire forward simulation, the forward flux estimates are extremely + // noisy and unreliable. In some cases, the noise may make the forward fluxes + // extremely low, leading to unphysically large adjoint source terms, + // resulting in weight windows that aggressively try to drive particles + // towards these regions. To fix this, we simply filter out any "small" source + // regions from consideration. If a source region is "small", we + // set its adjoint source to zero. This adds negligible bias to the adjoint + // flux solution, as the true total adjoint source contribution from small + // regions is likely to be negligible. +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + if (source_regions_.is_small(sr)) { + for (int g = 0; g < negroups_; g++) { + source_regions_.external_source(sr, g) = 0.0; + } + source_regions_.external_source_present(sr) = 0; + } + } + // Divide the fixed source term by sigma t (to save time when applying each + // iteration) +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + continue; + } + for (int g = 0; g < negroups_; g++) { + double sigma_t = sigma_t_[material * negroups_ + g]; + source_regions_.external_source(sr, g) /= sigma_t; + } + } +} + +void FlatSourceDomain::transpose_scattering_matrix() +{ + // Transpose the inner two dimensions for each material + for (int m = 0; m < n_materials_; ++m) { + int material_offset = m * negroups_ * negroups_; + for (int i = 0; i < negroups_; ++i) { + for (int j = i + 1; j < negroups_; ++j) { + // Calculate indices of the elements to swap + int idx1 = material_offset + i * negroups_ + j; + int idx2 = material_offset + j * negroups_ + i; + + // Swap the elements to transpose the matrix + std::swap(sigma_s_[idx1], sigma_s_[idx2]); + } + } + } +} + +void FlatSourceDomain::serialize_final_fluxes(vector& flux) +{ + // Ensure array is correct size + flux.resize(n_source_regions() * negroups_); +// Serialize the final fluxes for output +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + flux[se] = source_regions_.scalar_flux_final(se); + } +} + +void FlatSourceDomain::apply_mesh_to_cell_instances(int32_t i_cell, + int32_t mesh_idx, int target_material_id, const vector& instances, + bool is_target_void) +{ + Cell& cell = *model::cells[i_cell]; + if (cell.type_ != Fill::MATERIAL) + return; + for (int32_t j : instances) { + int cell_material_idx = cell.material(j); + int cell_material_id = (cell_material_idx == C_NONE) + ? C_NONE + : model::materials[cell_material_idx]->id(); + + if ((target_material_id == C_NONE && !is_target_void) || + cell_material_id == target_material_id) { + int64_t sr = source_region_offsets_[i_cell] + j; + // Check if the key is already present in the mesh_map_ + if (mesh_map_.find(sr) != mesh_map_.end()) { + fatal_error(fmt::format("Source region {} already has mesh idx {} " + "applied, but trying to apply mesh idx {}", + sr, mesh_map_[sr], mesh_idx)); + } + // If the SR has not already been assigned, then we can write to it + mesh_map_[sr] = mesh_idx; + } + } +} + +void FlatSourceDomain::apply_mesh_to_cell_and_children(int32_t i_cell, + int32_t mesh_idx, int32_t target_material_id, bool is_target_void) +{ + Cell& cell = *model::cells[i_cell]; + + if (cell.type_ == Fill::MATERIAL) { + vector instances(cell.n_instances()); + std::iota(instances.begin(), instances.end(), 0); + apply_mesh_to_cell_instances( + i_cell, mesh_idx, target_material_id, instances, is_target_void); + } else if (target_material_id == C_NONE && !is_target_void) { + for (int j = 0; j < cell.n_instances(); j++) { + std::unordered_map> cell_instance_list = + cell.get_contained_cells(j, nullptr); + for (const auto& pair : cell_instance_list) { + int32_t i_child_cell = pair.first; + apply_mesh_to_cell_instances(i_child_cell, mesh_idx, target_material_id, + pair.second, is_target_void); + } + } + } +} + +void FlatSourceDomain::apply_meshes() +{ + // Skip if there are no mappings between mesh IDs and domains + if (mesh_domain_map_.empty()) + return; + + // Loop over meshes + for (int mesh_idx = 0; mesh_idx < model::meshes.size(); mesh_idx++) { + Mesh* mesh = model::meshes[mesh_idx].get(); + int mesh_id = mesh->id(); + + // Skip if mesh id is not present in the map + if (mesh_domain_map_.find(mesh_id) == mesh_domain_map_.end()) + continue; + + // Loop over domains associated with the mesh + for (auto& domain : mesh_domain_map_[mesh_id]) { + Source::DomainType domain_type = domain.first; + int domain_id = domain.second; + + if (domain_type == Source::DomainType::MATERIAL) { + for (int i_cell = 0; i_cell < model::cells.size(); i_cell++) { + if (domain_id == C_NONE) { + apply_mesh_to_cell_and_children(i_cell, mesh_idx, domain_id, true); + } else { + apply_mesh_to_cell_and_children(i_cell, mesh_idx, domain_id, false); + } + } + } else if (domain_type == Source::DomainType::CELL) { + int32_t i_cell = model::cell_map[domain_id]; + apply_mesh_to_cell_and_children(i_cell, mesh_idx, C_NONE, false); + } else if (domain_type == Source::DomainType::UNIVERSE) { + int32_t i_universe = model::universe_map[domain_id]; + Universe& universe = *model::universes[i_universe]; + for (int32_t i_cell : universe.cells_) { + apply_mesh_to_cell_and_children(i_cell, mesh_idx, C_NONE, false); + } + } + } + } +} + +SourceRegionHandle FlatSourceDomain::get_subdivided_source_region_handle( + SourceRegionKey sr_key, Position r, Direction u) +{ + // Case 1: Check if the source region key is already present in the permanent + // map. This is the most common condition, as any source region visited in a + // previous power iteration will already be present in the permanent map. If + // the source region key is found, we translate the key into a specific 1D + // source region index and return a handle its position in the + // source_regions_ vector. + auto it = source_region_map_.find(sr_key); + if (it != source_region_map_.end()) { + int64_t sr = it->second; + return source_regions_.get_source_region_handle(sr); + } + + // Case 2: Check if the source region key is present in the temporary (thread + // safe) map. This is a common occurrence in the first power iteration when + // the source region has already been visited already by some other ray. We + // begin by locking the temporary map before any operations are performed. The + // lock is not global over the full data structure -- it will be dependent on + // which key is used. + discovered_source_regions_.lock(sr_key); + + // If the key is found in the temporary map, then we return a handle to the + // source region that is stored in the temporary map. + if (discovered_source_regions_.contains(sr_key)) { + SourceRegionHandle handle {discovered_source_regions_[sr_key]}; + discovered_source_regions_.unlock(sr_key); + return handle; + } + + // Case 3: The source region key is not present anywhere, but it is only due + // to floating point artifacts. These artifacts occur when the overlaid mesh + // overlaps with actual geometry surfaces. In these cases, roundoff error may + // result in the ray tracer detecting an additional (very short) segment + // though a mesh bin that is actually past the physical source region + // boundary. This is a result of the the multi-level ray tracing treatment in + // OpenMC, which depending on the number of universes in the hierarchy etc can + // result in the wrong surface being selected as the nearest. This can happen + // in a lattice when there are two directions that both are very close in + // distance, within the tolerance of FP_REL_PRECISION, and the are thus + // treated as being equivalent so alternative logic is used. However, when we + // go and ray trace on this with the mesh tracer we may go past the surface + // bounding the current source region. + // + // To filter out this case, before we create the new source region, we double + // check that the actual starting point of this segment (r) is still in the + // same geometry source region that we started in. If an artifact is detected, + // we discard the segment (and attenuation through it) as it is not really a + // valid source region and will have only an infinitessimally small cell + // combined with the mesh bin. Thankfully, this is a fairly rare condition, + // and only triggers for very short ray lengths. It can be fixed by decreasing + // the value of FP_REL_PRECISION in constants.h, but this may have unknown + // consequences for the general ray tracer, so for now we do the below sanity + // checks before generating phantom source regions. A significant extra cost + // is incurred in instantiating the GeometryState object and doing a cell + // lookup, but again, this is going to be an extremely rare thing to check + // after the first power iteration has completed. + + // Sanity check on source region id + GeometryState gs; + gs.r() = r + TINY_BIT * u; + gs.u() = {1.0, 0.0, 0.0}; + exhaustive_find_cell(gs); + int64_t sr_found = lookup_base_source_region_idx(gs); + if (sr_found != sr_key.base_source_region_id) { + discovered_source_regions_.unlock(sr_key); + SourceRegionHandle handle; + handle.is_numerical_fp_artifact_ = true; + return handle; + } + + // Sanity check on mesh bin + int mesh_idx = lookup_mesh_idx(sr_key.base_source_region_id); + if (mesh_idx == C_NONE) { + if (sr_key.mesh_bin != 0) { + discovered_source_regions_.unlock(sr_key); + SourceRegionHandle handle; + handle.is_numerical_fp_artifact_ = true; + return handle; + } + } else { + Mesh* mesh = model::meshes[mesh_idx].get(); + int bin_found = mesh->get_bin(r + TINY_BIT * u); + if (bin_found != sr_key.mesh_bin) { + discovered_source_regions_.unlock(sr_key); + SourceRegionHandle handle; + handle.is_numerical_fp_artifact_ = true; + return handle; + } + } + + // Case 4: The source region key is valid, but is not present anywhere. This + // condition only occurs the first time the source region is discovered + // (typically in the first power iteration). In this case, we need to handle + // creation of the new source region and its storage into the parallel map. + // Additionally, we need to determine the source region's material, initialize + // the starting scalar flux guess, and apply any known external sources. + + // Call the basic constructor for the source region and store in the parallel + // map. + bool is_linear = RandomRay::source_shape_ != RandomRaySourceShape::FLAT; + SourceRegion* sr_ptr = + discovered_source_regions_.emplace(sr_key, {negroups_, is_linear}); + SourceRegionHandle handle {*sr_ptr}; + + // Determine the material + int gs_i_cell = gs.lowest_coord().cell(); + Cell& cell = *model::cells[gs_i_cell]; + int material = cell.material(gs.cell_instance()); + + // If material total XS is extremely low, just set it to void to avoid + // problems with 1/Sigma_t + for (int g = 0; g < negroups_; g++) { + double sigma_t = sigma_t_[material * negroups_ + g]; + if (sigma_t < MINIMUM_MACRO_XS) { + material = MATERIAL_VOID; + break; + } + } + + handle.material() = material; + + // Store the mesh index (if any) assigned to this source region + handle.mesh() = mesh_idx; + + if (settings::run_mode == RunMode::FIXED_SOURCE) { + // Determine if there are any volumetric sources, and apply them. + // Volumetric sources are specifc only to the base SR idx. + auto it_vol = + external_volumetric_source_map_.find(sr_key.base_source_region_id); + if (it_vol != external_volumetric_source_map_.end()) { + const vector& vol_sources = it_vol->second; + for (int src_idx : vol_sources) { + apply_external_source_to_source_region(src_idx, handle); + } + } + + // Determine if there are any point sources, and apply them. + // Point sources are specific to the source region key. + auto it_point = external_point_source_map_.find(sr_key); + if (it_point != external_point_source_map_.end()) { + const vector& point_sources = it_point->second; + for (int src_idx : point_sources) { + apply_external_source_to_source_region(src_idx, handle); + } + } + + // Divide external source term by sigma_t + if (material != C_NONE) { + for (int g = 0; g < negroups_; g++) { + double sigma_t = sigma_t_[material * negroups_ + g]; + handle.external_source(g) /= sigma_t; + } + } + } + + // Compute the combined source term + update_single_neutron_source(handle); + + // Unlock the parallel map. Note: we may be tempted to release + // this lock earlier, and then just use the source region's lock to protect + // the flux/source initialization stages above. However, the rest of the code + // only protects updates to the new flux and volume fields, and assumes that + // the source is constant for the duration of transport. Thus, using just the + // source region's lock by itself would result in other threads potentially + // reading from the source before it is computed, as they won't use the lock + // when only reading from the SR's source. It would be expensive to protect + // those operations, whereas generating the SR is only done once, so we just + // hold the map's bucket lock until the source region is fully initialized. + discovered_source_regions_.unlock(sr_key); + + return handle; +} + +void FlatSourceDomain::finalize_discovered_source_regions() +{ + // Extract keys for entries with a valid volume. + vector keys; + for (const auto& pair : discovered_source_regions_) { + if (pair.second.volume_ > 0.0) { + keys.push_back(pair.first); + } + } + + if (!keys.empty()) { + // Sort the keys, so as to ensure reproducible ordering given that source + // regions may have been added to discovered_source_regions_ in an arbitrary + // order due to shared memory threading. + std::sort(keys.begin(), keys.end()); + + // Remember the index of the first new source region + int64_t start_sr_id = source_regions_.n_source_regions(); + + // Append the source regions in the sorted key order. + for (const auto& key : keys) { + const SourceRegion& sr = discovered_source_regions_[key]; + source_region_map_[key] = source_regions_.n_source_regions(); + source_regions_.push_back(sr); + } + + // Map all new source regions to tallies + convert_source_regions_to_tallies(start_sr_id); + } + + discovered_source_regions_.clear(); +} + +// This is the "diagonal stabilization" technique developed by Gunow et al. in: +// +// Geoffrey Gunow, Benoit Forget, Kord Smith, Stabilization of multi-group +// neutron transport with transport-corrected cross-sections, Annals of Nuclear +// Energy, Volume 126, 2019, Pages 211-219, ISSN 0306-4549, +// https://doi.org/10.1016/j.anucene.2018.10.036. +void FlatSourceDomain::apply_transport_stabilization() +{ + // Don't do anything if all in-group scattering + // cross sections are positive + if (!is_transport_stabilization_needed_) { + return; + } + + // Apply the stabilization factor to all source elements +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + continue; + } + for (int g = 0; g < negroups_; g++) { + // Only apply stabilization if the diagonal (in-group) scattering XS is + // negative + double sigma_s = + sigma_s_[material * negroups_ * negroups_ + g * negroups_ + g]; + if (sigma_s < 0.0) { + double sigma_t = sigma_t_[material * negroups_ + g]; + double phi_new = source_regions_.scalar_flux_new(sr, g); + double phi_old = source_regions_.scalar_flux_old(sr, g); + + // Equation 18 in the above Gunow et al. 2019 paper. For a default + // rho of 1.0, this ensures there are no negative diagonal elements + // in the iteration matrix. A lesser rho could be used (or exposed + // as a user input parameter) to reduce the negative impact on + // convergence rate though would need to be experimentally tested to see + // if it doesn't become unstable. rho = 1.0 is good as it gives the + // highest assurance of stability, and the impacts on convergence rate + // are pretty mild. + double D = diagonal_stabilization_rho_ * sigma_s / sigma_t; + + // Equation 16 in the above Gunow et al. 2019 paper + source_regions_.scalar_flux_new(sr, g) = + (phi_new - D * phi_old) / (1.0 - D); + } + } + } +} + +// Determines the base source region index (i.e., a material filled cell +// instance) that corresponds to a particular location in the geometry. Requires +// that the "gs" object passed in has already been initialized and has called +// find_cell etc. +int64_t FlatSourceDomain::lookup_base_source_region_idx( + const GeometryState& gs) const +{ + int i_cell = gs.lowest_coord().cell(); + int64_t sr = source_region_offsets_[i_cell] + gs.cell_instance(); + return sr; +} + +// Determines the index of the mesh (if any) that has been applied +// to a particular base source region index. +int FlatSourceDomain::lookup_mesh_idx(int64_t sr) const +{ + int mesh_idx = C_NONE; + auto mesh_it = mesh_map_.find(sr); + if (mesh_it != mesh_map_.end()) { + mesh_idx = mesh_it->second; + } + return mesh_idx; +} + +// Determines the source region key that corresponds to a particular location in +// the geometry. This takes into account both the base source region index as +// well as the mesh bin if a mesh is applied to this source region for +// subdivision. +SourceRegionKey FlatSourceDomain::lookup_source_region_key( + const GeometryState& gs) const +{ + int64_t sr = lookup_base_source_region_idx(gs); + int64_t mesh_bin = lookup_mesh_bin(sr, gs.r()); + return SourceRegionKey {sr, mesh_bin}; +} + +// Determines the mesh bin that corresponds to a particular base source region +// index and position. +int64_t FlatSourceDomain::lookup_mesh_bin(int64_t sr, Position r) const +{ + int mesh_idx = lookup_mesh_idx(sr); + int mesh_bin = 0; + if (mesh_idx != C_NONE) { + mesh_bin = model::meshes[mesh_idx]->get_bin(r); + } + return mesh_bin; +} + +} // namespace openmc diff --git a/src/random_ray/linear_source_domain.cpp b/src/random_ray/linear_source_domain.cpp new file mode 100644 index 0000000000..e1ad68e3d8 --- /dev/null +++ b/src/random_ray/linear_source_domain.cpp @@ -0,0 +1,197 @@ +#include "openmc/random_ray/linear_source_domain.h" + +#include "openmc/cell.h" +#include "openmc/geometry.h" +#include "openmc/material.h" +#include "openmc/message_passing.h" +#include "openmc/mgxs_interface.h" +#include "openmc/output.h" +#include "openmc/plot.h" +#include "openmc/random_ray/random_ray.h" +#include "openmc/simulation.h" +#include "openmc/tallies/filter.h" +#include "openmc/tallies/tally.h" +#include "openmc/tallies/tally_scoring.h" +#include "openmc/timer.h" + +namespace openmc { + +//============================================================================== +// LinearSourceDomain implementation +//============================================================================== + +void LinearSourceDomain::batch_reset() +{ + FlatSourceDomain::batch_reset(); +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + source_regions_.centroid_iteration(sr) = {0.0, 0.0, 0.0}; + source_regions_.mom_matrix(sr) = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + } +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.flux_moments_new(se) = {0.0, 0.0, 0.0}; + } +} + +void LinearSourceDomain::update_single_neutron_source(SourceRegionHandle& srh) +{ + // Reset all source regions to zero (important for void regions) + for (int g = 0; g < negroups_; g++) { + srh.source(g) = 0.0; + } + + // Add scattering + fission source + int material = srh.material(); + if (material != MATERIAL_VOID) { + double inverse_k_eff = 1.0 / k_eff_; + MomentMatrix invM = srh.mom_matrix().inverse(); + + for (int g_out = 0; g_out < negroups_; g_out++) { + double sigma_t = sigma_t_[material * negroups_ + g_out]; + + double scatter_flat = 0.0f; + double fission_flat = 0.0f; + MomentArray scatter_linear = {0.0, 0.0, 0.0}; + MomentArray fission_linear = {0.0, 0.0, 0.0}; + + for (int g_in = 0; g_in < negroups_; g_in++) { + // Handles for the flat and linear components of the flux + double flux_flat = srh.scalar_flux_old(g_in); + MomentArray flux_linear = srh.flux_moments_old(g_in); + + // Handles for cross sections + double sigma_s = + sigma_s_[material * negroups_ * negroups_ + g_out * negroups_ + g_in]; + double nu_sigma_f = nu_sigma_f_[material * negroups_ + g_in]; + double chi = chi_[material * negroups_ + g_out]; + + // Compute source terms for flat and linear components of the flux + scatter_flat += sigma_s * flux_flat; + fission_flat += nu_sigma_f * flux_flat * chi; + scatter_linear += sigma_s * flux_linear; + fission_linear += nu_sigma_f * flux_linear * chi; + } + + // Compute the flat source term + srh.source(g_out) = + (scatter_flat + fission_flat * inverse_k_eff) / sigma_t; + + // Compute the linear source terms. In the first 10 iterations when the + // centroids and spatial moments are not well known, we will leave the + // source gradients as zero so as to avoid causing any numerical + // instability. If a negative source is encountered, this region must be + // very small/noisy or have poorly developed spatial moments, so we zero + // the source gradients (effectively making this a flat source region + // temporarily), so as to improve stability. + if (simulation::current_batch > 10 && srh.source(g_out) >= 0.0) { + srh.source_gradients(g_out) = + invM * ((scatter_linear + fission_linear * inverse_k_eff) / sigma_t); + } else { + srh.source_gradients(g_out) = {0.0, 0.0, 0.0}; + } + } + } + + // Add external source if in fixed source mode + if (settings::run_mode == RunMode::FIXED_SOURCE) { + for (int g = 0; g < negroups_; g++) { + srh.source(g) += srh.external_source(g); + } + } +} + +void LinearSourceDomain::normalize_scalar_flux_and_volumes( + double total_active_distance_per_iteration) +{ + double normalization_factor = 1.0 / total_active_distance_per_iteration; + double volume_normalization_factor = + 1.0 / (total_active_distance_per_iteration * simulation::current_batch); + +// Normalize flux to total distance travelled by all rays this iteration +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.scalar_flux_new(se) *= normalization_factor; + source_regions_.flux_moments_new(se) *= normalization_factor; + } + +// Accumulate cell-wise ray length tallies collected this iteration, then +// update the simulation-averaged cell-wise volume estimates +#pragma omp parallel for + for (int64_t sr = 0; sr < n_source_regions(); sr++) { + source_regions_.centroid_t(sr) += source_regions_.centroid_iteration(sr); + source_regions_.mom_matrix_t(sr) += source_regions_.mom_matrix(sr); + source_regions_.volume_t(sr) += source_regions_.volume(sr); + source_regions_.volume_sq_t(sr) += source_regions_.volume_sq(sr); + source_regions_.volume_naive(sr) = + source_regions_.volume(sr) * normalization_factor; + source_regions_.volume(sr) = + source_regions_.volume_t(sr) * volume_normalization_factor; + source_regions_.volume_sq(sr) = + source_regions_.volume_sq_t(sr) / source_regions_.volume_t(sr); + if (source_regions_.volume_t(sr) > 0.0) { + double inv_volume = 1.0 / source_regions_.volume_t(sr); + source_regions_.centroid(sr) = source_regions_.centroid_t(sr); + source_regions_.centroid(sr) *= inv_volume; + source_regions_.mom_matrix(sr) = source_regions_.mom_matrix_t(sr); + source_regions_.mom_matrix(sr) *= inv_volume; + } + } +} + +void LinearSourceDomain::set_flux_to_flux_plus_source( + int64_t sr, double volume, int g) +{ + int material = source_regions_.material(sr); + if (material == MATERIAL_VOID) { + FlatSourceDomain::set_flux_to_flux_plus_source(sr, volume, g); + } else { + source_regions_.scalar_flux_new(sr, g) /= volume; + source_regions_.scalar_flux_new(sr, g) += source_regions_.source(sr, g); + } + // If a source region is small, then the moments are likely noisy, so we zero + // them. This is reasonable, given that small regions can get by with a flat + // source approximation anyhow. + if (source_regions_.is_small(sr)) { + source_regions_.flux_moments_new(sr, g) = {0.0, 0.0, 0.0}; + } else { + source_regions_.flux_moments_new(sr, g) *= (1.0 / volume); + } +} + +void LinearSourceDomain::set_flux_to_old_flux(int64_t sr, int g) +{ + source_regions_.scalar_flux_new(sr, g) = + source_regions_.scalar_flux_old(sr, g); + source_regions_.flux_moments_new(sr, g) = + source_regions_.flux_moments_old(sr, g); +} + +void LinearSourceDomain::accumulate_iteration_flux() +{ + // Accumulate scalar flux + FlatSourceDomain::accumulate_iteration_flux(); + + // Accumulate scalar flux moments +#pragma omp parallel for + for (int64_t se = 0; se < n_source_elements(); se++) { + source_regions_.flux_moments_t(se) += source_regions_.flux_moments_new(se); + } +} + +double LinearSourceDomain::evaluate_flux_at_point( + Position r, int64_t sr, int g) const +{ + double phi_flat = FlatSourceDomain::evaluate_flux_at_point(r, sr, g); + + Position local_r = r - source_regions_.centroid(sr); + MomentArray phi_linear = source_regions_.flux_moments_t(sr, g); + phi_linear *= 1.0 / (settings::n_batches - settings::n_inactive); + + MomentMatrix invM = source_regions_.mom_matrix(sr).inverse(); + MomentArray phi_solved = invM * phi_linear; + + return phi_flat + phi_solved.dot(local_r); +} + +} // namespace openmc diff --git a/src/random_ray/moment_matrix.cpp b/src/random_ray/moment_matrix.cpp new file mode 100644 index 0000000000..0324a14943 --- /dev/null +++ b/src/random_ray/moment_matrix.cpp @@ -0,0 +1,84 @@ +#include "openmc/random_ray/moment_matrix.h" +#include "openmc/error.h" + +#include + +namespace openmc { + +//============================================================================== +// UpperTriangular implementation +//============================================================================== + +// Inverts a 3x3 smmetric matrix labeled as: +// +// | a b c | +// | b d e | +// | c e f | +// +// We first check the determinant to ensure it is non-zero before proceeding +// with the inversion. If the determinant is zero, we return a matrix of zeros. +// Inversion is calculated by computing the adjoint matrix first, and then the +// inverse can be computed as: A^-1 = 1/det(A) * adj(A) +MomentMatrix MomentMatrix::inverse() const +{ + MomentMatrix inv; + + // Check if the determinant is zero + double det = determinant(); + if (det < std::abs(1.0e-10)) { + // Set the inverse to zero. In effect, this will + // result in all the linear terms of the source becoming + // zero, leaving just the flat source. + inv.set_to_zero(); + return inv; + } + + // Compute the adjoint matrix + inv.a = d * f - e * e; + inv.b = c * e - b * f; + inv.c = b * e - c * d; + inv.d = a * f - c * c; + inv.e = b * c - a * e; + inv.f = a * d - b * b; + + // A^-1 = 1/det(A) * adj(A) + inv *= 1.0 / det; + + return inv; +} + +// Computes the determinant of a 3x3 symmetric +// matrix, with elements labeled as follows: +// +// | a b c | +// | b d e | +// | c e f | +double MomentMatrix::determinant() const +{ + return a * (d * f - e * e) - b * (b * f - c * e) + c * (b * e - c * d); +} + +// Compute a 3x3 spatial moment matrix based on a single ray crossing. +// The matrix is symmetric, and is defined as: +// +// | a b c | +// | b d e | +// | c e f | +// +// The estimate of the obect's spatial moments matrix is computed based on the +// midpoint of the ray's crossing, the direction of the ray, and the distance +// the ray traveled through the 3D object. +void MomentMatrix::compute_spatial_moments_matrix( + const Position& r, const Direction& u, const double& distance) +{ + constexpr double one_over_twelve = 1.0 / 12.0; + const double distance2_12 = distance * distance * one_over_twelve; + a = r[0] * r[0] + u[0] * u[0] * distance2_12; + b = r[0] * r[1] + u[0] * u[1] * distance2_12; + c = r[0] * r[2] + u[0] * u[2] * distance2_12; + d = r[1] * r[1] + u[1] * u[1] * distance2_12; + e = r[1] * r[2] + u[1] * u[2] * distance2_12; + f = r[2] * r[2] + u[2] * u[2] * distance2_12; +} + +} // namespace openmc \ No newline at end of file diff --git a/src/random_ray/random_ray.cpp b/src/random_ray/random_ray.cpp new file mode 100644 index 0000000000..89a91449df --- /dev/null +++ b/src/random_ray/random_ray.cpp @@ -0,0 +1,864 @@ +#include "openmc/random_ray/random_ray.h" + +#include "openmc/constants.h" +#include "openmc/geometry.h" +#include "openmc/message_passing.h" +#include "openmc/mgxs_interface.h" +#include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/linear_source_domain.h" +#include "openmc/search.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" + +#include "openmc/distribution_spatial.h" +#include "openmc/random_dist.h" +#include "openmc/source.h" + +namespace openmc { + +//============================================================================== +// Non-method functions +//============================================================================== + +// returns 1 - exp(-tau) +// Equivalent to -(_expm1f(-tau)), but faster +// Written by Colin Josey. +float cjosey_exponential(float tau) +{ + constexpr float c1n = -1.0000013559236386308f; + constexpr float c2n = 0.23151368626911062025f; + constexpr float c3n = -0.061481916409314966140f; + constexpr float c4n = 0.0098619906458127653020f; + constexpr float c5n = -0.0012629460503540849940f; + constexpr float c6n = 0.00010360973791574984608f; + constexpr float c7n = -0.000013276571933735820960f; + + constexpr float c0d = 1.0f; + constexpr float c1d = -0.73151337729389001396f; + constexpr float c2d = 0.26058381273536471371f; + constexpr float c3d = -0.059892419041316836940f; + constexpr float c4d = 0.0099070188241094279067f; + constexpr float c5d = -0.0012623388962473160860f; + constexpr float c6d = 0.00010361277635498731388f; + constexpr float c7d = -0.000013276569500666698498f; + + float x = -tau; + + float den = c7d; + den = den * x + c6d; + den = den * x + c5d; + den = den * x + c4d; + den = den * x + c3d; + den = den * x + c2d; + den = den * x + c1d; + den = den * x + c0d; + + float num = c7n; + num = num * x + c6n; + num = num * x + c5n; + num = num * x + c4n; + num = num * x + c3n; + num = num * x + c2n; + num = num * x + c1n; + num = num * x; + + return num / den; +} + +// The below two functions (exponentialG and exponentialG2) were developed +// by Colin Josey. The implementation of these functions is closely based +// on the OpenMOC versions of these functions. The OpenMOC license is given +// below: + +// Copyright (C) 2012-2023 Massachusetts Institute of Technology and OpenMOC +// contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Computes y = 1/x-(1-exp(-x))/x**2 using a 5/6th order rational +// approximation. It is accurate to 2e-7 over [0, 1e5]. Developed by Colin +// Josey using Remez's algorithm, with original implementation in OpenMOC at: +// https://github.com/mit-crpg/OpenMOC/blob/develop/src/exponentials.h +float exponentialG(float tau) +{ + // Numerator coefficients in rational approximation for 1/x - (1 - exp(-x)) / + // x^2 + constexpr float d0n = 0.5f; + constexpr float d1n = 0.176558112351595f; + constexpr float d2n = 0.04041584305811143f; + constexpr float d3n = 0.006178333902037397f; + constexpr float d4n = 0.0006429894635552992f; + constexpr float d5n = 0.00006064409107557148f; + + // Denominator coefficients in rational approximation for 1/x - (1 - exp(-x)) + // / x^2 + constexpr float d0d = 1.0f; + constexpr float d1d = 0.6864462055546078f; + constexpr float d2d = 0.2263358514260129f; + constexpr float d3d = 0.04721469893686252f; + constexpr float d4d = 0.006883236664917246f; + constexpr float d5d = 0.0007036272419147752f; + constexpr float d6d = 0.00006064409107557148f; + + float x = tau; + + float num = d5n; + num = num * x + d4n; + num = num * x + d3n; + num = num * x + d2n; + num = num * x + d1n; + num = num * x + d0n; + + float den = d6d; + den = den * x + d5d; + den = den * x + d4d; + den = den * x + d3d; + den = den * x + d2d; + den = den * x + d1d; + den = den * x + d0d; + + return num / den; +} + +// Computes G2 : y = 2/3 - (1 + 2/x) * (1/x + 0.5 - (1 + 1/x) * (1-exp(-x)) / +// x) using a 5/5th order rational approximation. It is accurate to 1e-6 over +// [0, 1e6]. Developed by Colin Josey using Remez's algorithm, with original +// implementation in OpenMOC at: +// https://github.com/mit-crpg/OpenMOC/blob/develop/src/exponentials.h +float exponentialG2(float tau) +{ + + // Coefficients for numerator in rational approximation + constexpr float g1n = -0.08335775885589858f; + constexpr float g2n = -0.003603942303847604f; + constexpr float g3n = 0.0037673183263550827f; + constexpr float g4n = 0.00001124183494990467f; + constexpr float g5n = 0.00016837426505799449f; + + // Coefficients for denominator in rational approximation + constexpr float g1d = 0.7454048371823628f; + constexpr float g2d = 0.23794300531408347f; + constexpr float g3d = 0.05367250964303789f; + constexpr float g4d = 0.006125197988351906f; + constexpr float g5d = 0.0010102514456857377f; + + float x = tau; + + float num = g5n; + num = num * x + g4n; + num = num * x + g3n; + num = num * x + g2n; + num = num * x + g1n; + num = num * x; + + float den = g5d; + den = den * x + g4d; + den = den * x + g3d; + den = den * x + g2d; + den = den * x + g1d; + den = den * x + 1.0f; + + return num / den; +} + +// Implementation of the Fisher-Yates shuffle algorithm. +// Algorithm adapted from: +// https://en.cppreference.com/w/cpp/algorithm/random_shuffle#Version_3 +void fisher_yates_shuffle(vector& arr, uint64_t* seed) +{ + // Loop over the array from the last element down to the second + for (int i = arr.size() - 1; i > 0; --i) { + // Generate a random index in the range [0, i] + int j = uniform_int_distribution(0, i, seed); + std::swap(arr[i], arr[j]); + } +} + +// Function to generate randomized Halton sequence samples +// +// Algorithm adapted from: +// A. B. Owen. A randomized halton algorithm in r. Arxiv, 6 2017. +// URL https://arxiv.org/abs/1706.02808 +vector rhalton(int dim, uint64_t* seed, int64_t skip = 0) +{ + if (dim > 10) { + fatal_error("Halton sampling dimension too large"); + } + int64_t b, res, dig; + double b2r, ans; + const std::array primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; + vector halton(dim, 0.0); + + vector perm; + for (int D = 0; D < dim; ++D) { + b = primes[D]; + perm.resize(b); + b2r = 1.0 / b; + res = skip; + ans = 0.0; + + while ((1.0 - b2r) < 1.0) { + std::iota(perm.begin(), perm.end(), 0); + fisher_yates_shuffle(perm, seed); + dig = res % b; + ans += perm[dig] * b2r; + res = (res - dig) / b; + b2r /= b; + } + + halton[D] = ans; + } + + return halton; +} + +//============================================================================== +// RandomRay implementation +//============================================================================== + +// Static Variable Declarations +double RandomRay::distance_inactive_; +double RandomRay::distance_active_; +unique_ptr RandomRay::ray_source_; +RandomRaySourceShape RandomRay::source_shape_ {RandomRaySourceShape::FLAT}; +RandomRaySampleMethod RandomRay::sample_method_ {RandomRaySampleMethod::PRNG}; + +RandomRay::RandomRay() + : angular_flux_(data::mg.num_energy_groups_), + delta_psi_(data::mg.num_energy_groups_), + negroups_(data::mg.num_energy_groups_) +{ + if (source_shape_ == RandomRaySourceShape::LINEAR || + source_shape_ == RandomRaySourceShape::LINEAR_XY) { + delta_moments_.resize(negroups_); + } +} + +RandomRay::RandomRay(uint64_t ray_id, FlatSourceDomain* domain) : RandomRay() +{ + initialize_ray(ray_id, domain); +} + +// Transports ray until termination criteria are met +uint64_t RandomRay::transport_history_based_single_ray() +{ + using namespace openmc; + while (alive()) { + event_advance_ray(); + if (!alive()) + break; + event_cross_surface(); + // If ray has too many events, display warning and kill it + if (n_event() >= settings::max_particle_events) { + warning("Ray " + std::to_string(id()) + + " underwent maximum number of events, terminating ray."); + wgt() = 0.0; + } + } + + return n_event(); +} + +// Transports ray across a single source region +void RandomRay::event_advance_ray() +{ + // Find the distance to the nearest boundary + boundary() = distance_to_boundary(*this); + double distance = boundary().distance(); + + if (distance < 0.0) { + mark_as_lost("Negative transport distance detected for particle " + + std::to_string(id())); + return; + } + + if (is_active_) { + // If the ray is in the active length, need to check if it has + // reached its maximum termination distance. If so, reduce + // the ray traced length so that the ray does not overrun the + // maximum numerical length (so as to avoid numerical bias). + if (distance_travelled_ + distance >= distance_active_) { + distance = distance_active_ - distance_travelled_; + wgt() = 0.0; + } + + distance_travelled_ += distance; + attenuate_flux(distance, true); + } else { + // If the ray is still in the dead zone, need to check if it + // has entered the active phase. If so, split into two segments (one + // representing the final part of the dead zone, the other representing the + // first part of the active length) and attenuate each. Otherwise, if the + // full length of the segment is within the dead zone, attenuate as normal. + if (distance_travelled_ + distance >= distance_inactive_) { + is_active_ = true; + double distance_dead = distance_inactive_ - distance_travelled_; + attenuate_flux(distance_dead, false); + + double distance_alive = distance - distance_dead; + + // Ensure we haven't travelled past the active phase as well + if (distance_alive > distance_active_) { + distance_alive = distance_active_; + wgt() = 0.0; + } + + attenuate_flux(distance_alive, true, distance_dead); + distance_travelled_ = distance_alive; + } else { + distance_travelled_ += distance; + attenuate_flux(distance, false); + } + } + + // Advance particle + for (int j = 0; j < n_coord(); ++j) { + coord(j).r() += distance * coord(j).u(); + } +} + +void RandomRay::attenuate_flux(double distance, bool is_active, double offset) +{ + // Lookup base source region index + int64_t sr = domain_->lookup_base_source_region_idx(*this); + + // Perform ray tracing across mesh + // Determine the mesh index for the base source region, if any + int mesh_idx = domain_->lookup_mesh_idx(sr); + + if (mesh_idx == C_NONE) { + // If there's no mesh being applied to this cell, then + // we just attenuate the flux as normal, and set + // the mesh bin to 0 + attenuate_flux_inner(distance, is_active, sr, 0, r()); + } else { + // If there is a mesh being applied to this cell, then + // we loop over all the bin crossings and attenuate + // separately. + Mesh* mesh = model::meshes[mesh_idx].get(); + + // We adjust the start and end positions of the ray slightly + // to accomodate for floating point precision issues that tend + // to occur at mesh boundaries that overlap with geometry lattice + // boundaries. + Position start = r() + (offset + TINY_BIT) * u(); + Position end = start + (distance - 2.0 * TINY_BIT) * u(); + double reduced_distance = (end - start).norm(); + + // Ray trace through the mesh and record bins and lengths + mesh_bins_.resize(0); + mesh_fractional_lengths_.resize(0); + mesh->bins_crossed(start, end, u(), mesh_bins_, mesh_fractional_lengths_); + + // Loop over all mesh bins and attenuate flux + for (int b = 0; b < mesh_bins_.size(); b++) { + double physical_length = reduced_distance * mesh_fractional_lengths_[b]; + attenuate_flux_inner( + physical_length, is_active, sr, mesh_bins_[b], start); + start += physical_length * u(); + } + } +} + +void RandomRay::attenuate_flux_inner( + double distance, bool is_active, int64_t sr, int mesh_bin, Position r) +{ + SourceRegionKey sr_key {sr, mesh_bin}; + SourceRegionHandle srh; + srh = domain_->get_subdivided_source_region_handle(sr_key, r, u()); + if (srh.is_numerical_fp_artifact_) { + return; + } + + switch (source_shape_) { + case RandomRaySourceShape::FLAT: + if (srh.material() == MATERIAL_VOID) { + attenuate_flux_flat_source_void(srh, distance, is_active, r); + } else { + attenuate_flux_flat_source(srh, distance, is_active, r); + } + break; + case RandomRaySourceShape::LINEAR: + case RandomRaySourceShape::LINEAR_XY: + if (srh.material() == MATERIAL_VOID) { + attenuate_flux_linear_source_void(srh, distance, is_active, r); + } else { + attenuate_flux_linear_source(srh, distance, is_active, r); + } + break; + default: + fatal_error("Unknown source shape for random ray transport."); + } +} + +// This function forms the inner loop of the random ray transport process. +// It is responsible for several tasks. Based on the incoming angular flux +// of the ray and the source term in the region, the outgoing angular flux +// is computed. The delta psi between the incoming and outgoing fluxes is +// contributed to the estimate of the total scalar flux in the source region. +// Additionally, the contribution of the ray path to the stochastically +// estimated volume is also kept track of. All tasks involving writing +// to the data for the source region are done with a lock over the entire +// source region. Locks are used instead of atomics as all energy groups +// must be written, such that locking once is typically much more efficient +// than use of many atomic operations corresponding to each energy group +// individually (at least on CPU). Several other bookkeeping tasks are also +// performed when inside the lock. +void RandomRay::attenuate_flux_flat_source( + SourceRegionHandle& srh, double distance, bool is_active, Position r) +{ + // The number of geometric intersections is counted for reporting purposes + n_event()++; + + // Get material + int material = srh.material(); + + // MOC incoming flux attenuation + source contribution/attenuation equation + for (int g = 0; g < negroups_; g++) { + float sigma_t = domain_->sigma_t_[material * negroups_ + g]; + float tau = sigma_t * distance; + float exponential = cjosey_exponential(tau); // exponential = 1 - exp(-tau) + float new_delta_psi = (angular_flux_[g] - srh.source(g)) * exponential; + delta_psi_[g] = new_delta_psi; + angular_flux_[g] -= new_delta_psi; + } + + // If ray is in the active phase (not in dead zone), make contributions to + // source region bookkeeping + + // Aquire lock for source region + srh.lock(); + + if (is_active) { + // Accumulate delta psi into new estimate of source region flux for + // this iteration + for (int g = 0; g < negroups_; g++) { + srh.scalar_flux_new(g) += delta_psi_[g]; + } + + // Accomulate volume (ray distance) into this iteration's estimate + // of the source region's volume + srh.volume() += distance; + + srh.n_hits() += 1; + } + + // Tally valid position inside the source region (e.g., midpoint of + // the ray) if not done already + if (!srh.position_recorded()) { + Position midpoint = r + u() * (distance / 2.0); + srh.position() = midpoint; + srh.position_recorded() = 1; + } + + // Release lock + srh.unlock(); +} + +// Alternative flux attenuation function for true void regions. +void RandomRay::attenuate_flux_flat_source_void( + SourceRegionHandle& srh, double distance, bool is_active, Position r) +{ + // The number of geometric intersections is counted for reporting purposes + n_event()++; + + int material = srh.material(); + + // If ray is in the active phase (not in dead zone), make contributions to + // source region bookkeeping + if (is_active) { + + // Aquire lock for source region + srh.lock(); + + // Accumulate delta psi into new estimate of source region flux for + // this iteration + for (int g = 0; g < negroups_; g++) { + srh.scalar_flux_new(g) += angular_flux_[g] * distance; + } + + // Accomulate volume (ray distance) into this iteration's estimate + // of the source region's volume + srh.volume() += distance; + srh.volume_sq() += distance * distance; + srh.n_hits() += 1; + + // Tally valid position inside the source region (e.g., midpoint of + // the ray) if not done already + if (!srh.position_recorded()) { + Position midpoint = r + u() * (distance / 2.0); + srh.position() = midpoint; + srh.position_recorded() = 1; + } + + // Release lock + srh.unlock(); + } + + // Add source to incoming angular flux, assuming void region + if (settings::run_mode == RunMode::FIXED_SOURCE) { + for (int g = 0; g < negroups_; g++) { + angular_flux_[g] += srh.external_source(g) * distance; + } + } +} + +void RandomRay::attenuate_flux_linear_source( + SourceRegionHandle& srh, double distance, bool is_active, Position r) +{ + // The number of geometric intersections is counted for reporting purposes + n_event()++; + + int material = srh.material(); + + Position& centroid = srh.centroid(); + Position midpoint = r + u() * (distance / 2.0); + + // Determine the local position of the midpoint and the ray origin + // relative to the source region's centroid + Position rm_local; + Position r0_local; + + // In the first few iterations of the simulation, the source region + // may not yet have had any ray crossings, in which case there will + // be no estimate of its centroid. We detect this by checking if it has + // any accumulated volume. If its volume is zero, just use the midpoint + // of the ray as the region's centroid. + if (srh.volume_t()) { + rm_local = midpoint - centroid; + r0_local = r - centroid; + } else { + rm_local = {0.0, 0.0, 0.0}; + r0_local = -u() * 0.5 * distance; + } + double distance_2 = distance * distance; + + // Linear Source MOC incoming flux attenuation + source + // contribution/attenuation equation + for (int g = 0; g < negroups_; g++) { + + // Compute tau, the optical thickness of the ray segment + float sigma_t = domain_->sigma_t_[material * negroups_ + g]; + float tau = sigma_t * distance; + + // If tau is very small, set it to zero to avoid numerical issues. + // The following computations will still work with tau = 0. + if (tau < 1.0e-8f) { + tau = 0.0f; + } + + // Compute linear source terms, spatial and directional (dir), + // calculated from the source gradients dot product with local centroid + // and direction, respectively. + float spatial_source = + srh.source(g) + rm_local.dot(srh.source_gradients(g)); + float dir_source = u().dot(srh.source_gradients(g)); + + float gn = exponentialG(tau); + float f1 = 1.0f - tau * gn; + float f2 = (2.0f * gn - f1) * distance_2; + float new_delta_psi = (angular_flux_[g] - spatial_source) * f1 * distance - + 0.5 * dir_source * f2; + + float h1 = f1 - gn; + float g1 = 0.5f - h1; + float g2 = exponentialG2(tau); + g1 = g1 * spatial_source; + g2 = g2 * dir_source * distance * 0.5f; + h1 = h1 * angular_flux_[g]; + h1 = (g1 + g2 + h1) * distance_2; + spatial_source = spatial_source * distance + new_delta_psi; + + // Store contributions for this group into arrays, so that they can + // be accumulated into the source region's estimates inside of the locked + // region. + delta_psi_[g] = new_delta_psi; + delta_moments_[g] = r0_local * spatial_source + u() * h1; + + // Update the angular flux for this group + angular_flux_[g] -= new_delta_psi * sigma_t; + + // If 2D mode is enabled, the z-component of the flux moments is forced + // to zero + if (source_shape_ == RandomRaySourceShape::LINEAR_XY) { + delta_moments_[g].z = 0.0; + } + } + + // Compute an estimate of the spatial moments matrix for the source + // region based on parameters from this ray's crossing + MomentMatrix moment_matrix_estimate; + moment_matrix_estimate.compute_spatial_moments_matrix( + rm_local, u(), distance); + + // Aquire lock for source region + srh.lock(); + + // If ray is in the active phase (not in dead zone), make contributions to + // source region bookkeeping + + if (is_active) { + // Accumulate deltas into the new estimate of source region flux for this + // iteration + for (int g = 0; g < negroups_; g++) { + srh.scalar_flux_new(g) += delta_psi_[g]; + srh.flux_moments_new(g) += delta_moments_[g]; + } + + // Accumulate the volume (ray segment distance), centroid, and spatial + // momement estimates into the running totals for the iteration for this + // source region. The centroid and spatial momements estimates are scaled + // by the ray segment length as part of length averaging of the estimates. + srh.volume() += distance; + srh.centroid_iteration() += midpoint * distance; + moment_matrix_estimate *= distance; + srh.mom_matrix() += moment_matrix_estimate; + + srh.n_hits() += 1; + } + + // Tally valid position inside the source region (e.g., midpoint of + // the ray) if not done already + if (!srh.position_recorded()) { + srh.position() = midpoint; + srh.position_recorded() = 1; + } + + // Release lock + srh.unlock(); +} + +// If traveling through a void region, the source term is either zero +// or an external source. As all external sources are currently assumed +// to be flat, we don't really need this function and could instead just call +// the "attenuate_flux_flat_source_void" function and get the same numerical and +// tally results. However, computation of the flux moments in void regions is +// nonetheless useful as this information is still used by the plotter when +// estimating the flux at specific pixel coordinates. Thus, plots will look +// nicer/more accurate if we record flux moments, so this function is useful. +void RandomRay::attenuate_flux_linear_source_void( + SourceRegionHandle& srh, double distance, bool is_active, Position r) +{ + // The number of geometric intersections is counted for reporting purposes + n_event()++; + + Position& centroid = srh.centroid(); + Position midpoint = r + u() * (distance / 2.0); + + // Determine the local position of the midpoint and the ray origin + // relative to the source region's centroid + Position rm_local; + Position r0_local; + + // In the first few iterations of the simulation, the source region + // may not yet have had any ray crossings, in which case there will + // be no estimate of its centroid. We detect this by checking if it has + // any accumulated volume. If its volume is zero, just use the midpoint + // of the ray as the region's centroid. + if (srh.volume_t()) { + rm_local = midpoint - centroid; + r0_local = r - centroid; + } else { + rm_local = {0.0, 0.0, 0.0}; + r0_local = -u() * 0.5 * distance; + } + double distance_2 = distance * distance; + + // Compared to linear flux attenuation through solid regions, + // transport through a void region is greatly simplified. Here we + // compute the updated flux moments. + for (int g = 0; g < negroups_; g++) { + float spatial_source = 0.f; + if (settings::run_mode == RunMode::FIXED_SOURCE) { + spatial_source = srh.external_source(g); + } + float new_delta_psi = (angular_flux_[g] - spatial_source) * distance; + float h1 = 0.5f; + h1 = h1 * angular_flux_[g]; + h1 = h1 * distance_2; + spatial_source = spatial_source * distance + new_delta_psi; + + // Store contributions for this group into arrays, so that they can + // be accumulated into the source region's estimates inside of the locked + // region. + delta_moments_[g] = r0_local * spatial_source + u() * h1; + + // If 2D mode is enabled, the z-component of the flux moments is forced + // to zero + if (source_shape_ == RandomRaySourceShape::LINEAR_XY) { + delta_moments_[g].z = 0.0; + } + } + + // If ray is in the active phase (not in dead zone), make contributions to + // source region bookkeeping + if (is_active) { + // Compute an estimate of the spatial moments matrix for the source + // region based on parameters from this ray's crossing + MomentMatrix moment_matrix_estimate; + moment_matrix_estimate.compute_spatial_moments_matrix( + rm_local, u(), distance); + + // Aquire lock for source region + srh.lock(); + + // Accumulate delta psi into new estimate of source region flux for + // this iteration, and update flux momements + for (int g = 0; g < negroups_; g++) { + srh.scalar_flux_new(g) += angular_flux_[g] * distance; + srh.flux_moments_new(g) += delta_moments_[g]; + } + + // Accumulate the volume (ray segment distance), centroid, and spatial + // momement estimates into the running totals for the iteration for this + // source region. The centroid and spatial momements estimates are scaled by + // the ray segment length as part of length averaging of the estimates. + srh.volume() += distance; + srh.volume_sq() += distance_2; + srh.centroid_iteration() += midpoint * distance; + moment_matrix_estimate *= distance; + srh.mom_matrix() += moment_matrix_estimate; + + // Tally valid position inside the source region (e.g., midpoint of + // the ray) if not done already + if (!srh.position_recorded()) { + srh.position() = midpoint; + srh.position_recorded() = 1; + } + + srh.n_hits() += 1; + + // Release lock + srh.unlock(); + } + + // Add source to incoming angular flux, assuming void region + if (settings::run_mode == RunMode::FIXED_SOURCE) { + for (int g = 0; g < negroups_; g++) { + angular_flux_[g] += srh.external_source(g) * distance; + } + } +} + +void RandomRay::initialize_ray(uint64_t ray_id, FlatSourceDomain* domain) +{ + domain_ = domain; + + // Reset particle event counter + n_event() = 0; + + is_active_ = (distance_inactive_ <= 0.0); + + wgt() = 1.0; + + // set identifier for particle + id() = ray_id; + + // generate source site using sample method + SourceSite site; + switch (sample_method_) { + case RandomRaySampleMethod::PRNG: + site = sample_prng(); + break; + case RandomRaySampleMethod::HALTON: + site = sample_halton(); + break; + default: + fatal_error("Unknown sample method for random ray transport."); + } + + site.E = 0.0; + this->from_source(&site); + + // Locate ray + if (lowest_coord().cell() == C_NONE) { + if (!exhaustive_find_cell(*this)) { + this->mark_as_lost( + "Could not find the cell containing particle " + std::to_string(id())); + } + + // Set birth cell attribute + if (cell_born() == C_NONE) + cell_born() = lowest_coord().cell(); + } + + SourceRegionKey sr_key = domain_->lookup_source_region_key(*this); + SourceRegionHandle srh = + domain_->get_subdivided_source_region_handle(sr_key, r(), u()); + + // Initialize ray's starting angular flux to starting location's isotropic + // source + if (!srh.is_numerical_fp_artifact_) { + for (int g = 0; g < negroups_; g++) { + angular_flux_[g] = srh.source(g); + } + } +} + +SourceSite RandomRay::sample_prng() +{ + // set random number seed + int64_t particle_seed = + (simulation::current_batch - 1) * settings::n_particles + id(); + init_particle_seeds(particle_seed, seeds()); + stream() = STREAM_TRACKING; + + // Sample from ray source distribution + SourceSite site {ray_source_->sample(current_seed())}; + + return site; +} + +SourceSite RandomRay::sample_halton() +{ + SourceSite site; + + // Set random number seed + int64_t batch_seed = (simulation::current_batch - 1) * settings::n_particles; + int64_t skip = id(); + init_particle_seeds(batch_seed, seeds()); + stream() = STREAM_TRACKING; + + // Calculate next samples in LDS across 5 dimensions + vector samples = rhalton(5, current_seed(), skip = skip); + + // Get spatial box of ray_source_ + SpatialBox* sb = dynamic_cast( + dynamic_cast(RandomRay::ray_source_.get())->space()); + + // Sample spatial distribution + Position xi {samples[0], samples[1], samples[2]}; + // make a small shift in position to avoid geometry floating point issues + Position shift {FP_COINCIDENT, FP_COINCIDENT, FP_COINCIDENT}; + site.r = (sb->lower_left() + shift) + + xi * ((sb->upper_right() - shift) - (sb->lower_left() + shift)); + + // Sample Polar cosine and azimuthal angles + double mu = 2.0 * samples[3] - 1.0; + double azi = 2.0 * PI * samples[4]; + // Convert to Cartesian coordinates + double c = std::sqrt(1.0 - mu * mu); + site.u.x = mu; + site.u.y = std::cos(azi) * c; + site.u.z = std::sin(azi) * c; + + return site; +} + +} // namespace openmc diff --git a/src/random_ray/random_ray_simulation.cpp b/src/random_ray/random_ray_simulation.cpp new file mode 100644 index 0000000000..d475b2593e --- /dev/null +++ b/src/random_ray/random_ray_simulation.cpp @@ -0,0 +1,653 @@ +#include "openmc/random_ray/random_ray_simulation.h" + +#include "openmc/eigenvalue.h" +#include "openmc/geometry.h" +#include "openmc/message_passing.h" +#include "openmc/mgxs_interface.h" +#include "openmc/output.h" +#include "openmc/plot.h" +#include "openmc/random_ray/flat_source_domain.h" +#include "openmc/random_ray/random_ray.h" +#include "openmc/simulation.h" +#include "openmc/source.h" +#include "openmc/tallies/filter.h" +#include "openmc/tallies/tally.h" +#include "openmc/tallies/tally_scoring.h" +#include "openmc/timer.h" +#include "openmc/weight_windows.h" + +namespace openmc { + +//============================================================================== +// Non-member functions +//============================================================================== + +void openmc_run_random_ray() +{ + ////////////////////////////////////////////////////////// + // Run forward simulation + ////////////////////////////////////////////////////////// + + // Check if adjoint calculation is needed. If it is, we will run the forward + // calculation first and then the adjoint calculation later. + bool adjoint_needed = FlatSourceDomain::adjoint_; + + // Configure the domain for forward simulation + FlatSourceDomain::adjoint_ = false; + + // If we're going to do an adjoint simulation afterwards, report that this is + // the initial forward flux solve. + if (adjoint_needed && mpi::master) + header("FORWARD FLUX SOLVE", 3); + + // Initialize OpenMC general data structures + openmc_simulation_init(); + + // Validate that inputs meet requirements for random ray mode + if (mpi::master) + validate_random_ray_inputs(); + + // Initialize Random Ray Simulation Object + RandomRaySimulation sim; + + // Initialize fixed sources, if present + sim.apply_fixed_sources_and_mesh_domains(); + + // Begin main simulation timer + simulation::time_total.start(); + + // Execute random ray simulation + sim.simulate(); + + // End main simulation timer + simulation::time_total.stop(); + + // Normalize and save the final forward flux + double source_normalization_factor = + sim.domain()->compute_fixed_source_normalization_factor() / + (settings::n_batches - settings::n_inactive); + +#pragma omp parallel for + for (uint64_t se = 0; se < sim.domain()->n_source_elements(); se++) { + sim.domain()->source_regions_.scalar_flux_final(se) *= + source_normalization_factor; + } + + // Finalize OpenMC + openmc_simulation_finalize(); + + // Output all simulation results + sim.output_simulation_results(); + + ////////////////////////////////////////////////////////// + // Run adjoint simulation (if enabled) + ////////////////////////////////////////////////////////// + + if (!adjoint_needed) { + return; + } + + reset_timers(); + + // Configure the domain for adjoint simulation + FlatSourceDomain::adjoint_ = true; + + if (mpi::master) + header("ADJOINT FLUX SOLVE", 3); + + // Initialize OpenMC general data structures + openmc_simulation_init(); + + sim.domain()->k_eff_ = 1.0; + + // Initialize adjoint fixed sources, if present + sim.prepare_fixed_sources_adjoint(); + + // Transpose scattering matrix + sim.domain()->transpose_scattering_matrix(); + + // Swap nu_sigma_f and chi + sim.domain()->nu_sigma_f_.swap(sim.domain()->chi_); + + // Begin main simulation timer + simulation::time_total.start(); + + // Execute random ray simulation + sim.simulate(); + + // End main simulation timer + simulation::time_total.stop(); + + // Finalize OpenMC + openmc_simulation_finalize(); + + // Output all simulation results + sim.output_simulation_results(); +} + +// Enforces restrictions on inputs in random ray mode. While there are +// many features that don't make sense in random ray mode, and are therefore +// unsupported, we limit our testing/enforcement operations only to inputs +// that may cause erroneous/misleading output or crashes from the solver. +void validate_random_ray_inputs() +{ + // Validate tallies + /////////////////////////////////////////////////////////////////// + for (auto& tally : model::tallies) { + + // Validate score types + for (auto score_bin : tally->scores_) { + switch (score_bin) { + case SCORE_FLUX: + case SCORE_TOTAL: + case SCORE_FISSION: + case SCORE_NU_FISSION: + case SCORE_EVENTS: + break; + default: + fatal_error( + "Invalid score specified. Only flux, total, fission, nu-fission, and " + "event scores are supported in random ray mode."); + } + } + + // Validate filter types + for (auto f : tally->filters()) { + auto& filter = *model::tally_filters[f]; + + switch (filter.type()) { + case FilterType::CELL: + case FilterType::CELL_INSTANCE: + case FilterType::DISTRIBCELL: + case FilterType::ENERGY: + case FilterType::MATERIAL: + case FilterType::MESH: + case FilterType::UNIVERSE: + case FilterType::PARTICLE: + break; + default: + fatal_error("Invalid filter specified. Only cell, cell_instance, " + "distribcell, energy, material, mesh, and universe filters " + "are supported in random ray mode."); + } + } + } + + // Validate MGXS data + /////////////////////////////////////////////////////////////////// + for (auto& material : data::mg.macro_xs_) { + if (!material.is_isotropic) { + fatal_error("Anisotropic MGXS detected. Only isotropic XS data sets " + "supported in random ray mode."); + } + if (material.get_xsdata().size() > 1) { + warning("Non-isothermal MGXS detected. Only isothermal XS data sets " + "supported in random ray mode. Using lowest temperature."); + } + for (int g = 0; g < data::mg.num_energy_groups_; g++) { + if (material.exists_in_model) { + // Temperature and angle indices, if using multiple temperature + // data sets and/or anisotropic data sets. + // TODO: Currently assumes we are only using single temp/single angle + // data. + const int t = 0; + const int a = 0; + double sigma_t = + material.get_xs(MgxsType::TOTAL, g, NULL, NULL, NULL, t, a); + if (sigma_t <= 0.0) { + fatal_error("No zero or negative total macroscopic cross sections " + "allowed in random ray mode. If the intention is to make " + "a void material, use a cell fill of 'None' instead."); + } + } + } + } + + // Validate ray source + /////////////////////////////////////////////////////////////////// + + // Check for independent source + IndependentSource* is = + dynamic_cast(RandomRay::ray_source_.get()); + if (!is) { + fatal_error("Invalid ray source definition. Ray source must provided and " + "be of type IndependentSource."); + } + + // Check for box source + SpatialDistribution* space_dist = is->space(); + SpatialBox* sb = dynamic_cast(space_dist); + if (!sb) { + fatal_error( + "Invalid ray source definition -- only box sources are allowed."); + } + + // Check that box source is not restricted to fissionable areas + if (sb->only_fissionable()) { + fatal_error( + "Invalid ray source definition -- fissionable spatial distribution " + "not allowed."); + } + + // Check for isotropic source + UnitSphereDistribution* angle_dist = is->angle(); + Isotropic* id = dynamic_cast(angle_dist); + if (!id) { + fatal_error("Invalid ray source definition -- only isotropic sources are " + "allowed."); + } + + // Validate external sources + /////////////////////////////////////////////////////////////////// + if (settings::run_mode == RunMode::FIXED_SOURCE) { + if (model::external_sources.size() < 1) { + fatal_error("Must provide a particle source (in addition to ray source) " + "in fixed source random ray mode."); + } + + for (int i = 0; i < model::external_sources.size(); i++) { + Source* s = model::external_sources[i].get(); + + // Check for independent source + IndependentSource* is = dynamic_cast(s); + + if (!is) { + fatal_error( + "Only IndependentSource external source types are allowed in " + "random ray mode"); + } + + // Check for isotropic source + UnitSphereDistribution* angle_dist = is->angle(); + Isotropic* id = dynamic_cast(angle_dist); + if (!id) { + fatal_error( + "Invalid source definition -- only isotropic external sources are " + "allowed in random ray mode."); + } + + // Validate that a domain ID was specified OR that it is a point source + auto sp = dynamic_cast(is->space()); + if (is->domain_ids().size() == 0 && !sp) { + fatal_error("Fixed sources must be point source or spatially " + "constrained by domain id (cell, material, or universe) in " + "random ray mode."); + } else if (is->domain_ids().size() > 0 && sp) { + // If both a domain constraint and a non-default point source location + // are specified, notify user that domain constraint takes precedence. + if (sp->r().x == 0.0 && sp->r().y == 0.0 && sp->r().z == 0.0) { + warning("Fixed source has both a domain constraint and a point " + "type spatial distribution. The domain constraint takes " + "precedence in random ray mode -- point source coordinate " + "will be ignored."); + } + } + + // Check that a discrete energy distribution was used + Distribution* d = is->energy(); + Discrete* dd = dynamic_cast(d); + if (!dd) { + fatal_error( + "Only discrete (multigroup) energy distributions are allowed for " + "external sources in random ray mode."); + } + } + } + + // Validate plotting files + /////////////////////////////////////////////////////////////////// + for (int p = 0; p < model::plots.size(); p++) { + + // Get handle to OpenMC plot object + const auto& openmc_plottable = model::plots[p]; + Plot* openmc_plot = dynamic_cast(openmc_plottable.get()); + + // Random ray plots only support voxel plots + if (!openmc_plot) { + warning(fmt::format( + "Plot {} will not be used for end of simulation data plotting -- only " + "voxel plotting is allowed in random ray mode.", + openmc_plottable->id())); + continue; + } else if (openmc_plot->type_ != Plot::PlotType::voxel) { + warning(fmt::format( + "Plot {} will not be used for end of simulation data plotting -- only " + "voxel plotting is allowed in random ray mode.", + openmc_plottable->id())); + continue; + } + } + + // Warn about slow MPI domain replication, if detected + /////////////////////////////////////////////////////////////////// +#ifdef OPENMC_MPI + if (mpi::n_procs > 1) { + warning( + "MPI parallelism is not supported by the random ray solver. All work " + "will be performed by rank 0. Domain decomposition may be implemented in " + "the future to provide efficient MPI scaling."); + } +#endif + + // Warn about instability resulting from linear sources in small regions + // when generating weight windows with FW-CADIS and an overlaid mesh. + /////////////////////////////////////////////////////////////////// + if (RandomRay::source_shape_ == RandomRaySourceShape::LINEAR && + variance_reduction::weight_windows.size() > 0) { + warning( + "Linear sources may result in negative fluxes in small source regions " + "generated by mesh subdivision. Negative sources may result in low " + "quality FW-CADIS weight windows. We recommend you use flat source mode " + "when generating weight windows with an overlaid mesh tally."); + } +} + +void openmc_reset_random_ray() +{ + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID; + FlatSourceDomain::volume_normalized_flux_tallies_ = false; + FlatSourceDomain::adjoint_ = false; + FlatSourceDomain::mesh_domain_map_.clear(); + RandomRay::ray_source_.reset(); + RandomRay::source_shape_ = RandomRaySourceShape::FLAT; + RandomRay::sample_method_ = RandomRaySampleMethod::PRNG; +} + +//============================================================================== +// RandomRaySimulation implementation +//============================================================================== + +RandomRaySimulation::RandomRaySimulation() + : negroups_(data::mg.num_energy_groups_) +{ + // There are no source sites in random ray mode, so be sure to disable to + // ensure we don't attempt to write source sites to statepoint + settings::source_write = false; + + // Random ray mode does not have an inner loop over generations within a + // batch, so set the current gen to 1 + simulation::current_gen = 1; + + switch (RandomRay::source_shape_) { + case RandomRaySourceShape::FLAT: + domain_ = make_unique(); + break; + case RandomRaySourceShape::LINEAR: + case RandomRaySourceShape::LINEAR_XY: + domain_ = make_unique(); + break; + default: + fatal_error("Unknown random ray source shape"); + } + + // Convert OpenMC native MGXS into a more efficient format + // internal to the random ray solver + domain_->flatten_xs(); +} + +void RandomRaySimulation::apply_fixed_sources_and_mesh_domains() +{ + domain_->apply_meshes(); + if (settings::run_mode == RunMode::FIXED_SOURCE) { + // Transfer external source user inputs onto random ray source regions + domain_->convert_external_sources(); + domain_->count_external_source_regions(); + } +} + +void RandomRaySimulation::prepare_fixed_sources_adjoint() +{ + domain_->source_regions_.adjoint_reset(); + if (settings::run_mode == RunMode::FIXED_SOURCE) { + domain_->set_adjoint_sources(); + } +} + +void RandomRaySimulation::simulate() +{ + // Random ray power iteration loop + while (simulation::current_batch < settings::n_batches) { + // Initialize the current batch + initialize_batch(); + initialize_generation(); + + // MPI not supported in random ray solver, so all work is done by rank 0 + // TODO: Implement domain decomposition for MPI parallelism + if (mpi::master) { + + // Reset total starting particle weight used for normalizing tallies + simulation::total_weight = 1.0; + + // Update source term (scattering + fission) + domain_->update_all_neutron_sources(); + + // Reset scalar fluxes, iteration volume tallies, and region hit flags + // to zero + domain_->batch_reset(); + + // At the beginning of the simulation, if mesh subdivision is in use, we + // need to swap the main source region container into the base container, + // as the main source region container will be used to hold the true + // subdivided source regions. The base container will therefore only + // contain the external source region information, the mesh indices, + // material properties, and initial guess values for the flux/source. + + // Start timer for transport + simulation::time_transport.start(); + +// Transport sweep over all random rays for the iteration +#pragma omp parallel for schedule(dynamic) \ + reduction(+ : total_geometric_intersections_) + for (int i = 0; i < settings::n_particles; i++) { + RandomRay ray(i, domain_.get()); + total_geometric_intersections_ += + ray.transport_history_based_single_ray(); + } + + simulation::time_transport.stop(); + + // Add any newly discovered source regions to the main source region + // container. + domain_->finalize_discovered_source_regions(); + + // Normalize scalar flux and update volumes + domain_->normalize_scalar_flux_and_volumes( + settings::n_particles * RandomRay::distance_active_); + + // Add source to scalar flux, compute number of FSR hits + int64_t n_hits = domain_->add_source_to_scalar_flux(); + + // Apply transport stabilization factors + domain_->apply_transport_stabilization(); + + if (settings::run_mode == RunMode::EIGENVALUE) { + // Compute random ray k-eff + domain_->compute_k_eff(); + + // Store random ray k-eff into OpenMC's native k-eff variable + global_tally_tracklength = domain_->k_eff_; + } + + // Execute all tallying tasks, if this is an active batch + if (simulation::current_batch > settings::n_inactive) { + + // Add this iteration's scalar flux estimate to final accumulated + // estimate + domain_->accumulate_iteration_flux(); + + // Use above mapping to contribute FSR flux data to appropriate + // tallies + domain_->random_ray_tally(); + } + + // Set phi_old = phi_new + domain_->flux_swap(); + + // Check for any obvious insabilities/nans/infs + instability_check(n_hits, domain_->k_eff_, avg_miss_rate_); + } // End MPI master work + + // Finalize the current batch + finalize_generation(); + finalize_batch(); + } // End random ray power iteration loop + + domain_->count_external_source_regions(); +} + +void RandomRaySimulation::output_simulation_results() const +{ + // Print random ray results + if (mpi::master) { + print_results_random_ray(total_geometric_intersections_, + avg_miss_rate_ / settings::n_batches, negroups_, + domain_->n_source_regions(), domain_->n_external_source_regions_); + if (model::plots.size() > 0) { + domain_->output_to_vtk(); + } + } +} + +// Apply a few sanity checks to catch obvious cases of numerical instability. +// Instability typically only occurs if ray density is extremely low. +void RandomRaySimulation::instability_check( + int64_t n_hits, double k_eff, double& avg_miss_rate) const +{ + double percent_missed = ((domain_->n_source_regions() - n_hits) / + static_cast(domain_->n_source_regions())) * + 100.0; + avg_miss_rate += percent_missed; + + if (mpi::master) { + if (percent_missed > 10.0) { + warning(fmt::format( + "Very high FSR miss rate detected ({:.3f}%). Instability may occur. " + "Increase ray density by adding more rays and/or active distance.", + percent_missed)); + } else if (percent_missed > 1.0) { + warning( + fmt::format("Elevated FSR miss rate detected ({:.3f}%). Increasing " + "ray density by adding more rays and/or active " + "distance may improve simulation efficiency.", + percent_missed)); + } + + if (k_eff > 10.0 || k_eff < 0.01 || !(std::isfinite(k_eff))) { + fatal_error(fmt::format("Instability detected: k-eff = {:.5f}", k_eff)); + } + } +} + +// Print random ray simulation results +void RandomRaySimulation::print_results_random_ray( + uint64_t total_geometric_intersections, double avg_miss_rate, int negroups, + int64_t n_source_regions, int64_t n_external_source_regions) const +{ + using namespace simulation; + + if (settings::verbosity >= 6) { + double total_integrations = total_geometric_intersections * negroups; + double time_per_integration = + simulation::time_transport.elapsed() / total_integrations; + double misc_time = time_total.elapsed() - time_update_src.elapsed() - + time_transport.elapsed() - time_tallies.elapsed() - + time_bank_sendrecv.elapsed(); + + header("Simulation Statistics", 4); + fmt::print( + " Total Iterations = {}\n", settings::n_batches); + fmt::print( + " Number of Rays per Iteration = {}\n", settings::n_particles); + fmt::print(" Inactive Distance = {} cm\n", + RandomRay::distance_inactive_); + fmt::print(" Active Distance = {} cm\n", + RandomRay::distance_active_); + fmt::print(" Source Regions (SRs) = {}\n", n_source_regions); + fmt::print( + " SRs Containing External Sources = {}\n", n_external_source_regions); + fmt::print(" Total Geometric Intersections = {:.4e}\n", + static_cast(total_geometric_intersections)); + fmt::print(" Avg per Iteration = {:.4e}\n", + static_cast(total_geometric_intersections) / settings::n_batches); + fmt::print(" Avg per Iteration per SR = {:.2f}\n", + static_cast(total_geometric_intersections) / + static_cast(settings::n_batches) / n_source_regions); + fmt::print(" Avg SR Miss Rate per Iteration = {:.4f}%\n", avg_miss_rate); + fmt::print(" Energy Groups = {}\n", negroups); + fmt::print( + " Total Integrations = {:.4e}\n", total_integrations); + fmt::print(" Avg per Iteration = {:.4e}\n", + total_integrations / settings::n_batches); + + std::string estimator; + switch (domain_->volume_estimator_) { + case RandomRayVolumeEstimator::SIMULATION_AVERAGED: + estimator = "Simulation Averaged"; + break; + case RandomRayVolumeEstimator::NAIVE: + estimator = "Naive"; + break; + case RandomRayVolumeEstimator::HYBRID: + estimator = "Hybrid"; + break; + default: + fatal_error("Invalid volume estimator type"); + } + fmt::print(" Volume Estimator Type = {}\n", estimator); + + std::string adjoint_true = (FlatSourceDomain::adjoint_) ? "ON" : "OFF"; + fmt::print(" Adjoint Flux Mode = {}\n", adjoint_true); + + std::string shape; + switch (RandomRay::source_shape_) { + case RandomRaySourceShape::FLAT: + shape = "Flat"; + break; + case RandomRaySourceShape::LINEAR: + shape = "Linear"; + break; + case RandomRaySourceShape::LINEAR_XY: + shape = "Linear XY"; + break; + default: + fatal_error("Invalid random ray source shape"); + } + fmt::print(" Source Shape = {}\n", shape); + std::string sample_method = + (RandomRay::sample_method_ == RandomRaySampleMethod::PRNG) ? "PRNG" + : "Halton"; + fmt::print(" Sample Method = {}\n", sample_method); + + if (domain_->is_transport_stabilization_needed_) { + fmt::print(" Transport XS Stabilization Used = YES (rho = {:.3f})\n", + FlatSourceDomain::diagonal_stabilization_rho_); + } else { + fmt::print(" Transport XS Stabilization Used = NO\n"); + } + + header("Timing Statistics", 4); + show_time("Total time for initialization", time_initialize.elapsed()); + show_time("Reading cross sections", time_read_xs.elapsed(), 1); + show_time("Total simulation time", time_total.elapsed()); + show_time("Transport sweep only", time_transport.elapsed(), 1); + show_time("Source update only", time_update_src.elapsed(), 1); + show_time("Tally conversion only", time_tallies.elapsed(), 1); + show_time("MPI source reductions only", time_bank_sendrecv.elapsed(), 1); + show_time("Other iteration routines", misc_time, 1); + if (settings::run_mode == RunMode::EIGENVALUE) { + show_time("Time in inactive batches", time_inactive.elapsed()); + } + show_time("Time in active batches", time_active.elapsed()); + show_time("Time writing statepoints", time_statepoint.elapsed()); + show_time("Total time for finalization", time_finalize.elapsed()); + show_time("Time per integration", time_per_integration); + } + + if (settings::verbosity >= 4 && settings::run_mode == RunMode::EIGENVALUE) { + header("Results", 4); + fmt::print(" k-effective = {:.5f} +/- {:.5f}\n", + simulation::keff, simulation::keff_std); + } +} + +} // namespace openmc diff --git a/src/random_ray/source_region.cpp b/src/random_ray/source_region.cpp new file mode 100644 index 0000000000..3b06f0ed09 --- /dev/null +++ b/src/random_ray/source_region.cpp @@ -0,0 +1,261 @@ +#include "openmc/random_ray/source_region.h" + +#include "openmc/error.h" +#include "openmc/message_passing.h" +#include "openmc/simulation.h" + +namespace openmc { + +//============================================================================== +// SourceRegionHandle implementation +//============================================================================== +SourceRegionHandle::SourceRegionHandle(SourceRegion& sr) + : negroups_(sr.scalar_flux_old_.size()), material_(&sr.material_), + is_small_(&sr.is_small_), n_hits_(&sr.n_hits_), + is_linear_(sr.source_gradients_.size() > 0), lock_(&sr.lock_), + volume_(&sr.volume_), volume_t_(&sr.volume_t_), volume_sq_(&sr.volume_sq_), + volume_sq_t_(&sr.volume_sq_t_), volume_naive_(&sr.volume_naive_), + position_recorded_(&sr.position_recorded_), + external_source_present_(&sr.external_source_present_), + position_(&sr.position_), centroid_(&sr.centroid_), + centroid_iteration_(&sr.centroid_iteration_), centroid_t_(&sr.centroid_t_), + mom_matrix_(&sr.mom_matrix_), mom_matrix_t_(&sr.mom_matrix_t_), + volume_task_(&sr.volume_task_), mesh_(&sr.mesh_), + parent_sr_(&sr.parent_sr_), scalar_flux_old_(sr.scalar_flux_old_.data()), + scalar_flux_new_(sr.scalar_flux_new_.data()), source_(sr.source_.data()), + external_source_(sr.external_source_.data()), + scalar_flux_final_(sr.scalar_flux_final_.data()), + source_gradients_(sr.source_gradients_.data()), + flux_moments_old_(sr.flux_moments_old_.data()), + flux_moments_new_(sr.flux_moments_new_.data()), + flux_moments_t_(sr.flux_moments_t_.data()), + tally_task_(sr.tally_task_.data()) +{} + +//============================================================================== +// SourceRegion implementation +//============================================================================== +SourceRegion::SourceRegion(int negroups, bool is_linear) +{ + if (settings::run_mode == RunMode::EIGENVALUE) { + // If in eigenvalue mode, set starting flux to guess of 1 + scalar_flux_old_.assign(negroups, 1.0); + } else { + // If in fixed source mode, set starting flux to guess of zero + // and initialize external source arrays + scalar_flux_old_.assign(negroups, 0.0); + external_source_.assign(negroups, 0.0); + } + + scalar_flux_new_.assign(negroups, 0.0); + source_.assign(negroups, 0.0); + scalar_flux_final_.assign(negroups, 0.0); + + tally_task_.resize(negroups); + if (is_linear) { + source_gradients_.resize(negroups); + flux_moments_old_.resize(negroups); + flux_moments_new_.resize(negroups); + flux_moments_t_.resize(negroups); + } +} + +//============================================================================== +// SourceRegionContainer implementation +//============================================================================== + +void SourceRegionContainer::push_back(const SourceRegion& sr) +{ + n_source_regions_++; + + // Scalar fields + material_.push_back(sr.material_); + is_small_.push_back(sr.is_small_); + n_hits_.push_back(sr.n_hits_); + lock_.push_back(sr.lock_); + volume_.push_back(sr.volume_); + volume_t_.push_back(sr.volume_t_); + volume_sq_.push_back(sr.volume_sq_); + volume_sq_t_.push_back(sr.volume_sq_t_); + volume_naive_.push_back(sr.volume_naive_); + position_recorded_.push_back(sr.position_recorded_); + external_source_present_.push_back(sr.external_source_present_); + position_.push_back(sr.position_); + volume_task_.push_back(sr.volume_task_); + mesh_.push_back(sr.mesh_); + parent_sr_.push_back(sr.parent_sr_); + + // Only store these fields if is_linear_ is true + if (is_linear_) { + centroid_.push_back(sr.centroid_); + centroid_iteration_.push_back(sr.centroid_iteration_); + centroid_t_.push_back(sr.centroid_t_); + mom_matrix_.push_back(sr.mom_matrix_); + mom_matrix_t_.push_back(sr.mom_matrix_t_); + } + + // Energy-dependent fields + for (int g = 0; g < negroups_; ++g) { + scalar_flux_old_.push_back(sr.scalar_flux_old_[g]); + scalar_flux_new_.push_back(sr.scalar_flux_new_[g]); + scalar_flux_final_.push_back(sr.scalar_flux_final_[g]); + source_.push_back(sr.source_[g]); + if (settings::run_mode == RunMode::FIXED_SOURCE) { + external_source_.push_back(sr.external_source_[g]); + } + + // Only store these fields if is_linear_ is true + if (is_linear_) { + source_gradients_.push_back(sr.source_gradients_[g]); + flux_moments_old_.push_back(sr.flux_moments_old_[g]); + flux_moments_new_.push_back(sr.flux_moments_new_[g]); + flux_moments_t_.push_back(sr.flux_moments_t_[g]); + } + + // Tally tasks + tally_task_.emplace_back(sr.tally_task_[g]); + } +} + +void SourceRegionContainer::assign( + int n_source_regions, const SourceRegion& source_region) +{ + // Clear existing data + n_source_regions_ = 0; + material_.clear(); + is_small_.clear(); + n_hits_.clear(); + lock_.clear(); + volume_.clear(); + volume_t_.clear(); + volume_sq_.clear(); + volume_sq_t_.clear(); + volume_naive_.clear(); + position_recorded_.clear(); + external_source_present_.clear(); + position_.clear(); + mesh_.clear(); + parent_sr_.clear(); + + if (is_linear_) { + centroid_.clear(); + centroid_iteration_.clear(); + centroid_t_.clear(); + mom_matrix_.clear(); + mom_matrix_t_.clear(); + } + + scalar_flux_old_.clear(); + scalar_flux_new_.clear(); + scalar_flux_final_.clear(); + source_.clear(); + external_source_.clear(); + + if (is_linear_) { + source_gradients_.clear(); + flux_moments_old_.clear(); + flux_moments_new_.clear(); + flux_moments_t_.clear(); + } + + tally_task_.clear(); + volume_task_.clear(); + + // Fill with copies of source_region + for (int i = 0; i < n_source_regions; ++i) { + push_back(source_region); + } +} + +void SourceRegionContainer::flux_swap() +{ + scalar_flux_old_.swap(scalar_flux_new_); + if (is_linear_) { + flux_moments_old_.swap(flux_moments_new_); + } +} + +SourceRegionHandle SourceRegionContainer::get_source_region_handle(int64_t sr) +{ + SourceRegionHandle handle; + handle.negroups_ = negroups(); + handle.material_ = &material(sr); + handle.is_small_ = &is_small(sr); + handle.n_hits_ = &n_hits(sr); + handle.is_linear_ = is_linear(); + handle.lock_ = &lock(sr); + handle.volume_ = &volume(sr); + handle.volume_t_ = &volume_t(sr); + handle.volume_sq_ = &volume_sq(sr); + handle.volume_sq_t_ = &volume_sq_t(sr); + handle.volume_naive_ = &volume_naive(sr); + handle.position_recorded_ = &position_recorded(sr); + handle.external_source_present_ = &external_source_present(sr); + handle.position_ = &position(sr); + handle.volume_task_ = &volume_task(sr); + handle.mesh_ = &mesh(sr); + handle.parent_sr_ = &parent_sr(sr); + handle.scalar_flux_old_ = &scalar_flux_old(sr, 0); + handle.scalar_flux_new_ = &scalar_flux_new(sr, 0); + handle.source_ = &source(sr, 0); + if (settings::run_mode == RunMode::FIXED_SOURCE) { + handle.external_source_ = &external_source(sr, 0); + } else { + handle.external_source_ = nullptr; + } + handle.scalar_flux_final_ = &scalar_flux_final(sr, 0); + handle.tally_task_ = &tally_task(sr, 0); + + if (handle.is_linear_) { + handle.centroid_ = ¢roid(sr); + handle.centroid_iteration_ = ¢roid_iteration(sr); + handle.centroid_t_ = ¢roid_t(sr); + handle.mom_matrix_ = &mom_matrix(sr); + handle.mom_matrix_t_ = &mom_matrix_t(sr); + handle.source_gradients_ = &source_gradients(sr, 0); + handle.flux_moments_old_ = &flux_moments_old(sr, 0); + handle.flux_moments_new_ = &flux_moments_new(sr, 0); + handle.flux_moments_t_ = &flux_moments_t(sr, 0); + } + + return handle; +} + +void SourceRegionContainer::adjoint_reset() +{ + std::fill(n_hits_.begin(), n_hits_.end(), 0); + std::fill(volume_.begin(), volume_.end(), 0.0); + std::fill(volume_t_.begin(), volume_t_.end(), 0.0); + std::fill(volume_sq_.begin(), volume_sq_.end(), 0.0); + std::fill(volume_sq_t_.begin(), volume_sq_t_.end(), 0.0); + std::fill(volume_naive_.begin(), volume_naive_.end(), 0.0); + std::fill( + external_source_present_.begin(), external_source_present_.end(), 0); + std::fill(external_source_.begin(), external_source_.end(), 0.0); + std::fill(centroid_.begin(), centroid_.end(), Position {0.0, 0.0, 0.0}); + std::fill(centroid_iteration_.begin(), centroid_iteration_.end(), + Position {0.0, 0.0, 0.0}); + std::fill(centroid_t_.begin(), centroid_t_.end(), Position {0.0, 0.0, 0.0}); + std::fill(mom_matrix_.begin(), mom_matrix_.end(), + MomentMatrix {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}); + std::fill(mom_matrix_t_.begin(), mom_matrix_t_.end(), + MomentMatrix {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}); + if (settings::run_mode == RunMode::FIXED_SOURCE) { + std::fill(scalar_flux_old_.begin(), scalar_flux_old_.end(), 0.0); + } else { + std::fill(scalar_flux_old_.begin(), scalar_flux_old_.end(), 1.0); + } + std::fill(scalar_flux_new_.begin(), scalar_flux_new_.end(), 0.0); + std::fill(source_.begin(), source_.end(), 0.0f); + std::fill(external_source_.begin(), external_source_.end(), 0.0f); + std::fill(source_gradients_.begin(), source_gradients_.end(), + MomentArray {0.0, 0.0, 0.0}); + std::fill(flux_moments_old_.begin(), flux_moments_old_.end(), + MomentArray {0.0, 0.0, 0.0}); + std::fill(flux_moments_new_.begin(), flux_moments_new_.end(), + MomentArray {0.0, 0.0, 0.0}); + std::fill(flux_moments_t_.begin(), flux_moments_t_.end(), + MomentArray {0.0, 0.0, 0.0}); +} + +} // namespace openmc diff --git a/src/reaction.cpp b/src/reaction.cpp index c929677f2c..d96790c6d4 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -1,17 +1,20 @@ #include "openmc/reaction.h" +#include // for remove_if #include #include #include // for move #include +#include "openmc/chain.h" #include "openmc/constants.h" #include "openmc/endf.h" #include "openmc/hdf5_interface.h" #include "openmc/random_lcg.h" #include "openmc/search.h" #include "openmc/secondary_uncorrelated.h" +#include "openmc/settings.h" namespace openmc { @@ -19,7 +22,8 @@ namespace openmc { // Reaction implementation //============================================================================== -Reaction::Reaction(hid_t group, const vector& temperatures) +Reaction::Reaction( + hid_t group, const vector& temperatures, std::string name) { read_attribute(group, "Q_value", q_value_); read_attribute(group, "mt", mt_); @@ -63,11 +67,54 @@ Reaction::Reaction(hid_t group, const vector& temperatures) close_group(pgroup); } } + + if (settings::use_decay_photons) { + // Remove photon products for D1S method + products_.erase( + std::remove_if(products_.begin(), products_.end(), + [](const auto& p) { return p.particle_ == ParticleType::photon; }), + products_.end()); + + // Determine product for D1S method + auto nuclide_it = data::chain_nuclide_map.find(name); + if (nuclide_it != data::chain_nuclide_map.end()) { + const auto& chain_nuc = data::chain_nuclides[nuclide_it->second]; + const auto& rx_products = chain_nuc->reaction_products(); + auto product_it = rx_products.find(mt_); + if (product_it != rx_products.end()) { + auto decay_products = product_it->second; + for (const auto& decay_product : decay_products) { + auto product_it = data::chain_nuclide_map.find(decay_product.name); + if (product_it != data::chain_nuclide_map.end()) { + const auto& product_nuc = data::chain_nuclides[product_it->second]; + if (product_nuc->photon_energy()) { + products_.emplace_back(decay_product); + } + } + } + } + } + } } -double Reaction::collapse_rate(gsl::index i_temp, - gsl::span energy, gsl::span flux, - const vector& grid) const +double Reaction::xs(int64_t i_temp, int64_t i_grid, double interp_factor) const +{ + // If energy is below threshold, return 0. Otherwise interpolate between + // nearest grid points + const auto& x = xs_[i_temp]; + return (i_grid < x.threshold) + ? 0.0 + : (1.0 - interp_factor) * x.value[i_grid - x.threshold] + + interp_factor * x.value[i_grid - x.threshold + 1]; +} + +double Reaction::xs(const NuclideMicroXS& micro) const +{ + return this->xs(micro.index_temp, micro.index_grid, micro.interp_factor); +} + +double Reaction::collapse_rate(int64_t i_temp, span energy, + span flux, const vector& grid) const { // Find index corresponding to first energy const auto& xs = xs_[i_temp].value; @@ -154,9 +201,14 @@ std::unordered_map REACTION_NAME_MAP { {SCORE_INVERSE_VELOCITY, "inverse-velocity"}, {SCORE_FISS_Q_PROMPT, "fission-q-prompt"}, {SCORE_FISS_Q_RECOV, "fission-q-recoverable"}, + {SCORE_PULSE_HEIGHT, "pulse-height"}, + {SCORE_IFP_TIME_NUM, "ifp-time-numerator"}, + {SCORE_IFP_BETA_NUM, "ifp-beta-numerator"}, + {SCORE_IFP_DENOM, "ifp-denominator"}, // Normal ENDF-based reactions {TOTAL_XS, "(n,total)"}, {ELASTIC, "(n,elastic)"}, + {N_NONELASTIC, "(n,nonelastic)"}, {N_LEVEL, "(n,level)"}, {N_2ND, "(n,2nd)"}, {N_2N, "(n,2n)"}, diff --git a/src/reaction_product.cpp b/src/reaction_product.cpp index 4cef8d3a95..3ba2c0cfb0 100644 --- a/src/reaction_product.cpp +++ b/src/reaction_product.cpp @@ -83,6 +83,29 @@ ReactionProduct::ReactionProduct(hid_t group) } } +ReactionProduct::ReactionProduct(const ChainNuclide::Product& product) +{ + particle_ = ParticleType::photon; + emission_mode_ = EmissionMode::delayed; + + // Get chain nuclide object for radionuclide + parent_nuclide_ = data::chain_nuclide_map.at(product.name); + const auto& chain_nuc = data::chain_nuclides[parent_nuclide_].get(); + + // Determine decay constant in [s^-1] + decay_rate_ = chain_nuc->decay_constant(); + + // Determine number of photons per decay and set yield + double photon_per_sec = chain_nuc->photon_energy()->integral(); + double photon_per_decay = photon_per_sec / decay_rate_; + vector coef = {product.branching_ratio * photon_per_decay}; + yield_ = make_unique(coef); + + // Set decay photon angle-energy distribution + distribution_.push_back( + make_unique(chain_nuc->photon_energy())); +} + void ReactionProduct::sample( double E_in, double& E_out, double& mu, uint64_t* seed) const { diff --git a/src/relaxng/cross_sections.rnc b/src/relaxng/cross_sections.rnc deleted file mode 100644 index 7fbc610a28..0000000000 --- a/src/relaxng/cross_sections.rnc +++ /dev/null @@ -1,12 +0,0 @@ -element cross_sections { - element library { - (element materials { xsd:string } | - attribute materials { xsd:string }) & - (element type { xsd:string } | - attribute type { xsd:string }) & - (element path { xsd:string } | - attribute path { xsd:string }) - }* & - - element directory { xsd:string { maxLength = "255" } }? -} \ No newline at end of file diff --git a/src/relaxng/cross_sections.rng b/src/relaxng/cross_sections.rng deleted file mode 100644 index 435f7fa843..0000000000 --- a/src/relaxng/cross_sections.rng +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 255 - - - - - diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc deleted file mode 100644 index e3c88c445c..0000000000 --- a/src/relaxng/geometry.rnc +++ /dev/null @@ -1,55 +0,0 @@ -element geometry { - element cell { - (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - (element universe { xsd:int } | attribute universe { xsd:int })? & - ( - (element fill { xsd:int } | attribute fill { xsd:int }) | - (element material { list { ( xsd:int | "void" )+ } } | - attribute material { list { ( xsd:int | "void" )+ } }) - ) & - (element temperature { list { xsd:double+ } } | - attribute temperature { list { xsd:double+ } } )? & - (element region { xsd:string } | attribute region { xsd:string })? & - (element rotation { list { xsd:double+ } } | attribute rotation { list { xsd:double+ } })? & - (element translation { list { xsd:double+ } } | attribute translation { list { xsd:double+ } })? - }* - - & element surface { - (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - (element type { xsd:string { maxLength = "15" } } | - attribute type { xsd:string { maxLength = "15" } }) & - (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & - (element boundary { ( "transmit" | "reflective" | "vacuum" | "periodic" ) } | - attribute boundary { ( "transmit" | "reflective" | "vacuum" | "periodic" ) })? & - (element periodic_surface_id { xsd:int } | attribute periodic_surface_id { xsd:int })? - }* - - & element lattice { - (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & - (element pitch { list { xsd:double+ } } | attribute pitch { list { xsd:double+ } }) & - (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & - (element outer { xsd:int } | attribute outer { xsd:int })? - }* - - & element hex_lattice { - (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - (element n_rings { xsd:int } | attribute n_rings { xsd:int }) & - (element n_axial { xsd:int } | attribute n_axial { xsd:int })? & - (element center { list { xsd:double+ } } | attribute center { list { xsd:double+ } }) & - (element pitch { list { xsd:double+ } } | attribute pitch { list { xsd:double+ } }) & - (element orientation { ( "x" | "y" ) } | attribute orientation { ( "x" | "y" ) })? & - (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & - (element outer { xsd:int } | attribute outer { xsd:int })? - }* -} diff --git a/src/relaxng/geometry.rng b/src/relaxng/geometry.rng deleted file mode 100644 index 56bf385807..0000000000 --- a/src/relaxng/geometry.rng +++ /dev/null @@ -1,447 +0,0 @@ - - - - - - - - - - - - - - - - - - - 52 - - - - - 52 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void - - - - - - - - - - void - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 - - - - - 52 - - - - - - - - 15 - - - - - 15 - - - - - - - - - - - - - - - - - - - - - - - - transmit - reflective - vacuum - periodic - - - - - transmit - reflective - vacuum - periodic - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 - - - - - 52 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 - - - - - 52 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - x - y - - - - - x - y - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/relaxng/materials.rnc b/src/relaxng/materials.rnc deleted file mode 100644 index c82ecfeaac..0000000000 --- a/src/relaxng/materials.rnc +++ /dev/null @@ -1,41 +0,0 @@ -element materials { - element material { - (element id { xsd:int } | attribute id { xsd:int }) & - - (element name { xsd:string } | attribute name { xsd:string })? & - - (element depletable { xsd:boolean } | attribute depletable { xsd:boolean })? & - - (element volume { xsd:double } | attribute volume { xsd:double })? & - - (element temperature { xsd:double } | attribute temperature { xsd:double })? & - - element density { - (element value { xsd:double } | attribute value { xsd:double })? & - (element units { xsd:string { maxLength = "10" } } | - attribute units { xsd:string { maxLength = "10" } }) - } & - - element nuclide { - (element name { xsd:string } | attribute name { xsd:string }) & - ( - (element ao { xsd:double } | attribute ao { xsd:double }) | - (element wo { xsd:double } | attribute wo { xsd:double }) - ) - }* & - - element isotropic { xsd:string }? & - - element macroscopic { - (element name { xsd:string } | - attribute name { xsd:string }) - }* & - - element sab { - (element name { xsd:string } | attribute name { xsd:string }) & - (element fraction { xsd:double } | attribute fraction { xsd:double })? - }* - }+ & - - element cross_sections { xsd:string { maxLength = "255" } }? -} diff --git a/src/relaxng/materials.rng b/src/relaxng/materials.rng deleted file mode 100644 index e99fb7adfe..0000000000 --- a/src/relaxng/materials.rng +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 10 - - - - - 10 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 255 - - - - - diff --git a/src/relaxng/mg_cross_sections.rnc b/src/relaxng/mg_cross_sections.rnc deleted file mode 100644 index b2aaec4d40..0000000000 --- a/src/relaxng/mg_cross_sections.rnc +++ /dev/null @@ -1,61 +0,0 @@ -element cross_sections { - - element groups { xsd:int } & - - element group_structure { list { xsd:double+ } } & - - element inverse_velocities { list { xsd:double+ } }? & - - element xsdata { - (element name { xsd:string { maxLength = "15" } } | - attribute name { xsd:string { maxLength = "15" } }) & - (element alias { xsd:string { maxLength = "15" } } | - attribute alias { xsd:string { maxLength = "15" } })? & - (element kT { xsd:double } | attribute kT { xsd:double })? & - (element fissionable { ( "true" | "false" ) } | - attribute fissionable { ( "true" | "false" ) }) & - (element representation { ( "isotropic" | "angle" ) } | - attribute representation { ( "isotropic" | "angle" ) })? & - (element num_azimuthal { xsd:positiveInteger } | - attribute num_azimuthal { xsd:positiveInteger })? & - (element num_polar { xsd:positiveInteger } | - attribute num_polar { xsd:positiveInteger })? & - (element scatt_type { ( "legendre" | "histogram" | "tabular" ) } | - attribute scatt_type { ( "legendre" | "histogram" | "tabular" ) })? & - (element order { xsd:positiveInteger } | - attribute order { xsd:positiveInteger }) & - element tabular_legendre { - (element enable { ( "true" | "false" ) } | - attribute enable { ( "true" | "false" ) })? & - (element num_points { xsd:positiveInteger } | - attribute num_points { xsd:positiveInteger })? - }? & - - (element total { list { xsd:double+ } } | - attribute total { list { xsd:double+ } })? & - - (element absorption { list { xsd:double+ } } | - attribute absorption { list { xsd:double+ } }) & - - (element scatter { list { xsd:double+ } } | - attribute scatter { list { xsd:double+ } }) & - - (element fission { list { xsd:double+ } } | - attribute fission { list { xsd:double+ } })? & - - (element fission { list { xsd:double+ } } | - attribute fission { list { xsd:double+ } })? & - - (element k_fission { list { xsd:double+ } } | - attribute k_fission { list { xsd:double+ } })? & - - (element chi { list { xsd:double+ } } | - attribute chi { list { xsd:double+ } })? & - - (element nu_fission { list { xsd:double+ } } | - attribute nu_fission { list { xsd:double+ } })? - - }* - - -} \ No newline at end of file diff --git a/src/relaxng/mg_cross_sections.rng b/src/relaxng/mg_cross_sections.rng deleted file mode 100644 index b293cddbe4..0000000000 --- a/src/relaxng/mg_cross_sections.rng +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15 - - - - - 15 - - - - - - - - 15 - - - - - 15 - - - - - - - - - - - - - - - - - - true - false - - - - - true - false - - - - - - - - isotropic - angle - - - - - isotropic - angle - - - - - - - - - - - - - - - - - - - - - - - - - - - - - legendre - histogram - tabular - - - - - legendre - histogram - tabular - - - - - - - - - - - - - - - - - - - - true - false - - - - - true - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/relaxng/plots.rnc b/src/relaxng/plots.rnc deleted file mode 100644 index e53cc15f72..0000000000 --- a/src/relaxng/plots.rnc +++ /dev/null @@ -1,41 +0,0 @@ -element plots { - element plot { - (element id { xsd:int } | attribute id { xsd:int })? & - (element filename { xsd:string { maxLength = "50" } } | - attribute filename { xsd:string { maxLength = "50" } })? & - (element type { "slice" | "voxel" } | - attribute type { "slice" | "voxel" })? & - (element color_by { ( "cell" | "material" ) } | - attribute color_by { ( "cell" | "material" ) })? & - (element level { xsd:int } | attribute level { xsd:int })? & - (element origin { list { xsd:double+ } } | - attribute origin { list { xsd:double+ } })? & - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } })? & - (element basis { ( "xy" | "yz" | "xz" ) } | - attribute basis { ( "xy" | "yz" | "xz" ) })? & - (element pixels { list { xsd:int+ } } | - attribute pixels { list { xsd:int+ } })? & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } })? & - element color { - (element id { xsd:int } | attribute id { xsd:int }) & - (element rgb { list { xsd:int+ } } | - attribute rgb { list { xsd:int+ } }) - }* & - element mask { - (element components { list { xsd:int+ } } | - attribute components { list { xsd:int+ } }) & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } }) - }* & - element meshlines { - (element meshtype { ( "tally" | "entropy" | "ufs" | "cmfd" ) } | - attribute meshtype { ( "tally" | "entropy" | "ufs" | "cmfd" ) }) & - (element id { xsd:int } | attribute id { xsd:int })? & - (element linewidth { xsd:int } | attribute linewidth { xsd:int }) & - (element color { list { xsd:int+ } } | - attribute color { list { xsd:int+ } })? - }* - }* -} diff --git a/src/relaxng/plots.rng b/src/relaxng/plots.rng deleted file mode 100644 index 55d69008ee..0000000000 --- a/src/relaxng/plots.rng +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - - - - - - - - - - - - - - - 50 - - - - - 50 - - - - - - - - - slice - voxel - - - - - slice - voxel - - - - - - - - - cell - material - - - - - cell - material - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - xy - yz - xz - - - - - xy - yz - xz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tally - entropy - ufs - cmfd - - - - - tally - entropy - ufs - cmfd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/relaxng/readme.rst b/src/relaxng/readme.rst deleted file mode 100644 index 9660e3f8ce..0000000000 --- a/src/relaxng/readme.rst +++ /dev/null @@ -1,19 +0,0 @@ -===================== -Editing RelaxNG files -===================== - -All direct edits to RelaxNG files should be in the .rnc files. The program -TRANG_ should be used to generate a correcsponding .rng file. For Ubuntu, you -can install with: - -.. code-block:: bash - - sudo apt-get install trang - -To convert the .rnc file to .rng, use the following syntax: - -.. code-block:: bash - - trang {filename}.rnc {filename}.rng - -.. _TRANG: http://www.thaiopensource.com/relaxng/trang.html diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc deleted file mode 100644 index f9877a3f98..0000000000 --- a/src/relaxng/settings.rnc +++ /dev/null @@ -1,204 +0,0 @@ -element settings { - element batches { xsd:positiveInteger }? & - - element confidence_intervals { xsd:boolean }? & - - element create_fission_neutrons { xsd:boolean }? & - - element cutoff { - (element weight { xsd:double } | attribute weight { xsd:double })? & - (element weight_avg { xsd:double } | attribute weight_avg { xsd:double })? & - (element energy_neutron { xsd:double } | attribute energy_neutron { xsd:double })? & - (element energy_photon { xsd:double } | attribute energy_photon { xsd:double })? & - (element energy_electron { xsd:double } | attribute energy_electron { xsd:double })? & - (element energy_positron { xsd:double } | attribute energy_positron { xsd:double })? - }? & - - element delayed_photon_scaling { xsd:boolean }? & - - element electron_treatment { ( "led" | "ttb" ) }? & - - element energy_grid { ( "nuclide" | "log" | "logarithm" | "logarithmic" | "material-union" | "union" ) }? & - - element energy_mode { ( "continuous-energy" | "ce" | "CE" | "multi-group" | "mg" | "MG" ) }? & - - element entropy_mesh { xsd:positiveInteger }? & - - element event_based { xsd:boolean }? & - - element generations_per_batch { xsd:positiveInteger }? & - - element inactive { xsd:nonNegativeInteger }? & - - element keff_trigger { - (element type { xsd:string } | attribute type { xsd:string }) & - (element threshold { xsd:double} | attribute threshold { xsd:double }) - }? & - - element log_grid_bins { xsd:positiveInteger }? & - - element material_cell_offsets { xsd:boolean }? & - - element max_particles_in_flight { xsd:positiveInteger }? & - - element max_order { xsd:nonNegativeInteger }? & - - element mesh { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "regular" ) } | - attribute type { ( "regular" ) })? & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) - }* & - - element no_reduce { xsd:boolean }? & - - element output { - (element summary { xsd:boolean } | attribute summary { xsd:boolean })? & - (element tallies { xsd:boolean } | attribute tallies { xsd:boolean })? & - (element path { xsd:string } | attribute path { xsd:string })? - }? & - - element particles { xsd:positiveInteger }? & - - element photon_transport { xsd:boolean }? & - - element ptables { xsd:boolean }? & - - element dagmc { xsd:boolean }? & - - element run_mode { xsd:string }? & - - element seed { xsd:positiveInteger }? & - - element source { - grammar { - start = - (element particle { xsd:string } | attribute particle { xsd:string })? & - (element strength { xsd:double } | attribute strength { xsd:double })? & - (element file { xsd:string } | attribute file { xsd:string })? & - element space { - (element type { xsd:string } | attribute type { xsd:string }) & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? & - element x { distribution }? & - element y { distribution }? & - element z { distribution }? & - element r { distribution }? & - element theta { distribution }? & - element phi { distribution }? & - element origin { list { xsd:double, xsd:double, xsd:double } }? - }? & - element angle { - (element type { xsd:string } | attribute type { xsd:string }) & - (element reference_uvw { list { xsd:double, xsd:double, xsd:double } } | - attribute reference_uvw { list { xsd:double, xsd:double, xsd:double } })? & - element mu { distribution }? & - element phi { distribution }? - }? & - element energy { distribution }? - distribution = - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element interpolation { xsd:string } | - attribute interpolation { xsd:string })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - } - }* & - - element state_point { - ( - (element batches { list { xsd:positiveInteger+ } } | - attribute batches { list { xsd:positiveInteger+ } }) | - (element interval { xsd:positiveInteger } | - attribute interval { xsd:positiveInteger }) - ) - }? & - - element source_point { - ( - (element batches { list { xsd:positiveInteger+ } } | - attribute batches { list { xsd:positiveInteger+ } }) | - (element interval { xsd:positiveInteger } | - attribute interval { xsd:positiveInteger }) - )? & - (element separate { xsd:boolean } | - attribute separate { xsd:boolean })? & - (element write { xsd:boolean } | - attribute write { xsd:boolean })? & - (element overwrite_latest { xsd:boolean} | - attribute overwrite_latest {xsd:boolean})? - }? & - - element surf_source_read { - (element path { xsd:string } | attribute path { xsd:string }) - }? & - - element surf_source_write { - (element surface_ids { list { xsd:positiveInteger+ } } | - attribute surface_ids { list { xsd:positiveInteger+ } }) & - (element max_particles { xsd:positiveInteger } | - attribute max_particles { xsd:positiveInteger }) - }? & - - element survival_biasing { xsd:boolean }? & - - element temperature_default { xsd:double }? & - - element temperature_method { xsd:string }? & - - element temperature_multipole { xsd:boolean }? & - - element temperature_range { list { xsd:double, xsd:double } }? & - - element temperature_tolerance { xsd:double }? & - - element threads { xsd:positiveInteger }? & - - element trace { list { xsd:positiveInteger+ } }? & - - element track { list { xsd:positiveInteger+ } }? & - - element trigger { - (element active { xsd:boolean } | attribute active { xsd:boolean }) & - (element max_batches { xsd:positiveInteger } | attribute max_batches { xsd:positiveInteger }) & - (element batch_interval { xsd:positiveInteger } | attribute batch_interval { xsd:positiveInteger })? - }? & - - element ufs_mesh { xsd:positiveInteger }? & - - element verbosity { xsd:positiveInteger }? & - - element volume_calc { - (element domain_type { xsd:string } | - attribute domain_type { xsd:string }) & - (element domain_ids { list { xsd:integer+ } } | - attribute domain_ids { list { xsd:integer+ } }) & - (element samples { xsd:positiveInteger } | - attribute samples { xsd:positiveInteger }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) - }* & - - element write_initial_source { xsd:boolean }? & - - element resonance_scattering { - (element enable { xsd:boolean } | attribute enable { xsd:boolean })? & - (element method { xsd:string } | attribute method { xsd:string })? & - (element energy_min { xsd:double } | attribute energy_min { xsd:double })? & - (element energy_max { xsd:double } | attribute energy_max { xsd:double })? & - (element nuclides { list { xsd:string+ } } | - attribute nuclides { list { xsd:string+ } })? - }? -} diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng deleted file mode 100644 index 07b8a6d1d7..0000000000 --- a/src/relaxng/settings.rng +++ /dev/null @@ -1,921 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - led - ttb - - - - - - - nuclide - log - logarithm - logarithmic - material-union - union - - - - - - - continuous-energy - ce - CE - multi-group - mg - MG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - regular - - - regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 16 - - - - - 16 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/relaxng/tallies.rnc b/src/relaxng/tallies.rnc deleted file mode 100644 index 4e46c45a17..0000000000 --- a/src/relaxng/tallies.rnc +++ /dev/null @@ -1,114 +0,0 @@ -element tallies { - element mesh { - (element id { xsd:int } | attribute id { xsd:int }) & - ( - ( - (element type { ( "regular" ) } | - attribute type { ( "regular" ) }) & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) - ) | ( - (element type { ( "rectilinear" ) } | - attribute type { ( "rectilinear" ) }) & - (element x_grid { list { xsd:double+ } } | - attribute x_grid { list { xsd:double+ } }) & - (element y_grid { list { xsd:double+ } } | - attribute y_grid { list { xsd:double+ } }) & - (element z_grid { list { xsd:double+ } } | - attribute z_grid { list { xsd:double+ } }) - ) | ( - (element type { ( "cylindrical" ) } | - attribute type { ( "cylindrical" ) }) & - (element r_grid { list { xsd:double+ } } | - attribute r_grid { list { xsd:double+ } }) & - (element phi_grid { list { xsd:double+ } } | - attribute phi_grid { list { xsd:double+ } }) & - (element z_grid { list { xsd:double+ } } | - attribute z_grid { list { xsd:double+ } }) - ) | ( - (element type { ( "spherical" ) } | - attribute type { ( "spherical" ) }) & - (element r_grid { list { xsd:double+ } } | - attribute r_grid { list { xsd:double+ } }) & - (element theta_grid { list { xsd:double+ } } | - attribute theta_grid { list { xsd:double+ } }) & - (element phi_grid { list { xsd:double+ } } | - attribute phi_grid { list { xsd:double+ } }) - ) - ) - }* & - - element derivative { - (element id { xsd:int } | attribute id { xsd:int }) & - (element material { xsd:int } | attribute material { xsd:int }) & - ( (element variable { ( "density") } - | attribute variable { ( "density" ) } ) | - ( - (element variable { ( "nuclide_density" ) } - | attribute variable { ( "nuclide_density" ) } ) - & - (element nuclide { xsd:string { maxLength = "12" } } - | attribute nuclide { xsd:string { maxLength = "12" } } ) - ) | - (element variable { ( "temperature") } - | attribute variable { ( "temperature" ) } ) - ) - }* & - - element filter { - (element id { xsd:int } | attribute id { xsd:int }) & - ( - ( (element type { ( "cell" | "cellfrom" | "cellborn" | "material" | - "universe" | "surface" | "distribcell" | "mesh" | "energy" | - "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | - "energyfunction" | "meshsurface" | "cellinstance") } | - attribute type { ( "cell" | "cellfrom" | "cellborn" | "material" | - "universe" | "surface" | "distribcell" | "mesh" | "energy" | - "energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" | - "energyfunction" | "meshsurface" | "cellinstance") }) & - (element bins { list { xsd:double+ } } | - attribute bins { list { xsd:double+ } }) - ) | - ( - (element type { ("energyfunction") } | - attribute type { ("energyfunction") }) & - (element energy { list { xsd:double+ } } | - attribute energy { list { xsd:double+ } }) & - (element y { list { xsd:double+ } } | - attribute y { list { xsd:double+ } }) - ) - ) - }* & - - element tally { - (element id { xsd:int } | attribute id { xsd:int }) & - (element name { xsd:string { maxLength="52" } } | - attribute name { xsd:string { maxLength="52" } })? & - (element estimator { ( "analog" | "tracklength" | "collision" ) } | - attribute estimator { ( "analog" | "tracklength" | "collision" ) })? & - (element filters { list { xsd:int+ } } | - attribute filters { list { xsd:int+ } })? & - element nuclides { - list { xsd:string { maxLength = "12" }+ } - }? & - element scores { - list { xsd:string { maxLength = "20" }+ } - } & - element trigger { - (element type { xsd:string } | attribute type { xsd:string }) & - (element threshold { xsd:double} | attribute threshold { xsd:double }) & - (element scores { list { xsd:string { maxLength = "20" }+ } } | attribute scores { list { xsd:string { maxLength = "20"}+ } } )? - }* & - (element derivative { xsd:int } | attribute derivative { xsd:int } )? - }* & - - element assume_separate { xsd:boolean }? -} diff --git a/src/relaxng/tallies.rng b/src/relaxng/tallies.rng deleted file mode 100644 index 350c3cb4aa..0000000000 --- a/src/relaxng/tallies.rng +++ /dev/null @@ -1,480 +0,0 @@ - - - - - - - - - - - - - - - - - - - regular - - - regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - rectilinear - - - rectilinear - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - density - - - density - - - - - - nuclide_density - - - nuclide_density - - - - - - 12 - - - - - 12 - - - - - - - temperature - - - temperature - - - - - - - - - - - - - - - - - - - - - - - cell - cellfrom - cellborn - material - universe - surface - distribcell - mesh - energy - energyout - mu - polar - azimuthal - delayedgroup - energyfunction - meshsurface - cellinstance - - - - - cell - cellfrom - cellborn - material - universe - surface - distribcell - mesh - energy - energyout - mu - polar - azimuthal - delayedgroup - energyfunction - meshsurface - cellinstance - - - - - - - - - - - - - - - - - - - - - - - - energyfunction - - - energyfunction - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 - - - - - 52 - - - - - - - - - analog - tracklength - collision - - - - - analog - tracklength - collision - - - - - - - - - - - - - - - - - - - - - - - - - - - - 12 - - - - - - - - - - 20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 20 - - - - - - - - - 20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/scattdata.cpp b/src/scattdata.cpp index 813f9e19b5..21b18cbd92 100644 --- a/src/scattdata.cpp +++ b/src/scattdata.cpp @@ -37,22 +37,12 @@ void ScattData::base_init(int order, const xt::xtensor& in_gmin, mult[gin] = in_mult[gin]; // Make sure the multiplicity does not have 0s - unsigned long int num_converted = 0; for (int go = 0; go < mult[gin].size(); go++) { if (mult[gin][go] == 0.) { - num_converted += 1; mult[gin][go] = 1.; } } - if (num_converted > 0) { - // Raise a warning to the user if we did have to do the conversion - std::string msg = - std::to_string(num_converted) + - " entries in the Multiplicity Matrix were changed from 0 to 1"; - warning(msg); - } - // Make sure the energy is normalized double norm = std::accumulate(energy[gin].begin(), energy[gin].end(), 0.); diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 3677f6ed3a..8b9e8737c6 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -4,10 +4,9 @@ #include "openmc/random_lcg.h" #include "openmc/search.h" -#include - #include "xtensor/xview.hpp" +#include #include // for log, exp namespace openmc { @@ -40,7 +39,7 @@ void CoherentElasticAE::sample( const auto& energies {xs_.bragg_edges()}; - Expects(E_in >= energies.front()); + assert(E_in >= energies.front()); const int i = lower_bound_index(energies.begin(), energies.end(), E_in); @@ -332,4 +331,40 @@ void IncoherentInelasticAE::sample( mu += std::min(mu - mu_left, mu_right - mu) * (prn(seed) - 0.5); } +//============================================================================== +// MixedElasticAE implementation +//============================================================================== + +MixedElasticAE::MixedElasticAE( + hid_t group, const CoherentElasticXS& coh_xs, const Function1D& incoh_xs) + : coherent_dist_(coh_xs), coherent_xs_(coh_xs), incoherent_xs_(incoh_xs) +{ + // Read incoherent elastic distribution + hid_t incoherent_group = open_group(group, "incoherent"); + std::string temp; + read_attribute(incoherent_group, "type", temp); + if (temp == "incoherent_elastic") { + incoherent_dist_ = make_unique(incoherent_group); + } else if (temp == "incoherent_elastic_discrete") { + auto xs = dynamic_cast(&incoh_xs); + incoherent_dist_ = + make_unique(incoherent_group, xs->x()); + } + close_group(incoherent_group); +} + +void MixedElasticAE::sample( + double E_in, double& E_out, double& mu, uint64_t* seed) const +{ + // Evaluate coherent and incoherent elastic cross sections + double xs_coh = coherent_xs_(E_in); + double xs_incoh = incoherent_xs_(E_in); + + if (prn(seed) * (xs_coh + xs_incoh) < xs_coh) { + coherent_dist_.sample(E_in, E_out, mu, seed); + } else { + incoherent_dist_->sample(E_in, E_out, mu, seed); + } +} + } // namespace openmc diff --git a/src/settings.cpp b/src/settings.cpp index 795b164ec4..325256cdc5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,4 +1,5 @@ #include "openmc/settings.h" +#include "openmc/random_ray/flat_source_domain.h" #include // for ceil, pow #include // for numeric_limits @@ -18,10 +19,13 @@ #include "openmc/eigenvalue.h" #include "openmc/error.h" #include "openmc/file_utils.h" +#include "openmc/mcpl_interface.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" #include "openmc/output.h" +#include "openmc/plot.h" #include "openmc/random_lcg.h" +#include "openmc/random_ray/random_ray.h" #include "openmc/simulation.h" #include "openmc/source.h" #include "openmc/string_utils.h" @@ -43,10 +47,12 @@ bool assume_separate {false}; bool check_overlaps {false}; bool cmfd_run {false}; bool confidence_intervals {false}; +bool create_delayed_neutrons {true}; bool create_fission_neutrons {true}; bool delayed_photon_scaling {true}; bool entropy_on {false}; bool event_based {false}; +bool ifp_on {false}; bool legendre_to_tabular {true}; bool material_cell_offsets {true}; bool output_summary {true}; @@ -60,15 +66,22 @@ bool run_CE {true}; bool source_latest {false}; bool source_separate {false}; bool source_write {true}; +bool source_mcpl_write {false}; bool surf_source_write {false}; +bool surf_mcpl_write {false}; bool surf_source_read {false}; bool survival_biasing {false}; +bool survival_normalization {false}; bool temperature_multipole {false}; bool trigger_on {false}; bool trigger_predict {false}; +bool uniform_source_sampling {false}; bool ufs_on {false}; bool urr_ptables_on {true}; +bool use_decay_photons {false}; bool weight_windows_on {false}; +bool weight_window_checkpoint_surface {false}; +bool weight_window_checkpoint_collision {true}; bool write_all_tracks {false}; bool write_initial_source {false}; @@ -78,32 +91,47 @@ std::string path_output; std::string path_particle_restart; std::string path_sourcepoint; std::string path_statepoint; +const char* path_statepoint_c {path_statepoint.c_str()}; +std::string weight_windows_file; int32_t n_inactive {0}; int32_t max_lost_particles {10}; double rel_max_lost_particles {1.0e-6}; +int32_t max_write_lost_particles {-1}; int32_t gen_per_batch {1}; int64_t n_particles {-1}; int64_t max_particles_in_flight {100000}; +int max_particle_events {1000000}; ElectronTreatment electron_treatment {ElectronTreatment::TTB}; array energy_cutoff {0.0, 1000.0, 0.0, 0.0}; +array time_cutoff {INFTY, INFTY, INFTY, INFTY}; +int ifp_n_generation {-1}; +IFPParameter ifp_parameter {IFPParameter::None}; int legendre_to_tabular_points {C_NONE}; int max_order {0}; int n_log_bins {8000}; int n_batches; int n_max_batches; -int max_splits {1000}; +int max_secondaries {10000}; +int max_history_splits {10'000'000}; +int max_tracks {1000}; ResScatMethod res_scat_method {ResScatMethod::rvs}; double res_scat_energy_min {0.01}; double res_scat_energy_max {1000.0}; vector res_scat_nuclides; RunMode run_mode {RunMode::UNSET}; +SolverType solver_type {SolverType::MONTE_CARLO}; std::unordered_set sourcepoint_batch; std::unordered_set statepoint_batch; +double source_rejection_fraction {0.05}; +double free_gas_threshold {400.0}; std::unordered_set source_write_surf_id; -int64_t max_surface_particles; +int64_t ssw_max_particles; +int64_t ssw_max_files; +int64_t ssw_cell_id {C_NONE}; +SSWCellType ssw_cell_type {SSWCellType::None}; TemperatureMethod temperature_method {TemperatureMethod::NEAREST}; double temperature_tolerance {10.0}; double temperature_default {293.6}; @@ -144,6 +172,12 @@ void get_run_parameters(pugi::xml_node node_base) std::stoll(get_node_value(node_base, "max_particles_in_flight")); } + // Get maximum number of events allowed per particle + if (check_for_node(node_base, "max_particle_events")) { + max_particle_events = + std::stoll(get_node_value(node_base, "max_particle_events")); + } + // Get number of basic batches if (check_for_node(node_base, "batches")) { n_batches = std::stoi(get_node_value(node_base, "batches")); @@ -163,8 +197,15 @@ void get_run_parameters(pugi::xml_node node_base) std::stod(get_node_value(node_base, "rel_max_lost_particles")); } + // Get relative number of lost particles + if (check_for_node(node_base, "max_write_lost_particles")) { + max_write_lost_particles = + std::stoi(get_node_value(node_base, "max_write_lost_particles")); + } + // Get number of inactive batches - if (run_mode == RunMode::EIGENVALUE) { + if (run_mode == RunMode::EIGENVALUE || + solver_type == SolverType::RANDOM_RAY) { if (check_for_node(node_base, "inactive")) { n_inactive = std::stoi(get_node_value(node_base, "inactive")); } @@ -208,26 +249,132 @@ void get_run_parameters(pugi::xml_node node_base) } } } + + // Random ray variables + if (solver_type == SolverType::RANDOM_RAY) { + xml_node random_ray_node = node_base.child("random_ray"); + if (check_for_node(random_ray_node, "distance_active")) { + RandomRay::distance_active_ = + std::stod(get_node_value(random_ray_node, "distance_active")); + if (RandomRay::distance_active_ <= 0.0) { + fatal_error("Random ray active distance must be greater than 0"); + } + } else { + fatal_error("Specify random ray active distance in settings XML"); + } + if (check_for_node(random_ray_node, "distance_inactive")) { + RandomRay::distance_inactive_ = + std::stod(get_node_value(random_ray_node, "distance_inactive")); + if (RandomRay::distance_inactive_ < 0) { + fatal_error( + "Random ray inactive distance must be greater than or equal to 0"); + } + } else { + fatal_error("Specify random ray inactive distance in settings XML"); + } + if (check_for_node(random_ray_node, "source")) { + xml_node source_node = random_ray_node.child("source"); + // Get point to list of elements and make sure there is at least + // one + RandomRay::ray_source_ = Source::create(source_node); + } else { + fatal_error("Specify random ray source in settings XML"); + } + if (check_for_node(random_ray_node, "volume_estimator")) { + std::string temp_str = + get_node_value(random_ray_node, "volume_estimator", true, true); + if (temp_str == "simulation_averaged") { + FlatSourceDomain::volume_estimator_ = + RandomRayVolumeEstimator::SIMULATION_AVERAGED; + } else if (temp_str == "naive") { + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::NAIVE; + } else if (temp_str == "hybrid") { + FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID; + } else { + fatal_error("Unrecognized volume estimator: " + temp_str); + } + } + if (check_for_node(random_ray_node, "source_shape")) { + std::string temp_str = + get_node_value(random_ray_node, "source_shape", true, true); + if (temp_str == "flat") { + RandomRay::source_shape_ = RandomRaySourceShape::FLAT; + } else if (temp_str == "linear") { + RandomRay::source_shape_ = RandomRaySourceShape::LINEAR; + } else if (temp_str == "linear_xy") { + RandomRay::source_shape_ = RandomRaySourceShape::LINEAR_XY; + } else { + fatal_error("Unrecognized source shape: " + temp_str); + } + } + if (check_for_node(random_ray_node, "volume_normalized_flux_tallies")) { + FlatSourceDomain::volume_normalized_flux_tallies_ = + get_node_value_bool(random_ray_node, "volume_normalized_flux_tallies"); + } + if (check_for_node(random_ray_node, "adjoint")) { + FlatSourceDomain::adjoint_ = + get_node_value_bool(random_ray_node, "adjoint"); + } + if (check_for_node(random_ray_node, "sample_method")) { + std::string temp_str = + get_node_value(random_ray_node, "sample_method", true, true); + if (temp_str == "prng") { + RandomRay::sample_method_ = RandomRaySampleMethod::PRNG; + } else if (temp_str == "halton") { + RandomRay::sample_method_ = RandomRaySampleMethod::HALTON; + } else { + fatal_error("Unrecognized sample method: " + temp_str); + } + } + if (check_for_node(random_ray_node, "source_region_meshes")) { + pugi::xml_node node_source_region_meshes = + random_ray_node.child("source_region_meshes"); + for (pugi::xml_node node_mesh : + node_source_region_meshes.children("mesh")) { + int mesh_id = std::stoi(node_mesh.attribute("id").value()); + for (pugi::xml_node node_domain : node_mesh.children("domain")) { + int domain_id = std::stoi(node_domain.attribute("id").value()); + std::string domain_type = node_domain.attribute("type").value(); + Source::DomainType type; + if (domain_type == "material") { + type = Source::DomainType::MATERIAL; + } else if (domain_type == "cell") { + type = Source::DomainType::CELL; + } else if (domain_type == "universe") { + type = Source::DomainType::UNIVERSE; + } else { + throw std::runtime_error("Unknown domain type: " + domain_type); + } + FlatSourceDomain::mesh_domain_map_[mesh_id].emplace_back( + type, domain_id); + } + } + } + if (check_for_node(random_ray_node, "diagonal_stabilization_rho")) { + FlatSourceDomain::diagonal_stabilization_rho_ = std::stod( + get_node_value(random_ray_node, "diagonal_stabilization_rho")); + if (FlatSourceDomain::diagonal_stabilization_rho_ < 0.0 || + FlatSourceDomain::diagonal_stabilization_rho_ > 1.0) { + fatal_error("Random ray diagonal stabilization rho factor must be " + "between 0 and 1"); + } + } + } } void read_settings_xml() { using namespace settings; using namespace pugi; - // Check if settings.xml exists - std::string filename = path_input + "settings.xml"; + std::string filename = settings::path_input + "settings.xml"; if (!file_exists(filename)) { if (run_mode != RunMode::PLOTTING) { - fatal_error( - fmt::format("Settings XML file '{}' does not exist! In order " - "to run OpenMC, you first need a set of input files; at a " - "minimum, this " - "includes settings.xml, geometry.xml, and materials.xml. " - "Please consult " - "the user's guide at https://docs.openmc.org for further " - "information.", - filename)); + fatal_error("Could not find any XML input files! In order to run OpenMC, " + "you first need a set of input files; at a minimum, this " + "includes settings.xml, geometry.xml, and materials.xml or a " + "single model XML file. Please consult the user's guide at " + "https://docs.openmc.org for further information."); } else { // The settings.xml file is optional if we just want to make a plot. return; @@ -255,8 +402,17 @@ void read_settings_xml() if (verbosity >= 2) title(); } + write_message("Reading settings XML file...", 5); + read_settings_xml(root); +} + +void read_settings_xml(pugi::xml_node root) +{ + using namespace settings; + using namespace pugi; + // Find if a multi-group or continuous-energy simulation is desired if (check_for_node(root, "energy_mode")) { std::string temp_str = get_node_value(root, "energy_mode", true, true); @@ -267,6 +423,9 @@ void read_settings_xml() } } + // Check for user meshes and allocate + read_meshes(root); + // Look for deprecated cross_sections.xml file in settings.xml if (check_for_node(root, "cross_sections")) { warning( @@ -357,6 +516,14 @@ void read_settings_xml() } } + // Check solver type + if (check_for_node(root, "random_ray")) { + solver_type = SolverType::RANDOM_RAY; + if (run_CE) + fatal_error("multi-group energy mode must be specified in settings XML " + "when using the random ray solver."); + } + if (run_mode == RunMode::EIGENVALUE || run_mode == RunMode::FIXED_SOURCE) { // Read run parameters get_run_parameters(node_mode); @@ -376,12 +543,24 @@ void read_settings_xml() } } + // Copy plotting random number seed if specified + if (check_for_node(root, "plot_seed")) { + auto seed = std::stoll(get_node_value(root, "plot_seed")); + model::plotter_seed = seed; + } + // Copy random number seed if specified if (check_for_node(root, "seed")) { auto seed = std::stoll(get_node_value(root, "seed")); openmc_set_seed(seed); } + // Copy random number stride if specified + if (check_for_node(root, "stride")) { + auto stride = std::stoull(get_node_value(root, "stride")); + openmc_set_stride(stride); + } + // Check for electron treatment if (check_for_node(root, "electron_treatment")) { auto temp_str = get_node_value(root, "electron_treatment", true, true); @@ -426,23 +605,7 @@ void read_settings_xml() // Get point to list of elements and make sure there is at least one for (pugi::xml_node node : root.children("source")) { - if (check_for_node(node, "file")) { - auto path = get_node_value(node, "file", false, true); - model::external_sources.push_back(make_unique(path)); - } else if (check_for_node(node, "library")) { - // Get shared library path and parameters - auto path = get_node_value(node, "library", false, true); - std::string parameters; - if (check_for_node(node, "parameters")) { - parameters = get_node_value(node, "parameters", false, true); - } - - // Create custom source - model::external_sources.push_back( - make_unique(path, parameters)); - } else { - model::external_sources.push_back(make_unique(node)); - } + model::external_sources.push_back(Source::create(node)); } // Check if the user has specified to read surface source @@ -460,8 +623,9 @@ void read_settings_xml() } // If no source specified, default to isotropic point source at origin with - // Watt spectrum - if (model::external_sources.empty()) { + // Watt spectrum. No default source is needed in random ray mode. + if (model::external_sources.empty() && + settings::solver_type != SolverType::RANDOM_RAY) { double T[] {0.0}; double p[] {1.0}; model::external_sources.push_back(make_unique( @@ -470,11 +634,28 @@ void read_settings_xml() UPtrDist {new Discrete(T, p, 1)})); } + // Build probability mass function for sampling external sources + vector source_strengths; + for (auto& s : model::external_sources) { + source_strengths.push_back(s->strength()); + } + model::external_sources_probability.assign(source_strengths); + // Check if we want to write out source if (check_for_node(root, "write_initial_source")) { write_initial_source = get_node_value_bool(root, "write_initial_source"); } + // Get relative number of lost particles + if (check_for_node(root, "source_rejection_fraction")) { + source_rejection_fraction = + std::stod(get_node_value(root, "source_rejection_fraction")); + } + + if (check_for_node(root, "free_gas_threshold")) { + free_gas_threshold = std::stod(get_node_value(root, "free_gas_threshold")); + } + // Survival biasing if (check_for_node(root, "survival_biasing")) { survival_biasing = get_node_value_bool(root, "survival_biasing"); @@ -494,6 +675,10 @@ void read_settings_xml() if (check_for_node(node_cutoff, "weight_avg")) { weight_survive = std::stod(get_node_value(node_cutoff, "weight_avg")); } + if (check_for_node(node_cutoff, "survival_normalization")) { + survival_normalization = + get_node_value_bool(node_cutoff, "survival_normalization"); + } if (check_for_node(node_cutoff, "energy_neutron")) { energy_cutoff[0] = std::stod(get_node_value(node_cutoff, "energy_neutron")); @@ -514,6 +699,18 @@ void read_settings_xml() energy_cutoff[3] = std::stod(get_node_value(node_cutoff, "energy_positron")); } + if (check_for_node(node_cutoff, "time_neutron")) { + time_cutoff[0] = std::stod(get_node_value(node_cutoff, "time_neutron")); + } + if (check_for_node(node_cutoff, "time_photon")) { + time_cutoff[1] = std::stod(get_node_value(node_cutoff, "time_photon")); + } + if (check_for_node(node_cutoff, "time_electron")) { + time_cutoff[2] = std::stod(get_node_value(node_cutoff, "time_electron")); + } + if (check_for_node(node_cutoff, "time_positron")) { + time_cutoff[3] = std::stod(get_node_value(node_cutoff, "time_positron")); + } } // Particle trace @@ -547,33 +744,37 @@ void read_settings_xml() } } - // Read meshes - read_meshes(root); - - // Shannon Entropy mesh - if (check_for_node(root, "entropy_mesh")) { - int temp = std::stoi(get_node_value(root, "entropy_mesh")); - if (model::mesh_map.find(temp) == model::mesh_map.end()) { - fatal_error(fmt::format( - "Mesh {} specified for Shannon entropy does not exist.", temp)); + // Shannon entropy + if (solver_type == SolverType::RANDOM_RAY) { + if (check_for_node(root, "entropy_mesh")) { + fatal_error("Random ray uses FSRs to compute the Shannon entropy. " + "No user-defined entropy mesh is supported."); } - - auto* m = - dynamic_cast(model::meshes[model::mesh_map.at(temp)].get()); - if (!m) - fatal_error("Only regular meshes can be used as an entropy mesh"); - simulation::entropy_mesh = m; - - // Turn on Shannon entropy calculation entropy_on = true; + } else if (solver_type == SolverType::MONTE_CARLO) { + if (check_for_node(root, "entropy_mesh")) { + int temp = std::stoi(get_node_value(root, "entropy_mesh")); + if (model::mesh_map.find(temp) == model::mesh_map.end()) { + fatal_error(fmt::format( + "Mesh {} specified for Shannon entropy does not exist.", temp)); + } - } else if (check_for_node(root, "entropy")) { - fatal_error( - "Specifying a Shannon entropy mesh via the element " - "is deprecated. Please create a mesh using and then reference " - "it by specifying its ID in an element."); + auto* m = dynamic_cast( + model::meshes[model::mesh_map.at(temp)].get()); + if (!m) + fatal_error("Only regular meshes can be used as an entropy mesh"); + simulation::entropy_mesh = m; + + // Turn on Shannon entropy calculation + entropy_on = true; + + } else if (check_for_node(root, "entropy")) { + fatal_error( + "Specifying a Shannon entropy mesh via the element " + "is deprecated. Please create a mesh using and then reference " + "it by specifying its ID in an element."); + } } - // Uniform fission source weighting mesh if (check_for_node(root, "ufs_mesh")) { auto temp = std::stoi(get_node_value(root, "ufs_mesh")); @@ -646,6 +847,9 @@ void read_settings_xml() if (check_for_node(node_sp, "write")) { source_write = get_node_value_bool(node_sp, "write"); } + if (check_for_node(node_sp, "mcpl")) { + source_mcpl_write = get_node_value_bool(node_sp, "mcpl"); + } if (check_for_node(node_sp, "overwrite_latest")) { source_latest = get_node_value_bool(node_sp, "overwrite_latest"); source_separate = source_latest; @@ -657,13 +861,21 @@ void read_settings_xml() sourcepoint_batch = statepoint_batch; } + // Check is the user specified to convert strength to statistical weight + if (check_for_node(root, "uniform_source_sampling")) { + uniform_source_sampling = + get_node_value_bool(root, "uniform_source_sampling"); + } + // Check if the user has specified to write surface source if (check_for_node(root, "surf_source_write")) { surf_source_write = true; // Get surface source write node xml_node node_ssw = root.child("surf_source_write"); - // Determine surface ids at which crossing particles are to be banked + // Determine surface ids at which crossing particles are to be banked. + // If no surfaces are specified, all surfaces in the model will be used + // to bank source points. if (check_for_node(node_ssw, "surface_ids")) { auto temp = get_node_array(node_ssw, "surface_ids"); for (const auto& b : temp) { @@ -673,12 +885,47 @@ void read_settings_xml() // Get maximum number of particles to be banked per surface if (check_for_node(node_ssw, "max_particles")) { - max_surface_particles = - std::stoll(get_node_value(node_ssw, "max_particles")); + ssw_max_particles = std::stoll(get_node_value(node_ssw, "max_particles")); + } else { + fatal_error("A maximum number of particles needs to be specified " + "using the 'max_particles' parameter to store surface " + "source points."); + } + + // Get maximum number of surface source files to be created + if (check_for_node(node_ssw, "max_source_files")) { + ssw_max_files = std::stoll(get_node_value(node_ssw, "max_source_files")); + } else { + ssw_max_files = 1; + } + + if (check_for_node(node_ssw, "mcpl")) { + surf_mcpl_write = get_node_value_bool(node_ssw, "mcpl"); + } + // Get cell information + if (check_for_node(node_ssw, "cell")) { + ssw_cell_id = std::stoll(get_node_value(node_ssw, "cell")); + ssw_cell_type = SSWCellType::Both; + } + if (check_for_node(node_ssw, "cellfrom")) { + if (ssw_cell_id != C_NONE) { + fatal_error( + "'cell', 'cellfrom' and 'cellto' cannot be used at the same time."); + } + ssw_cell_id = std::stoll(get_node_value(node_ssw, "cellfrom")); + ssw_cell_type = SSWCellType::From; + } + if (check_for_node(node_ssw, "cellto")) { + if (ssw_cell_id != C_NONE) { + fatal_error( + "'cell', 'cellfrom' and 'cellto' cannot be used at the same time."); + } + ssw_cell_id = std::stoll(get_node_value(node_ssw, "cellto")); + ssw_cell_type = SSWCellType::To; } } - // If source is not seperate and is to be written out in the statepoint file, + // If source is not separate and is to be written out in the statepoint file, // make sure that the sourcepoint batch numbers are contained in the // statepoint list if (!source_separate) { @@ -802,6 +1049,12 @@ void read_settings_xml() } if (check_for_node(root, "temperature_multipole")) { temperature_multipole = get_node_value_bool(root, "temperature_multipole"); + + // Multipole currently doesn't work with photon transport + if (temperature_multipole && photon_transport) { + fatal_error("Multipole data cannot currently be used in conjunction with " + "photon transport."); + } } if (check_for_node(root, "temperature_range")) { auto range = get_node_array(root, "temperature_range"); @@ -809,6 +1062,20 @@ void read_settings_xml() temperature_range[1] = range.at(1); } + // Check for user value for the number of generation of the Iterated Fission + // Probability (IFP) method + if (check_for_node(root, "ifp_n_generation")) { + ifp_n_generation = std::stoi(get_node_value(root, "ifp_n_generation")); + if (ifp_n_generation <= 0) { + fatal_error("'ifp_n_generation' must be greater than 0."); + } + // Avoid tallying 0 if IFP logs are not complete when active cycles start + if (ifp_n_generation > n_inactive) { + fatal_error("'ifp_n_generation' must be lower than or equal to the " + "number of inactive cycles."); + } + } + // Check for tabular_legendre options if (check_for_node(root, "tabular_legendre")) { // Get pointer to tabular_legendre node @@ -831,6 +1098,12 @@ void read_settings_xml() } } + // Check whether create delayed neutrons in fission + if (check_for_node(root, "create_delayed_neutrons")) { + create_delayed_neutrons = + get_node_value_bool(root, "create_delayed_neutrons"); + } + // Check whether create fission sites if (run_mode == RunMode::FIXED_SOURCE) { if (check_for_node(root, "create_fission_neutrons")) { @@ -859,17 +1132,71 @@ void read_settings_xml() for (pugi::xml_node node_ww : root.children("weight_windows")) { variance_reduction::weight_windows.emplace_back( std::make_unique(node_ww)); - - // Enable weight windows by default if one or more are present - settings::weight_windows_on = true; } + // Enable weight windows by default if one or more are present + if (variance_reduction::weight_windows.size() > 0) + settings::weight_windows_on = true; + + // read weight windows from file + if (check_for_node(root, "weight_windows_file")) { + weight_windows_file = get_node_value(root, "weight_windows_file"); + } + + // read settings for weight windows value, this will override + // the automatic setting even if weight windows are present if (check_for_node(root, "weight_windows_on")) { weight_windows_on = get_node_value_bool(root, "weight_windows_on"); } - if (check_for_node(root, "max_splits")) { - settings::max_splits = std::stoi(get_node_value(root, "max_splits")); + if (check_for_node(root, "max_secondaries")) { + settings::max_secondaries = + std::stoi(get_node_value(root, "max_secondaries")); + } + + if (check_for_node(root, "max_history_splits")) { + settings::max_history_splits = + std::stoi(get_node_value(root, "max_history_splits")); + } + + if (check_for_node(root, "max_tracks")) { + settings::max_tracks = std::stoi(get_node_value(root, "max_tracks")); + } + + // Create weight window generator objects + if (check_for_node(root, "weight_window_generators")) { + auto wwgs_node = root.child("weight_window_generators"); + for (pugi::xml_node node_wwg : + wwgs_node.children("weight_windows_generator")) { + variance_reduction::weight_windows_generators.emplace_back( + std::make_unique(node_wwg)); + } + // if any of the weight windows are intended to be generated otf, make sure + // they're applied + for (const auto& wwg : variance_reduction::weight_windows_generators) { + if (wwg->on_the_fly_) { + settings::weight_windows_on = true; + break; + } + } + } + + // Set up weight window checkpoints + if (check_for_node(root, "weight_window_checkpoints")) { + xml_node ww_checkpoints = root.child("weight_window_checkpoints"); + if (check_for_node(ww_checkpoints, "collision")) { + weight_window_checkpoint_collision = + get_node_value_bool(ww_checkpoints, "collision"); + } + if (check_for_node(ww_checkpoints, "surface")) { + weight_window_checkpoint_surface = + get_node_value_bool(ww_checkpoints, "surface"); + } + } + + if (check_for_node(root, "use_decay_photons")) { + settings::use_decay_photons = + get_node_value_bool(root, "use_decay_photons"); } } diff --git a/src/simulation.cpp b/src/simulation.cpp index b7f5b58f16..f55546c4ce 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -7,7 +7,9 @@ #include "openmc/error.h" #include "openmc/event.h" #include "openmc/geometry_aux.h" +#include "openmc/ifp.h" #include "openmc/material.h" +#include "openmc/mcpl_interface.h" #include "openmc/message_passing.h" #include "openmc/nuclide.h" #include "openmc/output.h" @@ -23,6 +25,7 @@ #include "openmc/tallies/trigger.h" #include "openmc/timer.h" #include "openmc/track_output.h" +#include "openmc/weight_windows.h" #ifdef _OPENMP #include @@ -52,8 +55,14 @@ int openmc_run() openmc::simulation::time_total.start(); openmc_simulation_init(); - int err = 0; + // Ensure that a batch isn't executed in the case that the maximum number of + // batches has already been run in a restart statepoint file int status = 0; + if (openmc::simulation::current_batch >= openmc::settings::n_max_batches) { + status = openmc::STATUS_EXIT_MAX_BATCH; + } + + int err = 0; while (status == 0 && err == 0) { err = openmc_next_batch(&status); } @@ -82,6 +91,11 @@ int openmc_simulation_init() // Allocate source, fission and surface source banks. allocate_banks(); + // Create track file if needed + if (!settings::track_identifiers.empty() || settings::write_all_tracks) { + open_track_file(); + } + // If doing an event-based simulation, intialize the particle buffer // and event queues if (settings::event_based) { @@ -92,6 +106,7 @@ int openmc_simulation_init() // Allocate tally results arrays if they're not allocated yet for (auto& t : model::tallies) { + t->set_strides(); t->init_results(); } @@ -103,6 +118,7 @@ int openmc_simulation_init() // Reset global variables -- this is done before loading state point (as that // will potentially populate k_generation and entropy) simulation::current_batch = 0; + simulation::ssw_current_file = 1; simulation::k_generation.clear(); simulation::entropy.clear(); openmc_reset(); @@ -114,7 +130,8 @@ int openmc_simulation_init() write_message("Resuming simulation...", 6); } else { // Only initialize primary source bank for eigenvalue simulations - if (settings::run_mode == RunMode::EIGENVALUE) { + if (settings::run_mode == RunMode::EIGENVALUE && + settings::solver_type == SolverType::MONTE_CARLO) { initialize_source(); } } @@ -122,14 +139,27 @@ int openmc_simulation_init() // Display header if (mpi::master) { if (settings::run_mode == RunMode::FIXED_SOURCE) { - header("FIXED SOURCE TRANSPORT SIMULATION", 3); + if (settings::solver_type == SolverType::MONTE_CARLO) { + header("FIXED SOURCE TRANSPORT SIMULATION", 3); + } else if (settings::solver_type == SolverType::RANDOM_RAY) { + header("FIXED SOURCE TRANSPORT SIMULATION (RANDOM RAY SOLVER)", 3); + } } else if (settings::run_mode == RunMode::EIGENVALUE) { - header("K EIGENVALUE SIMULATION", 3); + if (settings::solver_type == SolverType::MONTE_CARLO) { + header("K EIGENVALUE SIMULATION", 3); + } else if (settings::solver_type == SolverType::RANDOM_RAY) { + header("K EIGENVALUE SIMULATION (RANDOM RAY SOLVER)", 3); + } if (settings::verbosity >= 7) print_columns(); } } + // load weight windows from file + if (!settings::weight_windows_file.empty()) { + openmc_weight_windows_import(settings::weight_windows_file.c_str()); + } + // Set flag indicating initialization is done simulation::initialized = true; return 0; @@ -152,6 +182,11 @@ int openmc_simulation_finalize() mat->mat_nuclide_index_.clear(); } + // Close track file if open + if (!settings::track_identifiers.empty() || settings::write_all_tracks) { + close_track_file(); + } + // Increment total number of generations simulation::total_gen += simulation::current_batch * settings::gen_per_batch; @@ -163,6 +198,12 @@ int openmc_simulation_finalize() if (settings::output_tallies && mpi::master) write_tallies(); + // If weight window generators are present in this simulation, + // write a weight windows file + if (variance_reduction::weight_windows_generators.size() > 0) { + openmc_weight_windows_export(); + } + // Deactivate all tallies for (auto& t : model::tallies) { t->active_ = false; @@ -172,10 +213,12 @@ int openmc_simulation_finalize() simulation::time_finalize.stop(); simulation::time_total.stop(); if (mpi::master) { - if (settings::verbosity >= 6) - print_runtime(); - if (settings::verbosity >= 4) - print_results(); + if (settings::solver_type != SolverType::RANDOM_RAY) { + if (settings::verbosity >= 6) + print_runtime(); + if (settings::verbosity >= 4) + print_results(); + } } if (settings::check_overlaps) print_overlap_check(); @@ -224,7 +267,7 @@ int openmc_next_batch(int* status) // Check simulation ending criteria if (status) { - if (simulation::current_batch == settings::n_max_batches) { + if (simulation::current_batch >= settings::n_max_batches) { *status = STATUS_EXIT_MAX_BATCH; } else if (simulation::satisfy_triggers) { *status = STATUS_EXIT_ON_TRIGGER; @@ -267,6 +310,7 @@ int n_lost_particles {0}; bool need_depletion_rx {false}; int restart_batch; bool satisfy_triggers {false}; +int ssw_current_file; int total_gen {0}; double total_weight; int64_t work_per_rank; @@ -285,17 +329,23 @@ vector work_index; void allocate_banks() { - if (settings::run_mode == RunMode::EIGENVALUE) { + if (settings::run_mode == RunMode::EIGENVALUE && + settings::solver_type == SolverType::MONTE_CARLO) { // Allocate source bank simulation::source_bank.resize(simulation::work_per_rank); // Allocate fission bank init_fission_bank(3 * simulation::work_per_rank); + + // Allocate IFP bank + if (settings::ifp_on) { + resize_simulation_ifp_banks(); + } } if (settings::surf_source_write) { // Allocate surface source bank - simulation::surf_source_bank.reserve(settings::max_surface_particles); + simulation::surf_source_bank.reserve(settings::ssw_max_particles); } } @@ -303,9 +353,14 @@ void initialize_batch() { // Increment current batch ++simulation::current_batch; - if (settings::run_mode == RunMode::FIXED_SOURCE) { - write_message(6, "Simulating batch {}", simulation::current_batch); + if (settings::solver_type == SolverType::RANDOM_RAY && + simulation::current_batch < settings::n_inactive + 1) { + write_message( + 6, "Simulating batch {:<4} (inactive)", simulation::current_batch); + } else { + write_message(6, "Simulating batch {}", simulation::current_batch); + } } // Reset total starting particle weight used for normalizing tallies @@ -344,6 +399,14 @@ void finalize_batch() accumulate_tallies(); simulation::time_tallies.stop(); + // update weight windows if needed + if (settings::solver_type != SolverType::RANDOM_RAY || + simulation::current_batch == settings::n_batches) { + for (const auto& wwg : variance_reduction::weight_windows_generators) { + wwg->update(); + } + } + // Reset global tally results if (simulation::current_batch <= settings::n_inactive) { xt::view(simulation::global_tallies, xt::all()) = 0.0; @@ -380,21 +443,55 @@ void finalize_batch() // Write out a separate source point if it's been specified for this batch if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_write && settings::source_separate) { - write_source_point(nullptr); + + // Determine width for zero padding + int w = std::to_string(settings::n_max_batches).size(); + std::string source_point_filename = fmt::format("{0}source.{1:0{2}}", + settings::path_output, simulation::current_batch, w); + span bankspan(simulation::source_bank); + write_source_point(source_point_filename, bankspan, + simulation::work_index, settings::source_mcpl_write); } // Write a continously-overwritten source point if requested. if (settings::source_latest) { - auto filename = settings::path_output + "source.h5"; - write_source_point(filename.c_str()); + auto filename = settings::path_output + "source"; + span bankspan(simulation::source_bank); + write_source_point(filename, bankspan, simulation::work_index, + settings::source_mcpl_write); } } // Write out surface source if requested. if (settings::surf_source_write && - simulation::current_batch == settings::n_batches) { - auto filename = settings::path_output + "surface_source.h5"; - write_source_point(filename.c_str(), true); + simulation::ssw_current_file <= settings::ssw_max_files) { + bool last_batch = (simulation::current_batch == settings::n_batches); + if (simulation::surf_source_bank.full() || last_batch) { + // Determine appropriate filename + auto filename = fmt::format("{}surface_source.{}", settings::path_output, + simulation::current_batch); + if (settings::ssw_max_files == 1 || + (simulation::ssw_current_file == 1 && last_batch)) { + filename = settings::path_output + "surface_source"; + } + + // Get span of source bank and calculate parallel index vector + auto surf_work_index = mpi::calculate_parallel_index_vector( + simulation::surf_source_bank.size()); + span surfbankspan(simulation::surf_source_bank.begin(), + simulation::surf_source_bank.size()); + + // Write surface source file + write_source_point( + filename, surfbankspan, surf_work_index, settings::surf_mcpl_write); + + // Reset surface source bank and increment counter + simulation::surf_source_bank.clear(); + if (!last_batch && settings::ssw_max_files >= 1) { + simulation::surf_source_bank.reserve(settings::ssw_max_particles); + } + ++simulation::ssw_current_file; + } } } @@ -436,7 +533,8 @@ void finalize_generation() } global_tally_leakage = 0.0; - if (settings::run_mode == RunMode::EIGENVALUE) { + if (settings::run_mode == RunMode::EIGENVALUE && + settings::solver_type == SolverType::MONTE_CARLO) { // If using shared memory, stable sort the fission bank (by parent IDs) // so as to allow for reproducibility regardless of which order particles // are run in. @@ -444,9 +542,13 @@ void finalize_generation() // Distribute fission bank across processors evenly synchronize_bank(); + } + + if (settings::run_mode == RunMode::EIGENVALUE) { // Calculate shannon entropy - if (settings::entropy_on) + if (settings::entropy_on && + settings::solver_type == SolverType::MONTE_CARLO) shannon_entropy(); // Collect results and statistics @@ -493,6 +595,12 @@ void initialize_history(Particle& p, int64_t index_source) // Reset weight window ratio p.ww_factor() = 0.0; + // set particle history start weight + p.wgt_born() = p.wgt(); + + // Reset pulse_height_storage + std::fill(p.pht_storage().begin(), p.pht_storage().end(), 0); + // set random number seed int64_t particle_seed = (simulation::total_gen + overall_generation() - 1) * settings::n_particles + @@ -507,25 +615,18 @@ void initialize_history(Particle& p, int64_t index_source) p.trace() = true; // Set particle track. - p.write_track() = false; - if (settings::write_all_tracks) { - p.write_track() = true; - } else if (settings::track_identifiers.size() > 0) { - for (const auto& t : settings::track_identifiers) { - if (simulation::current_batch == t[0] && - simulation::current_gen == t[1] && p.id() == t[2]) { - p.write_track() = true; - break; - } - } - } + p.write_track() = check_track_criteria(p); + + // Set the particle's initial weight window value. + p.wgt_ww_born() = -1.0; + apply_weight_windows(p); // Display message if high verbosity or trace is on if (settings::verbosity >= 9 || p.trace()) { write_message("Simulating Particle {}", p.id()); } -// Add paricle's starting weight to count for normalizing tallies later +// Add particle's starting weight to count for normalizing tallies later #pragma omp atomic simulation::total_weight += p.wgt(); @@ -573,8 +674,9 @@ void calculate_work() void initialize_data() { // Determine minimum/maximum energy for incident neutron/photon data - data::energy_max = {INFTY, INFTY}; - data::energy_min = {0.0, 0.0}; + data::energy_max = {INFTY, INFTY, INFTY, INFTY}; + data::energy_min = {0.0, 0.0, 0.0, 0.0}; + for (const auto& nuc : data::nuclides) { if (nuc->grid_.size() >= 1) { int neutron = static_cast(ParticleType::neutron); @@ -602,11 +704,21 @@ void initialize_data() // than the current minimum/maximum if (data::ttb_e_grid.size() >= 1) { int photon = static_cast(ParticleType::photon); + int electron = static_cast(ParticleType::electron); + int positron = static_cast(ParticleType::positron); int n_e = data::ttb_e_grid.size(); + + const std::vector charged = {electron, positron}; + for (auto t : charged) { + data::energy_min[t] = std::exp(data::ttb_e_grid(1)); + data::energy_max[t] = std::exp(data::ttb_e_grid(n_e - 1)); + } + data::energy_min[photon] = - std::max(data::energy_min[photon], std::exp(data::ttb_e_grid(1))); - data::energy_max[photon] = std::min( - data::energy_max[photon], std::exp(data::ttb_e_grid(n_e - 1))); + std::max(data::energy_min[photon], data::energy_min[electron]); + + data::energy_max[photon] = + std::min(data::energy_max[photon], data::energy_max[electron]); } } } @@ -683,19 +795,19 @@ void free_memory_simulation() void transport_history_based_single_particle(Particle& p) { - while (true) { + while (p.alive()) { p.event_calculate_xs(); - if (!p.alive()) - break; - p.event_advance(); - if (p.collision_distance() > p.boundary().distance) { - p.event_cross_surface(); - } else { - p.event_collide(); + if (p.alive()) { + p.event_advance(); + } + if (p.alive()) { + if (p.collision_distance() > p.boundary().distance()) { + p.event_cross_surface(); + } else if (p.alive()) { + p.event_collide(); + } } p.event_revive_from_secondary(); - if (!p.alive()) - break; } p.event_death(); } diff --git a/src/source.cpp b/src/source.cpp index 11ede5fc4e..f6aa665ebd 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -4,7 +4,7 @@ #define HAS_DYNAMIC_LINKING #endif -#include // for move +#include // for move #ifdef HAS_DYNAMIC_LINKING #include // for dlopen, dlsym, dlclose, dlerror @@ -16,10 +16,13 @@ #include "openmc/bank.h" #include "openmc/capi.h" #include "openmc/cell.h" +#include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/file_utils.h" +#include "openmc/geometry.h" #include "openmc/hdf5_interface.h" #include "openmc/material.h" +#include "openmc/mcpl_interface.h" #include "openmc/memory.h" #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" @@ -29,6 +32,7 @@ #include "openmc/settings.h" #include "openmc/simulation.h" #include "openmc/state_point.h" +#include "openmc/string_utils.h" #include "openmc/xml_interface.h" namespace openmc { @@ -40,6 +44,230 @@ namespace openmc { namespace model { vector> external_sources; + +DiscreteIndex external_sources_probability; + +} // namespace model + +//============================================================================== +// Source implementation +//============================================================================== + +Source::Source(pugi::xml_node node) +{ + // Check for source strength + if (check_for_node(node, "strength")) { + strength_ = std::stod(get_node_value(node, "strength")); + if (strength_ < 0.0) { + fatal_error("Source strength is negative."); + } + } + + // Check for additional defined constraints + read_constraints(node); +} + +unique_ptr Source::create(pugi::xml_node node) +{ + // if the source type is present, use it to determine the type + // of object to create + if (check_for_node(node, "type")) { + std::string source_type = get_node_value(node, "type"); + if (source_type == "independent") { + return make_unique(node); + } else if (source_type == "file") { + return make_unique(node); + } else if (source_type == "compiled") { + return make_unique(node); + } else if (source_type == "mesh") { + return make_unique(node); + } else { + fatal_error(fmt::format("Invalid source type '{}' found.", source_type)); + } + } else { + // support legacy source format + if (check_for_node(node, "file")) { + return make_unique(node); + } else if (check_for_node(node, "library")) { + return make_unique(node); + } else { + return make_unique(node); + } + } +} + +void Source::read_constraints(pugi::xml_node node) +{ + // Check for constraints node. For backwards compatibility, if no constraints + // node is given, still try searching for domain constraints from top-level + // node. + pugi::xml_node constraints_node = node.child("constraints"); + if (constraints_node) { + node = constraints_node; + } + + // Check for domains to reject from + if (check_for_node(node, "domain_type")) { + std::string domain_type = get_node_value(node, "domain_type"); + if (domain_type == "cell") { + domain_type_ = DomainType::CELL; + } else if (domain_type == "material") { + domain_type_ = DomainType::MATERIAL; + } else if (domain_type == "universe") { + domain_type_ = DomainType::UNIVERSE; + } else { + fatal_error( + std::string("Unrecognized domain type for constraint: " + domain_type)); + } + + auto ids = get_node_array(node, "domain_ids"); + domain_ids_.insert(ids.begin(), ids.end()); + } + + if (check_for_node(node, "time_bounds")) { + auto ids = get_node_array(node, "time_bounds"); + if (ids.size() != 2) { + fatal_error("Time bounds must be represented by two numbers."); + } + time_bounds_ = std::make_pair(ids[0], ids[1]); + } + if (check_for_node(node, "energy_bounds")) { + auto ids = get_node_array(node, "energy_bounds"); + if (ids.size() != 2) { + fatal_error("Energy bounds must be represented by two numbers."); + } + energy_bounds_ = std::make_pair(ids[0], ids[1]); + } + + if (check_for_node(node, "fissionable")) { + only_fissionable_ = get_node_value_bool(node, "fissionable"); + } + + // Check for how to handle rejected particles + if (check_for_node(node, "rejection_strategy")) { + std::string rejection_strategy = get_node_value(node, "rejection_strategy"); + if (rejection_strategy == "kill") { + rejection_strategy_ = RejectionStrategy::KILL; + } else if (rejection_strategy == "resample") { + rejection_strategy_ = RejectionStrategy::RESAMPLE; + } else { + fatal_error(std::string( + "Unrecognized strategy source rejection: " + rejection_strategy)); + } + } +} + +void check_rejection_fraction(int64_t n_reject, int64_t n_accept) +{ + // Don't check unless we've hit a minimum number of total sites rejected + if (n_reject < EXTSRC_REJECT_THRESHOLD) + return; + + // Compute fraction of accepted sites and compare against minimum + double fraction = static_cast(n_accept) / n_reject; + if (fraction <= settings::source_rejection_fraction) { + fatal_error(fmt::format( + "Too few source sites satisfied the constraints (minimum source " + "rejection fraction = {}). Please check your source definition or " + "set a lower value of Settings.source_rejection_fraction.", + settings::source_rejection_fraction)); + } +} + +SourceSite Source::sample_with_constraints(uint64_t* seed) const +{ + bool accepted = false; + static int64_t n_reject = 0; + static int64_t n_accept = 0; + SourceSite site; + + while (!accepted) { + // Sample a source site without considering constraints yet + site = this->sample(seed); + + if (constraints_applied()) { + accepted = true; + } else { + // Check whether sampled site satisfies constraints + accepted = satisfies_spatial_constraints(site.r) && + satisfies_energy_constraints(site.E) && + satisfies_time_constraints(site.time); + if (!accepted) { + // Increment number of rejections and check against minimum fraction + ++n_reject; + check_rejection_fraction(n_reject, n_accept); + + // For the "kill" strategy, accept particle but set weight to 0 so that + // it is terminated immediately + if (rejection_strategy_ == RejectionStrategy::KILL) { + accepted = true; + site.wgt = 0.0; + } + } + } + } + + // Increment number of accepted samples + ++n_accept; + + return site; +} + +bool Source::satisfies_energy_constraints(double E) const +{ + return E > energy_bounds_.first && E < energy_bounds_.second; +} + +bool Source::satisfies_time_constraints(double time) const +{ + return time > time_bounds_.first && time < time_bounds_.second; +} + +bool Source::satisfies_spatial_constraints(Position r) const +{ + GeometryState geom_state; + geom_state.r() = r; + geom_state.u() = {0.0, 0.0, 1.0}; + + // Reject particle if it's not in the geometry at all + bool found = exhaustive_find_cell(geom_state); + if (!found) + return false; + + // Check the geometry state against specified domains + bool accepted = true; + if (!domain_ids_.empty()) { + if (domain_type_ == DomainType::MATERIAL) { + auto mat_index = geom_state.material(); + if (mat_index == MATERIAL_VOID) { + accepted = false; + } else { + accepted = contains(domain_ids_, model::materials[mat_index]->id()); + } + } else { + for (int i = 0; i < geom_state.n_coord(); i++) { + auto id = + (domain_type_ == DomainType::CELL) + ? model::cells[geom_state.coord(i).cell()].get()->id_ + : model::universes[geom_state.coord(i).universe()].get()->id_; + if ((accepted = contains(domain_ids_, id))) + break; + } + } + } + + // Check if spatial site is in fissionable material + if (accepted && only_fissionable_) { + // Determine material + auto mat_index = geom_state.material(); + if (mat_index == MATERIAL_VOID) { + accepted = false; + } else { + accepted = model::materials[mat_index]->fissionable(); + } + } + + return accepted; } //============================================================================== @@ -52,7 +280,7 @@ IndependentSource::IndependentSource( energy_ {std::move(energy)}, time_ {std::move(time)} {} -IndependentSource::IndependentSource(pugi::xml_node node) +IndependentSource::IndependentSource(pugi::xml_node node) : Source(node) { // Check for particle type if (check_for_node(node, "particle")) { @@ -62,16 +290,17 @@ IndependentSource::IndependentSource(pugi::xml_node node) } else if (temp_str == "photon") { particle_ = ParticleType::photon; settings::photon_transport = true; + } else if (temp_str == "electron") { + particle_ = ParticleType::electron; + settings::photon_transport = true; + } else if (temp_str == "positron") { + particle_ = ParticleType::positron; + settings::photon_transport = true; } else { fatal_error(std::string("Unknown source particle type: ") + temp_str); } } - // Check for source strength - if (check_for_node(node, "strength")) { - strength_ = std::stod(get_node_value(node, "strength")); - } - // Check for external source file if (check_for_node(node, "file")) { @@ -79,55 +308,24 @@ IndependentSource::IndependentSource(pugi::xml_node node) // Spatial distribution for external source if (check_for_node(node, "space")) { - // Get pointer to spatial distribution - pugi::xml_node node_space = node.child("space"); - - // Check for type of spatial distribution and read - std::string type; - if (check_for_node(node_space, "type")) - type = get_node_value(node_space, "type", true, true); - if (type == "cartesian") { - space_ = UPtrSpace {new CartesianIndependent(node_space)}; - } else if (type == "cylindrical") { - space_ = UPtrSpace {new CylindricalIndependent(node_space)}; - } else if (type == "spherical") { - space_ = UPtrSpace {new SphericalIndependent(node_space)}; - } else if (type == "box") { - space_ = UPtrSpace {new SpatialBox(node_space)}; - } else if (type == "fission") { - space_ = UPtrSpace {new SpatialBox(node_space, true)}; - } else if (type == "point") { - space_ = UPtrSpace {new SpatialPoint(node_space)}; - } else { - fatal_error(fmt::format( - "Invalid spatial distribution for external source: {}", type)); - } - + space_ = SpatialDistribution::create(node.child("space")); } else { // If no spatial distribution specified, make it a point source space_ = UPtrSpace {new SpatialPoint()}; } + // For backwards compatibility, check for only fissionable setting on box + // source + auto space_box = dynamic_cast(space_.get()); + if (space_box) { + if (!only_fissionable_) { + only_fissionable_ = space_box->only_fissionable(); + } + } + // Determine external source angular distribution if (check_for_node(node, "angle")) { - // Get pointer to angular distribution - pugi::xml_node node_angle = node.child("angle"); - - // Check for type of angular distribution - std::string type; - if (check_for_node(node_angle, "type")) - type = get_node_value(node_angle, "type", true, true); - if (type == "isotropic") { - angle_ = UPtrAngle {new Isotropic()}; - } else if (type == "monodirectional") { - angle_ = UPtrAngle {new Monodirectional(node_angle)}; - } else if (type == "mu-phi") { - angle_ = UPtrAngle {new PolarAzimuthal(node_angle)}; - } else { - fatal_error(fmt::format( - "Invalid angular distribution for external source: {}", type)); - } - + angle_ = UnitSphereDistribution::create(node.child("angle")); } else { angle_ = UPtrAngle {new Isotropic()}; } @@ -157,86 +355,63 @@ IndependentSource::IndependentSource(pugi::xml_node node) SourceSite IndependentSource::sample(uint64_t* seed) const { SourceSite site; + site.particle = particle_; - // Repeat sampling source location until a good site has been found - bool found = false; - int n_reject = 0; - static int n_accept = 0; - while (!found) { - // Set particle type - site.particle = particle_; + // Repeat sampling source location until a good site has been accepted + bool accepted = false; + static int64_t n_reject = 0; + static int64_t n_accept = 0; + + while (!accepted) { // Sample spatial distribution site.r = space_->sample(seed); - // Now search to see if location exists in geometry - int32_t cell_index, instance; - double xyz[] {site.r.x, site.r.y, site.r.z}; - int err = openmc_find_cell(xyz, &cell_index, &instance); - found = (err != OPENMC_E_GEOMETRY); - - // Check if spatial site is in fissionable material - if (found) { - auto space_box = dynamic_cast(space_.get()); - if (space_box) { - if (space_box->only_fissionable()) { - // Determine material - const auto& c = model::cells[cell_index]; - auto mat_index = - c->material_.size() == 1 ? c->material_[0] : c->material_[instance]; - - if (mat_index == MATERIAL_VOID) { - found = false; - } else { - if (!model::materials[mat_index]->fissionable_) - found = false; - } - } - } - } + // Check if sampled position satisfies spatial constraints + accepted = satisfies_spatial_constraints(site.r); // Check for rejection - if (!found) { + if (!accepted) { ++n_reject; - if (n_reject >= EXTSRC_REJECT_THRESHOLD && - static_cast(n_accept) / n_reject <= EXTSRC_REJECT_FRACTION) { - fatal_error("More than 95% of external source sites sampled were " - "rejected. Please check your external source definition."); - } + check_rejection_fraction(n_reject, n_accept); } } - // Increment number of accepted samples - ++n_accept; - // Sample angle site.u = angle_->sample(seed); - // Check for monoenergetic source above maximum particle energy - auto p = static_cast(particle_); - auto energy_ptr = dynamic_cast(energy_.get()); - if (energy_ptr) { - auto energies = xt::adapt(energy_ptr->x()); - if (xt::any(energies > data::energy_max[p])) { - fatal_error("Source energy above range of energies of at least " - "one cross section table"); - } else if (xt::any(energies < data::energy_min[p])) { - fatal_error("Source energy below range of energies of at least " - "one cross section table"); + // Sample energy and time for neutron and photon sources + if (settings::solver_type != SolverType::RANDOM_RAY) { + // Check for monoenergetic source above maximum particle energy + auto p = static_cast(particle_); + auto energy_ptr = dynamic_cast(energy_.get()); + if (energy_ptr) { + auto energies = xt::adapt(energy_ptr->x()); + if (xt::any(energies > data::energy_max[p])) { + fatal_error("Source energy above range of energies of at least " + "one cross section table"); + } } + + while (true) { + // Sample energy spectrum + site.E = energy_->sample(seed); + + // Resample if energy falls above maximum particle energy + if (site.E < data::energy_max[p] && + (satisfies_energy_constraints(site.E))) + break; + + n_reject++; + check_rejection_fraction(n_reject, n_accept); + } + + // Sample particle creation time + site.time = time_->sample(seed); } - while (true) { - // Sample energy spectrum - site.E = energy_->sample(seed); - - // Resample if energy falls outside minimum or maximum particle energy - if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) - break; - } - - // Sample particle creation time - site.time = time_->sample(seed); + // Increment number of accepted samples + ++n_accept; return site; } @@ -245,46 +420,72 @@ SourceSite IndependentSource::sample(uint64_t* seed) const // FileSource implementation //============================================================================== -FileSource::FileSource(std::string path) +FileSource::FileSource(pugi::xml_node node) : Source(node) { - // Check if source file exists - if (!file_exists(path)) { - fatal_error(fmt::format("Source file '{}' does not exist.", path)); + auto path = get_node_value(node, "file", false, true); + load_sites_from_file(path); +} + +FileSource::FileSource(const std::string& path) +{ + load_sites_from_file(path); +} + +void FileSource::load_sites_from_file(const std::string& path) +{ + // If MCPL file, use the dedicated file reader + if (ends_with(path, ".mcpl") || ends_with(path, ".mcpl.gz")) { + sites_ = mcpl_source_sites(path); + } else { + // Check if source file exists + if (!file_exists(path)) { + fatal_error(fmt::format("Source file '{}' does not exist.", path)); + } + + write_message(6, "Reading source file from {}...", path); + + // Open the binary file + hid_t file_id = file_open(path, 'r', true); + + // Check to make sure this is a source file + std::string filetype; + read_attribute(file_id, "filetype", filetype); + if (filetype != "source" && filetype != "statepoint") { + fatal_error("Specified starting source file not a source file type."); + } + + // Read in the source particles + read_source_bank(file_id, sites_, false); + + // Close file + file_close(file_id); } - - // Read the source from a binary file instead of sampling from some - // assumed source distribution - write_message(6, "Reading source file from {}...", path); - - // Open the binary file - hid_t file_id = file_open(path, 'r', true); - - // Check to make sure this is a source file - std::string filetype; - read_attribute(file_id, "filetype", filetype); - if (filetype != "source" && filetype != "statepoint") { - fatal_error("Specified starting source file not a source file type."); - } - - // Read in the source particles - read_source_bank(file_id, sites_, false); - - // Close file - file_close(file_id); } SourceSite FileSource::sample(uint64_t* seed) const { + // Sample a particle randomly from list size_t i_site = sites_.size() * prn(seed); return sites_[i_site]; } //============================================================================== -// CustomSourceWrapper implementation +// CompiledSourceWrapper implementation //============================================================================== -CustomSourceWrapper::CustomSourceWrapper( - std::string path, std::string parameters) +CompiledSourceWrapper::CompiledSourceWrapper(pugi::xml_node node) : Source(node) +{ + // Get shared library path and parameters + auto path = get_node_value(node, "library", false, true); + std::string parameters; + if (check_for_node(node, "parameters")) { + parameters = get_node_value(node, "parameters", false, true); + } + setup(path, parameters); +} + +void CompiledSourceWrapper::setup( + const std::string& path, const std::string& parameters) { #ifdef HAS_DYNAMIC_LINKING // Open the library @@ -297,7 +498,7 @@ CustomSourceWrapper::CustomSourceWrapper( dlerror(); // get the function to create the custom source from the library - auto create_custom_source = reinterpret_cast( + auto create_compiled_source = reinterpret_cast( dlsym(shared_library_, "openmc_create_source")); // check for any dlsym errors @@ -310,7 +511,7 @@ CustomSourceWrapper::CustomSourceWrapper( } // create a pointer to an instance of the custom source - custom_source_ = create_custom_source(parameters); + compiled_source_ = create_compiled_source(parameters); #else fatal_error("Custom source libraries have not yet been implemented for " @@ -318,11 +519,11 @@ CustomSourceWrapper::CustomSourceWrapper( #endif } -CustomSourceWrapper::~CustomSourceWrapper() +CompiledSourceWrapper::~CompiledSourceWrapper() { // Make sure custom source is cleared before closing shared library - if (custom_source_.get()) - custom_source_.reset(); + if (compiled_source_.get()) + compiled_source_.reset(); #ifdef HAS_DYNAMIC_LINKING dlclose(shared_library_); @@ -332,6 +533,67 @@ CustomSourceWrapper::~CustomSourceWrapper() #endif } +//============================================================================== +// MeshElementSpatial implementation +//============================================================================== + +Position MeshElementSpatial::sample(uint64_t* seed) const +{ + return model::meshes[mesh_index_]->sample_element(elem_index_, seed); +} + +//============================================================================== +// MeshSource implementation +//============================================================================== + +MeshSource::MeshSource(pugi::xml_node node) : Source(node) +{ + int32_t mesh_id = stoi(get_node_value(node, "mesh")); + int32_t mesh_idx = model::mesh_map.at(mesh_id); + const auto& mesh = model::meshes[mesh_idx]; + + std::vector strengths; + // read all source distributions and populate strengths vector for MeshSpatial + // object + for (auto source_node : node.children("source")) { + auto src = Source::create(source_node); + if (auto ptr = dynamic_cast(src.get())) { + src.release(); + sources_.emplace_back(ptr); + } else { + fatal_error( + "The source assigned to each element must be an IndependentSource."); + } + strengths.push_back(sources_.back()->strength()); + } + + // Set spatial distributions for each mesh element + for (int elem_index = 0; elem_index < sources_.size(); ++elem_index) { + sources_[elem_index]->set_space( + std::make_unique(mesh_idx, elem_index)); + } + + // the number of source distributions should either be one or equal to the + // number of mesh elements + if (sources_.size() > 1 && sources_.size() != mesh->n_bins()) { + fatal_error(fmt::format("Incorrect number of source distributions ({}) for " + "mesh source with {} elements.", + sources_.size(), mesh->n_bins())); + } + + space_ = std::make_unique(mesh_idx, strengths); +} + +SourceSite MeshSource::sample(uint64_t* seed) const +{ + // Sample a mesh element based on the relative strengths + int32_t element = space_->sample_element_index(seed); + + // Sample the distribution for the specific mesh element; note that the + // spatial distribution has been set for each element using MeshElementSpatial + return source(element)->sample_with_constraints(seed); +} + //============================================================================== // Non-member functions //============================================================================== @@ -357,32 +619,35 @@ void initialize_source() write_message("Writing out initial source...", 5); std::string filename = settings::path_output + "initial_source.h5"; hid_t file_id = file_open(filename, 'w', true); - write_source_bank(file_id, false); + write_source_bank(file_id, simulation::source_bank, simulation::work_index); file_close(file_id); } } SourceSite sample_external_source(uint64_t* seed) { - // Determine total source strength - double total_strength = 0.0; - for (auto& s : model::external_sources) - total_strength += s->strength(); - // Sample from among multiple source distributions int i = 0; - if (model::external_sources.size() > 1) { - double xi = prn(seed) * total_strength; - double c = 0.0; - for (; i < model::external_sources.size(); ++i) { - c += model::external_sources[i]->strength(); - if (xi < c) - break; + int n_sources = model::external_sources.size(); + if (n_sources > 1) { + if (settings::uniform_source_sampling) { + i = prn(seed) * n_sources; + } else { + i = model::external_sources_probability.sample(seed); } } // Sample source site from i-th source distribution - SourceSite site {model::external_sources[i]->sample(seed)}; + SourceSite site {model::external_sources[i]->sample_with_constraints(seed)}; + + // For uniform source sampling, multiply the weight by the ratio of the actual + // probability of sampling source i to the biased probability of sampling + // source i, which is (strength_i / total_strength) / (1 / n) + if (n_sources > 1 && settings::uniform_source_sampling) { + double total_strength = model::external_sources_probability.integral(); + site.wgt *= + model::external_sources[i]->strength() * n_sources / total_strength; + } // If running in MG, convert site.E to group if (!settings::run_CE) { @@ -399,4 +664,28 @@ void free_memory_source() model::external_sources.clear(); } +//============================================================================== +// C API +//============================================================================== + +extern "C" int openmc_sample_external_source( + size_t n, uint64_t* seed, void* sites) +{ + if (!sites || !seed) { + set_errmsg("Received null pointer."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (model::external_sources.empty()) { + set_errmsg("No external sources have been defined."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + auto sites_array = static_cast(sites); + for (size_t i = 0; i < n; ++i) { + sites_array[i] = sample_external_source(seed); + } + return 0; +} + } // namespace openmc diff --git a/src/state_point.cpp b/src/state_point.cpp index 9ea0459153..8195c48650 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -13,7 +13,9 @@ #include "openmc/constants.h" #include "openmc/eigenvalue.h" #include "openmc/error.h" +#include "openmc/file_utils.h" #include "openmc/hdf5_interface.h" +#include "openmc/mcpl_interface.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" @@ -34,7 +36,8 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) { simulation::time_statepoint.start(); - // Set the filename + // If a nullptr is passed in, we assume that the user + // wants a default name for this, of the form like output/statepoint.20.h5 std::string filename_; if (filename) { filename_ = filename; @@ -47,6 +50,13 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) simulation::current_batch, w); } + // If a file name was specified, ensure it has .h5 file extension + const auto extension = get_file_extension(filename_); + if (extension != "h5") { + warning("openmc_statepoint_write was passed a file extension differing " + "from .h5, but an hdf5 file will be written."); + } + // Determine whether or not to write the source bank bool write_source_ = write_source ? *write_source : true; @@ -79,6 +89,9 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) // Write out random number seed write_dataset(file_id, "seed", openmc_get_seed()); + // Write out random number stride + write_dataset(file_id, "stride", openmc_get_stride()); + // Write run information write_dataset(file_id, "energy_mode", settings::run_CE ? "continuous-energy" : "multi-group"); @@ -182,6 +195,12 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) continue; } + if (tally->multiply_density()) { + write_attribute(tally_group, "multiply_density", 1); + } else { + write_attribute(tally_group, "multiply_density", 0); + } + if (tally->estimator_ == TallyEstimator::ANALOG) { write_dataset(tally_group, "estimator", "analog"); } else if (tally->estimator_ == TallyEstimator::TRACKLENGTH) { @@ -316,12 +335,12 @@ extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) if (write_source_) { if (mpi::master || parallel) file_id = file_open(filename_, 'a', true); - write_source_bank(file_id, false); + write_source_bank(file_id, simulation::source_bank, simulation::work_index); if (mpi::master || parallel) file_close(file_id); } -#if defined(LIBMESH) || defined(DAGMC) +#if defined(OPENMC_LIBMESH_ENABLED) || defined(OPENMC_DAGMC_ENABLED) // write unstructured mesh tally files write_unstructured_mesh_results(); #endif @@ -347,11 +366,27 @@ void restart_set_keff() void load_state_point() { - // Write message - write_message("Loading state point " + settings::path_statepoint + "...", 5); + write_message( + fmt::format("Loading state point {}...", settings::path_statepoint_c), 5); + openmc_statepoint_load(settings::path_statepoint.c_str()); +} +void statepoint_version_check(hid_t file_id) +{ + // Read revision number for state point file and make sure it matches with + // current version + array version_array; + read_attribute(file_id, "version", version_array); + if (version_array != VERSION_STATEPOINT) { + fatal_error( + "State point version does not match current version in OpenMC."); + } +} + +extern "C" int openmc_statepoint_load(const char* filename) +{ // Open file for reading - hid_t file_id = file_open(settings::path_statepoint.c_str(), 'r', true); + hid_t file_id = file_open(filename, 'r', true); // Read filetype std::string word; @@ -360,20 +395,18 @@ void load_state_point() fatal_error("OpenMC tried to restart from a non-statepoint file."); } - // Read revision number for state point file and make sure it matches with - // current version - array array; - read_attribute(file_id, "version", array); - if (array != VERSION_STATEPOINT) { - fatal_error( - "State point version does not match current version in OpenMC."); - } + statepoint_version_check(file_id); // Read and overwrite random number seed int64_t seed; read_dataset(file_id, "seed", seed); openmc_set_seed(seed); + // Read and overwrite random number stride + uint64_t stride; + read_dataset(file_id, "stride", stride); + openmc_set_stride(stride); + // It is not impossible for a state point to be generated from a CE run but // to be loaded in to an MG run (or vice versa), check to prevent that. read_dataset(file_id, "energy_mode", word); @@ -403,9 +436,13 @@ void load_state_point() // Read batch number to restart at read_dataset(file_id, "current_batch", simulation::restart_batch); - if (simulation::restart_batch > settings::n_batches) { - fatal_error("The number batches specified in settings.xml is fewer " - " than the number of batches in the given statepoint file."); + if (settings::restart_run && + simulation::restart_batch >= settings::n_max_batches) { + warning(fmt::format( + "The number of batches specified for simulation ({}) is smaller " + "than or equal to the number of batches in the restart statepoint file " + "({})", + settings::n_max_batches, simulation::restart_batch)); } // Logical flag for source present in statepoint file @@ -470,7 +507,6 @@ void load_state_point() if (internal) { tally->writable_ = false; } else { - auto& results = tally->results_; read_tally_results(tally_group, results.shape()[0], results.shape()[1], results.data()); @@ -478,7 +514,6 @@ void load_state_point() close_group(tally_group); } } - close_group(tallies_group); } } @@ -506,6 +541,8 @@ void load_state_point() // Close file file_close(file_id); + + return 0; } hid_t h5banktype() @@ -527,6 +564,7 @@ hid_t h5banktype() H5Tinsert(banktype, "r", HOFFSET(SourceSite, r), postype); H5Tinsert(banktype, "u", HOFFSET(SourceSite, u), postype); H5Tinsert(banktype, "E", HOFFSET(SourceSite, E), H5T_NATIVE_DOUBLE); + H5Tinsert(banktype, "time", HOFFSET(SourceSite, time), H5T_NATIVE_DOUBLE); H5Tinsert(banktype, "wgt", HOFFSET(SourceSite, wgt), H5T_NATIVE_DOUBLE); H5Tinsert(banktype, "delayed_group", HOFFSET(SourceSite, delayed_group), H5T_NATIVE_INT); @@ -538,31 +576,25 @@ hid_t h5banktype() return banktype; } -vector calculate_surf_source_size() +void write_source_point(std::string filename, span source_bank, + const vector& bank_index, bool use_mcpl) { - vector surf_source_index; - surf_source_index.reserve(mpi::n_procs + 1); + std::string ext = use_mcpl ? "mcpl" : "h5"; + write_message("Creating source file {}.{} with {} particles ...", filename, + ext, source_bank.size(), 5); -#ifdef OPENMC_MPI - surf_source_index.resize(mpi::n_procs); - vector bank_size(mpi::n_procs); - - // Populate the surf_source_index with cumulative sum of the number of - // surface source banks per process - int64_t size = simulation::surf_source_bank.size(); - MPI_Scan(&size, bank_size.data(), 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); - MPI_Allgather(bank_size.data(), 1, MPI_INT64_T, surf_source_index.data(), 1, - MPI_INT64_T, mpi::intracomm); - surf_source_index.insert(surf_source_index.begin(), 0); -#else - surf_source_index.push_back(0); - surf_source_index.push_back(simulation::surf_source_bank.size()); -#endif - - return surf_source_index; + // Dispatch to appropriate function based on file type + if (use_mcpl) { + filename.append(".mcpl"); + write_mcpl_source_point(filename.c_str(), source_bank, bank_index); + } else { + filename.append(".h5"); + write_h5_source_point(filename.c_str(), source_bank, bank_index); + } } -void write_source_point(const char* filename, bool surf_source_bank) +void write_h5_source_point(const char* filename, span source_bank, + const vector& bank_index) { // When using parallel HDF5, the file is written to collectively by all // processes. With MPI-only, the file is opened and written by the master @@ -574,58 +606,37 @@ void write_source_point(const char* filename, bool surf_source_bank) bool parallel = false; #endif - std::string filename_; - if (filename) { - filename_ = filename; - } else { - // Determine width for zero padding - int w = std::to_string(settings::n_max_batches).size(); + if (!filename) + fatal_error("write_source_point filename needs a nonempty name."); - filename_ = fmt::format("{0}source.{1:0{2}}.h5", settings::path_output, - simulation::current_batch, w); + std::string filename_(filename); + const auto extension = get_file_extension(filename_); + if (extension != "h5") { + warning("write_source_point was passed a file extension differing " + "from .h5, but an hdf5 file will be written."); } hid_t file_id; if (mpi::master || parallel) { - file_id = file_open(filename_, 'w', true); + file_id = file_open(filename_.c_str(), 'w', true); write_attribute(file_id, "filetype", "source"); } // Get pointer to source bank and write to file - write_source_bank(file_id, surf_source_bank); + write_source_bank(file_id, source_bank, bank_index); if (mpi::master || parallel) file_close(file_id); } -void write_source_bank(hid_t group_id, bool surf_source_bank) +void write_source_bank(hid_t group_id, span source_bank, + const vector& bank_index) { hid_t banktype = h5banktype(); // Set total and individual process dataspace sizes for source bank - int64_t dims_size = settings::n_particles; - int64_t count_size = simulation::work_per_rank; - - // Set vectors for source bank and starting bank index of each process - vector* bank_index = &simulation::work_index; - vector* source_bank = &simulation::source_bank; - vector surf_source_index_vector; - vector surf_source_bank_vector; - - // Reset dataspace sizes and vectors for surface source bank - if (surf_source_bank) { - surf_source_index_vector = calculate_surf_source_size(); - dims_size = surf_source_index_vector[mpi::n_procs]; - count_size = simulation::surf_source_bank.size(); - - bank_index = &surf_source_index_vector; - - // Copy data in a SharedArray into a vector. - surf_source_bank_vector.resize(count_size); - surf_source_bank_vector.assign(simulation::surf_source_bank.data(), - simulation::surf_source_bank.data() + count_size); - source_bank = &surf_source_bank_vector; - } + int64_t dims_size = bank_index.back(); + int64_t count_size = bank_index[mpi::rank + 1] - bank_index[mpi::rank]; #ifdef PHDF5 // Set size of total dataspace for all procs and rank @@ -639,7 +650,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) hid_t memspace = H5Screate_simple(1, count, nullptr); // Select hyperslab for this dataspace - hsize_t start[] {static_cast((*bank_index)[mpi::rank])}; + hsize_t start[] {static_cast(bank_index[mpi::rank])}; H5Sselect_hyperslab(dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Set up the property list for parallel writing @@ -647,7 +658,7 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) H5Pset_dxpl_mpio(plist, H5FD_MPIO_COLLECTIVE); // Write data to file in parallel - H5Dwrite(dset, banktype, memspace, dspace, plist, source_bank->data()); + H5Dwrite(dset, banktype, memspace, dspace, plist, source_bank.data()); // Free resources H5Sclose(dspace); @@ -666,31 +677,30 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) // Save source bank sites since the array is overwritten below #ifdef OPENMC_MPI - vector temp_source {source_bank->begin(), source_bank->end()}; + vector temp_source {source_bank.begin(), source_bank.end()}; #endif for (int i = 0; i < mpi::n_procs; ++i) { // Create memory space - hsize_t count[] { - static_cast((*bank_index)[i + 1] - (*bank_index)[i])}; + hsize_t count[] {static_cast(bank_index[i + 1] - bank_index[i])}; hid_t memspace = H5Screate_simple(1, count, nullptr); #ifdef OPENMC_MPI // Receive source sites from other processes if (i > 0) - MPI_Recv(source_bank->data(), count[0], mpi::source_site, i, i, + MPI_Recv(source_bank.data(), count[0], mpi::source_site, i, i, mpi::intracomm, MPI_STATUS_IGNORE); #endif // Select hyperslab for this dataspace dspace = H5Dget_space(dset); - hsize_t start[] {static_cast((*bank_index)[i])}; + hsize_t start[] {static_cast(bank_index[i])}; H5Sselect_hyperslab( dspace, H5S_SELECT_SET, start, nullptr, count, nullptr); // Write data to hyperslab H5Dwrite( - dset, banktype, memspace, dspace, H5P_DEFAULT, (*source_bank).data()); + dset, banktype, memspace, dspace, H5P_DEFAULT, source_bank.data()); H5Sclose(memspace); H5Sclose(dspace); @@ -701,11 +711,11 @@ void write_source_bank(hid_t group_id, bool surf_source_bank) #ifdef OPENMC_MPI // Restore state of source bank - std::copy(temp_source.begin(), temp_source.end(), source_bank->begin()); + std::copy(temp_source.begin(), temp_source.end(), source_bank.begin()); #endif } else { #ifdef OPENMC_MPI - MPI_Send(source_bank->data(), count_size, mpi::source_site, 0, mpi::rank, + MPI_Send(source_bank.data(), count_size, mpi::source_site, 0, mpi::rank, mpi::intracomm); #endif } @@ -803,7 +813,7 @@ void write_unstructured_mesh_results() vector tally_scores; for (auto filter_idx : tally->filters()) { auto& filter = model::tally_filters[filter_idx]; - if (filter->type() != "mesh") + if (filter->type() != FilterType::MESH) continue; // check if the filter uses an unstructured mesh @@ -818,6 +828,16 @@ void write_unstructured_mesh_results() if (!umesh->output_) continue; + if (umesh->library() == "moab") { + if (mpi::master) + warning(fmt::format( + "Output for a MOAB mesh (mesh {}) was " + "requested but will not be written. Please use the Python " + "API to generated the desired VTK tetrahedral mesh.", + umesh->id_)); + continue; + } + // if this tally has more than one filter, print // warning and skip writing the mesh if (tally->filters().size() > 1) { @@ -889,12 +909,10 @@ void write_unstructured_mesh_results() std::string filename = fmt::format("tally_{0}.{1:0{2}}", tally->id_, simulation::current_batch, batch_width); - if (umesh->library() == "moab" && !mpi::master) - continue; - // Write the unstructured mesh and data to file umesh->write(filename); + // remove score data added for this mesh write umesh->remove_scores(); } } diff --git a/src/string_utils.cpp b/src/string_utils.cpp index 3b1c1b4a67..74f048e8d2 100644 --- a/src/string_utils.cpp +++ b/src/string_utils.cpp @@ -2,7 +2,6 @@ #include // for equal #include // for tolower, isspace -#include namespace openmc { @@ -36,7 +35,7 @@ void to_lower(std::string& str) str[i] = std::tolower(str[i]); } -int word_count(std::string const& str) +int word_count(const std::string& str) { std::stringstream stream(str); std::string dum; diff --git a/src/summary.cpp b/src/summary.cpp index fc81ac7bb8..b3ea0254e6 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -22,8 +22,11 @@ void write_summary() // Display output message write_message("Writing summary.h5 file...", 5); + // Set filename for summary file + std::string filename = fmt::format("{}summary.h5", settings::path_output); + // Create a new file using default properties. - hid_t file = file_open("summary.h5", 'w'); + hid_t file = file_open(filename, 'w'); write_header(file); write_nuclides(file); diff --git a/src/surface.cpp b/src/surface.cpp index d19387fa46..0621bda84c 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -2,11 +2,11 @@ #include #include +#include #include #include #include -#include #include "openmc/array.h" #include "openmc/container_util.h" @@ -34,100 +34,21 @@ vector> surfaces; // Helper functions for reading the "coeffs" node of an XML surface element //============================================================================== -void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1) -{ - // Check the given number of coefficients. - std::string coeffs = get_node_value(surf_node, "coeffs"); - int n_words = word_count(coeffs); - if (n_words != 1) { - fatal_error(fmt::format( - "Surface {} expects 1 coeff but was given {}", surf_id, n_words)); - } - - // Parse the coefficients. - int stat = sscanf(coeffs.c_str(), "%lf", &c1); - if (stat != 1) { - fatal_error(fmt::format( - "Something went wrong reading coeffs for surface {}", surf_id)); - } -} - void read_coeffs( - pugi::xml_node surf_node, int surf_id, double& c1, double& c2, double& c3) + pugi::xml_node surf_node, int surf_id, std::initializer_list coeffs) { // Check the given number of coefficients. - std::string coeffs = get_node_value(surf_node, "coeffs"); - int n_words = word_count(coeffs); - if (n_words != 3) { - fatal_error(fmt::format( - "Surface {} expects 3 coeffs but was given {}", surf_id, n_words)); + auto coeffs_file = get_node_array(surf_node, "coeffs"); + if (coeffs_file.size() != coeffs.size()) { + fatal_error( + fmt::format("Surface {} expects {} coefficient but was given {}", surf_id, + coeffs.size(), coeffs_file.size())); } - // Parse the coefficients. - int stat = sscanf(coeffs.c_str(), "%lf %lf %lf", &c1, &c2, &c3); - if (stat != 3) { - fatal_error(fmt::format( - "Something went wrong reading coeffs for surface {}", surf_id)); - } -} - -void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, - double& c3, double& c4) -{ - // Check the given number of coefficients. - std::string coeffs = get_node_value(surf_node, "coeffs"); - int n_words = word_count(coeffs); - if (n_words != 4) { - fatal_error(fmt::format( - "Surface {} expects 4 coeffs but was given ", surf_id, n_words)); - } - - // Parse the coefficients. - int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf", &c1, &c2, &c3, &c4); - if (stat != 4) { - fatal_error(fmt::format( - "Something went wrong reading coeffs for surface {}", surf_id)); - } -} - -void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, - double& c3, double& c4, double& c5, double& c6) -{ - // Check the given number of coefficients. - std::string coeffs = get_node_value(surf_node, "coeffs"); - int n_words = word_count(coeffs); - if (n_words != 6) { - fatal_error(fmt::format( - "Surface {} expects 6 coeffs but was given {}", surf_id, n_words)); - } - - // Parse the coefficients. - int stat = sscanf( - coeffs.c_str(), "%lf %lf %lf %lf %lf %lf", &c1, &c2, &c3, &c4, &c5, &c6); - if (stat != 6) { - fatal_error(fmt::format( - "Something went wrong reading coeffs for surface {}", surf_id)); - } -} - -void read_coeffs(pugi::xml_node surf_node, int surf_id, double& c1, double& c2, - double& c3, double& c4, double& c5, double& c6, double& c7, double& c8, - double& c9, double& c10) -{ - // Check the given number of coefficients. - std::string coeffs = get_node_value(surf_node, "coeffs"); - int n_words = word_count(coeffs); - if (n_words != 10) { - fatal_error(fmt::format( - "Surface {} expects 10 coeffs but was given {}", surf_id, n_words)); - } - - // Parse the coefficients. - int stat = sscanf(coeffs.c_str(), "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", - &c1, &c2, &c3, &c4, &c5, &c6, &c7, &c8, &c9, &c10); - if (stat != 10) { - fatal_error(fmt::format( - "Something went wrong reading coeffs for surface {}", surf_id)); + // Copy the coefficients + int i = 0; + for (auto c : coeffs) { + *c = coeffs_file[i++]; } } @@ -141,7 +62,8 @@ Surface::Surface(pugi::xml_node surf_node) { if (check_for_node(surf_node, "id")) { id_ = std::stoi(get_node_value(surf_node, "id")); - if (contains(settings::source_write_surf_id, id_)) { + if (contains(settings::source_write_surf_id, id_) || + settings::source_write_surf_id.empty()) { surf_source_ = true; } } else { @@ -158,19 +80,36 @@ Surface::Surface(pugi::xml_node surf_node) if (surf_bc == "transmission" || surf_bc == "transmit" || surf_bc.empty()) { // Leave the bc_ a nullptr } else if (surf_bc == "vacuum") { - bc_ = std::make_shared(); + bc_ = make_unique(); } else if (surf_bc == "reflective" || surf_bc == "reflect" || surf_bc == "reflecting") { - bc_ = std::make_shared(); + bc_ = make_unique(); } else if (surf_bc == "white") { - bc_ = std::make_shared(); + bc_ = make_unique(); } else if (surf_bc == "periodic") { - // periodic BC's are handled separately + // Periodic BCs are handled separately } else { fatal_error(fmt::format("Unknown boundary condition \"{}\" specified " "on surface {}", surf_bc, id_)); } + + if (check_for_node(surf_node, "albedo") && bc_) { + double surf_alb = std::stod(get_node_value(surf_node, "albedo")); + + if (surf_alb < 0.0) + fatal_error(fmt::format("Surface {} has an albedo of {}. " + "Albedo values must be positive.", + id_, surf_alb)); + + if (surf_alb > 1.0) + warning(fmt::format("Surface {} has an albedo of {}. " + "Albedos greater than 1 may cause " + "unphysical behaviour.", + id_, surf_alb)); + + bc_->set_albedo(surf_alb); + } } } @@ -190,7 +129,7 @@ bool Surface::sense(Position r, Direction u) const return f > 0.0; } -Direction Surface::reflect(Position r, Direction u, Particle* p) const +Direction Surface::reflect(Position r, Direction u, GeometryState* p) const { // Determine projection of direction onto normal and squared magnitude of // normal. @@ -225,13 +164,27 @@ void Surface::to_hdf5(hid_t group_id) const { hid_t surf_group = create_group(group_id, fmt::format("surface {}", id_)); - if (geom_type_ == GeometryType::DAG) { + if (geom_type() == GeometryType::DAG) { write_string(surf_group, "geom_type", "dagmc", false); - } else if (geom_type_ == GeometryType::CSG) { + } else if (geom_type() == GeometryType::CSG) { write_string(surf_group, "geom_type", "csg", false); if (bc_) { write_string(surf_group, "boundary_type", bc_->type(), false); + bc_->to_hdf5(surf_group); + + // write periodic surface ID + if (bc_->type() == "periodic") { + auto pbc = dynamic_cast(bc_.get()); + Surface& surf1 {*model::surfaces[pbc->i_surf()]}; + Surface& surf2 {*model::surfaces[pbc->j_surf()]}; + + if (id_ == surf1.id_) { + write_dataset(surf_group, "periodic_surface_id", surf2.id_); + } else { + write_dataset(surf_group, "periodic_surface_id", surf1.id_); + } + } } else { write_string(surf_group, "boundary_type", "transmission", false); } @@ -246,15 +199,6 @@ void Surface::to_hdf5(hid_t group_id) const close_group(surf_group); } -CSGSurface::CSGSurface() : Surface {} -{ - geom_type_ = GeometryType::CSG; -}; -CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface {surf_node} -{ - geom_type_ = GeometryType::CSG; -}; - //============================================================================== // Generic functions for x-, y-, and z-, planes. //============================================================================== @@ -277,9 +221,9 @@ double axis_aligned_plane_distance( // SurfaceXPlane implementation //============================================================================== -SurfaceXPlane::SurfaceXPlane(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceXPlane::SurfaceXPlane(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_); + read_coeffs(surf_node, id_, {&x0_}); } double SurfaceXPlane::evaluate(Position r) const @@ -313,7 +257,8 @@ BoundingBox SurfaceXPlane::bounding_box(bool pos_side) const } } -bool SurfaceXPlane::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceXPlane::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } @@ -321,15 +266,14 @@ bool SurfaceXPlane::triso_in_mesh(vector mesh_center, vector lat //============================================================================== // SurfaceYPlane implementation //============================================================================== - -bool SurfaceYPlane::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceYPlane::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceYPlane::SurfaceYPlane(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceYPlane::SurfaceYPlane(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, y0_); + read_coeffs(surf_node, id_, {&y0_}); } double SurfaceYPlane::evaluate(Position r) const @@ -366,15 +310,14 @@ BoundingBox SurfaceYPlane::bounding_box(bool pos_side) const //============================================================================== // SurfaceZPlane implementation //============================================================================== - -bool SurfaceZPlane::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceZPlane::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceZPlane::SurfaceZPlane(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceZPlane::SurfaceZPlane(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, z0_); + read_coeffs(surf_node, id_, {&z0_}); } double SurfaceZPlane::evaluate(Position r) const @@ -411,14 +354,15 @@ BoundingBox SurfaceZPlane::bounding_box(bool pos_side) const //============================================================================== // SurfacePlane implementation //============================================================================== -bool SurfacePlane::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfacePlane::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } -SurfacePlane::SurfacePlane(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfacePlane::SurfacePlane(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, A_, B_, C_, D_); + read_coeffs(surf_node, id_, {&A_, &B_, &C_, &D_}); } double SurfacePlane::evaluate(Position r) const @@ -534,15 +478,16 @@ Direction axis_aligned_cylinder_normal( // SurfaceXCylinder implementation //============================================================================== -bool SurfaceXCylinder::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceXCylinder::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node) - : CSGSurface(surf_node) + : Surface(surf_node) { - read_coeffs(surf_node, id_, y0_, z0_, radius_); + read_coeffs(surf_node, id_, {&y0_, &z0_, &radius_}); } double SurfaceXCylinder::evaluate(Position r) const @@ -582,15 +527,16 @@ BoundingBox SurfaceXCylinder::bounding_box(bool pos_side) const // SurfaceYCylinder implementation //============================================================================== -bool SurfaceYCylinder::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceYCylinder::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node) - : CSGSurface(surf_node) + : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, z0_, radius_); + read_coeffs(surf_node, id_, {&x0_, &z0_, &radius_}); } double SurfaceYCylinder::evaluate(Position r) const @@ -631,15 +577,16 @@ BoundingBox SurfaceYCylinder::bounding_box(bool pos_side) const // SurfaceZCylinder implementation //============================================================================== -bool SurfaceZCylinder::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceZCylinder::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node) - : CSGSurface(surf_node) + : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, radius_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &radius_}); } double SurfaceZCylinder::evaluate(Position r) const @@ -680,9 +627,9 @@ BoundingBox SurfaceZCylinder::bounding_box(bool pos_side) const // SurfaceSphere implementation //============================================================================== -SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &radius_}); } double SurfaceSphere::evaluate(Position r) const @@ -756,58 +703,61 @@ BoundingBox SurfaceSphere::bounding_box(bool pos_side) const void SurfaceSphere::connect_to_triso_base(int triso_index, std::string key) { - if (key=="base") { - triso_base_index_=triso_index; - is_triso_surface_=true; - } else if (key=="particle") { - triso_particle_index_=triso_index; + if (key == "base") { + triso_base_index_ = triso_index; + is_triso_surface_ = true; + } else if (key == "particle") { + triso_particle_index_ = triso_index; } } -vector SurfaceSphere::get_center() const { - return {x0_,y0_,z0_}; +vector SurfaceSphere::get_center() const +{ + return {x0_, y0_, z0_}; } -double SurfaceSphere::get_radius() const { +double SurfaceSphere::get_radius() const +{ return radius_; } -bool SurfaceSphere::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceSphere::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { double dis_x; double dis_y; double dis_z; - double x_min=mesh_center[0]-lattice_pitch[0]/2; - double x_max=mesh_center[0]+lattice_pitch[0]/2; - double y_min=mesh_center[1]-lattice_pitch[1]/2; - double y_max=mesh_center[1]+lattice_pitch[1]/2; - double z_min=mesh_center[2]-lattice_pitch[2]/2; - double z_max=mesh_center[2]+lattice_pitch[2]/2; - if (x0_>=x_min && x0_<=x_max) { - dis_x=0; - } else if (x0_= x_min && x0_ <= x_max) { + dis_x = 0; + } else if (x0_ < x_min) { + dis_x = pow(x_min - x0_, 2); } else { - dis_x=pow(x_max-x0_, 2); + dis_x = pow(x_max - x0_, 2); } - if (y0_>=y_min && y0_<=y_max) { - dis_y=0; - } else if (y0_= y_min && y0_ <= y_max) { + dis_y = 0; + } else if (y0_ < y_min) { + dis_y = pow(y_min - y0_, 2); } else { - dis_y=pow(y_max-y0_, 2); + dis_y = pow(y_max - y0_, 2); } - if (z0_>=z_min && z0_<=z_max) { - dis_z=0; - } else if (z0_= z_min && z0_ <= z_max) { + dis_z = 0; + } else if (z0_ < z_min) { + dis_z = pow(z_min - z0_, 2); } else { - dis_z=pow(z_max-z0_, 2); + dis_z = pow(z_max - z0_, 2); } - if (sqrt(dis_x+dis_y+dis_z) < radius_) { + if (sqrt(dis_x + dis_y + dis_z) < radius_) { return true; } else { return false; @@ -905,15 +855,14 @@ Direction axis_aligned_cone_normal( //============================================================================== // SurfaceXCone implementation //============================================================================== - -bool SurfaceXCone::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceXCone::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &radius_sq_}); } double SurfaceXCone::evaluate(Position r) const @@ -942,15 +891,14 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const //============================================================================== // SurfaceYCone implementation //============================================================================== - -bool SurfaceYCone::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceYCone::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &radius_sq_}); } double SurfaceYCone::evaluate(Position r) const @@ -979,15 +927,15 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const //============================================================================== // SurfaceZCone implementation //============================================================================== - -bool SurfaceZCone::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceZCone::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } -SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, radius_sq_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &radius_sq_}); } double SurfaceZCone::evaluate(Position r) const @@ -1016,15 +964,15 @@ void SurfaceZCone::to_hdf5_inner(hid_t group_id) const //============================================================================== // SurfaceQuadric implementation //============================================================================== - -bool SurfaceQuadric::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceQuadric::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceQuadric::SurfaceQuadric(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceQuadric::SurfaceQuadric(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, A_, B_, C_, D_, E_, F_, G_, H_, J_, K_); + read_coeffs( + surf_node, id_, {&A_, &B_, &C_, &D_, &E_, &F_, &G_, &H_, &J_, &K_}); } double SurfaceQuadric::evaluate(Position r) const @@ -1140,9 +1088,11 @@ double torus_distance(double x1, double x2, double x3, double u1, double u2, double c1p = 2 * four_A2 * (u1 * x1 + u2 * x2); double c0p = four_A2 * (x1 * x1 + x2 * x2); - // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0 + // Coefficient for equation: a t^4 + b t^3 + c t^2 + d t + e = 0. If the point + // is coincident, the 'e' coefficient should be zero. Explicitly setting it to + // zero helps avoid numerical issues below with root finding. double coeff[5]; - coeff[0] = c0 * c0 - c0p; + coeff[0] = coincident ? 0.0 : c0 * c0 - c0p; coeff[1] = 2 * c0 * c1 - c1p; coeff[2] = c1 * c1 + 2 * c0 * c2 - c2p; coeff[3] = 2 * c1 * c2; @@ -1156,12 +1106,19 @@ double torus_distance(double x1, double x2, double x3, double u1, double u2, // zero but possibly small and positive. A tolerance is set to discard that // zero. double distance = INFTY; - double cutoff = coincident ? 1e-10 : 0.0; + double cutoff = coincident ? TORUS_TOL : 0.0; for (int i = 0; i < 4; ++i) { if (roots[i].imag() == 0) { double root = roots[i].real(); if (root > cutoff && root < distance) { - distance = root; + // Avoid roots corresponding to internal surfaces + double s1 = x1 + u1 * root; + double s2 = x2 + u2 * root; + double s3 = x3 + u3 * root; + double check = D * s3 * s3 + s1 * s1 + s2 * s2 + A * A - C * C; + if (check >= 0) { + distance = root; + } } } } @@ -1171,15 +1128,14 @@ double torus_distance(double x1, double x2, double x3, double u1, double u2, //============================================================================== // SurfaceXTorus implementation //============================================================================== - -bool SurfaceXTorus::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceXTorus::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceXTorus::SurfaceXTorus(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &A_, &B_, &C_}); } void SurfaceXTorus::to_hdf5_inner(hid_t group_id) const @@ -1229,15 +1185,14 @@ Direction SurfaceXTorus::normal(Position r) const //============================================================================== // SurfaceYTorus implementation //============================================================================== - -bool SurfaceYTorus::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceYTorus::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } - -SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceYTorus::SurfaceYTorus(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &A_, &B_, &C_}); } void SurfaceYTorus::to_hdf5_inner(hid_t group_id) const @@ -1287,14 +1242,15 @@ Direction SurfaceYTorus::normal(Position r) const //============================================================================== // SurfaceZTorus implementation //============================================================================== -bool SurfaceZTorus::triso_in_mesh(vector mesh_center, vector lattice_pitch) const +bool SurfaceZTorus::triso_in_mesh( + vector mesh_center, vector lattice_pitch) const { return false; } -SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) : CSGSurface(surf_node) +SurfaceZTorus::SurfaceZTorus(pugi::xml_node surf_node) : Surface(surf_node) { - read_coeffs(surf_node, id_, x0_, y0_, z0_, A_, B_, C_); + read_coeffs(surf_node, id_, {&x0_, &y0_, &z0_, &A_, &B_, &C_}); } void SurfaceZTorus::to_hdf5_inner(hid_t group_id) const @@ -1352,14 +1308,15 @@ void read_surfaces(pugi::xml_node node) } // Loop over XML surface elements and populate the array. Keep track of - // periodic surfaces. + // periodic surfaces and their albedos. model::surfaces.reserve(n_surfaces); std::set> periodic_pairs; + std::unordered_map albedo_map; { pugi::xml_node surf_node; int i_surf; for (surf_node = node.child("surface"), i_surf = 0; surf_node; - surf_node = surf_node.next_sibling("surface"), i_surf++) { + surf_node = surf_node.next_sibling("surface"), i_surf++) { std::string surf_type = get_node_value(surf_node, "type", true, true); // Allocate and initialize the new surface @@ -1417,6 +1374,12 @@ void read_surfaces(pugi::xml_node node) if (check_for_node(surf_node, "boundary")) { std::string surf_bc = get_node_value(surf_node, "boundary", true, true); if (surf_bc == "periodic") { + // Check for surface albedo. Skip sanity check as it is already done + // in the Surface class's constructor. + if (check_for_node(surf_node, "albedo")) { + albedo_map[model::surfaces.back()->id_] = + std::stod(get_node_value(surf_node, "albedo")); + } if (check_for_node(surf_node, "periodic_surface_id")) { int i_periodic = std::stoi(get_node_value(surf_node, "periodic_surface_id")); @@ -1480,7 +1443,7 @@ void read_surfaces(pugi::xml_node node) periodic_pairs.erase(second_unresolved); } - // Assign the periodic boundary conditions + // Assign the periodic boundary conditions with albedos for (auto periodic_pair : periodic_pairs) { int i_surf = model::surface_map[periodic_pair.first]; int j_surf = model::surface_map[periodic_pair.second]; @@ -1498,11 +1461,19 @@ void read_surfaces(pugi::xml_node node) // planes are parallel which indicates a translational periodic boundary // condition. Otherwise, it is a rotational periodic BC. if (std::abs(1.0 - dot_prod) < FP_PRECISION) { - surf1.bc_ = std::make_shared(i_surf, j_surf); - surf2.bc_ = surf1.bc_; + surf1.bc_ = make_unique(i_surf, j_surf); + surf2.bc_ = make_unique(i_surf, j_surf); } else { - surf1.bc_ = std::make_shared(i_surf, j_surf); - surf2.bc_ = surf1.bc_; + surf1.bc_ = make_unique(i_surf, j_surf); + surf2.bc_ = make_unique(i_surf, j_surf); + } + + // If albedo data is present in albedo map, set the boundary albedo. + if (albedo_map.count(surf1.id_)) { + surf1.bc_->set_albedo(albedo_map[surf1.id_]); + } + if (albedo_map.count(surf2.id_)) { + surf2.bc_->set_albedo(albedo_map[surf2.id_]); } } } diff --git a/src/tallies/filter.cpp b/src/tallies/filter.cpp index a1e5c709f3..79817981db 100644 --- a/src/tallies/filter.cpp +++ b/src/tallies/filter.cpp @@ -1,7 +1,8 @@ #include "openmc/tallies/filter.h" #include // for max -#include // for strcpy +#include +#include // for strcpy #include #include @@ -21,9 +22,14 @@ #include "openmc/tallies/filter_energyfunc.h" #include "openmc/tallies/filter_legendre.h" #include "openmc/tallies/filter_material.h" +#include "openmc/tallies/filter_materialfrom.h" #include "openmc/tallies/filter_mesh.h" +#include "openmc/tallies/filter_meshborn.h" +#include "openmc/tallies/filter_meshmaterial.h" #include "openmc/tallies/filter_meshsurface.h" #include "openmc/tallies/filter_mu.h" +#include "openmc/tallies/filter_musurface.h" +#include "openmc/tallies/filter_parent_nuclide.h" #include "openmc/tallies/filter_particle.h" #include "openmc/tallies/filter_polar.h" #include "openmc/tallies/filter_sph_harm.h" @@ -31,6 +37,7 @@ #include "openmc/tallies/filter_surface.h" #include "openmc/tallies/filter_time.h" #include "openmc/tallies/filter_universe.h" +#include "openmc/tallies/filter_weight.h" #include "openmc/tallies/filter_zernike.h" #include "openmc/xml_interface.h" @@ -71,21 +78,6 @@ Filter::~Filter() model::filter_map.erase(id_); } -template -T* Filter::create(int32_t id) -{ - static_assert(std::is_base_of::value, - "Type specified is not derived from openmc::Filter"); - // Create filter and add to filters vector - auto filter = make_unique(); - auto ptr_out = filter.get(); - model::tally_filters.emplace_back(std::move(filter)); - // Assign ID - model::tally_filters.back()->set_id(id); - - return ptr_out; -} - Filter* Filter::create(pugi::xml_node node) { // Copy filter id @@ -115,7 +107,7 @@ Filter* Filter::create(const std::string& type, int32_t id) } else if (type == "cell") { return Filter::create(id); } else if (type == "cellborn") { - return Filter::create(id); + return Filter::create(id); } else if (type == "cellfrom") { return Filter::create(id); } else if (type == "cellinstance") { @@ -136,12 +128,22 @@ Filter* Filter::create(const std::string& type, int32_t id) return Filter::create(id); } else if (type == "material") { return Filter::create(id); + } else if (type == "materialfrom") { + return Filter::create(id); } else if (type == "mesh") { return Filter::create(id); + } else if (type == "meshborn") { + return Filter::create(id); + } else if (type == "meshmaterial") { + return Filter::create(id); } else if (type == "meshsurface") { return Filter::create(id); } else if (type == "mu") { return Filter::create(id); + } else if (type == "musurface") { + return Filter::create(id); + } else if (type == "parentnuclide") { + return Filter::create(id); } else if (type == "particle") { return Filter::create(id); } else if (type == "polar") { @@ -156,6 +158,8 @@ Filter* Filter::create(const std::string& type, int32_t id) return Filter::create(id); } else if (type == "universe") { return Filter::create(id); + } else if (type == "weight") { + return Filter::create(id); } else if (type == "zernike") { return Filter::create(id); } else if (type == "zernikeradial") { @@ -168,7 +172,7 @@ Filter* Filter::create(const std::string& type, int32_t id) void Filter::set_id(int32_t id) { - Expects(id >= 0 || id == C_NONE); + assert(id >= 0 || id == C_NONE); // Clear entry in filter map if an ID was already assigned before if (id_ != C_NONE) { @@ -232,7 +236,16 @@ extern "C" int openmc_filter_get_type(int32_t index, char* type) if (int err = verify_filter(index)) return err; - std::strcpy(type, model::tally_filters[index]->type().c_str()); + std::strcpy(type, model::tally_filters[index]->type_str().c_str()); + return 0; +} + +extern "C" int openmc_filter_get_num_bins(int32_t index, int* n_bins) +{ + if (int err = verify_filter(index)) + return err; + + *n_bins = model::tally_filters[index]->n_bins(); return 0; } diff --git a/src/tallies/filter_azimuthal.cpp b/src/tallies/filter_azimuthal.cpp index e77aa8bdc1..6525f326d3 100644 --- a/src/tallies/filter_azimuthal.cpp +++ b/src/tallies/filter_azimuthal.cpp @@ -34,14 +34,14 @@ void AzimuthalFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void AzimuthalFilter::set_bins(gsl::span bins) +void AzimuthalFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); bins_.reserve(bins.size()); // Copy bins, ensuring they are valid - for (gsl::index i = 0; i < bins.size(); ++i) { + for (int64_t i = 0; i < bins.size(); ++i) { if (i > 0 && bins[i] <= bins[i - 1]) { throw std::runtime_error { "Azimuthal bins must be monotonically increasing."}; diff --git a/src/tallies/filter_cell.cpp b/src/tallies/filter_cell.cpp index 9ccae6b488..7a63949569 100644 --- a/src/tallies/filter_cell.cpp +++ b/src/tallies/filter_cell.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_cell.h" +#include + #include #include "openmc/capi.h" @@ -25,7 +27,7 @@ void CellFilter::from_xml(pugi::xml_node node) this->set_cells(cells); } -void CellFilter::set_cells(gsl::span cells) +void CellFilter::set_cells(span cells) { // Clear existing cells cells_.clear(); @@ -34,8 +36,8 @@ void CellFilter::set_cells(gsl::span cells) // Update cells and mapping for (auto& index : cells) { - Expects(index >= 0); - Expects(index < model::cells.size()); + assert(index >= 0); + assert(index < model::cells.size()); cells_.push_back(index); map_[index] = cells_.size() - 1; } @@ -47,7 +49,7 @@ void CellFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { for (int i = 0; i < p.n_coord(); i++) { - auto search = map_.find(p.coord(i).cell); + auto search = map_.find(p.coord(i).cell()); if (search != map_.end()) { match.bins_.push_back(search->second); match.weights_.push_back(1.0); @@ -80,7 +82,7 @@ extern "C" int openmc_cell_filter_get_bins( return err; const auto& filt = model::tally_filters[index].get(); - if (filt->type() != "cell") { + if (filt->type() != FilterType::CELL) { set_errmsg("Tried to get cells from a non-cell filter."); return OPENMC_E_INVALID_TYPE; } diff --git a/src/tallies/filter_cell_instance.cpp b/src/tallies/filter_cell_instance.cpp index 59f28c7d66..316a758d11 100644 --- a/src/tallies/filter_cell_instance.cpp +++ b/src/tallies/filter_cell_instance.cpp @@ -1,5 +1,6 @@ #include "openmc/tallies/filter_cell_instance.h" +#include #include #include @@ -12,7 +13,7 @@ namespace openmc { -CellInstanceFilter::CellInstanceFilter(gsl::span instances) +CellInstanceFilter::CellInstanceFilter(span instances) { this->set_cell_instances(instances); } @@ -21,26 +22,26 @@ void CellInstanceFilter::from_xml(pugi::xml_node node) { // Get cell IDs/instances auto cells = get_node_array(node, "bins"); - Expects(cells.size() % 2 == 0); + assert(cells.size() % 2 == 0); // Convert into vector of CellInstance vector instances; - for (gsl::index i = 0; i < cells.size() / 2; ++i) { + for (int64_t i = 0; i < cells.size() / 2; ++i) { int32_t cell_id = cells[2 * i]; - gsl::index instance = cells[2 * i + 1]; + int64_t instance = cells[2 * i + 1]; auto search = model::cell_map.find(cell_id); if (search == model::cell_map.end()) { throw std::runtime_error {fmt::format( "Could not find cell {} specified on tally filter.", cell_id)}; } - gsl::index index = search->second; + int64_t index = search->second; instances.push_back({index, instance}); } this->set_cell_instances(instances); } -void CellInstanceFilter::set_cell_instances(gsl::span instances) +void CellInstanceFilter::set_cell_instances(span instances) { // Clear existing cells cell_instances_.clear(); @@ -50,8 +51,8 @@ void CellInstanceFilter::set_cell_instances(gsl::span instances) // Update cells and mapping for (auto& x : instances) { - Expects(x.index_cell >= 0); - Expects(x.index_cell < model::cells.size()); + assert(x.index_cell >= 0); + assert(x.index_cell < model::cells.size()); cell_instances_.push_back(x); cells_.insert(x.index_cell); map_[x] = cell_instances_.size() - 1; @@ -72,8 +73,8 @@ void CellInstanceFilter::set_cell_instances(gsl::span instances) void CellInstanceFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { - gsl::index index_cell = p.coord(p.n_coord() - 1).cell; - gsl::index instance = p.cell_instance(); + int64_t index_cell = p.lowest_coord().cell(); + int64_t instance = p.cell_instance(); if (cells_.count(index_cell) > 0) { auto search = map_.find({index_cell, instance}); @@ -88,13 +89,13 @@ void CellInstanceFilter::get_all_bins( return; for (int i = 0; i < p.n_coord() - 1; i++) { - gsl::index index_cell = p.coord(i).cell; + int64_t index_cell = p.coord(i).cell(); // if this cell isn't used on the filter, move on if (cells_.count(index_cell) == 0) continue; // if this cell is used in the filter, check the instance as well - gsl::index instance = cell_instance_at_level(p, i); + int64_t instance = cell_instance_at_level(p, i); auto search = map_.find({index_cell, instance}); if (search != map_.end()) { match.bins_.push_back(search->second); @@ -108,7 +109,7 @@ void CellInstanceFilter::to_statepoint(hid_t filter_group) const Filter::to_statepoint(filter_group); size_t n = cell_instances_.size(); xt::xtensor data({n, 2}); - for (gsl::index i = 0; i < n; ++i) { + for (int64_t i = 0; i < n; ++i) { const auto& x = cell_instances_[i]; data(i, 0) = model::cells[x.index_cell]->id_; data(i, 1) = x.instance; diff --git a/src/tallies/filter_cellborn.cpp b/src/tallies/filter_cellborn.cpp index d0d25c9a0d..ad8363e7b4 100644 --- a/src/tallies/filter_cellborn.cpp +++ b/src/tallies/filter_cellborn.cpp @@ -4,7 +4,7 @@ namespace openmc { -void CellbornFilter::get_all_bins( +void CellBornFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { auto search = map_.find(p.cell_born()); @@ -14,7 +14,7 @@ void CellbornFilter::get_all_bins( } } -std::string CellbornFilter::text_label(int bin) const +std::string CellBornFilter::text_label(int bin) const { return "Birth Cell " + std::to_string(model::cells[cells_[bin]]->id_); } diff --git a/src/tallies/filter_collision.cpp b/src/tallies/filter_collision.cpp index fbb186a238..c614c3c83c 100644 --- a/src/tallies/filter_collision.cpp +++ b/src/tallies/filter_collision.cpp @@ -19,7 +19,7 @@ void CollisionFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void CollisionFilter::set_bins(gsl::span bins) +void CollisionFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); @@ -27,7 +27,7 @@ void CollisionFilter::set_bins(gsl::span bins) map_.clear(); // Copy bins - for (gsl::index i = 0; i < bins.size(); ++i) { + for (int64_t i = 0; i < bins.size(); ++i) { bins_.push_back(bins[i]); map_[bins[i]] = i; } diff --git a/src/tallies/filter_delayedgroup.cpp b/src/tallies/filter_delayedgroup.cpp index c6ec217666..01e39e554a 100644 --- a/src/tallies/filter_delayedgroup.cpp +++ b/src/tallies/filter_delayedgroup.cpp @@ -11,7 +11,7 @@ void DelayedGroupFilter::from_xml(pugi::xml_node node) this->set_groups(groups); } -void DelayedGroupFilter::set_groups(gsl::span groups) +void DelayedGroupFilter::set_groups(span groups) { // Clear existing groups groups_.clear(); diff --git a/src/tallies/filter_distribcell.cpp b/src/tallies/filter_distribcell.cpp index 89349b61f0..f511a68168 100644 --- a/src/tallies/filter_distribcell.cpp +++ b/src/tallies/filter_distribcell.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_distribcell.h" +#include + #include #include "openmc/cell.h" @@ -29,10 +31,10 @@ void DistribcellFilter::from_xml(pugi::xml_node node) void DistribcellFilter::set_cell(int32_t cell) { - Expects(cell >= 0); - Expects(cell < model::cells.size()); + assert(cell >= 0); + assert(cell < model::cells.size()); cell_ = cell; - n_bins_ = model::cells[cell]->n_instances_; + n_bins_ = model::cells[cell]->n_instances(); } void DistribcellFilter::get_all_bins( @@ -41,17 +43,18 @@ void DistribcellFilter::get_all_bins( int offset = 0; auto distribcell_index = model::cells[cell_]->distribcell_index_; for (int i = 0; i < p.n_coord(); i++) { - auto& c {*model::cells[p.coord(i).cell]}; + auto& c {*model::cells[p.coord(i).cell()]}; if (c.type_ == Fill::UNIVERSE) { offset += c.offset_[distribcell_index]; } else if (c.type_ == Fill::LATTICE) { - auto& lat {*model::lattices[p.coord(i + 1).lattice]}; - const auto& i_xyz {p.coord(i + 1).lattice_i}; + auto& lat {*model::lattices[p.coord(i + 1).lattice()]}; + const auto& i_xyz {p.coord(i + 1).lattice_index()}; if (lat.are_valid_indices(i_xyz)) { - offset += lat.offset(distribcell_index, i_xyz); + offset += + lat.offset(distribcell_index, i_xyz) + c.offset_[distribcell_index]; } } - if (cell_ == p.coord(i).cell) { + if (cell_ == p.coord(i).cell()) { match.bins_.push_back(offset); match.weights_.push_back(1.0); return; diff --git a/src/tallies/filter_energy.cpp b/src/tallies/filter_energy.cpp index 48448cb1f0..0b954cce3a 100644 --- a/src/tallies/filter_energy.cpp +++ b/src/tallies/filter_energy.cpp @@ -3,7 +3,7 @@ #include #include "openmc/capi.h" -#include "openmc/constants.h" // For F90_NONE +#include "openmc/constants.h" // For C_NONE #include "openmc/mgxs_interface.h" #include "openmc/search.h" #include "openmc/settings.h" @@ -21,14 +21,14 @@ void EnergyFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void EnergyFilter::set_bins(gsl::span bins) +void EnergyFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); bins_.reserve(bins.size()); // Copy bins, ensuring they are valid - for (gsl::index i = 0; i < bins.size(); ++i) { + for (int64_t i = 0; i < bins.size(); ++i) { if (i > 0 && bins[i] <= bins[i - 1]) { throw std::runtime_error { "Energy bins must be monotonically increasing."}; @@ -46,7 +46,7 @@ void EnergyFilter::set_bins(gsl::span bins) if (!settings::run_CE) { if (n_bins_ == data::mg.num_energy_groups_) { matches_transport_groups_ = true; - for (gsl::index i = 0; i < n_bins_ + 1; ++i) { + for (int64_t i = 0; i < n_bins_ + 1; ++i) { if (data::mg.rev_energy_bins_[i] != bins_[i]) { matches_transport_groups_ = false; break; @@ -59,7 +59,7 @@ void EnergyFilter::set_bins(gsl::span bins) void EnergyFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { - if (p.g() != F90_NONE && matches_transport_groups_) { + if (p.g() != C_NONE && matches_transport_groups_) { if (estimator == TallyEstimator::TRACKLENGTH) { match.bins_.push_back(data::mg.num_energy_groups_ - p.g() - 1); } else { @@ -98,7 +98,7 @@ std::string EnergyFilter::text_label(int bin) const void EnergyoutFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { - if (p.g() != F90_NONE && matches_transport_groups_) { + if (p.g() != C_NONE && matches_transport_groups_) { match.bins_.push_back(data::mg.num_energy_groups_ - p.g() - 1); match.weights_.push_back(1.0); diff --git a/src/tallies/filter_energyfunc.cpp b/src/tallies/filter_energyfunc.cpp index fd595e3320..fc4ba0ff91 100644 --- a/src/tallies/filter_energyfunc.cpp +++ b/src/tallies/filter_energyfunc.cpp @@ -3,6 +3,7 @@ #include #include "openmc/error.h" +#include "openmc/interpolate.h" #include "openmc/search.h" #include "openmc/settings.h" #include "openmc/xml_interface.h" @@ -24,12 +25,18 @@ void EnergyFunctionFilter::from_xml(pugi::xml_node node) fatal_error("y values not specified for EnergyFunction filter."); auto y = get_node_array(node, "y"); - this->set_data(energy, y); + + // default to linear-linear interpolation + interpolation_ = Interpolation::lin_lin; + if (check_for_node(node, "interpolation")) { + std::string interpolation = get_node_value(node, "interpolation"); + this->set_interpolation(interpolation); + } } void EnergyFunctionFilter::set_data( - gsl::span energy, gsl::span y) + span energy, span y) { // Check for consistent sizes with new data if (energy.size() != y.size()) { @@ -41,7 +48,7 @@ void EnergyFunctionFilter::set_data( y_.reserve(y.size()); // Copy over energy values, ensuring they are valid - for (gsl::index i = 0; i < energy.size(); ++i) { + for (int64_t i = 0; i < energy.size(); ++i) { if (i > 0 && energy[i] <= energy[i - 1]) { throw std::runtime_error { "Energy bins must be monotonically increasing."}; @@ -51,19 +58,48 @@ void EnergyFunctionFilter::set_data( } } +void EnergyFunctionFilter::set_interpolation(const std::string& interpolation) +{ + if (interpolation == "histogram") { + interpolation_ = Interpolation::histogram; + } else if (interpolation == "linear-linear") { + interpolation_ = Interpolation::lin_lin; + } else if (interpolation == "linear-log") { + interpolation_ = Interpolation::lin_log; + } else if (interpolation == "log-linear") { + interpolation_ = Interpolation::log_lin; + } else if (interpolation == "log-log") { + interpolation_ = Interpolation::log_log; + } else if (interpolation == "quadratic") { + if (energy_.size() < 3) + fatal_error( + fmt::format("Quadratic interpolation on EnergyFunctionFilter {} " + "requires at least 3 data points.", + this->id())); + interpolation_ = Interpolation::quadratic; + } else if (interpolation == "cubic") { + if (energy_.size() < 4) + fatal_error(fmt::format("Cubic interpolation on EnergyFunctionFilter " + "{} requires at least 4 data points.", + this->id())); + interpolation_ = Interpolation::cubic; + } else { + fatal_error(fmt::format( + "Found invalid interpolation type '{}' on EnergyFunctionFilter {}.", + interpolation, this->id())); + } +} + void EnergyFunctionFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { if (p.E_last() >= energy_.front() && p.E_last() <= energy_.back()) { - // Search for the incoming energy bin. - auto i = lower_bound_index(energy_.begin(), energy_.end(), p.E_last()); - // Compute the interpolation factor between the nearest bins. - double f = (p.E_last() - energy_[i]) / (energy_[i + 1] - energy_[i]); + double w = interpolate(energy_, y_, p.E_last(), interpolation_); // Interpolate on the lin-lin grid. match.bins_.push_back(0); - match.weights_.push_back((1 - f) * y_[i] + f * y_[i + 1]); + match.weights_.push_back(w); } } @@ -72,6 +108,10 @@ void EnergyFunctionFilter::to_statepoint(hid_t filter_group) const Filter::to_statepoint(filter_group); write_dataset(filter_group, "energy", energy_); write_dataset(filter_group, "y", y_); + hid_t y_dataset = open_dataset(filter_group, "y"); + write_attribute( + y_dataset, "interpolation", static_cast(interpolation_)); + close_dataset(y_dataset); } std::string EnergyFunctionFilter::text_label(int bin) const @@ -154,4 +194,51 @@ extern "C" int openmc_energyfunc_filter_get_y( return 0; } +extern "C" int openmc_energyfunc_filter_set_interpolation( + int32_t index, const char* interp) +{ + // ensure this is a valid index to allocated filter + if (int err = verify_filter(index)) + return err; + + // get a pointer to the filter + const auto& filt_base = model::tally_filters[index].get(); + // downcast to EnergyFunctionFilter + auto* filt = dynamic_cast(filt_base); + + // check if a valid filter was produced + if (!filt) { + set_errmsg( + "Tried to set interpolation data for non-energy function filter."); + return OPENMC_E_INVALID_TYPE; + } + + // Set interpolation + filt->set_interpolation(interp); + return 0; +} + +extern "C" int openmc_energyfunc_filter_get_interpolation( + int32_t index, int* interp) +{ + // ensure this is a valid index to allocated filter + if (int err = verify_filter(index)) + return err; + + // get a pointer to the filter + const auto& filt_base = model::tally_filters[index].get(); + // downcast to EnergyFunctionFilter + auto* filt = dynamic_cast(filt_base); + + // check if a valid filter was produced + if (!filt) { + set_errmsg( + "Tried to set interpolation data for non-energy function filter."); + return OPENMC_E_INVALID_TYPE; + } + + *interp = static_cast(filt->interpolation()); + return 0; +} + } // namespace openmc diff --git a/src/tallies/filter_material.cpp b/src/tallies/filter_material.cpp index 6669ab2fa3..215c9af72f 100644 --- a/src/tallies/filter_material.cpp +++ b/src/tallies/filter_material.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_material.h" +#include + #include #include "openmc/capi.h" @@ -24,7 +26,7 @@ void MaterialFilter::from_xml(pugi::xml_node node) this->set_materials(mats); } -void MaterialFilter::set_materials(gsl::span materials) +void MaterialFilter::set_materials(span materials) { // Clear existing materials materials_.clear(); @@ -33,8 +35,8 @@ void MaterialFilter::set_materials(gsl::span materials) // Update materials and mapping for (auto& index : materials) { - Expects(index >= 0); - Expects(index < model::materials.size()); + assert(index >= 0); + assert(index < model::materials.size()); materials_.push_back(index); map_[index] = materials_.size() - 1; } diff --git a/src/tallies/filter_materialfrom.cpp b/src/tallies/filter_materialfrom.cpp new file mode 100644 index 0000000000..91f03aef85 --- /dev/null +++ b/src/tallies/filter_materialfrom.cpp @@ -0,0 +1,24 @@ +#include "openmc/tallies/filter_materialfrom.h" + +#include "openmc/cell.h" +#include "openmc/material.h" + +namespace openmc { + +void MaterialFromFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + auto search = map_.find(p.material_last()); + if (search != map_.end()) { + match.bins_.push_back(search->second); + match.weights_.push_back(1.0); + } +} + +std::string MaterialFromFilter::text_label(int bin) const +{ + return "Material from " + + std::to_string(model::materials[materials_[bin]]->id_); +} + +} // namespace openmc diff --git a/src/tallies/filter_mesh.cpp b/src/tallies/filter_mesh.cpp index 3f895b4f85..4edfbec4b9 100644 --- a/src/tallies/filter_mesh.cpp +++ b/src/tallies/filter_mesh.cpp @@ -1,7 +1,6 @@ #include "openmc/tallies/filter_mesh.h" #include -#include #include "openmc/capi.h" #include "openmc/constants.h" @@ -16,7 +15,7 @@ void MeshFilter::from_xml(pugi::xml_node node) auto bins_ = get_node_array(node, "bins"); if (bins_.size() != 1) { fatal_error( - "Only one mesh can be specified per " + type() + " mesh filter."); + "Only one mesh can be specified per " + type_str() + " mesh filter."); } auto id = bins_[0]; @@ -77,8 +76,10 @@ std::string MeshFilter::text_label(int bin) const void MeshFilter::set_mesh(int32_t mesh) { + // perform any additional perparation for mesh tallies here mesh_ = mesh; n_bins_ = model::meshes[mesh_]->n_bins(); + model::meshes[mesh_]->prepare_for_point_location(); } void MeshFilter::set_translation(const Position& translation) @@ -158,7 +159,9 @@ extern "C" int openmc_mesh_filter_get_translation( // Check the filter type const auto& filter = model::tally_filters[index]; - if (filter->type() != "mesh" && filter->type() != "meshsurface") { + if (filter->type() != FilterType::MESH && + filter->type() != FilterType::MESHBORN && + filter->type() != FilterType::MESH_SURFACE) { set_errmsg("Tried to get a translation from a non-mesh-based filter."); return OPENMC_E_INVALID_TYPE; } @@ -182,7 +185,9 @@ extern "C" int openmc_mesh_filter_set_translation( const auto& filter = model::tally_filters[index]; // Check the filter type - if (filter->type() != "mesh" && filter->type() != "meshsurface") { + if (filter->type() != FilterType::MESH && + filter->type() != FilterType::MESHBORN && + filter->type() != FilterType::MESH_SURFACE) { set_errmsg("Tried to set mesh on a non-mesh-based filter."); return OPENMC_E_INVALID_TYPE; } diff --git a/src/tallies/filter_meshborn.cpp b/src/tallies/filter_meshborn.cpp new file mode 100644 index 0000000000..c95dc3dc78 --- /dev/null +++ b/src/tallies/filter_meshborn.cpp @@ -0,0 +1,61 @@ +#include "openmc/tallies/filter_meshborn.h" + +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/mesh.h" + +namespace openmc { + +void MeshBornFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + Position r_born = p.r_born(); + + // apply translation if present + if (translated_) { + r_born -= translation(); + } + + auto bin = model::meshes[mesh_]->get_bin(r_born); + if (bin >= 0) { + match.bins_.push_back(bin); + match.weights_.push_back(1.0); + } +} + +std::string MeshBornFilter::text_label(int bin) const +{ + auto& mesh = *model::meshes.at(mesh_); + return mesh.bin_label(bin) + " (born)"; +} + +//============================================================================== +// C-API functions +//============================================================================== + +extern "C" int openmc_meshborn_filter_get_mesh( + int32_t index, int32_t* index_mesh) +{ + return openmc_mesh_filter_get_mesh(index, index_mesh); +} + +extern "C" int openmc_meshborn_filter_set_mesh( + int32_t index, int32_t index_mesh) +{ + return openmc_mesh_filter_set_mesh(index, index_mesh); +} + +extern "C" int openmc_meshborn_filter_get_translation( + int32_t index, double translation[3]) +{ + return openmc_mesh_filter_get_translation(index, translation); +} + +extern "C" int openmc_meshborn_filter_set_translation( + int32_t index, double translation[3]) +{ + return openmc_mesh_filter_set_translation(index, translation); +} + +} // namespace openmc diff --git a/src/tallies/filter_meshmaterial.cpp b/src/tallies/filter_meshmaterial.cpp new file mode 100644 index 0000000000..6e1f30380f --- /dev/null +++ b/src/tallies/filter_meshmaterial.cpp @@ -0,0 +1,185 @@ +#include "openmc/tallies/filter_meshmaterial.h" + +#include // for move + +#include + +#include "openmc/capi.h" +#include "openmc/constants.h" +#include "openmc/container_util.h" +#include "openmc/error.h" +#include "openmc/material.h" +#include "openmc/mesh.h" +#include "openmc/xml_interface.h" + +namespace openmc { + +void MeshMaterialFilter::from_xml(pugi::xml_node node) +{ + // Get mesh ID + auto mesh = get_node_array(node, "mesh"); + if (mesh.size() != 1) { + fatal_error( + "Only one mesh can be specified per " + type_str() + " mesh filter."); + } + + auto id = mesh[0]; + auto search = model::mesh_map.find(id); + if (search == model::mesh_map.end()) { + fatal_error( + fmt::format("Could not find mesh {} specified on tally filter.", id)); + } + set_mesh(search->second); + + // Get pairs of (element index, material) and set the bins + auto bins = get_node_array(node, "bins"); + this->set_bins(bins); + + if (check_for_node(node, "translation")) { + set_translation(get_node_array(node, "translation")); + } +} + +void MeshMaterialFilter::set_bins(span bins) +{ + if (bins.size() % 2 != 0) { + fatal_error( + fmt::format("Size of mesh material bins is not even: {}", bins.size())); + } + + // Create a vector of ElementMat pairs from the flat vector of bins + vector element_mats; + for (int64_t i = 0; i < bins.size() / 2; ++i) { + int32_t element = bins[2 * i]; + int32_t mat_id = bins[2 * i + 1]; + auto search = model::material_map.find(mat_id); + if (search == model::material_map.end()) { + fatal_error(fmt::format( + "Could not find material {} specified on tally filter.", mat_id)); + } + int32_t mat_index = search->second; + element_mats.push_back({element, mat_index}); + } + + this->set_bins(std::move(element_mats)); +} + +void MeshMaterialFilter::set_bins(vector&& bins) +{ + // Swap internal bins_ with the provided vector to avoid copying + bins_.swap(bins); + + // Clear and update the mapping and vector of materials + materials_.clear(); + map_.clear(); + for (std::size_t i = 0; i < bins_.size(); ++i) { + const auto& x = bins_[i]; + assert(x.index_mat >= 0); + assert(x.index_mat < model::materials.size()); + materials_.insert(x.index_mat); + map_[x] = i; + } + + n_bins_ = bins_.size(); +} + +void MeshMaterialFilter::set_mesh(int32_t mesh) +{ + // perform any additional perparation for mesh tallies here + mesh_ = mesh; + model::meshes[mesh_]->prepare_for_point_location(); +} + +void MeshMaterialFilter::set_translation(const Position& translation) +{ + translated_ = true; + translation_ = translation; +} + +void MeshMaterialFilter::set_translation(const double translation[3]) +{ + this->set_translation({translation[0], translation[1], translation[2]}); +} + +void MeshMaterialFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + // If current material is not in any bins, don't bother checking + if (!contains(materials_, p.material())) { + return; + } + + Position last_r = p.r_last(); + Position r = p.r(); + Position u = p.u(); + + // apply translation if present + if (translated_) { + last_r -= translation(); + r -= translation(); + } + + if (estimator != TallyEstimator::TRACKLENGTH) { + int32_t index_element = model::meshes[mesh_]->get_bin(r); + if (index_element >= 0) { + auto search = map_.find({index_element, p.material()}); + if (search != map_.end()) { + match.bins_.push_back(search->second); + match.weights_.push_back(1.0); + } + } + } else { + // First determine which elements the particle crosses (may or may not + // actually match bins so we have to adjust bins_/weight_ after) + int32_t n_start = match.bins_.size(); + model::meshes[mesh_]->bins_crossed( + last_r, r, u, match.bins_, match.weights_); + int32_t n_end = match.bins_.size(); + + // Go through bins and weights and check which ones are actually a match + // based on the (element, material) pair. For matches, overwrite the bin. + int i = 0; + for (int j = n_start; j < n_end; ++j) { + int32_t index_element = match.bins_[j]; + double weight = match.weights_[j]; + auto search = map_.find({index_element, p.material()}); + if (search != map_.end()) { + match.bins_[n_start + i] = search->second; + match.weights_[n_start + i] = weight; + ++i; + } + } + + // Resize the vectors to remove the unmatched bins + match.bins_.resize(n_start + i); + } +} + +void MeshMaterialFilter::to_statepoint(hid_t filter_group) const +{ + Filter::to_statepoint(filter_group); + write_dataset(filter_group, "mesh", model::meshes[mesh_]->id_); + + size_t n = bins_.size(); + xt::xtensor data({n, 2}); + for (int64_t i = 0; i < n; ++i) { + const auto& x = bins_[i]; + data(i, 0) = x.index_element; + data(i, 1) = model::materials[x.index_mat]->id_; + } + write_dataset(filter_group, "bins", data); + + if (translated_) { + write_dataset(filter_group, "translation", translation_); + } +} + +std::string MeshMaterialFilter::text_label(int bin) const +{ + auto& x = bins_[bin]; + auto& mesh = *model::meshes.at(mesh_); + return fmt::format("Mesh {}, {}, Material {}", mesh.id(), + mesh.bin_label(x.index_element), model::materials[x.index_mat]->id_); +} + +} // namespace openmc diff --git a/src/tallies/filter_meshsurface.cpp b/src/tallies/filter_meshsurface.cpp index b22085ebbf..b26cd198b3 100644 --- a/src/tallies/filter_meshsurface.cpp +++ b/src/tallies/filter_meshsurface.cpp @@ -100,4 +100,16 @@ extern "C" int openmc_meshsurface_filter_set_mesh( return openmc_mesh_filter_set_mesh(index, index_mesh); } +extern "C" int openmc_meshsurface_filter_get_translation( + int32_t index, double translation[3]) +{ + return openmc_mesh_filter_get_translation(index, translation); +} + +extern "C" int openmc_meshsurface_filter_set_translation( + int32_t index, double translation[3]) +{ + return openmc_mesh_filter_set_translation(index, translation); +} + } // namespace openmc diff --git a/src/tallies/filter_mu.cpp b/src/tallies/filter_mu.cpp index 95bb3b2108..63915a5334 100644 --- a/src/tallies/filter_mu.cpp +++ b/src/tallies/filter_mu.cpp @@ -31,14 +31,14 @@ void MuFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void MuFilter::set_bins(gsl::span bins) +void MuFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); bins_.reserve(bins.size()); // Copy bins, ensuring they are valid - for (gsl::index i = 0; i < bins.size(); ++i) { + for (int64_t i = 0; i < bins.size(); ++i) { if (i > 0 && bins[i] <= bins[i - 1]) { throw std::runtime_error {"Mu bins must be monotonically increasing."}; } diff --git a/src/tallies/filter_musurface.cpp b/src/tallies/filter_musurface.cpp new file mode 100644 index 0000000000..340149d4cf --- /dev/null +++ b/src/tallies/filter_musurface.cpp @@ -0,0 +1,36 @@ +#include "openmc/tallies/filter_musurface.h" + +#include // for abs, copysign + +#include "openmc/search.h" +#include "openmc/surface.h" +#include "openmc/tallies/tally_scoring.h" + +namespace openmc { + +void MuSurfaceFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + // Get surface normal (and make sure it is a unit vector) + const auto surf {model::surfaces[p.surface_index()].get()}; + auto n = surf->normal(p.r()); + n /= n.norm(); + + // Determine whether normal should be pointing in or out + if (p.surface() < 0) + n *= -1; + + // Determine cosine of angle between normal and particle direction + double mu = p.u().dot(n); + if (std::abs(mu) > 1.0) + mu = std::copysign(1.0, mu); + + // Find matching bin + if (mu >= bins_.front() && mu <= bins_.back()) { + auto bin = lower_bound_index(bins_.begin(), bins_.end(), mu); + match.bins_.push_back(bin); + match.weights_.push_back(1.0); + } +} + +} // namespace openmc diff --git a/src/tallies/filter_parent_nuclide.cpp b/src/tallies/filter_parent_nuclide.cpp new file mode 100644 index 0000000000..d049410752 --- /dev/null +++ b/src/tallies/filter_parent_nuclide.cpp @@ -0,0 +1,79 @@ +#include "openmc/tallies/filter_parent_nuclide.h" + +#include // for int64_t + +#include + +#include "openmc/capi.h" +#include "openmc/chain.h" +#include "openmc/search.h" +#include "openmc/settings.h" +#include "openmc/xml_interface.h" + +namespace openmc { + +//============================================================================== +// ParentNuclideFilter implementation +//============================================================================== + +void ParentNuclideFilter::from_xml(pugi::xml_node node) +{ + nuclides_ = get_node_array(node, "bins"); + + // Convert nuclides to indices in data::chain_nuclides + std::vector bins; + for (const auto& nuclide : nuclides_) { + auto it = data::chain_nuclide_map.find(nuclide); + if (it != data::chain_nuclide_map.end()) { + bins.push_back(it->second); + } else { + // The default value of parent_nuclide is -1, so to prevent a score to + // this bin assign the value -2. + bins.push_back(-2); + } + } + this->set_bins(bins); +} + +void ParentNuclideFilter::set_bins(span bins) +{ + // Clear existing bins + bins_.clear(); + bins_.reserve(bins.size()); + map_.clear(); + + // Set bins based on chain nuclide indexing + for (int64_t i = 0; i < bins.size(); ++i) { + bins_.push_back(bins[i]); + map_[bins[i]] = i; + } + + n_bins_ = bins_.size(); +} + +void ParentNuclideFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + // Get the particle's parent nuclide + int parent_nuclide = p.parent_nuclide(); + + // Find bin matching parent nuclide + auto search = map_.find(parent_nuclide); + if (search != map_.end()) { + match.bins_.push_back(search->second); + match.weights_.push_back(1.0); + } +} + +void ParentNuclideFilter::to_statepoint(hid_t filter_group) const +{ + Filter::to_statepoint(filter_group); + write_dataset(filter_group, "bins", nuclides_); +} + +std::string ParentNuclideFilter::text_label(int bin) const +{ + return fmt::format("Parent Nuclide {}", nuclides_[bin]); +} + +} // namespace openmc diff --git a/src/tallies/filter_particle.cpp b/src/tallies/filter_particle.cpp index 3865824f67..eef1d1e63c 100644 --- a/src/tallies/filter_particle.cpp +++ b/src/tallies/filter_particle.cpp @@ -18,7 +18,7 @@ void ParticleFilter::from_xml(pugi::xml_node node) this->set_particles(types); } -void ParticleFilter::set_particles(gsl::span particles) +void ParticleFilter::set_particles(span particles) { // Clear existing particles particles_.clear(); @@ -58,4 +58,23 @@ std::string ParticleFilter::text_label(int bin) const return fmt::format("Particle: {}", particle_type_to_str(p)); } +extern "C" int openmc_particle_filter_get_bins(int32_t idx, int bins[]) +{ + if (int err = verify_filter(idx)) + return err; + + const auto& f = model::tally_filters[idx]; + auto pf = dynamic_cast(f.get()); + if (pf) { + const auto& particles = pf->particles(); + for (int i = 0; i < particles.size(); i++) { + bins[i] = static_cast(particles[i]); + } + } else { + set_errmsg("The filter at the specified index is not a ParticleFilter"); + return OPENMC_E_INVALID_ARGUMENT; + } + return 0; +} + } // namespace openmc diff --git a/src/tallies/filter_polar.cpp b/src/tallies/filter_polar.cpp index d132ccf423..29be6a437e 100644 --- a/src/tallies/filter_polar.cpp +++ b/src/tallies/filter_polar.cpp @@ -32,14 +32,14 @@ void PolarFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void PolarFilter::set_bins(gsl::span bins) +void PolarFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); bins_.reserve(bins.size()); // Copy bins, ensuring they are valid - for (gsl::index i = 0; i < bins.size(); ++i) { + for (int64_t i = 0; i < bins.size(); ++i) { if (i > 0 && bins[i] <= bins[i - 1]) { throw std::runtime_error {"Polar bins must be monotonically increasing."}; } diff --git a/src/tallies/filter_sph_harm.cpp b/src/tallies/filter_sph_harm.cpp index 359df379b2..1989e6ef15 100644 --- a/src/tallies/filter_sph_harm.cpp +++ b/src/tallies/filter_sph_harm.cpp @@ -1,9 +1,9 @@ #include "openmc/tallies/filter_sph_harm.h" +#include #include // For pair #include -#include #include "openmc/capi.h" #include "openmc/error.h" @@ -25,12 +25,15 @@ void SphericalHarmonicsFilter::set_order(int order) if (order < 0) { throw std::invalid_argument { "Spherical harmonics order must be non-negative."}; + } else if (order > 10) { + throw std::invalid_argument {"Spherical harmonics orders greater than 10 " + "are currently not supported!"}; } order_ = order; n_bins_ = (order_ + 1) * (order_ + 1); } -void SphericalHarmonicsFilter::set_cosine(gsl::cstring_span cosine) +void SphericalHarmonicsFilter::set_cosine(const std::string& cosine) { if (cosine == "scatter") { cosine_ = SphericalHarmonicsCosine::scatter; @@ -39,7 +42,7 @@ void SphericalHarmonicsFilter::set_cosine(gsl::cstring_span cosine) } else { throw std::invalid_argument {fmt::format("Unrecognized cosine type, \"{}\" " "in spherical harmonics filter", - gsl::to_string(cosine))}; + cosine)}; } } @@ -87,7 +90,7 @@ void SphericalHarmonicsFilter::to_statepoint(hid_t filter_group) const std::string SphericalHarmonicsFilter::text_label(int bin) const { - Expects(bin >= 0 && bin < n_bins_); + assert(bin >= 0 && bin < n_bins_); for (int n = 0; n < order_ + 1; n++) { if (bin < (n + 1) * (n + 1)) { int m = (bin - n * n) - n; diff --git a/src/tallies/filter_surface.cpp b/src/tallies/filter_surface.cpp index a84d177282..82f3d71789 100644 --- a/src/tallies/filter_surface.cpp +++ b/src/tallies/filter_surface.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_surface.h" +#include + #include #include "openmc/error.h" @@ -26,7 +28,7 @@ void SurfaceFilter::from_xml(pugi::xml_node node) this->set_surfaces(surfaces); } -void SurfaceFilter::set_surfaces(gsl::span surfaces) +void SurfaceFilter::set_surfaces(span surfaces) { // Clear existing surfaces surfaces_.clear(); @@ -35,8 +37,8 @@ void SurfaceFilter::set_surfaces(gsl::span surfaces) // Update surfaces and mapping for (auto& index : surfaces) { - Expects(index >= 0); - Expects(index < model::surfaces.size()); + assert(index >= 0); + assert(index < model::surfaces.size()); surfaces_.push_back(index); map_[index] = surfaces_.size() - 1; } @@ -47,7 +49,7 @@ void SurfaceFilter::set_surfaces(gsl::span surfaces) void SurfaceFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { - auto search = map_.find(std::abs(p.surface()) - 1); + auto search = map_.find(p.surface_index()); if (search != map_.end()) { match.bins_.push_back(search->second); if (p.surface() < 0) { diff --git a/src/tallies/filter_time.cpp b/src/tallies/filter_time.cpp index 3f78d1817f..948d1347a0 100644 --- a/src/tallies/filter_time.cpp +++ b/src/tallies/filter_time.cpp @@ -21,7 +21,7 @@ void TimeFilter::from_xml(pugi::xml_node node) this->set_bins(bins); } -void TimeFilter::set_bins(gsl::span bins) +void TimeFilter::set_bins(span bins) { // Clear existing bins bins_.clear(); @@ -55,13 +55,16 @@ void TimeFilter::get_all_bins( // the current track and find where it overlaps with time bins and score // accordingly - // Skip if time interval is zero - if (t_start == t_end) - return; - // Determine first bin containing a portion of time interval auto i_bin = lower_bound_index(bins_.begin(), bins_.end(), t_start); + // If time interval is zero, add a match corresponding to the starting time + if (t_end == t_start) { + match.bins_.push_back(i_bin); + match.weights_.push_back(1.0); + return; + } + // Find matching bins double dt_total = t_end - t_start; for (; i_bin < bins_.size() - 1; ++i_bin) { @@ -77,10 +80,11 @@ void TimeFilter::get_all_bins( if (t_end < bins_[i_bin + 1]) break; } - } else { + } else if (t_end < bins_.back()) { // ------------------------------------------------------------------------- // For collision estimator or surface tallies, find a match based on the // exact time of the particle + const auto i_bin = lower_bound_index(bins_.begin(), bins_.end(), t_end); match.bins_.push_back(i_bin); match.weights_.push_back(1.0); diff --git a/src/tallies/filter_universe.cpp b/src/tallies/filter_universe.cpp index 96ad0212d5..f4b22decd5 100644 --- a/src/tallies/filter_universe.cpp +++ b/src/tallies/filter_universe.cpp @@ -1,5 +1,7 @@ #include "openmc/tallies/filter_universe.h" +#include + #include #include "openmc/cell.h" @@ -24,7 +26,7 @@ void UniverseFilter::from_xml(pugi::xml_node node) this->set_universes(universes); } -void UniverseFilter::set_universes(gsl::span universes) +void UniverseFilter::set_universes(span universes) { // Clear existing universes universes_.clear(); @@ -33,8 +35,8 @@ void UniverseFilter::set_universes(gsl::span universes) // Update universes and mapping for (auto& index : universes) { - Expects(index >= 0); - Expects(index < model::universes.size()); + assert(index >= 0); + assert(index < model::universes.size()); universes_.push_back(index); map_[index] = universes_.size() - 1; } @@ -46,7 +48,7 @@ void UniverseFilter::get_all_bins( const Particle& p, TallyEstimator estimator, FilterMatch& match) const { for (int i = 0; i < p.n_coord(); i++) { - auto search = map_.find(p.coord(i).universe); + auto search = map_.find(p.coord(i).universe()); if (search != map_.end()) { match.bins_.push_back(search->second); match.weights_.push_back(1.0); diff --git a/src/tallies/filter_weight.cpp b/src/tallies/filter_weight.cpp new file mode 100644 index 0000000000..31f4bd1bf7 --- /dev/null +++ b/src/tallies/filter_weight.cpp @@ -0,0 +1,63 @@ +#include "openmc/tallies/filter_weight.h" + +#include // for is_sorted +#include // for runtime_error + +#include + +#include "openmc/search.h" +#include "openmc/xml_interface.h" + +namespace openmc { + +//============================================================================== +// WeightFilter implementation +//============================================================================== + +void WeightFilter::from_xml(pugi::xml_node node) +{ + auto bins = get_node_array(node, "bins"); + this->set_bins(bins); +} + +void WeightFilter::set_bins(span bins) +{ + if (!std::is_sorted(bins.begin(), bins.end())) { + throw std::runtime_error {"Weight bins must be monotonically increasing."}; + } + + // Clear existing bins + bins_.clear(); + bins_.reserve(bins.size()); + + // Copy bins + bins_.insert(bins_.end(), bins.begin(), bins.end()); + n_bins_ = bins_.size() - 1; +} + +void WeightFilter::get_all_bins( + const Particle& p, TallyEstimator estimator, FilterMatch& match) const +{ + // Get particle weight + double wgt = p.wgt_last(); + + // Bin the weight + if (wgt >= bins_.front() && wgt <= bins_.back()) { + auto bin = lower_bound_index(bins_.begin(), bins_.end(), wgt); + match.bins_.push_back(bin); + match.weights_.push_back(1.0); + } +} + +void WeightFilter::to_statepoint(hid_t filter_group) const +{ + Filter::to_statepoint(filter_group); + write_dataset(filter_group, "bins", bins_); +} + +std::string WeightFilter::text_label(int bin) const +{ + return fmt::format("Weight [{}, {}]", bins_[bin], bins_[bin + 1]); +} + +} // namespace openmc diff --git a/src/tallies/filter_zernike.cpp b/src/tallies/filter_zernike.cpp index eb4c8bdfd0..af5b595aab 100644 --- a/src/tallies/filter_zernike.cpp +++ b/src/tallies/filter_zernike.cpp @@ -1,11 +1,11 @@ #include "openmc/tallies/filter_zernike.h" +#include #include #include #include // For pair #include -#include #include "openmc/capi.h" #include "openmc/error.h" @@ -57,7 +57,7 @@ void ZernikeFilter::to_statepoint(hid_t filter_group) const std::string ZernikeFilter::text_label(int bin) const { - Expects(bin >= 0 && bin < n_bins_); + assert(bin >= 0 && bin < n_bins_); for (int n = 0; n < order_ + 1; n++) { int last = (n + 1) * (n + 2) / 2; if (bin < last) { diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 2d673d7bcc..b9c615ecb5 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -3,6 +3,7 @@ #include "openmc/array.h" #include "openmc/capi.h" #include "openmc/constants.h" +#include "openmc/container_util.h" #include "openmc/error.h" #include "openmc/file_utils.h" #include "openmc/mesh.h" @@ -18,16 +19,20 @@ #include "openmc/tallies/derivative.h" #include "openmc/tallies/filter.h" #include "openmc/tallies/filter_cell.h" +#include "openmc/tallies/filter_cellborn.h" #include "openmc/tallies/filter_cellfrom.h" #include "openmc/tallies/filter_collision.h" #include "openmc/tallies/filter_delayedgroup.h" #include "openmc/tallies/filter_energy.h" #include "openmc/tallies/filter_legendre.h" #include "openmc/tallies/filter_mesh.h" +#include "openmc/tallies/filter_meshborn.h" +#include "openmc/tallies/filter_meshmaterial.h" #include "openmc/tallies/filter_meshsurface.h" #include "openmc/tallies/filter_particle.h" #include "openmc/tallies/filter_sph_harm.h" #include "openmc/tallies/filter_surface.h" +#include "openmc/tallies/filter_time.h" #include "openmc/xml_interface.h" #include "xtensor/xadapt.hpp" @@ -35,8 +40,10 @@ #include "xtensor/xview.hpp" #include -#include // for max -#include // for size_t +#include // for max, set_union +#include +#include // for size_t +#include // for back_inserter #include namespace openmc { @@ -46,14 +53,19 @@ namespace openmc { //============================================================================== namespace model { +//! a mapping of tally ID to index in the tallies vector std::unordered_map tally_map; vector> tallies; vector active_tallies; vector active_analog_tallies; vector active_tracklength_tallies; +vector active_timed_tracklength_tallies; vector active_collision_tallies; vector active_meshsurf_tallies; vector active_surface_tallies; +vector active_pulse_height_tallies; +vector pulse_height_cells; +vector time_grid; } // namespace model namespace simulation { @@ -91,6 +103,10 @@ Tally::Tally(pugi::xml_node node) if (check_for_node(node, "name")) name_ = get_node_value(node, "name"); + if (check_for_node(node, "multiply_density")) { + multiply_density_ = get_node_value_bool(node, "multiply_density"); + } + // ======================================================================= // READ DATA FOR FILTERS @@ -130,7 +146,7 @@ Tally::Tally(pugi::xml_node node) // Check for the presence of certain filter types bool has_energyout = energyout_filter_ >= 0; int particle_filter_index = C_NONE; - for (gsl::index j = 0; j < filters_.size(); ++j) { + for (int64_t j = 0; j < filters_.size(); ++j) { int i_filter = filters_[j]; const auto& f = model::tally_filters[i_filter].get(); @@ -139,16 +155,18 @@ Tally::Tally(pugi::xml_node node) particle_filter_index = i_filter; // Change the tally estimator if a filter demands it - std::string filt_type = f->type(); - if (filt_type == "energyout" || filt_type == "legendre") { + FilterType filt_type = f->type(); + if (filt_type == FilterType::ENERGY_OUT || + filt_type == FilterType::LEGENDRE) { estimator_ = TallyEstimator::ANALOG; - } else if (filt_type == "sphericalharmonics") { + } else if (filt_type == FilterType::SPHERICAL_HARMONICS) { auto sf = dynamic_cast(f); if (sf->cosine() == SphericalHarmonicsCosine::scatter) { estimator_ = TallyEstimator::ANALOG; } - } else if (filt_type == "spatiallegendre" || filt_type == "zernike" || - filt_type == "zernikeradial") { + } else if (filt_type == FilterType::SPATIAL_LEGENDRE || + filt_type == FilterType::ZERNIKE || + filt_type == FilterType::ZERNIKE_RADIAL) { estimator_ = TallyEstimator::COLLISION; } } @@ -167,7 +185,75 @@ Tally::Tally(pugi::xml_node node) fatal_error(fmt::format("No scores specified on tally {}.", id_)); } + // Set IFP if needed + if (!settings::ifp_on) { + // Determine if this tally has an IFP score + bool has_ifp_score = false; + for (int score : scores_) { + if (score == SCORE_IFP_TIME_NUM || score == SCORE_IFP_BETA_NUM || + score == SCORE_IFP_DENOM) { + has_ifp_score = true; + break; + } + } + + // Check for errors + if (has_ifp_score) { + if (settings::run_mode == RunMode::EIGENVALUE) { + if (settings::ifp_n_generation < 0) { + settings::ifp_n_generation = DEFAULT_IFP_N_GENERATION; + warning(fmt::format( + "{} generations will be used for IFP (default value). It can be " + "changed using the 'ifp_n_generation' settings.", + settings::ifp_n_generation)); + } + if (settings::ifp_n_generation > settings::n_inactive) { + fatal_error("'ifp_n_generation' must be lower than or equal to the " + "number of inactive cycles."); + } + settings::ifp_on = true; + } else { + fatal_error( + "Iterated Fission Probability can only be used in an eigenvalue " + "calculation."); + } + } + } + + // Set IFP parameters if needed + if (settings::ifp_on) { + for (int score : scores_) { + switch (score) { + case SCORE_IFP_TIME_NUM: + if (settings::ifp_parameter == IFPParameter::None) { + settings::ifp_parameter = IFPParameter::GenerationTime; + } else if (settings::ifp_parameter == IFPParameter::BetaEffective) { + settings::ifp_parameter = IFPParameter::Both; + } + break; + case SCORE_IFP_BETA_NUM: + case SCORE_IFP_DENOM: + if (settings::ifp_parameter == IFPParameter::None) { + settings::ifp_parameter = IFPParameter::BetaEffective; + } else if (settings::ifp_parameter == IFPParameter::GenerationTime) { + settings::ifp_parameter = IFPParameter::Both; + } + break; + } + } + } + // Check if tally is compatible with particle type + if (!settings::photon_transport) { + for (int score : scores_) { + switch (score) { + case SCORE_PULSE_HEIGHT: + fatal_error("For pulse-height tallies, photon transport needs to be " + "activated."); + break; + } + } + } if (settings::photon_transport) { if (particle_filter_index == C_NONE) { for (int score : scores_) { @@ -188,9 +274,9 @@ Tally::Tally(pugi::xml_node node) case SCORE_DELAYED_NU_FISSION: case SCORE_PROMPT_NU_FISSION: case SCORE_DECAY_RATE: - warning("Particle filter is not used with photon transport" - " on and " + - reaction_name(score) + " score."); + warning("You are tallying the '" + reaction_name(score) + + "' score and haven't used a particle filter. This score will " + "include contributions from all particles."); break; } } @@ -237,7 +323,8 @@ Tally::Tally(pugi::xml_node node) if (has_energyout && i_nuc == -1) { fatal_error(fmt::format( "Error on tally {}: Cannot use a " - "'nuclide_density' or 'temperature' derivative on a tally with an " + "'nuclide_density' or 'temperature' derivative on a tally with " + "an " "outgoing energy filter and 'total' nuclide rate. Instead, tally " "each nuclide in the material individually.", id_)); @@ -295,7 +382,7 @@ Tally::Tally(pugi::xml_node node) } } -#ifdef LIBMESH +#ifdef OPENMC_LIBMESH_ENABLED // ensure a tracklength tally isn't used with a libMesh filter for (auto i : this->filters_) { auto df = dynamic_cast(model::tally_filters[i].get()); @@ -323,7 +410,7 @@ Tally* Tally::create(int32_t id) void Tally::set_id(int32_t id) { - Expects(id >= 0 || id == C_NONE); + assert(id >= 0 || id == C_NONE); // Clear entry in tally map if an ID was already assigned before if (id_ != C_NONE) { @@ -351,7 +438,35 @@ void Tally::set_id(int32_t id) model::tally_map[id] = index_; } -void Tally::set_filters(gsl::span filters) +std::vector Tally::filter_types() const +{ + std::vector filter_types; + for (auto idx : this->filters()) + filter_types.push_back(model::tally_filters[idx]->type()); + return filter_types; +} + +std::unordered_map Tally::filter_indices() const +{ + std::unordered_map filter_indices; + for (int i = 0; i < this->filters().size(); i++) { + const auto& f = model::tally_filters[this->filters(i)]; + + filter_indices[f->type()] = i; + } + return filter_indices; +} + +bool Tally::has_filter(FilterType filter_type) const +{ + for (auto idx : this->filters()) { + if (model::tally_filters[idx]->type() == filter_type) + return true; + } + return false; +} + +void Tally::set_filters(span filters) { // Clear old data. filters_.clear(); @@ -361,28 +476,32 @@ void Tally::set_filters(gsl::span filters) auto n = filters.size(); filters_.reserve(n); - for (int i = 0; i < n; ++i) { - // Add index to vector of filters - auto& f {filters[i]}; - filters_.push_back(model::filter_map.at(f->id())); - - // Keep track of indices for special filters. - if (dynamic_cast(f)) { - energyout_filter_ = i; - } else if (dynamic_cast(f)) { - delayedgroup_filter_ = i; - } + for (auto* filter : filters) { + add_filter(filter); } +} - // Set the strides. - set_strides(); +void Tally::add_filter(Filter* filter) +{ + int32_t filter_idx = model::filter_map.at(filter->id()); + // if this filter is already present, do nothing and return + if (std::find(filters_.begin(), filters_.end(), filter_idx) != filters_.end()) + return; + + // Keep track of indices for special filters + if (filter->type() == FilterType::ENERGY_OUT) { + energyout_filter_ = filters_.size(); + } else if (filter->type() == FilterType::DELAYED_GROUP) { + delayedgroup_filter_ = filters_.size(); + } + filters_.push_back(filter_idx); } void Tally::set_strides() { - // Set the strides. Filters are traversed in reverse so that the last filter - // has the shortest stride in memory and the first filter has the longest - // stride. + // Set the strides. Filters are traversed in reverse so that the last + // filter has the shortest stride in memory and the first filter has the + // longest stride. auto n = filters_.size(); strides_.resize(n, 0); int stride = 1; @@ -415,26 +534,34 @@ void Tally::set_scores(const vector& scores) bool cellfrom_present = false; bool surface_present = false; bool meshsurface_present = false; + bool non_cell_energy_present = false; for (auto i_filt : filters_) { const auto* filt {model::tally_filters[i_filt].get()}; - if (dynamic_cast(filt)) { + // Checking for only cell and energy filters for pulse-height tally + if (!(filt->type() == FilterType::CELL || + filt->type() == FilterType::ENERGY)) { + non_cell_energy_present = true; + } + if (filt->type() == FilterType::LEGENDRE) { legendre_present = true; - } else if (dynamic_cast(filt)) { + } else if (filt->type() == FilterType::CELLFROM) { cellfrom_present = true; - } else if (dynamic_cast(filt)) { + } else if (filt->type() == FilterType::CELL) { cell_present = true; - } else if (dynamic_cast(filt)) { + } else if (filt->type() == FilterType::SURFACE) { surface_present = true; - } else if (dynamic_cast(filt)) { + } else if (filt->type() == FilterType::MESH_SURFACE) { meshsurface_present = true; } } // Iterate over the given scores. for (auto score_str : scores) { - // Make sure a delayed group filter wasn't used with an incompatible score. + // Make sure a delayed group filter wasn't used with an incompatible + // score. if (delayedgroup_filter_ != C_NONE) { - if (score_str != "delayed-nu-fission" && score_str != "decay-rate") + if (score_str != "delayed-nu-fission" && score_str != "decay-rate" && + score_str != "ifp-beta-numerator") fatal_error("Cannot tally " + score_str + "with a delayedgroup filter"); } @@ -497,6 +624,36 @@ void Tally::set_scores(const vector& scores) if (settings::photon_transport) estimator_ = TallyEstimator::COLLISION; break; + + case SCORE_PULSE_HEIGHT: + if (non_cell_energy_present) { + fatal_error("Pulse-height tallies are not compatible with filters " + "other than CellFilter and EnergyFilter"); + } + type_ = TallyType::PULSE_HEIGHT; + + // Collecting indices of all cells covered by the filters in the pulse + // height tally in global variable pulse_height_cells + for (const auto& i_filt : filters_) { + auto cell_filter = + dynamic_cast(model::tally_filters[i_filt].get()); + if (cell_filter) { + const auto& cells = cell_filter->cells(); + for (int i = 0; i < cell_filter->n_bins(); i++) { + int cell_index = cells[i]; + if (!contains(model::pulse_height_cells, cell_index)) { + model::pulse_height_cells.push_back(cell_index); + } + } + } + } + break; + + case SCORE_IFP_TIME_NUM: + case SCORE_IFP_BETA_NUM: + case SCORE_IFP_DENOM: + estimator_ = TallyEstimator::COLLISION; + break; } scores_.push_back(score); @@ -542,22 +699,10 @@ void Tally::set_nuclides(pugi::xml_node node) return; } - if (get_node_value(node, "nuclides") == "all") { - // This tally should bin every nuclide in the problem. It should also bin - // the total material rates. To achieve this, set the nuclides_ vector to - // 0, 1, 2, ..., -1. - nuclides_.reserve(data::nuclides.size() + 1); - for (auto i = 0; i < data::nuclides.size(); ++i) - nuclides_.push_back(i); - nuclides_.push_back(-1); - all_nuclides_ = true; - - } else { - // The user provided specifics nuclides. Parse it as an array with either - // "total" or a nuclide name like "U-235" in each position. - auto words = get_node_array(node, "nuclides"); - this->set_nuclides(words); - } + // The user provided specifics nuclides. Parse it as an array with either + // "total" or a nuclide name like "U235" in each position. + auto words = get_node_array(node, "nuclides"); + this->set_nuclides(words); } void Tally::set_nuclides(const vector& nuclides) @@ -569,11 +714,12 @@ void Tally::set_nuclides(const vector& nuclides) nuclides_.push_back(-1); } else { auto search = data::nuclide_map.find(nuc); - if (search == data::nuclide_map.end()) - fatal_error(fmt::format("Could not find the nuclide {} specified in " - "tally {} in any material", - nuc, id_)); - nuclides_.push_back(search->second); + if (search == data::nuclide_map.end()) { + int err = openmc_load_nuclide(nuc.c_str(), nullptr, 0); + if (err < 0) + throw std::runtime_error {openmc_err_msg}; + } + nuclides_.push_back(data::nuclide_map.at(nuc)); } } } @@ -612,6 +758,12 @@ void Tally::init_triggers(pugi::xml_node node) "Must specify trigger threshold for tally {} in tally XML file", id_)); } + // Read whether to allow zero-tally bins to be ignored. + bool ignore_zeros = false; + if (check_for_node(trigger_node, "ignore_zeros")) { + ignore_zeros = get_node_value_bool(trigger_node, "ignore_zeros"); + } + // Read the trigger scores. vector trigger_scores; if (check_for_node(trigger_node, "scores")) { @@ -625,12 +777,12 @@ void Tally::init_triggers(pugi::xml_node node) if (score_str == "all") { triggers_.reserve(triggers_.size() + this->scores_.size()); for (auto i_score = 0; i_score < this->scores_.size(); ++i_score) { - triggers_.push_back({metric, threshold, i_score}); + triggers_.push_back({metric, threshold, ignore_zeros, i_score}); } } else { int i_score = 0; for (; i_score < this->scores_.size(); ++i_score) { - if (reaction_name(this->scores_[i_score]) == score_str) + if (this->scores_[i_score] == reaction_type(score_str)) break; } if (i_score == this->scores_.size()) { @@ -639,7 +791,7 @@ void Tally::init_triggers(pugi::xml_node node) "{} but it was listed in a trigger on that tally", score_str, id_)); } - triggers_.push_back({metric, threshold, i_score}); + triggers_.push_back({metric, threshold, ignore_zeros, i_score}); } } } @@ -668,9 +820,7 @@ void Tally::accumulate() // Calculate total source strength for normalization double total_source = 0.0; if (settings::run_mode == RunMode::FIXED_SOURCE) { - for (const auto& s : model::external_sources) { - total_source += s->strength(); - } + total_source = model::external_sources_probability.integral(); } else { total_source = 1.0; } @@ -679,6 +829,10 @@ void Tally::accumulate() double norm = total_source / (settings::n_particles * settings::gen_per_batch); + if (settings::solver_type == SolverType::RANDOM_RAY) { + norm = 1.0; + } + // Accumulate each result #pragma omp parallel for for (int i = 0; i < results_.shape()[0]; ++i) { @@ -692,6 +846,31 @@ void Tally::accumulate() } } +int Tally::score_index(const std::string& score) const +{ + for (int i = 0; i < scores_.size(); i++) { + if (this->score_name(i) == score) + return i; + } + return -1; +} + +xt::xarray Tally::get_reshaped_data() const +{ + std::vector shape; + for (auto f : filters()) { + shape.push_back(model::tally_filters[f]->n_bins()); + } + + // add number of scores and nuclides to tally + shape.push_back(results_.shape()[1]); + shape.push_back(results_.shape()[2]); + + xt::xarray reshaped_results = results_; + reshaped_results.reshape(shape); + return reshaped_results; +} + std::string Tally::score_name(int score_idx) const { if (score_idx < 0 || score_idx >= scores_.size()) { @@ -700,6 +879,14 @@ std::string Tally::score_name(int score_idx) const return reaction_name(scores_[score_idx]); } +std::vector Tally::scores() const +{ + std::vector score_names; + for (int score : scores_) + score_names.push_back(reaction_name(score)); + return score_names; +} + std::string Tally::nuclide_name(int nuclide_idx) const { if (nuclide_idx < 0 || nuclide_idx >= nuclides_.size()) { @@ -731,6 +918,11 @@ void read_tallies_xml() doc.load_file(filename.c_str()); pugi::xml_node root = doc.document_element(); + read_tallies_xml(root); +} + +void read_tallies_xml(pugi::xml_node root) +{ // Check for setting if (check_for_node(root, "assume_separate")) { settings::assume_separate = get_node_value_bool(root, "assume_separate"); @@ -800,8 +992,8 @@ void reduce_tally_results() } } - // Note that global tallies are *always* reduced even when no_reduce option is - // on. + // Note that global tallies are *always* reduced even when no_reduce option + // is on. // Get view of global tally values auto& gt = simulation::global_tallies; @@ -837,8 +1029,9 @@ void accumulate_tallies() { #ifdef OPENMC_MPI // Combine tally results onto master process - if (mpi::n_procs > 1) + if (mpi::n_procs > 1 && settings::solver_type == SolverType::MONTE_CARLO) { reduce_tally_results(); + } #endif // Increase number of realizations (only used for global tallies) @@ -879,20 +1072,59 @@ void accumulate_tallies() } } +double distance_to_time_boundary(double time, double speed) +{ + if (model::time_grid.empty()) { + return INFTY; + } else if (time >= model::time_grid.back()) { + return INFTY; + } else { + double next_time = + *std::upper_bound(model::time_grid.begin(), model::time_grid.end(), time); + return (next_time - time) * speed; + } +} + +//! Add new points to the global time grid +// +//! \param grid Vector of new time points to add +void add_to_time_grid(vector grid) +{ + if (grid.empty()) + return; + + // Create new vector with enough space to hold old and new grid points + vector merged; + merged.reserve(model::time_grid.size() + grid.size()); + + // Merge and remove duplicates + std::set_union(model::time_grid.begin(), model::time_grid.end(), grid.begin(), + grid.end(), std::back_inserter(merged)); + + // Swap in the new grid + model::time_grid.swap(merged); +} + void setup_active_tallies() { model::active_tallies.clear(); model::active_analog_tallies.clear(); model::active_tracklength_tallies.clear(); + model::active_timed_tracklength_tallies.clear(); model::active_collision_tallies.clear(); model::active_meshsurf_tallies.clear(); model::active_surface_tallies.clear(); + model::active_pulse_height_tallies.clear(); + model::time_grid.clear(); for (auto i = 0; i < model::tallies.size(); ++i) { const auto& tally {*model::tallies[i]}; if (tally.active_) { model::active_tallies.push_back(i); + bool mesh_present = (tally.get_filter() || + tally.get_filter()); + auto time_filter = tally.get_filter(); switch (tally.type_) { case TallyType::VOLUME: @@ -901,7 +1133,12 @@ void setup_active_tallies() model::active_analog_tallies.push_back(i); break; case TallyEstimator::TRACKLENGTH: - model::active_tracklength_tallies.push_back(i); + if (time_filter && mesh_present) { + model::active_timed_tracklength_tallies.push_back(i); + add_to_time_grid(time_filter->bins()); + } else { + model::active_tracklength_tallies.push_back(i); + } break; case TallyEstimator::COLLISION: model::active_collision_tallies.push_back(i); @@ -914,6 +1151,11 @@ void setup_active_tallies() case TallyType::SURFACE: model::active_surface_tallies.push_back(i); + break; + + case TallyType::PULSE_HEIGHT: + model::active_pulse_height_tallies.push_back(i); + break; } } } @@ -932,9 +1174,12 @@ void free_memory_tally() model::active_tallies.clear(); model::active_analog_tallies.clear(); model::active_tracklength_tallies.clear(); + model::active_timed_tracklength_tallies.clear(); model::active_collision_tallies.clear(); model::active_meshsurf_tallies.clear(); model::active_surface_tallies.clear(); + model::active_pulse_height_tallies.clear(); + model::time_grid.clear(); model::tally_map.clear(); } @@ -1056,6 +1301,8 @@ extern "C" int openmc_tally_set_type(int32_t index, const char* type) model::tallies[index]->type_ = TallyType::MESH_SURFACE; } else if (strcmp(type, "surface") == 0) { model::tallies[index]->type_ = TallyType::SURFACE; + } else if (strcmp(type, "pulse-height") == 0) { + model::tallies[index]->type_ = TallyType::PULSE_HEIGHT; } else { set_errmsg(fmt::format("Unknown tally type: {}", type)); return OPENMC_E_INVALID_ARGUMENT; @@ -1108,6 +1355,28 @@ extern "C" int openmc_tally_set_writable(int32_t index, bool writable) return 0; } +extern "C" int openmc_tally_get_multiply_density(int32_t index, bool* value) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + *value = model::tallies[index]->multiply_density(); + + return 0; +} + +extern "C" int openmc_tally_set_multiply_density(int32_t index, bool value) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + model::tallies[index]->set_multiply_density(value); + + return 0; +} + extern "C" int openmc_tally_get_scores(int32_t index, int** scores, int* n) { if (index < 0 || index >= model::tallies.size()) { @@ -1170,10 +1439,13 @@ extern "C" int openmc_tally_set_nuclides( } else { auto search = data::nuclide_map.find(word); if (search == data::nuclide_map.end()) { - set_errmsg("Nuclide \"" + word + "\" has not been loaded yet"); - return OPENMC_E_DATA; + int err = openmc_load_nuclide(word.c_str(), nullptr, 0); + if (err < 0) { + set_errmsg(openmc_err_msg); + return OPENMC_E_DATA; + } } - nucs.push_back(search->second); + nucs.push_back(data::nuclide_map.at(word)); } } @@ -1208,7 +1480,7 @@ extern "C" int openmc_tally_set_filters( try { // Convert indices to filter pointers vector filters; - for (gsl::index i = 0; i < n; ++i) { + for (int64_t i = 0; i < n; ++i) { int32_t i_filt = indices[i]; filters.push_back(model::tally_filters.at(i_filt).get()); } @@ -1246,8 +1518,8 @@ extern "C" int openmc_tally_get_n_realizations(int32_t index, int32_t* n) return 0; } -//! \brief Returns a pointer to a tally results array along with its shape. This -//! allows a user to obtain in-memory tally results from Python directly. +//! \brief Returns a pointer to a tally results array along with its shape. +//! This allows a user to obtain in-memory tally results from Python directly. extern "C" int openmc_tally_results( int32_t index, double** results, size_t* shape) { @@ -1283,4 +1555,20 @@ extern "C" size_t tallies_size() return model::tallies.size(); } +// given a tally ID, remove it from the tallies vector +extern "C" int openmc_remove_tally(int32_t index) +{ + // check that id is in the map + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + + // delete the tally via iterator pointing to correct position + // this calls the Tally destructor, removing the tally from the map as well + model::tallies.erase(model::tallies.begin() + index); + + return 0; +} + } // namespace openmc diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index fd75091661..67e851644a 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -4,6 +4,7 @@ #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/error.h" +#include "openmc/ifp.h" #include "openmc/material.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" @@ -15,6 +16,7 @@ #include "openmc/string_utils.h" #include "openmc/tallies/derivative.h" #include "openmc/tallies/filter.h" +#include "openmc/tallies/filter_cell.h" #include "openmc/tallies/filter_delayedgroup.h" #include "openmc/tallies/filter_energy.h" @@ -203,10 +205,10 @@ double score_fission_q(const Particle& p, int score_bin, const Tally& tally, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // fission scaled by the Q-value - if (p.neutron_xs(p.event_nuclide()).absorption > 0) { - return p.wgt_absorb() * get_nuc_fission_q(nuc, p, score_bin) * + if (p.neutron_xs(p.event_nuclide()).total > 0) { + return p.wgt_last() * get_nuc_fission_q(nuc, p, score_bin) * p.neutron_xs(p.event_nuclide()).fission * flux / - p.neutron_xs(p.event_nuclide()).absorption; + p.neutron_xs(p.event_nuclide()).total; } } else { // Skip any non-absorption events @@ -231,7 +233,7 @@ double score_fission_q(const Particle& p, int score_bin, const Tally& tally, double score {0.0}; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); + auto atom_density = material.atom_density(i, p.density_mult()); const Nuclide& nuc {*data::nuclides[j_nuclide]}; score += get_nuc_fission_q(nuc, p, score_bin) * atom_density * p.neutron_xs(j_nuclide).fission; @@ -251,20 +253,16 @@ double get_nuclide_neutron_heating( if (mt == C_NONE) return 0.0; - auto i_temp = p.neutron_xs(i_nuclide).index_temp; + const auto& micro = p.neutron_xs(i_nuclide); + auto i_temp = micro.index_temp; if (i_temp < 0) return 0.0; // Can be true due to multipole - const auto& rxn {*nuc.reactions_[mt]}; - const auto& xs {rxn.xs_[i_temp]}; - auto i_grid = p.neutron_xs(i_nuclide).index_grid; - if (i_grid < xs.threshold) - return 0.0; - // Determine total kerma - auto f = p.neutron_xs(i_nuclide).interp_factor; - double kerma = (1.0 - f) * xs.value[i_grid - xs.threshold] + - f * xs.value[i_grid - xs.threshold + 1]; + const auto& rx {*nuc.reactions_[mt]}; + double kerma = rx.xs(micro); + if (kerma == 0.0) + return 0.0; if (settings::run_mode == RunMode::EIGENVALUE) { // Determine kerma for fission as (EFR + EB)*sigma_f @@ -291,7 +289,6 @@ double get_nuclide_neutron_heating( double score_neutron_heating(const Particle& p, const Tally& tally, double flux, int rxn_bin, int i_nuclide, double atom_density) { - double score; // Get heating macroscopic "cross section" double heating_xs; if (i_nuclide >= 0) { @@ -318,21 +315,66 @@ double score_neutron_heating(const Particle& p, const Tally& tally, double flux, } } } - score = heating_xs * flux; + double score = heating_xs * flux; if (tally.estimator_ == TallyEstimator::ANALOG) { // All events score to a heating tally bin. We actually use a // collision estimator in place of an analog one since there is no // reaction-wise heating cross section - if (settings::survival_biasing) { - // Account for the fact that some weight has been absorbed - score *= p.wgt_last() + p.wgt_absorb(); - } else { - score *= p.wgt_last(); - } + score *= p.wgt_last(); } return score; } +//! Helper function to obtain reaction Q value for photons and charged particles +double get_reaction_q_value(const Particle& p) +{ + if (p.type() == ParticleType::photon && p.event_mt() == PAIR_PROD) { + // pair production + return -2 * MASS_ELECTRON_EV; + } else if (p.type() == ParticleType::positron) { + // positron annihilation + return 2 * MASS_ELECTRON_EV; + } else { + return 0.0; + } +} + +//! Helper function to obtain particle heating [eV] + +double score_particle_heating(const Particle& p, const Tally& tally, + double flux, int rxn_bin, int i_nuclide, double atom_density) +{ + if (p.type() == ParticleType::neutron) + return score_neutron_heating( + p, tally, flux, rxn_bin, i_nuclide, atom_density); + if (i_nuclide == -1 || i_nuclide == p.event_nuclide() || + p.event_nuclide() == -1) { + // For pair production and positron annihilation, we need to account for the + // reaction Q value + double Q = get_reaction_q_value(p); + + // Get the pre-collision energy of the particle. + auto E = p.E_last(); + + // The energy deposited is the sum of the incident energy and the reaction + // Q-value less the energy of any outgoing particles + double score = E + Q - p.E() - p.bank_second_E(); + + score *= p.wgt_last(); + + // if no event_nuclide (charged particle) scale energy deposition by + // fractional charge density + if (i_nuclide != -1 && p.event_nuclide() == -1) { + const auto& mat {model::materials[p.material()]}; + int z = data::nuclides[i_nuclide]->Z_; + auto i = mat->mat_nuclide_index_[i_nuclide]; + score *= (z * mat->atom_density_[i] / mat->charge_density()); + } + return score; + } + return 0.0; +} + //! Helper function for nu-fission tallies with energyout filters. // //! In this case, we may need to score to multiple bins if there were multiple @@ -483,7 +525,7 @@ double get_nuclide_xs(const Particle& p, int i_nuclide, int score_bin) auto m = nuc.reaction_index_[score_bin]; if (m == C_NONE) return 0.0; - const auto& rxn {*nuc.reactions_[m]}; + const auto& rx {*nuc.reactions_[m]}; const auto& micro {p.neutron_xs(i_nuclide)}; // In the URR, the (n,gamma) cross section is sampled randomly from @@ -500,14 +542,7 @@ double get_nuclide_xs(const Particle& p, int i_nuclide, int score_bin) auto f = micro.interp_factor; // Calculate interpolated cross section - const auto& xs {rxn.xs_[i_temp]}; - double value; - if (i_grid >= xs.threshold) { - value = ((1.0 - f) * xs.value[i_grid - xs.threshold] + - f * xs.value[i_grid - xs.threshold + 1]); - } else { - value = 0.0; - } + double xs = rx.xs(micro); if (settings::run_mode == RunMode::EIGENVALUE && score_bin == HEATING_LOCAL) { @@ -521,29 +556,26 @@ double get_nuclide_xs(const Particle& p, int i_nuclide, int score_bin) : 0.0; // Determine non-fission kerma as difference - double kerma_non_fission = value - kerma_fission; + double kerma_non_fission = xs - kerma_fission; // Re-weight non-fission kerma by keff to properly balance energy release // and deposition. See D. P. Griesheimer, S. J. Douglass, and M. H. // Stedry, "Self-consistent energy normalization for quasistatic reactor // calculations", Proc. PHYSOR, Cambridge, UK, Mar 29-Apr 2, 2020. - value = simulation::keff * kerma_non_fission + kerma_fission; + xs = simulation::keff * kerma_non_fission + kerma_fission; } - return value; + return xs; } else { // For multipole, calculate (n,gamma) from other reactions - return rxn.mt_ == N_GAMMA ? micro.absorption - micro.fission : 0.0; + return rx.mt_ == N_GAMMA ? micro.absorption - micro.fission : 0.0; } return 0.0; } -//! Update tally results for continuous-energy tallies with any estimator. -// -//! For analog tallies, the flux estimate depends on the score type so the flux -//! argument is really just used for filter weights. The atom_density argument -//! is not used for analog tallies. +//! Update tally results for continuous-energy tallies with a tracklength or +//! collision estimator. -void score_general_ce(Particle& p, int i_tally, int start_index, +void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, int filter_index, double filter_weight, int i_nuclide, double atom_density, double flux) { @@ -561,50 +593,18 @@ void score_general_ce(Particle& p, int i_tally, int start_index, switch (score_bin) { case SCORE_FLUX: - if (tally.estimator_ == TallyEstimator::ANALOG) { - // All events score to a flux bin. We actually use a collision estimator - // in place of an analog one since there is no way to count 'events' - // exactly for the flux - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = p.wgt_last() + p.wgt_absorb(); - } else { - score = p.wgt_last(); - } - - if (p.type() == Type::neutron || p.type() == Type::photon) { - score *= flux / p.macro_xs().total; - } else { - score = 0.; - } - } else { - score = flux; - } + score = flux; break; case SCORE_TOTAL: - if (tally.estimator_ == TallyEstimator::ANALOG) { - // All events will score to the total reaction rate. We can just use - // use the weight of the particle entering the collision as the score - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = (p.wgt_last() + p.wgt_absorb()) * flux; - } else { - score = p.wgt_last() * flux; + if (i_nuclide >= 0) { + if (p.type() == Type::neutron) { + score = p.neutron_xs(i_nuclide).total * atom_density * flux; + } else if (p.type() == Type::photon) { + score = p.photon_xs(i_nuclide).total * atom_density * flux; } - } else { - if (i_nuclide >= 0) { - if (p.type() == Type::neutron) { - score = p.neutron_xs(i_nuclide).total * atom_density * flux; - } else if (p.type() == Type::photon) { - score = p.photon_xs(i_nuclide).total * atom_density * flux; - } - } else { - score = p.macro_xs().total * flux; - } + score = p.macro_xs().total * flux; } break; @@ -612,77 +612,28 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::neutron) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - // All events score to an inverse velocity bin. We actually use a - // collision estimator in place of an analog one since there is no way - // to count 'events' exactly for the inverse velocity - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = p.wgt_last() + p.wgt_absorb(); - } else { - score = p.wgt_last(); - } - score *= flux / p.macro_xs().total; - } else { - score = flux; - } // Score inverse velocity in units of s/cm. - score /= p.speed(); + score = flux / p.speed(); break; case SCORE_SCATTER: if (p.type() != Type::neutron && p.type() != Type::photon) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - // Skip any event where the particle didn't scatter - if (p.event() != TallyEvent::SCATTER) - continue; - // Since only scattering events make it here, again we can use the - // weight entering the collision as the estimator for the reaction rate - score = p.wgt_last() * flux; - } else { - if (i_nuclide >= 0) { - if (p.type() == Type::neutron) { - const auto& micro = p.neutron_xs(i_nuclide); - score = (micro.total - micro.absorption) * atom_density * flux; - } else { - const auto& micro = p.photon_xs(i_nuclide); - score = (micro.coherent + micro.incoherent) * atom_density * flux; - } + if (i_nuclide >= 0) { + if (p.type() == Type::neutron) { + const auto& micro = p.neutron_xs(i_nuclide); + score = (micro.total - micro.absorption) * atom_density * flux; } else { - if (p.type() == Type::neutron) { - score = (p.macro_xs().total - p.macro_xs().absorption) * flux; - } else { - score = (p.macro_xs().coherent + p.macro_xs().incoherent) * flux; - } + const auto& micro = p.photon_xs(i_nuclide); + score = (micro.coherent + micro.incoherent) * atom_density * flux; } - } - break; - - case SCORE_NU_SCATTER: - if (p.type() != Type::neutron) - continue; - - // Only analog estimators are available. - // Skip any event where the particle didn't scatter - if (p.event() != TallyEvent::SCATTER) - continue; - // For scattering production, we need to use the pre-collision weight - // times the yield as the estimate for the number of neutrons exiting a - // reaction with neutrons in the exit channel - if (p.event_mt() == ELASTIC || p.event_mt() == N_LEVEL || - (p.event_mt() >= N_N1 && p.event_mt() <= N_NC)) { - // Don't waste time on very common reactions we know have - // multiplicities of one. - score = p.wgt_last() * flux; } else { - // Get yield and apply to score - auto m = - data::nuclides[p.event_nuclide()]->reaction_index_[p.event_mt()]; - const auto& rxn {*data::nuclides[p.event_nuclide()]->reactions_[m]}; - score = p.wgt_last() * flux * (*rxn.products_[0].yield_)(E); + if (p.type() == Type::neutron) { + score = (p.macro_xs().total - p.macro_xs().absorption) * flux; + } else { + score = (p.macro_xs().coherent + p.macro_xs().incoherent) * flux; + } } break; @@ -690,171 +641,132 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::neutron && p.type() != Type::photon) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing) { - // No absorption events actually occur if survival biasing is on -- - // just use weight absorbed in survival biasing - score = p.wgt_absorb() * flux; + if (i_nuclide >= 0) { + if (p.type() == Type::neutron) { + score = p.neutron_xs(i_nuclide).absorption * atom_density * flux; } else { - // Skip any event where the particle wasn't absorbed - if (p.event() == TallyEvent::SCATTER) - continue; - // All fission and absorption events will contribute here, so we - // can just use the particle's weight entering the collision - score = p.wgt_last() * flux; + const auto& xs = p.photon_xs(i_nuclide); + score = + (xs.total - xs.coherent - xs.incoherent) * atom_density * flux; } } else { - if (i_nuclide >= 0) { - if (p.type() == Type::neutron) { - score = p.neutron_xs(i_nuclide).absorption * atom_density * flux; - } else { - const auto& xs = p.photon_xs(i_nuclide); - score = - (xs.total - xs.coherent - xs.incoherent) * atom_density * flux; - } + if (p.type() == Type::neutron) { + score = p.macro_xs().absorption * flux; } else { - if (p.type() == Type::neutron) { - score = p.macro_xs().absorption * flux; - } else { - score = - (p.macro_xs().photoelectric + p.macro_xs().pair_production) * - flux; - } + score = + (p.macro_xs().photoelectric + p.macro_xs().pair_production) * flux; } } break; case SCORE_FISSION: - if (p.macro_xs().absorption == 0) + if (p.macro_xs().fission == 0) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // fission - if (p.neutron_xs(p.event_nuclide()).absorption > 0) { - score = p.wgt_absorb() * p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } else { - score = 0.; - } - } else { - // Skip any non-absorption events - if (p.event() == TallyEvent::SCATTER) - continue; - // All fission events will contribute, so again we can use particle's - // weight entering the collision as the estimate for the fission - // reaction rate - score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } + + if (i_nuclide >= 0) { + score = p.neutron_xs(i_nuclide).fission * atom_density * flux; } else { - if (i_nuclide >= 0) { - score = p.neutron_xs(i_nuclide).fission * atom_density * flux; - } else { - score = p.macro_xs().fission * flux; - } + score = p.macro_xs().fission * flux; } break; case SCORE_NU_FISSION: - if (p.macro_xs().absorption == 0) + if (p.macro_xs().fission == 0) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing || p.fission()) { - if (tally.energyout_filter_ != C_NONE) { - // Fission has multiple outgoing neutrons so this helper function - // is used to handle scoring the multiple filter bins. - score_fission_eout(p, i_tally, score_index, score_bin); - continue; - } - } - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // nu-fission - if (p.neutron_xs(p.event_nuclide()).absorption > 0) { - score = p.wgt_absorb() * - p.neutron_xs(p.event_nuclide()).nu_fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } else { - score = 0.; - } - } else { - // Skip any non-fission events - if (!p.fission()) - continue; - // If there is no outgoing energy filter, than we only need to score - // to one bin. For the score to be 'analog', we need to score the - // number of particles that were banked in the fission bank. Since - // this was weighted by 1/keff, we multiply by keff to get the proper - // score. - score = simulation::keff * p.wgt_bank() * flux; - } + + if (i_nuclide >= 0) { + score = p.neutron_xs(i_nuclide).nu_fission * atom_density * flux; } else { - if (i_nuclide >= 0) { - score = p.neutron_xs(i_nuclide).nu_fission * atom_density * flux; - } else { - score = p.macro_xs().nu_fission * flux; - } + score = p.macro_xs().nu_fission * flux; } break; case SCORE_PROMPT_NU_FISSION: - if (p.macro_xs().absorption == 0) + if (p.macro_xs().fission == 0) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing || p.fission()) { - if (tally.energyout_filter_ != C_NONE) { - // Fission has multiple outgoing neutrons so this helper function - // is used to handle scoring the multiple filter bins. - score_fission_eout(p, i_tally, score_index, score_bin); - continue; - } - } - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // prompt-nu-fission - if (p.neutron_xs(p.event_nuclide()).absorption > 0) { - score = p.wgt_absorb() * p.neutron_xs(p.event_nuclide()).fission * - data::nuclides[p.event_nuclide()]->nu( - E, ReactionProduct::EmissionMode::prompt) / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } else { - score = 0.; - } - } else { - // Skip any non-fission events - if (!p.fission()) - continue; - // If there is no outgoing energy filter, than we only need to score - // to one bin. For the score to be 'analog', we need to score the - // number of particles that were banked in the fission bank. Since - // this was weighted by 1/keff, we multiply by keff to get the proper - // score. - auto n_delayed = std::accumulate( - p.n_delayed_bank(), p.n_delayed_bank() + MAX_DELAYED_GROUPS, 0); - auto prompt_frac = 1. - n_delayed / static_cast(p.n_bank()); - score = simulation::keff * p.wgt_bank() * prompt_frac * flux; - } + if (i_nuclide >= 0) { + score = p.neutron_xs(i_nuclide).fission * + data::nuclides[i_nuclide]->nu( + E, ReactionProduct::EmissionMode::prompt) * + atom_density * flux; } else { - if (i_nuclide >= 0) { + score = 0.; + // Add up contributions from each nuclide in the material. + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + score += p.neutron_xs(j_nuclide).fission * + data::nuclides[j_nuclide]->nu( + E, ReactionProduct::EmissionMode::prompt) * + atom_density * flux; + } + } + } + break; + + case SCORE_DELAYED_NU_FISSION: + if (p.macro_xs().fission == 0) + continue; + if (i_nuclide >= 0) { + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + auto yield = data::nuclides[i_nuclide]->nu( + E, ReactionProduct::EmissionMode::delayed, d); + score = + p.neutron_xs(i_nuclide).fission * yield * atom_density * flux; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + continue; + } else { + // If the delayed group filter is not present, compute the score + // by multiplying the delayed-nu-fission macro xs by the flux score = p.neutron_xs(i_nuclide).fission * data::nuclides[i_nuclide]->nu( - E, ReactionProduct::EmissionMode::prompt) * + E, ReactionProduct::EmissionMode::delayed) * atom_density * flux; - } else { - score = 0.; - // Add up contributions from each nuclide in the material. + } + } else { + // Need to add up contributions for each nuclide + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; if (p.material() != MATERIAL_VOID) { const Material& material {*model::materials[p.material()]}; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); + auto atom_density = material.atom_density(i, p.density_mult()); + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + auto yield = data::nuclides[j_nuclide]->nu( + E, ReactionProduct::EmissionMode::delayed, d); + score = + p.neutron_xs(j_nuclide).fission * yield * atom_density * flux; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + } + } + continue; + } else { + score = 0.; + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); score += p.neutron_xs(j_nuclide).fission * data::nuclides[j_nuclide]->nu( - E, ReactionProduct::EmissionMode::prompt) * + E, ReactionProduct::EmissionMode::delayed) * atom_density * flux; } } @@ -862,325 +774,100 @@ void score_general_ce(Particle& p, int i_tally, int start_index, } break; - case SCORE_DELAYED_NU_FISSION: - if (p.macro_xs().absorption == 0) + case SCORE_DECAY_RATE: + if (p.macro_xs().fission == 0) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing || p.fission()) { - if (tally.energyout_filter_ != C_NONE) { - // Fission has multiple outgoing neutrons so this helper function - // is used to handle scoring the multiple filter bins. - score_fission_eout(p, i_tally, score_index, score_bin); - continue; - } - } - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // delayed-nu-fission - if (p.neutron_xs(p.event_nuclide()).absorption > 0 && - data::nuclides[p.event_nuclide()]->fissionable_) { - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt { - *dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto dg = filt.groups()[d_bin]; - auto yield = data::nuclides[p.event_nuclide()]->nu( - E, ReactionProduct::EmissionMode::delayed, dg); - score = p.wgt_absorb() * yield * - p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - continue; - } else { - // If the delayed group filter is not present, compute the score - // by multiplying the absorbed weight by the fraction of the - // delayed-nu-fission xs to the absorption xs - score = p.wgt_absorb() * p.neutron_xs(p.event_nuclide()).fission * - data::nuclides[p.event_nuclide()]->nu( - E, ReactionProduct::EmissionMode::delayed) / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } + if (i_nuclide >= 0) { + const auto& nuc {*data::nuclides[i_nuclide]}; + if (!nuc.fissionable_) + continue; + const auto& rxn {*nuc.fission_rx_[0]}; + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = rxn.products_[d].decay_rate_; + score = p.neutron_xs(i_nuclide).fission * yield * flux * + atom_density * rate; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); } + continue; } else { - // Skip any non-fission events - if (!p.fission()) - continue; - // If there is no outgoing energy filter, than we only need to score - // to one bin. For the score to be 'analog', we need to score the - // number of particles that were banked in the fission bank. Since - // this was weighted by 1/keff, we multiply by keff to get the proper - // score. Loop over the neutrons produced from fission and check which - // ones are delayed. If a delayed neutron is encountered, add its - // contribution to the fission bank to the score. - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt {*dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - score = simulation::keff * p.wgt_bank() / p.n_bank() * - p.n_delayed_bank(d - 1) * flux; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - continue; - } else { - // Add the contribution from all delayed groups - auto n_delayed = std::accumulate( - p.n_delayed_bank(), p.n_delayed_bank() + MAX_DELAYED_GROUPS, 0); - score = - simulation::keff * p.wgt_bank() / p.n_bank() * n_delayed * flux; + score = 0.; + // We need to be careful not to overshoot the number of + // delayed groups since this could cause the range of the + // rxn.products_ array to be exceeded. Hence, we use the size + // of this array and not the MAX_DELAYED_GROUPS constant for + // this loop. + for (auto d = 1; d < rxn.products_.size(); ++d) { + const auto& product = rxn.products_[d]; + if (product.particle_ != Type::neutron) + continue; + + auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = product.decay_rate_; + score += p.neutron_xs(i_nuclide).fission * flux * yield * + atom_density * rate; } } } else { - if (i_nuclide >= 0) { - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt {*dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - auto yield = data::nuclides[i_nuclide]->nu( - E, ReactionProduct::EmissionMode::delayed, d); - score = - p.neutron_xs(i_nuclide).fission * yield * atom_density * flux; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - continue; - } else { - // If the delayed group filter is not present, compute the score - // by multiplying the delayed-nu-fission macro xs by the flux - score = p.neutron_xs(i_nuclide).fission * - data::nuclides[i_nuclide]->nu( - E, ReactionProduct::EmissionMode::delayed) * - atom_density * flux; - } - } else { - // Need to add up contributions for each nuclide - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt {*dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + const auto& nuc {*data::nuclides[j_nuclide]}; + if (nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; // Tally each delayed group bin individually for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { auto d = filt.groups()[d_bin]; - auto yield = data::nuclides[j_nuclide]->nu( - E, ReactionProduct::EmissionMode::delayed, d); - score = p.neutron_xs(j_nuclide).fission * yield * - atom_density * flux; + auto yield = + nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = rxn.products_[d].decay_rate_; + score = p.neutron_xs(j_nuclide).fission * yield * flux * + atom_density * rate; score_fission_delayed_dg( i_tally, d_bin, score, score_index, p.filter_matches()); } } } - continue; - } else { - score = 0.; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - score += p.neutron_xs(j_nuclide).fission * - data::nuclides[j_nuclide]->nu( - E, ReactionProduct::EmissionMode::delayed) * - atom_density * flux; - } - } - } - } - } - break; - - case SCORE_DECAY_RATE: - if (p.macro_xs().absorption == 0) - continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // delayed-nu-fission - const auto& nuc {*data::nuclides[p.event_nuclide()]}; - if (p.neutron_xs(p.event_nuclide()).absorption > 0 && - nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt { - *dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - auto yield = - nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); - auto rate = rxn.products_[d].decay_rate_; - score = p.wgt_absorb() * yield * - p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * rate * - flux; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - continue; - } else { - // If the delayed group filter is not present, compute the score - // by multiplying the absorbed weight by the fraction of the - // delayed-nu-fission xs to the absorption xs for all delayed - // groups - score = 0.; - // We need to be careful not to overshoot the number of - // delayed groups since this could cause the range of the - // rxn.products_ array to be exceeded. Hence, we use the size - // of this array and not the MAX_DELAYED_GROUPS constant for - // this loop. - for (auto d = 0; d < rxn.products_.size() - 2; ++d) { - auto yield = - nuc.nu(E, ReactionProduct::EmissionMode::delayed, d + 1); - auto rate = rxn.products_[d + 1].decay_rate_; - score += rate * p.wgt_absorb() * - p.neutron_xs(p.event_nuclide()).fission * yield / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } - } } + continue; } else { - // Skip any non-fission events - if (!p.fission()) - continue; - // If there is no outgoing energy filter, than we only need to score - // to one bin. For the score to be 'analog', we need to score the - // number of particles that were banked in the fission bank. Since - // this was weighted by 1/keff, we multiply by keff to get the proper - // score. Loop over the neutrons produced from fission and check which - // ones are delayed. If a delayed neutron is encountered, add its - // contribution to the fission bank to the score. score = 0.; - for (auto i = 0; i < p.n_bank(); ++i) { - const auto& bank = p.nu_bank(i); - auto g = bank.delayed_group; - if (g != 0) { - const auto& nuc {*data::nuclides[p.event_nuclide()]}; - const auto& rxn {*nuc.fission_rx_[0]}; - auto rate = rxn.products_[g].decay_rate_; - score += simulation::keff * bank.wgt * rate * flux; - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt { - *dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Find the corresponding filter bin and then score - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - if (d == g) - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - score = 0.; - } - } - } - } - } else { - if (i_nuclide >= 0) { - const auto& nuc {*data::nuclides[i_nuclide]}; - if (!nuc.fissionable_) - continue; - const auto& rxn {*nuc.fission_rx_[0]}; - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt {*dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); - auto rate = rxn.products_[d].decay_rate_; - score = p.neutron_xs(i_nuclide).fission * yield * flux * - atom_density * rate; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - continue; - } else { - score = 0.; - // We need to be careful not to overshoot the number of - // delayed groups since this could cause the range of the - // rxn.products_ array to be exceeded. Hence, we use the size - // of this array and not the MAX_DELAYED_GROUPS constant for - // this loop. - for (auto d = 0; d < rxn.products_.size() - 2; ++d) { - auto yield = - nuc.nu(E, ReactionProduct::EmissionMode::delayed, d + 1); - auto rate = rxn.products_[d + 1].decay_rate_; - score += p.neutron_xs(i_nuclide).fission * flux * yield * - atom_density * rate; - } - } - } else { - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt {*dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - const auto& nuc {*data::nuclides[j_nuclide]}; - if (nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - // Tally each delayed group bin individually - for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { - auto d = filt.groups()[d_bin]; - auto yield = - nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); - auto rate = rxn.products_[d].decay_rate_; - score = p.neutron_xs(j_nuclide).fission * yield * flux * - atom_density * rate; - score_fission_delayed_dg( - i_tally, d_bin, score, score_index, p.filter_matches()); - } - } - } - } - continue; - } else { - score = 0.; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - const auto& nuc {*data::nuclides[j_nuclide]}; - if (nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - // We need to be careful not to overshoot the number of - // delayed groups since this could cause the range of the - // rxn.products_ array to be exceeded. Hence, we use the size - // of this array and not the MAX_DELAYED_GROUPS constant for - // this loop. - for (auto d = 0; d < rxn.products_.size() - 2; ++d) { - auto yield = - nuc.nu(E, ReactionProduct::EmissionMode::delayed, d + 1); - auto rate = rxn.products_[d + 1].decay_rate_; - score += p.neutron_xs(j_nuclide).fission * yield * - atom_density * flux * rate; - } + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + const auto& nuc {*data::nuclides[j_nuclide]}; + if (nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; + // We need to be careful not to overshoot the number of + // delayed groups since this could cause the range of the + // rxn.products_ array to be exceeded. Hence, we use the size + // of this array and not the MAX_DELAYED_GROUPS constant for + // this loop. + for (auto d = 1; d < rxn.products_.size(); ++d) { + const auto& product = rxn.products_[d]; + if (product.particle_ != Type::neutron) + continue; + + auto yield = + nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = product.decay_rate_; + score += p.neutron_xs(j_nuclide).fission * yield * + atom_density * flux * rate; } } } @@ -1190,59 +877,28 @@ void score_general_ce(Particle& p, int i_tally, int start_index, break; case SCORE_KAPPA_FISSION: - if (p.macro_xs().absorption == 0.) + if (p.macro_xs().fission == 0.) continue; score = 0.; // Kappa-fission values are determined from the Q-value listed for the // fission cross section. - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (settings::survival_biasing) { - // No fission events occur if survival biasing is on -- need to - // calculate fraction of absorptions that would have resulted in - // fission scaled by the Q-value - const auto& nuc {*data::nuclides[p.event_nuclide()]}; - if (p.neutron_xs(p.event_nuclide()).absorption > 0 && - nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - score = p.wgt_absorb() * rxn.q_value_ * - p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } - } else { - // Skip any non-absorption events - if (p.event() == TallyEvent::SCATTER) - continue; - // All fission events will contribute, so again we can use particle's - // weight entering the collision as the estimate for the fission - // reaction rate - const auto& nuc {*data::nuclides[p.event_nuclide()]}; - if (p.neutron_xs(p.event_nuclide()).absorption > 0 && - nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - score = p.wgt_last() * rxn.q_value_ * - p.neutron_xs(p.event_nuclide()).fission / - p.neutron_xs(p.event_nuclide()).absorption * flux; - } + if (i_nuclide >= 0) { + const auto& nuc {*data::nuclides[i_nuclide]}; + if (nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; + score = rxn.q_value_ * p.neutron_xs(i_nuclide).fission * + atom_density * flux; } - } else { - if (i_nuclide >= 0) { - const auto& nuc {*data::nuclides[i_nuclide]}; + } else if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + const auto& nuc {*data::nuclides[j_nuclide]}; if (nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; - score = rxn.q_value_ * p.neutron_xs(i_nuclide).fission * - atom_density * flux; - } - } else if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - const auto& nuc {*data::nuclides[j_nuclide]}; - if (nuc.fissionable_) { - const auto& rxn {*nuc.fission_rx_[0]}; - score += rxn.q_value_ * p.neutron_xs(j_nuclide).fission * - atom_density * flux; - } + score += rxn.q_value_ * p.neutron_xs(j_nuclide).fission * + atom_density * flux; } } } @@ -1258,27 +914,20 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::neutron) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - // Check if event MT matches - if (p.event_mt() != ELASTIC) - continue; - score = p.wgt_last() * flux; + if (i_nuclide >= 0) { + if (p.neutron_xs(i_nuclide).elastic == CACHE_INVALID) + data::nuclides[i_nuclide]->calculate_elastic_xs(p); + score = p.neutron_xs(i_nuclide).elastic * atom_density * flux; } else { - if (i_nuclide >= 0) { - if (p.neutron_xs(i_nuclide).elastic == CACHE_INVALID) - data::nuclides[i_nuclide]->calculate_elastic_xs(p); - score = p.neutron_xs(i_nuclide).elastic * atom_density * flux; - } else { - score = 0.; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - if (p.neutron_xs(j_nuclide).elastic == CACHE_INVALID) - data::nuclides[j_nuclide]->calculate_elastic_xs(p); - score += p.neutron_xs(j_nuclide).elastic * atom_density * flux; - } + score = 0.; + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + if (p.neutron_xs(j_nuclide).elastic == CACHE_INVALID) + data::nuclides[j_nuclide]->calculate_elastic_xs(p); + score += p.neutron_xs(j_nuclide).elastic * atom_density * flux; } } } @@ -1286,7 +935,606 @@ void score_general_ce(Particle& p, int i_tally, int start_index, case SCORE_FISS_Q_PROMPT: case SCORE_FISS_Q_RECOV: - if (p.macro_xs().absorption == 0.) + if (p.macro_xs().fission == 0.) + continue; + score = + score_fission_q(p, score_bin, tally, flux, i_nuclide, atom_density); + break; + + case SCORE_IFP_TIME_NUM: + if (settings::ifp_on) { + if ((p.type() == Type::neutron) && (p.fission())) { + if (is_generation_time_or_both()) { + const auto& lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + if (lifetimes.size() == settings::ifp_n_generation) { + score = lifetimes[0] * p.wgt_last(); + } + } + } + } + break; + + case SCORE_IFP_BETA_NUM: + if (settings::ifp_on) { + if ((p.type() == Type::neutron) && (p.fission())) { + if (is_beta_effective_or_both()) { + const auto& delayed_groups = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; + if (delayed_groups.size() == settings::ifp_n_generation) { + if (delayed_groups[0] > 0) { + score = p.wgt_last(); + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt { + *dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + score_fission_delayed_dg(i_tally, delayed_groups[0] - 1, + score, score_index, p.filter_matches()); + continue; + } + } + } + } + } + } + break; + + case SCORE_IFP_DENOM: + if (settings::ifp_on) { + if ((p.type() == Type::neutron) && (p.fission())) { + int ifp_data_size; + if (is_beta_effective_or_both()) { + ifp_data_size = static_cast( + simulation::ifp_source_delayed_group_bank[p.current_work() - 1] + .size()); + } else { + ifp_data_size = static_cast( + simulation::ifp_source_lifetime_bank[p.current_work() - 1] + .size()); + } + if (ifp_data_size == settings::ifp_n_generation) { + score = p.wgt_last(); + } + } + } + break; + + case N_2N: + case N_3N: + case N_4N: + case N_GAMMA: + case N_P: + case N_A: + // This case block only works if cross sections for these reactions have + // been precalculated. When they are not, we revert to the default case, + // which looks up cross sections + if (!simulation::need_depletion_rx) + goto default_case; + + if (p.type() != Type::neutron) + continue; + + int m; + switch (score_bin) { + // clang-format off + case N_GAMMA: m = 0; break; + case N_P: m = 1; break; + case N_A: m = 2; break; + case N_2N: m = 3; break; + case N_3N: m = 4; break; + case N_4N: m = 5; break; + // clang-format on + } + if (i_nuclide >= 0) { + score = p.neutron_xs(i_nuclide).reaction[m] * atom_density * flux; + } else { + score = 0.; + if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + score += p.neutron_xs(j_nuclide).reaction[m] * atom_density * flux; + } + } + } + break; + + case COHERENT: + case INCOHERENT: + case PHOTOELECTRIC: + case PAIR_PROD: + if (p.type() != Type::photon) + continue; + + if (i_nuclide >= 0) { + const auto& micro = p.photon_xs(i_nuclide); + double xs = (score_bin == COHERENT) ? micro.coherent + : (score_bin == INCOHERENT) ? micro.incoherent + : (score_bin == PHOTOELECTRIC) ? micro.photoelectric + : micro.pair_production; + score = xs * atom_density * flux; + } else { + double xs = (score_bin == COHERENT) ? p.macro_xs().coherent + : (score_bin == INCOHERENT) ? p.macro_xs().incoherent + : (score_bin == PHOTOELECTRIC) + ? p.macro_xs().photoelectric + : p.macro_xs().pair_production; + score = xs * flux; + } + break; + + case HEATING: + score = score_particle_heating( + p, tally, flux, HEATING, i_nuclide, atom_density); + break; + + default: + default_case: + + // The default block is really only meant for redundant neutron reactions + // (e.g. 444, 901) + if (p.type() != Type::neutron) + continue; + + // Any other cross section has to be calculated on-the-fly + if (score_bin < 2) + fatal_error("Invalid score type on tally " + std::to_string(tally.id_)); + score = 0.; + if (i_nuclide >= 0) { + score = get_nuclide_xs(p, i_nuclide, score_bin) * atom_density * flux; + } else if (p.material() != MATERIAL_VOID) { + const Material& material {*model::materials[p.material()]}; + for (auto i = 0; i < material.nuclide_.size(); ++i) { + auto j_nuclide = material.nuclide_[i]; + auto atom_density = material.atom_density(i, p.density_mult()); + score += + get_nuclide_xs(p, j_nuclide, score_bin) * atom_density * flux; + } + } + } + + // Add derivative information on score for differential tallies. + if (tally.deriv_ != C_NONE) + apply_derivative_to_score( + p, i_tally, i_nuclide, atom_density, score_bin, score); + +// Update tally results +#pragma omp atomic + tally.results_(filter_index, score_index, TallyResult::VALUE) += + score * filter_weight; + } +} + +//! Update tally results for continuous-energy tallies with an analog estimator. +// +//! For analog tallies, the flux estimate depends on the score type so the flux +//! argument is really just used for filter weights. The atom_density argument +//! is not used for analog tallies. + +void score_general_ce_analog(Particle& p, int i_tally, int start_index, + int filter_index, double filter_weight, int i_nuclide, double atom_density, + double flux) +{ + Tally& tally {*model::tallies[i_tally]}; + + // Get the pre-collision energy of the particle. + auto E = p.E_last(); + + // Determine how much weight was absorbed due to survival biasing + double wgt_absorb = settings::survival_biasing + ? p.wgt_last() * + p.neutron_xs(p.event_nuclide()).absorption / + p.neutron_xs(p.event_nuclide()).total + : 0.0; + + using Type = ParticleType; + + for (auto i = 0; i < tally.scores_.size(); ++i) { + auto score_bin = tally.scores_[i]; + auto score_index = start_index + i; + double score = 0.0; + + switch (score_bin) { + case SCORE_FLUX: + // All events score to a flux bin. We actually use a collision estimator + // in place of an analog one since there is no way to count 'events' + // exactly for the flux + if (p.type() == Type::neutron || p.type() == Type::photon) { + score = flux * p.wgt_last() / p.macro_xs().total; + } else { + score = 0.; + } + break; + + case SCORE_TOTAL: + // All events will score to the total reaction rate. We can just use + // use the weight of the particle entering the collision as the score + score = p.wgt_last() * flux; + break; + + case SCORE_INVERSE_VELOCITY: + if (p.type() != Type::neutron) + continue; + + // All events score to an inverse velocity bin. We actually use a + // collision estimator in place of an analog one since there is no way + // to count 'events' exactly for the inverse velocity + score = flux * p.wgt_last() / (p.macro_xs().total * p.speed()); + break; + + case SCORE_SCATTER: + if (p.type() != Type::neutron && p.type() != Type::photon) + continue; + + // Skip any event where the particle didn't scatter + if (p.event() != TallyEvent::SCATTER) + continue; + // Since only scattering events make it here, again we can use the + // weight entering the collision as the estimator for the reaction rate + score = (p.wgt_last() - wgt_absorb) * flux; + break; + + case SCORE_NU_SCATTER: + if (p.type() != Type::neutron) + continue; + + // Only analog estimators are available. + // Skip any event where the particle didn't scatter + if (p.event() != TallyEvent::SCATTER) + continue; + // For scattering production, we need to use the pre-collision weight + // times the yield as the estimate for the number of neutrons exiting a + // reaction with neutrons in the exit channel + score = (p.wgt_last() - wgt_absorb) * flux; + + // Don't waste time on very common reactions we know have multiplicities + // of one. + if (p.event_mt() != ELASTIC && p.event_mt() != N_LEVEL && + !(p.event_mt() >= N_N1 && p.event_mt() <= N_NC)) { + // Get yield and apply to score + auto m = + data::nuclides[p.event_nuclide()]->reaction_index_[p.event_mt()]; + const auto& rxn {*data::nuclides[p.event_nuclide()]->reactions_[m]}; + score *= (*rxn.products_[0].yield_)(E); + } + break; + + case SCORE_ABSORPTION: + if (p.type() != Type::neutron && p.type() != Type::photon) + continue; + + if (settings::survival_biasing) { + // No absorption events actually occur if survival biasing is on -- + // just use weight absorbed in survival biasing + score = wgt_absorb * flux; + } else { + // Skip any event where the particle wasn't absorbed + if (p.event() == TallyEvent::SCATTER) + continue; + // All fission and absorption events will contribute here, so we + // can just use the particle's weight entering the collision + score = p.wgt_last() * flux; + } + break; + + case SCORE_FISSION: + if (p.macro_xs().fission == 0) + continue; + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- use collision + // estimator instead + if (p.neutron_xs(p.event_nuclide()).total > 0) { + score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).total * flux; + } else { + score = 0.; + } + } else { + // Skip any non-absorption events + if (p.event() == TallyEvent::SCATTER) + continue; + // All fission events will contribute, so again we can use particle's + // weight entering the collision as the estimate for the fission + // reaction rate + score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).absorption * flux; + } + break; + + case SCORE_NU_FISSION: + if (p.macro_xs().fission == 0) + continue; + if (settings::survival_biasing || p.fission()) { + if (tally.energyout_filter_ != C_NONE) { + // Fission has multiple outgoing neutrons so this helper function + // is used to handle scoring the multiple filter bins. + score_fission_eout(p, i_tally, score_index, score_bin); + continue; + } + } + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- use collision + // estimator instead + if (p.neutron_xs(p.event_nuclide()).total > 0) { + score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).nu_fission / + p.neutron_xs(p.event_nuclide()).total * flux; + } else { + score = 0.; + } + } else { + // Skip any non-fission events + if (!p.fission()) + continue; + // If there is no outgoing energy filter, than we only need to score + // to one bin. For the score to be 'analog', we need to score the + // number of particles that were banked in the fission bank. Since + // this was weighted by 1/keff, we multiply by keff to get the proper + // score. + score = simulation::keff * p.wgt_bank() * flux; + } + break; + + case SCORE_PROMPT_NU_FISSION: + if (p.macro_xs().fission == 0) + continue; + if (settings::survival_biasing || p.fission()) { + if (tally.energyout_filter_ != C_NONE) { + // Fission has multiple outgoing neutrons so this helper function + // is used to handle scoring the multiple filter bins. + score_fission_eout(p, i_tally, score_index, score_bin); + continue; + } + } + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- need to + // calculate fraction of absorptions that would have resulted in + // prompt-nu-fission + if (p.neutron_xs(p.event_nuclide()).total > 0) { + score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).fission * + data::nuclides[p.event_nuclide()]->nu( + E, ReactionProduct::EmissionMode::prompt) / + p.neutron_xs(p.event_nuclide()).total * flux; + } else { + score = 0.; + } + } else { + // Skip any non-fission events + if (!p.fission()) + continue; + // If there is no outgoing energy filter, than we only need to score + // to one bin. For the score to be 'analog', we need to score the + // number of particles that were banked in the fission bank. Since + // this was weighted by 1/keff, we multiply by keff to get the proper + // score. + auto n_delayed = std::accumulate( + p.n_delayed_bank(), p.n_delayed_bank() + MAX_DELAYED_GROUPS, 0); + auto prompt_frac = 1. - n_delayed / static_cast(p.n_bank()); + score = simulation::keff * p.wgt_bank() * prompt_frac * flux; + } + break; + + case SCORE_DELAYED_NU_FISSION: + if (p.macro_xs().fission == 0) + continue; + if (settings::survival_biasing || p.fission()) { + if (tally.energyout_filter_ != C_NONE) { + // Fission has multiple outgoing neutrons so this helper function + // is used to handle scoring the multiple filter bins. + score_fission_eout(p, i_tally, score_index, score_bin); + continue; + } + } + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- need to + // calculate fraction of absorptions that would have resulted in + // delayed-nu-fission + if (p.neutron_xs(p.event_nuclide()).total > 0 && + data::nuclides[p.event_nuclide()]->fissionable_) { + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto dg = filt.groups()[d_bin]; + auto yield = data::nuclides[p.event_nuclide()]->nu( + E, ReactionProduct::EmissionMode::delayed, dg); + score = p.wgt_last() * yield * + p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).total * flux; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + continue; + } else { + // If the delayed group filter is not present, compute the score + // by multiplying the absorbed weight by the fraction of the + // delayed-nu-fission xs to the absorption xs + score = p.wgt_last() * p.neutron_xs(p.event_nuclide()).fission * + data::nuclides[p.event_nuclide()]->nu( + E, ReactionProduct::EmissionMode::delayed) / + p.neutron_xs(p.event_nuclide()).total * flux; + } + } + } else { + // Skip any non-fission events + if (!p.fission()) + continue; + // If there is no outgoing energy filter, than we only need to score + // to one bin. For the score to be 'analog', we need to score the + // number of particles that were banked in the fission bank. Since + // this was weighted by 1/keff, we multiply by keff to get the proper + // score. Loop over the neutrons produced from fission and check which + // ones are delayed. If a delayed neutron is encountered, add its + // contribution to the fission bank to the score. + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + score = simulation::keff * p.wgt_bank() / p.n_bank() * + p.n_delayed_bank(d - 1) * flux; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + continue; + } else { + // Add the contribution from all delayed groups + auto n_delayed = std::accumulate( + p.n_delayed_bank(), p.n_delayed_bank() + MAX_DELAYED_GROUPS, 0); + score = + simulation::keff * p.wgt_bank() / p.n_bank() * n_delayed * flux; + } + } + break; + + case SCORE_DECAY_RATE: + if (p.macro_xs().fission == 0) + continue; + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- need to + // calculate fraction of absorptions that would have resulted in + // delayed-nu-fission + const auto& nuc {*data::nuclides[p.event_nuclide()]}; + if (p.neutron_xs(p.event_nuclide()).total > 0 && nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Tally each delayed group bin individually + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = rxn.products_[d].decay_rate_; + score = p.wgt_last() * yield * + p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).total * rate * flux; + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + continue; + } else { + // If the delayed group filter is not present, compute the score + // by multiplying the absorbed weight by the fraction of the + // delayed-nu-fission xs to the absorption xs for all delayed + // groups + score = 0.; + // We need to be careful not to overshoot the number of + // delayed groups since this could cause the range of the + // rxn.products_ array to be exceeded. Hence, we use the size + // of this array and not the MAX_DELAYED_GROUPS constant for + // this loop. + for (auto d = 1; d < rxn.products_.size(); ++d) { + const auto& product = rxn.products_[d]; + if (product.particle_ != Type::neutron) + continue; + + auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); + auto rate = product.decay_rate_; + score += rate * p.wgt_last() * + p.neutron_xs(p.event_nuclide()).fission * yield / + p.neutron_xs(p.event_nuclide()).total * flux; + } + } + } + } else { + // Skip any non-fission events + if (!p.fission()) + continue; + // If there is no outgoing energy filter, than we only need to score + // to one bin. For the score to be 'analog', we need to score the + // number of particles that were banked in the fission bank. Since + // this was weighted by 1/keff, we multiply by keff to get the proper + // score. Loop over the neutrons produced from fission and check which + // ones are delayed. If a delayed neutron is encountered, add its + // contribution to the fission bank to the score. + score = 0.; + for (auto i = 0; i < p.n_bank(); ++i) { + const auto& bank = p.nu_bank(i); + auto g = bank.delayed_group; + if (g != 0) { + const auto& nuc {*data::nuclides[p.event_nuclide()]}; + const auto& rxn {*nuc.fission_rx_[0]}; + auto rate = rxn.products_[g].decay_rate_; + score += simulation::keff * bank.wgt * rate * flux; + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt { + *dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + // Find the corresponding filter bin and then score + for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { + auto d = filt.groups()[d_bin]; + if (d == g) + score_fission_delayed_dg( + i_tally, d_bin, score, score_index, p.filter_matches()); + } + score = 0.; + } + } + } + } + break; + + case SCORE_KAPPA_FISSION: + if (p.macro_xs().fission == 0.) + continue; + score = 0.; + // Kappa-fission values are determined from the Q-value listed for the + // fission cross section. + if (settings::survival_biasing) { + // No fission events occur if survival biasing is on -- need to + // calculate fraction of absorptions that would have resulted in + // fission scaled by the Q-value + const auto& nuc {*data::nuclides[p.event_nuclide()]}; + if (p.neutron_xs(p.event_nuclide()).total > 0 && nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; + score = p.wgt_last() * rxn.q_value_ * + p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).total * flux; + } + } else { + // Skip any non-absorption events + if (p.event() == TallyEvent::SCATTER) + continue; + // All fission events will contribute, so again we can use particle's + // weight entering the collision as the estimate for the fission + // reaction rate + const auto& nuc {*data::nuclides[p.event_nuclide()]}; + if (p.neutron_xs(p.event_nuclide()).absorption > 0 && + nuc.fissionable_) { + const auto& rxn {*nuc.fission_rx_[0]}; + score = p.wgt_last() * rxn.q_value_ * + p.neutron_xs(p.event_nuclide()).fission / + p.neutron_xs(p.event_nuclide()).absorption * flux; + } + } + break; + + case SCORE_EVENTS: +// Simply count the number of scoring events +#pragma omp atomic + tally.results_(filter_index, score_index, TallyResult::VALUE) += 1.0; + continue; + + case ELASTIC: + if (p.type() != Type::neutron) + continue; + + // Check if event MT matches + if (p.event_mt() != ELASTIC) + continue; + score = (p.wgt_last() - wgt_absorb) * flux; + break; + + case SCORE_FISS_Q_PROMPT: + case SCORE_FISS_Q_RECOV: + if (p.macro_xs().fission == 0.) continue; score = score_fission_q(p, score_bin, tally, flux, i_nuclide, atom_density); @@ -1307,48 +1555,10 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::neutron) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - // Check if the event MT matches - if (p.event_mt() != score_bin) - continue; - score = p.wgt_last() * flux; - } else { - int m; - switch (score_bin) { - case N_GAMMA: - m = 0; - break; - case N_P: - m = 1; - break; - case N_A: - m = 2; - break; - case N_2N: - m = 3; - break; - case N_3N: - m = 4; - break; - case N_4N: - m = 5; - break; - } - if (i_nuclide >= 0) { - score = p.neutron_xs(i_nuclide).reaction[m] * atom_density * flux; - } else { - score = 0.; - if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - score += - p.neutron_xs(j_nuclide).reaction[m] * atom_density * flux; - } - } - } - } + // Check if the event MT matches + if (p.event_mt() != score_bin) + continue; + score = (p.wgt_last() - wgt_absorb) * flux; break; case COHERENT: @@ -1358,60 +1568,23 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::photon) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - if (score_bin == PHOTOELECTRIC) { - // Photoelectric events are assigned an MT value corresponding to the - // shell cross section. Also, photons below the energy cutoff are - // assumed to have been absorbed via photoelectric absorption - if ((p.event_mt() < 534 || p.event_mt() > 572) && - p.event_mt() != REACTION_NONE) - continue; - } else { - if (p.event_mt() != score_bin) - continue; - } - score = p.wgt_last() * flux; + if (score_bin == PHOTOELECTRIC) { + // Photoelectric events are assigned an MT value corresponding to the + // shell cross section. Also, photons below the energy cutoff are + // assumed to have been absorbed via photoelectric absorption + if ((p.event_mt() < 534 || p.event_mt() > 572) && + p.event_mt() != REACTION_NONE) + continue; } else { - if (i_nuclide >= 0) { - const auto& micro = p.photon_xs(i_nuclide); - double xs = (score_bin == COHERENT) ? micro.coherent - : (score_bin == INCOHERENT) - ? micro.incoherent - : (score_bin == PHOTOELECTRIC) - ? micro.photoelectric - : micro.pair_production; - score = xs * atom_density * flux; - } else { - double xs = (score_bin == COHERENT) - ? p.macro_xs().coherent - : (score_bin == INCOHERENT) - ? p.macro_xs().incoherent - : (score_bin == PHOTOELECTRIC) - ? p.macro_xs().photoelectric - : p.macro_xs().pair_production; - score = xs * flux; - } + if (p.event_mt() != score_bin) + continue; } + score = p.wgt_last() * flux; break; case HEATING: - if (p.type() == Type::neutron) { - score = score_neutron_heating( - p, tally, flux, HEATING, i_nuclide, atom_density); - } else { - // The energy deposited is the difference between the pre-collision and - // post-collision energy... - score = E - p.E(); - - // ...less the energy of any secondary particles since they will be - // transported individually later - const auto& bank = p.secondary_bank(); - for (auto it = bank.end() - p.n_bank_second(); it < bank.end(); ++it) { - score -= it->E; - } - - score *= p.wgt_last(); - } + score = score_particle_heating( + p, tally, flux, HEATING, i_nuclide, atom_density); break; default: @@ -1422,31 +1595,11 @@ void score_general_ce(Particle& p, int i_tally, int start_index, if (p.type() != Type::neutron) continue; - if (tally.estimator_ == TallyEstimator::ANALOG) { - - // Any other score is assumed to be a MT number. Thus, we just need - // to check if it matches the MT number of the event - if (p.event_mt() != score_bin) - continue; - score = p.wgt_last() * flux; - } else { - // Any other cross section has to be calculated on-the-fly - if (score_bin < 2) - fatal_error( - "Invalid score type on tally " + std::to_string(tally.id_)); - score = 0.; - if (i_nuclide >= 0) { - score = get_nuclide_xs(p, i_nuclide, score_bin) * atom_density * flux; - } else if (p.material() != MATERIAL_VOID) { - const Material& material {*model::materials[p.material()]}; - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto j_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - score += - get_nuclide_xs(p, j_nuclide, score_bin) * atom_density * flux; - } - } - } + // Any other score is assumed to be a MT number. Thus, we just need + // to check if it matches the MT number of the event + if (p.event_mt() != score_bin) + continue; + score = (p.wgt_last() - wgt_absorb) * flux; } // Add derivative information on score for differential tallies. @@ -1475,10 +1628,12 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // Set the direction and group to use with get_xs Direction p_u; int p_g; + double wgt_absorb = 0.0; if (tally.estimator_ == TallyEstimator::ANALOG || tally.estimator_ == TallyEstimator::COLLISION) { - if (settings::survival_biasing) { + // Determine weight that was absorbed + wgt_absorb = p.wgt_last() * p.macro_xs().absorption / p.macro_xs().total; // Then we either are alive and had a scatter (and so g changed), // or are dead and g did not change @@ -1509,14 +1664,18 @@ void score_general_mg(Particle& p, int i_tally, int start_index, } // For shorthand, assign pointers to the material and nuclide xs set - auto& nuc_xs = (i_nuclide >= 0) ? data::mg.nuclides_[i_nuclide] : data::mg.macro_xs_[p.material()]; + auto& nuc_xs = (i_nuclide >= 0) ? data::mg.nuclides_[i_nuclide] + : data::mg.macro_xs_[p.material()]; auto& macro_xs = data::mg.macro_xs_[p.material()]; // Find the temperature and angle indices of interest - macro_xs.set_angle_index(p_u); + int macro_t = p.mg_xs_cache().t; + int macro_a = macro_xs.get_angle_index(p_u); + int nuc_t = 0; + int nuc_a = 0; if (i_nuclide >= 0) { - nuc_xs.set_temperature_index(p.sqrtkT()); - nuc_xs.set_angle_index(p_u); + nuc_t = nuc_xs.get_temperature_index(p.sqrtkT()); + nuc_a = nuc_xs.get_angle_index(p_u); } for (auto i = 0; i < tally.scores_.size(); ++i) { @@ -1532,14 +1691,7 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // All events score to a flux bin. We actually use a collision estimator // in place of an analog one since there is no way to count 'events' // exactly for the flux - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = p.wgt_last() + p.wgt_absorb(); - } else { - score = p.wgt_last(); - } - score *= flux / p.macro_xs().total; + score = flux * p.wgt_last() / p.macro_xs().total; } else { score = flux; } @@ -1549,21 +1701,16 @@ void score_general_mg(Particle& p, int i_tally, int start_index, if (tally.estimator_ == TallyEstimator::ANALOG) { // All events will score to the total reaction rate. We can just use // use the weight of the particle entering the collision as the score - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = p.wgt_last() + p.wgt_absorb(); - } else { - score = p.wgt_last(); - } - // TODO: should flux be multiplied in above instead of below? + score = flux * p.wgt_last(); if (i_nuclide >= 0) { - score *= flux * atom_density * nuc_xs.get_xs(MgxsType::TOTAL, p_g) / - macro_xs.get_xs(MgxsType::TOTAL, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::TOTAL, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::TOTAL, p_g, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = atom_density * flux * nuc_xs.get_xs(MgxsType::TOTAL, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::TOTAL, p_g, nuc_t, nuc_a); } else { score = p.macro_xs().total * flux; } @@ -1576,25 +1723,23 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // All events score to an inverse velocity bin. We actually use a // collision estimator in place of an analog one since there is no way // to count 'events' exactly for the inverse velocity - if (settings::survival_biasing) { - // We need to account for the fact that some weight was already - // absorbed - score = p.wgt_last() + p.wgt_absorb(); - } else { - score = p.wgt_last(); - } + score = flux * p.wgt_last(); if (i_nuclide >= 0) { - score *= flux * nuc_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g) / - macro_xs.get_xs(MgxsType::TOTAL, p_g); + score *= + nuc_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::TOTAL, p_g, macro_t, macro_a); } else { - score *= flux * macro_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g) / - macro_xs.get_xs(MgxsType::TOTAL, p_g); + score *= + macro_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g, macro_t, macro_a) / + macro_xs.get_xs(MgxsType::TOTAL, p_g, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = flux * nuc_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g); + score = + flux * nuc_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::INVERSE_VELOCITY, p_g); + score = flux * macro_xs.get_xs( + MgxsType::INVERSE_VELOCITY, p_g, macro_t, macro_a); } } break; @@ -1606,22 +1751,22 @@ void score_general_mg(Particle& p, int i_tally, int start_index, continue; // Since only scattering events make it here, again we can use the // weight entering the collision as the estimator for the reaction rate - score = p.wgt_last() * flux; + score = (p.wgt_last() - wgt_absorb) * flux; if (i_nuclide >= 0) { score *= atom_density * nuc_xs.get_xs(MgxsType::SCATTER_FMU, p.g_last(), &p.g(), - &p.mu(), nullptr) / + &p.mu(), nullptr, nuc_t, nuc_a) / macro_xs.get_xs(MgxsType::SCATTER_FMU, p.g_last(), &p.g(), - &p.mu(), nullptr); + &p.mu(), nullptr, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = - atom_density * flux * - nuc_xs.get_xs(MgxsType::SCATTER, p_g, nullptr, &p.mu(), nullptr); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::SCATTER, p_g, nullptr, &p.mu(), + nullptr, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs( - MgxsType::SCATTER, p_g, nullptr, &p.mu(), nullptr); + score = flux * macro_xs.get_xs(MgxsType::SCATTER, p_g, nullptr, + &p.mu(), nullptr, macro_t, macro_a); } } break; @@ -1634,23 +1779,24 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // For scattering production, we need to use the pre-collision weight // times the multiplicity as the estimate for the number of neutrons // exiting a reaction with neutrons in the exit channel - score = p.wgt() * flux; + score = (p.wgt_last() - wgt_absorb) * flux; // Since we transport based on material data, the angle selected // was not selected from the f(mu) for the nuclide. Therefore // adjust the score by the actual probability for that nuclide. if (i_nuclide >= 0) { score *= atom_density * nuc_xs.get_xs(MgxsType::NU_SCATTER_FMU, p.g_last(), &p.g(), - &p.mu(), nullptr) / + &p.mu(), nullptr, nuc_t, nuc_a) / macro_xs.get_xs(MgxsType::NU_SCATTER_FMU, p.g_last(), &p.g(), - &p.mu(), nullptr); + &p.mu(), nullptr, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = - atom_density * flux * nuc_xs.get_xs(MgxsType::NU_SCATTER, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::NU_SCATTER, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::NU_SCATTER, p_g); + score = + flux * macro_xs.get_xs(MgxsType::NU_SCATTER, p_g, macro_t, macro_a); } } break; @@ -1660,7 +1806,7 @@ void score_general_mg(Particle& p, int i_tally, int start_index, if (settings::survival_biasing) { // No absorption events actually occur if survival biasing is on -- // just use weight absorbed in survival biasing - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; } else { // Skip any event where the particle wasn't absorbed if (p.event() == TallyEvent::SCATTER) @@ -1670,13 +1816,14 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = p.wgt_last() * flux; } if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::ABSORPTION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::ABSORPTION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = - atom_density * flux * nuc_xs.get_xs(MgxsType::ABSORPTION, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::ABSORPTION, p_g, nuc_t, nuc_a); } else { score = p.macro_xs().absorption * flux; } @@ -1689,7 +1836,7 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // fission - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; } else { // Skip any non-absorption events if (p.event() == TallyEvent::SCATTER) @@ -1700,17 +1847,20 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = p.wgt_last() * flux; } if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } else { - score *= macro_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = atom_density * flux * nuc_xs.get_xs(MgxsType::FISSION, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::FISSION, p_g); + score = + flux * macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } } break; @@ -1729,13 +1879,16 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // nu-fission - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::NU_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= + atom_density * + nuc_xs.get_xs(MgxsType::NU_FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } else { - score *= macro_xs.get_xs(MgxsType::NU_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= + macro_xs.get_xs(MgxsType::NU_FISSION, p_g, macro_t, macro_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } } else { // Skip any non-fission events @@ -1748,16 +1901,18 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // score. score = simulation::keff * p.wgt_bank() * flux; if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::FISSION, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } } } else { if (i_nuclide >= 0) { - score = - atom_density * flux * nuc_xs.get_xs(MgxsType::NU_FISSION, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::NU_FISSION, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::NU_FISSION, p_g); + score = + flux * macro_xs.get_xs(MgxsType::NU_FISSION, p_g, macro_t, macro_a); } } break; @@ -1776,14 +1931,17 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // prompt-nu-fission - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; if (i_nuclide >= 0) { - score *= atom_density * - nuc_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= + atom_density * + nuc_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } else { - score *= macro_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= + macro_xs.get_xs( + MgxsType::PROMPT_NU_FISSION, p_g, macro_t, macro_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } } else { // Skip any non-fission events @@ -1799,16 +1957,18 @@ void score_general_mg(Particle& p, int i_tally, int start_index, auto prompt_frac = 1. - n_delayed / static_cast(p.n_bank()); score = simulation::keff * p.wgt_bank() * prompt_frac * flux; if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::FISSION, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } } } else { if (i_nuclide >= 0) { score = atom_density * flux * - nuc_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g); + nuc_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::PROMPT_NU_FISSION, p_g); + score = flux * macro_xs.get_xs( + MgxsType::PROMPT_NU_FISSION, p_g, macro_t, macro_a); } } break; @@ -1827,7 +1987,8 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // delayed-nu-fission - double abs_xs = macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + double abs_xs = + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); if (abs_xs > 0.) { if (tally.delayedgroup_filter_ != C_NONE) { auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; @@ -1837,14 +1998,14 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // Tally each delayed group bin individually for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { auto d = filt.groups()[d_bin] - 1; - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; if (i_nuclide >= 0) { score *= nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, nuc_t, nuc_a) / abs_xs; } else { score *= macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, macro_t, macro_a) / abs_xs; } score_fission_delayed_dg( @@ -1855,13 +2016,15 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // If the delayed group filter is not present, compute the score // by multiplying the absorbed weight by the fraction of the // delayed-nu-fission xs to the absorption xs - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; if (i_nuclide >= 0) { - score *= - nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g) / abs_xs; + score *= nuc_xs.get_xs( + MgxsType::DELAYED_NU_FISSION, p_g, nuc_t, nuc_a) / + abs_xs; } else { - score *= - macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g) / abs_xs; + score *= macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, + macro_t, macro_a) / + abs_xs; } } } @@ -1886,8 +2049,10 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = simulation::keff * p.wgt_bank() / p.n_bank() * p.n_delayed_bank(d - 1) * flux; if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::FISSION, p_g); + score *= + atom_density * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } score_fission_delayed_dg( i_tally, d_bin, score, score_index, p.filter_matches()); @@ -1900,8 +2065,10 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = simulation::keff * p.wgt_bank() / p.n_bank() * n_delayed * flux; if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::FISSION, p_g); + score *= + atom_density * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } } } @@ -1916,10 +2083,10 @@ void score_general_mg(Particle& p, int i_tally, int start_index, if (i_nuclide >= 0) { score = flux * atom_density * nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, nullptr, - nullptr, &d); + nullptr, &d, nuc_t, nuc_a); } else { score = flux * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d); + nullptr, nullptr, &d, macro_t, macro_a); } score_fission_delayed_dg( i_tally, d_bin, score, score_index, p.filter_matches()); @@ -1927,10 +2094,12 @@ void score_general_mg(Particle& p, int i_tally, int start_index, continue; } else { if (i_nuclide >= 0) { - score = flux * atom_density * - nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g); + score = + flux * atom_density * + nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g); + score = flux * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, + macro_t, macro_a); } } } @@ -1942,7 +2111,8 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // delayed-nu-fission - double abs_xs = macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + double abs_xs = + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); if (abs_xs > 0) { if (tally.delayedgroup_filter_ != C_NONE) { auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; @@ -1952,18 +2122,18 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // Tally each delayed group bin individually for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { auto d = filt.groups()[d_bin] - 1; - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; if (i_nuclide >= 0) { - score *= nuc_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + score *= nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, nuc_t, nuc_a) * nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, nuc_t, nuc_a) / abs_xs; } else { - score *= macro_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + score *= macro_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, macro_t, macro_a) * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, macro_t, macro_a) / abs_xs; } score_fission_delayed_dg( @@ -1978,18 +2148,18 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = 0.; for (auto d = 0; d < data::mg.num_delayed_groups_; ++d) { if (i_nuclide >= 0) { - score += p.wgt_absorb() * flux * - nuc_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + score += wgt_absorb * flux * + nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, nuc_t, nuc_a) * nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, nuc_t, nuc_a) / abs_xs; } else { - score += p.wgt_absorb() * flux * - macro_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + score += wgt_absorb * flux * + macro_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, macro_t, macro_a) * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d) / + nullptr, nullptr, &d, macro_t, macro_a) / abs_xs; } } @@ -2012,15 +2182,16 @@ void score_general_mg(Particle& p, int i_tally, int start_index, auto d = bank.delayed_group - 1; if (d != -1) { if (i_nuclide >= 0) { - score += simulation::keff * atom_density * bank.wgt * flux * - nuc_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * - nuc_xs.get_xs(MgxsType::FISSION, p_g) / - macro_xs.get_xs(MgxsType::FISSION, p_g); + score += + simulation::keff * atom_density * bank.wgt * flux * + nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d, + nuc_t, nuc_a) * + nuc_xs.get_xs(MgxsType::FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::FISSION, p_g, macro_t, macro_a); } else { score += simulation::keff * bank.wgt * flux * - macro_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d); + macro_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, macro_t, macro_a); } if (tally.delayedgroup_filter_ != C_NONE) { auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; @@ -2050,17 +2221,17 @@ void score_general_mg(Particle& p, int i_tally, int start_index, for (auto d_bin = 0; d_bin < filt.n_bins(); ++d_bin) { auto d = filt.groups()[d_bin] - 1; if (i_nuclide >= 0) { - score = - atom_density * flux * - nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * - nuc_xs.get_xs( - MgxsType::DELAYED_NU_FISSION, p_g, nullptr, nullptr, &d); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, nullptr, + &d, nuc_t, nuc_a) * + nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, nullptr, + nullptr, &d, nuc_t, nuc_a); } else { score = flux * - macro_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + macro_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, macro_t, macro_a) * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d); + nullptr, nullptr, &d, macro_t, macro_a); } score_fission_delayed_dg( i_tally, d_bin, score, score_index, p.filter_matches()); @@ -2070,17 +2241,17 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = 0.; for (auto d = 0; d < data::mg.num_delayed_groups_; ++d) { if (i_nuclide >= 0) { - score += - atom_density * flux * - nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * - nuc_xs.get_xs( - MgxsType::DELAYED_NU_FISSION, p_g, nullptr, nullptr, &d); + score += atom_density * flux * + nuc_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, nuc_t, nuc_a) * + nuc_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, nullptr, + nullptr, &d, nuc_t, nuc_a); } else { score += flux * - macro_xs.get_xs( - MgxsType::DECAY_RATE, p_g, nullptr, nullptr, &d) * + macro_xs.get_xs(MgxsType::DECAY_RATE, p_g, nullptr, + nullptr, &d, macro_t, macro_a) * macro_xs.get_xs(MgxsType::DELAYED_NU_FISSION, p_g, - nullptr, nullptr, &d); + nullptr, nullptr, &d, macro_t, macro_a); } } } @@ -2093,7 +2264,7 @@ void score_general_mg(Particle& p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // fission scaled by the Q-value - score = p.wgt_absorb() * flux; + score = wgt_absorb * flux; } else { // Skip any non-absorption events if (p.event() == TallyEvent::SCATTER) @@ -2104,18 +2275,21 @@ void score_general_mg(Particle& p, int i_tally, int start_index, score = p.wgt_last() * flux; } if (i_nuclide >= 0) { - score *= atom_density * nuc_xs.get_xs(MgxsType::KAPPA_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= atom_density * + nuc_xs.get_xs(MgxsType::KAPPA_FISSION, p_g, nuc_t, nuc_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } else { - score *= macro_xs.get_xs(MgxsType::KAPPA_FISSION, p_g) / - macro_xs.get_xs(MgxsType::ABSORPTION, p_g); + score *= + macro_xs.get_xs(MgxsType::KAPPA_FISSION, p_g, macro_t, macro_a) / + macro_xs.get_xs(MgxsType::ABSORPTION, p_g, macro_t, macro_a); } } else { if (i_nuclide >= 0) { - score = - atom_density * flux * nuc_xs.get_xs(MgxsType::KAPPA_FISSION, p_g); + score = atom_density * flux * + nuc_xs.get_xs(MgxsType::KAPPA_FISSION, p_g, nuc_t, nuc_a); } else { - score = flux * macro_xs.get_xs(MgxsType::KAPPA_FISSION, p_g); + score = flux * macro_xs.get_xs( + MgxsType::KAPPA_FISSION, p_g, macro_t, macro_a); } } break; @@ -2137,43 +2311,6 @@ void score_general_mg(Particle& p, int i_tally, int start_index, } } -//! Tally rates for when the user requests a tally on all nuclides. - -void score_all_nuclides( - Particle& p, int i_tally, double flux, int filter_index, double filter_weight) -{ - const Tally& tally {*model::tallies[i_tally]}; - const Material& material {*model::materials[p.material()]}; - - // Score all individual nuclide reaction rates. - for (auto i = 0; i < material.nuclide_.size(); ++i) { - auto i_nuclide = material.nuclide_[i]; - auto atom_density = material.atom_density_(i); - - // TODO: consider replacing this "if" with pointers or templates - if (settings::run_CE) { - score_general_ce(p, i_tally, i_nuclide * tally.scores_.size(), - filter_index, filter_weight, i_nuclide, atom_density, flux); - } else { - score_general_mg(p, i_tally, i_nuclide * tally.scores_.size(), - filter_index, filter_weight, i_nuclide, atom_density, flux); - } - } - - // Score total material reaction rates. - int i_nuclide = -1; - double atom_density = 0.; - auto n_nuclides = data::nuclides.size(); - // TODO: consider replacing this "if" with pointers or templates - if (settings::run_CE) { - score_general_ce(p, i_tally, n_nuclides * tally.scores_.size(), - filter_index, filter_weight, i_nuclide, atom_density, flux); - } else { - score_general_mg(p, i_tally, n_nuclides * tally.scores_.size(), - filter_index, filter_weight, i_nuclide, atom_density, flux); - } -} - void score_analog_tally_ce(Particle& p) { // Since electrons/positrons are not transported, we assign a flux of zero. @@ -2201,30 +2338,15 @@ void score_analog_tally_ce(Particle& p) auto filter_weight = filter_iter.weight_; // Loop over nuclide bins. - if (!tally.all_nuclides_) { - for (auto i = 0; i < tally.nuclides_.size(); ++i) { - auto i_nuclide = tally.nuclides_[i]; + for (auto i = 0; i < tally.nuclides_.size(); ++i) { + auto i_nuclide = tally.nuclides_[i]; - // Tally this event in the present nuclide bin if that bin represents - // the event nuclide or the total material. Note that the atomic - // density argument for score_general is not used for analog tallies. - if (i_nuclide == p.event_nuclide() || i_nuclide == -1) - score_general_ce(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, i_nuclide, -1.0, flux); - } - - } else { - // In the case that the user has requested to tally all nuclides, we - // can take advantage of the fact that we know exactly how nuclide - // bins correspond to nuclide indices. First, tally the nuclide. - auto i = p.event_nuclide(); - score_general_ce(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, -1, -1.0, flux); - - // Now tally the total material. - i = tally.nuclides_.size(); - score_general_ce(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, -1, -1.0, flux); + // Tally this event in the present nuclide bin if that bin represents + // the event nuclide or the total material. Note that the atomic + // density argument for score_general is not used for analog tallies. + if (i_nuclide == p.event_nuclide() || i_nuclide == -1) + score_general_ce_analog(p, i_tally, i * tally.scores_.size(), + filter_index, filter_weight, i_nuclide, -1.0, flux); } } @@ -2269,7 +2391,8 @@ void score_analog_tally_mg(Particle& p) model::materials[p.material()]->mat_nuclide_index_[i_nuclide]; if (j == C_NONE) continue; - atom_density = model::materials[p.material()]->atom_density_(j); + atom_density = + model::materials[p.material()]->atom_density(j, p.density_mult()); } score_general_mg(p, i_tally, i * tally.scores_.size(), filter_index, @@ -2290,12 +2413,13 @@ void score_analog_tally_mg(Particle& p) match.bins_present_ = false; } -void score_tracklength_tally(Particle& p, double distance) +void score_tracklength_tally_general( + Particle& p, double flux, const vector& tallies) { - // Determine the tracklength estimate of the flux - double flux = p.wgt() * distance; + // Set 'none' value for log union grid index + int i_log_union = C_NONE; - for (auto i_tally : model::active_tracklength_tallies) { + for (auto i_tally : tallies) { const Tally& tally {*model::tallies[i_tally]}; // Initialize an iterator over valid filter bin combinations. If there are @@ -2312,34 +2436,42 @@ void score_tracklength_tally(Particle& p, double distance) auto filter_weight = filter_iter.weight_; // Loop over nuclide bins. - if (tally.all_nuclides_) { - if (p.material() != MATERIAL_VOID) - score_all_nuclides( - p, i_tally, flux * filter_weight, filter_index, filter_weight); + for (auto i = 0; i < tally.nuclides_.size(); ++i) { + auto i_nuclide = tally.nuclides_[i]; - } else { - for (auto i = 0; i < tally.nuclides_.size(); ++i) { - auto i_nuclide = tally.nuclides_[i]; + double atom_density = 0.; + if (i_nuclide >= 0) { + if (p.material() != MATERIAL_VOID) { + const auto& mat = model::materials[p.material()]; + auto j = mat->mat_nuclide_index_[i_nuclide]; + if (j == C_NONE) { + // Determine log union grid index + if (i_log_union == C_NONE) { + int neutron = static_cast(ParticleType::neutron); + i_log_union = std::log(p.E() / data::energy_min[neutron]) / + simulation::log_spacing; + } - double atom_density = 0.; - if (i_nuclide >= 0) { - if (p.material() != MATERIAL_VOID) { - auto j = - model::materials[p.material()]->mat_nuclide_index_[i_nuclide]; - if (j == C_NONE) - continue; - atom_density = model::materials[p.material()]->atom_density_(j); + // Update micro xs cache + if (!tally.multiply_density()) { + p.update_neutron_xs(i_nuclide, i_log_union); + atom_density = 1.0; + } + } else { + atom_density = tally.multiply_density() + ? mat->atom_density(j, p.density_mult()) + : 1.0; } } + } - // TODO: consider replacing this "if" with pointers or templates - if (settings::run_CE) { - score_general_ce(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, i_nuclide, atom_density, flux); - } else { - score_general_mg(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, i_nuclide, atom_density, flux); - } + // TODO: consider replacing this "if" with pointers or templates + if (settings::run_CE) { + score_general_ce_nonanalog(p, i_tally, i * tally.scores_.size(), + filter_index, filter_weight, i_nuclide, atom_density, flux); + } else { + score_general_mg(p, i_tally, i * tally.scores_.size(), filter_index, + filter_weight, i_nuclide, atom_density, flux); } } } @@ -2357,18 +2489,68 @@ void score_tracklength_tally(Particle& p, double distance) match.bins_present_ = false; } +void score_timed_tracklength_tally(Particle& p, double total_distance) +{ + double speed = p.speed(); + double total_dt = total_distance / speed; + + // save particle last state + auto time_last = p.time_last(); + auto r_last = p.r_last(); + + // move particle back + p.move_distance(-total_distance); + p.time() -= total_dt; + p.lifetime() -= total_dt; + + double distance_traveled = 0.0; + while (distance_traveled < total_distance) { + + double distance = std::min(distance_to_time_boundary(p.time(), speed), + total_distance - distance_traveled); + double dt = distance / speed; + + // Save particle last state for tracklength tallies + p.time_last() = p.time(); + p.r_last() = p.r(); + + // Advance particle in space and time + p.move_distance(distance); + p.time() += dt; + p.lifetime() += dt; + + // Determine the tracklength estimate of the flux + double flux = p.wgt() * distance; + + score_tracklength_tally_general( + p, flux, model::active_timed_tracklength_tallies); + distance_traveled += distance; + } + + p.time_last() = time_last; + p.r_last() = r_last; +} + +void score_tracklength_tally(Particle& p, double distance) +{ + + // Determine the tracklength estimate of the flux + double flux = p.wgt() * distance; + + score_tracklength_tally_general(p, flux, model::active_tracklength_tallies); +} + void score_collision_tally(Particle& p) { // Determine the collision estimate of the flux double flux = 0.0; if (p.type() == ParticleType::neutron || p.type() == ParticleType::photon) { - if (!settings::survival_biasing) { - flux = p.wgt_last() / p.macro_xs().total; - } else { - flux = (p.wgt_last() + p.wgt_absorb()) / p.macro_xs().total; - } + flux = p.wgt_last() / p.macro_xs().total; } + // Set 'none value for log union grid index + int i_log_union = C_NONE; + for (auto i_tally : model::active_collision_tallies) { const Tally& tally {*model::tallies[i_tally]}; @@ -2386,32 +2568,41 @@ void score_collision_tally(Particle& p) auto filter_weight = filter_iter.weight_; // Loop over nuclide bins. - if (tally.all_nuclides_) { - score_all_nuclides( - p, i_tally, flux * filter_weight, filter_index, filter_weight); + for (auto i = 0; i < tally.nuclides_.size(); ++i) { + auto i_nuclide = tally.nuclides_[i]; - } else { - for (auto i = 0; i < tally.nuclides_.size(); ++i) { - auto i_nuclide = tally.nuclides_[i]; + double atom_density = 0.; + if (i_nuclide >= 0) { + const auto& mat = model::materials[p.material()]; + auto j = mat->mat_nuclide_index_[i_nuclide]; + if (j == C_NONE) { + // Determine log union grid index + if (i_log_union == C_NONE) { + int neutron = static_cast(ParticleType::neutron); + i_log_union = std::log(p.E() / data::energy_min[neutron]) / + simulation::log_spacing; + } - double atom_density = 0.; - if (i_nuclide >= 0) { - auto j = - model::materials[p.material()]->mat_nuclide_index_[i_nuclide]; - if (j == C_NONE) - continue; - atom_density = model::materials[p.material()]->atom_density_(j); - } - - // TODO: consider replacing this "if" with pointers or templates - if (settings::run_CE) { - score_general_ce(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, i_nuclide, atom_density, flux); + // Update micro xs cache + if (!tally.multiply_density()) { + p.update_neutron_xs(i_nuclide, i_log_union); + atom_density = 1.0; + } } else { - score_general_mg(p, i_tally, i * tally.scores_.size(), filter_index, - filter_weight, i_nuclide, atom_density, flux); + atom_density = tally.multiply_density() + ? mat->atom_density(j, p.density_mult()) + : 1.0; } } + + // TODO: consider replacing this "if" with pointers or templates + if (settings::run_CE) { + score_general_ce_nonanalog(p, i_tally, i * tally.scores_.size(), + filter_index, filter_weight, i_nuclide, atom_density, flux); + } else { + score_general_mg(p, i_tally, i * tally.scores_.size(), filter_index, + filter_weight, i_nuclide, atom_density, flux); + } } } @@ -2430,8 +2621,7 @@ void score_collision_tally(Particle& p) void score_surface_tally(Particle& p, const vector& tallies) { - // No collision, so no weight change when survival biasing - double flux = p.wgt(); + double current = p.wgt_last(); for (auto i_tally : tallies) { auto& tally {*model::tallies[i_tally]}; @@ -2452,7 +2642,7 @@ void score_surface_tally(Particle& p, const vector& tallies) // Loop over scores. // There is only one score type for current tallies so there is no need // for a further scoring function. - double score = flux * filter_weight; + double score = current * filter_weight; for (auto score_index = 0; score_index < tally.scores_.size(); ++score_index) { #pragma omp atomic @@ -2473,4 +2663,79 @@ void score_surface_tally(Particle& p, const vector& tallies) match.bins_present_ = false; } +void score_pulse_height_tally(Particle& p, const vector& tallies) +{ + // The pulse height tally in OpenMC hijacks the logic of CellFilter and + // EnergyFilter to score specific quantities related to particle pulse height. + // This is achieved by setting the pulse-height cell of the tally to the cell + // of the particle being scored, and the energy to the particle's last + // recorded energy (E_last()). After the tally is scored, the values are reset + // to ensure proper accounting and avoid interference with subsequent + // calculations or tallies. + + // Save original cell/energy information + int orig_n_coord = p.n_coord(); + int orig_cell = p.coord(0).cell(); + double orig_E_last = p.E_last(); + + for (auto i_tally : tallies) { + auto& tally {*model::tallies[i_tally]}; + + // Determine all CellFilter in the tally + for (const auto& filter : tally.filters()) { + auto cell_filter = + dynamic_cast(model::tally_filters[filter].get()); + if (cell_filter != nullptr) { + + const auto& cells = cell_filter->cells(); + // Loop over all cells in the CellFilter + for (auto cell_index = 0; cell_index < cells.size(); ++cell_index) { + int cell_id = cells[cell_index]; + + // Temporarily change cell of particle + p.n_coord() = 1; + p.coord(0).cell() = cell_id; + + // Determine index of cell in model::pulse_height_cells + auto it = std::find(model::pulse_height_cells.begin(), + model::pulse_height_cells.end(), cell_id); + int index = std::distance(model::pulse_height_cells.begin(), it); + + // Temporarily change energy of particle to pulse-height value + p.E_last() = p.pht_storage()[index]; + + // Initialize an iterator over valid filter bin combinations. If + // there are no valid combinations, use a continue statement to ensure + // we skip the assume_separate break below. + auto filter_iter = FilterBinIter(tally, p); + auto end = FilterBinIter(tally, true, &p.filter_matches()); + if (filter_iter == end) + continue; + + // Loop over filter bins. + for (; filter_iter != end; ++filter_iter) { + auto filter_index = filter_iter.index_; + auto filter_weight = filter_iter.weight_; + + // Loop over scores. + for (auto score_index = 0; score_index < tally.scores_.size(); + ++score_index) { +#pragma omp atomic + tally.results_(filter_index, score_index, TallyResult::VALUE) += + filter_weight; + } + } + + // Reset all the filter matches for the next tally event. + for (auto& match : p.filter_matches()) + match.bins_present_ = false; + } + } + } + // Restore cell/energy + p.n_coord() = orig_n_coord; + p.coord(0).cell() = orig_cell; + p.E_last() = orig_E_last; + } +} } // namespace openmc diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 2c155980e7..f1f83e2982 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -37,6 +37,11 @@ std::pair get_tally_uncertainty( int n = tally->n_realizations_; auto mean = sum / n; + + // if the result has no contributions, return an invalid pair + if (mean == 0) + return {-1, -1}; + double std_dev = std::sqrt((sum_sq / n - mean * mean) / (n - 1)); double rel_err = (mean != 0.) ? std_dev / std::abs(mean) : 0.; @@ -68,42 +73,49 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score) const auto& results = t.results_; for (auto filter_index = 0; filter_index < results.shape()[0]; ++filter_index) { - for (auto score_index = 0; score_index < results.shape()[1]; - ++score_index) { - // Compute the tally uncertainty metrics. - auto uncert_pair = - get_tally_uncertainty(i_tally, score_index, filter_index); - double std_dev = uncert_pair.first; - double rel_err = uncert_pair.second; + // Compute the tally uncertainty metrics. + auto uncert_pair = + get_tally_uncertainty(i_tally, trigger.score_index, filter_index); - // Pick out the relevant uncertainty metric for this trigger. - double uncertainty; - switch (trigger.metric) { - case TriggerMetric::variance: - uncertainty = std_dev * std_dev; - break; - case TriggerMetric::standard_deviation: - uncertainty = std_dev; - break; - case TriggerMetric::relative_error: - uncertainty = rel_err; - break; - case TriggerMetric::not_active: - UNREACHABLE(); - } + // If there is a score without contributions, set ratio to inf and + // exit early, unless zero scores are ignored for this trigger. + if (uncert_pair.first == -1 && !trigger.ignore_zeros) { + ratio = INFINITY; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; + return; + } - // Compute the uncertainty / threshold ratio. - double this_ratio = uncertainty / trigger.threshold; - if (trigger.metric == TriggerMetric::variance) { - this_ratio = std::sqrt(ratio); - } + double std_dev = uncert_pair.first; + double rel_err = uncert_pair.second; - // If this is the most uncertain value, set the output variables. - if (this_ratio > ratio) { - ratio = this_ratio; - score = t.scores_[trigger.score_index]; - tally_id = t.id_; - } + // Pick out the relevant uncertainty metric for this trigger. + double uncertainty; + switch (trigger.metric) { + case TriggerMetric::variance: + uncertainty = std_dev * std_dev; + break; + case TriggerMetric::standard_deviation: + uncertainty = std_dev; + break; + case TriggerMetric::relative_error: + uncertainty = rel_err; + break; + case TriggerMetric::not_active: + UNREACHABLE(); + } + + // Compute the uncertainty / threshold ratio. + double this_ratio = uncertainty / trigger.threshold; + if (trigger.metric == TriggerMetric::variance) { + this_ratio = std::sqrt(ratio); + } + + // If this is the most uncertain value, set the output variables. + if (this_ratio > ratio) { + ratio = this_ratio; + score = t.scores_[trigger.score_index]; + tally_id = t.id_; } } } @@ -181,9 +193,15 @@ void check_triggers() "eigenvalue", keff_ratio); } else { - msg = fmt::format( - "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", - tally_ratio, reaction_name(score), tally_id); + if (tally_ratio == INFINITY) { + msg = fmt::format( + "Triggers unsatisfied, no result tallied for score {} in tally {}", + reaction_name(score), tally_id); + } else { + msg = fmt::format( + "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}", + tally_ratio, reaction_name(score), tally_id); + } } write_message(msg, 7); @@ -196,13 +214,20 @@ void check_triggers() auto n_pred_batches = static_cast(n_active * max_ratio * max_ratio) + settings::n_inactive + 1; - std::string msg = - fmt::format("The estimated number of batches is {}", n_pred_batches); - if (n_pred_batches > settings::n_max_batches) { - msg.append(" --- greater than max batches"); - warning(msg); - } else { + if (max_ratio == INFINITY) { + std::string msg = + fmt::format("One or more tallies with triggers have no scores. Unable " + "to estimate the number of remaining batches."); write_message(msg, 7); + } else { + std::string msg = + fmt::format("The estimated number of batches is {}", n_pred_batches); + if (n_pred_batches > settings::n_max_batches) { + msg.append(" --- greater than max batches"); + warning(msg); + } else { + write_message(msg, 7); + } } } } diff --git a/src/thermal.cpp b/src/thermal.cpp index 1101d54857..cbe0983ed6 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -19,6 +19,7 @@ #include "openmc/secondary_correlated.h" #include "openmc/secondary_thermal.h" #include "openmc/settings.h" +#include "openmc/string_utils.h" namespace openmc { @@ -59,7 +60,7 @@ ThermalScattering::ThermalScattering( // Read temperature value double T; read_dataset(kT_group, dset_names[i].data(), T); - temps_available[i] = T / K_BOLTZMANN; + temps_available[i] = std::round(T / K_BOLTZMANN); } std::sort(temps_available.begin(), temps_available.end()); @@ -89,9 +90,12 @@ ThermalScattering::ThermalScattering( temps_to_read.push_back(std::round(temp_actual)); } } else { - fatal_error(fmt::format("Nuclear data library does not contain cross " - "sections for {} at or near {} K.", - name_, std::round(T))); + fatal_error(fmt::format( + "Nuclear data library does not contain cross sections " + "for {} at or near {} K. Available temperatures " + "are {} K. Consider making use of openmc.Settings.temperature " + "to specify how intermediate temperatures are treated.", + name_, std::round(T), concatenate(temps_available))); } } break; @@ -103,8 +107,8 @@ ThermalScattering::ThermalScattering( bool found = false; for (int j = 0; j < temps_available.size() - 1; ++j) { if (temps_available[j] <= T && T < temps_available[j + 1]) { - int T_j = std::round(temps_available[j]); - int T_j1 = std::round(temps_available[j + 1]); + int T_j = temps_available[j]; + int T_j1 = temps_available[j + 1]; if (std::find(temps_to_read.begin(), temps_to_read.end(), T_j) == temps_to_read.end()) { temps_to_read.push_back(T_j); @@ -117,10 +121,26 @@ ThermalScattering::ThermalScattering( } } if (!found) { - fatal_error( - fmt::format("Nuclear data library does not contain cross " - "sections for {} at temperatures that bound {} K.", - name_, std::round(T))); + // If no pairs found, check if the desired temperature falls within + // bounds' tolerance + if (std::abs(T - temps_available[0]) <= + settings::temperature_tolerance) { + if (std::find(temps_to_read.begin(), temps_to_read.end(), + temps_available[0]) == temps_to_read.end()) { + temps_to_read.push_back(temps_available[0]); + } + } else if (std::abs(T - temps_available[n - 1]) <= + settings::temperature_tolerance) { + if (std::find(temps_to_read.begin(), temps_to_read.end(), + temps_available[n - 1]) == temps_to_read.end()) { + temps_to_read.push_back(temps_available[n - 1]); + } + } else { + fatal_error( + fmt::format("Nuclear data library does not contain cross " + "sections for {} at temperatures that bound {} K.", + name_, std::round(T))); + } } } } @@ -159,20 +179,27 @@ void ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, auto n = kTs_.size(); if (n > 1) { - // Find temperatures that bound the actual temperature - while (kTs_[i + 1] < kT && i + 1 < n - 1) - ++i; - if (settings::temperature_method == TemperatureMethod::NEAREST) { + while (kTs_[i + 1] < kT && i + 1 < n - 1) + ++i; // Pick closer of two bounding temperatures if (kT - kTs_[i] > kTs_[i + 1] - kT) ++i; - } else { - // Randomly sample between temperature i and i+1 - double f = (kT - kTs_[i]) / (kTs_[i + 1] - kTs_[i]); - if (f > prn(seed)) - ++i; + // If current kT outside of the bounds of available, snap to the bound + if (kT < kTs_.front()) { + i = 0; + } else if (kT > kTs_.back()) { + i = kTs_.size() - 1; + } else { + // Find temperatures that bound the actual temperature + while (kTs_[i + 1] < kT && i + 1 < n - 1) + ++i; + // Randomly sample between temperature i and i+1 + double f = (kT - kTs_[i]) / (kTs_[i + 1] - kTs_[i]); + if (f > prn(seed)) + ++i; + } } } @@ -210,14 +237,22 @@ ThermalData::ThermalData(hid_t group) if (temp == "coherent_elastic") { auto xs = dynamic_cast(elastic_.xs.get()); elastic_.distribution = make_unique(*xs); - } else { - if (temp == "incoherent_elastic") { - elastic_.distribution = make_unique(dgroup); - } else if (temp == "incoherent_elastic_discrete") { - auto xs = dynamic_cast(elastic_.xs.get()); - elastic_.distribution = - make_unique(dgroup, xs->x()); - } + } else if (temp == "incoherent_elastic") { + elastic_.distribution = make_unique(dgroup); + } else if (temp == "incoherent_elastic_discrete") { + auto xs = dynamic_cast(elastic_.xs.get()); + elastic_.distribution = + make_unique(dgroup, xs->x()); + } else if (temp == "mixed_elastic") { + // Get coherent/incoherent cross sections + auto mixed_xs = dynamic_cast(elastic_.xs.get()); + const auto& coh_xs = + dynamic_cast(mixed_xs->functions(0).get()); + const auto& incoh_xs = mixed_xs->functions(1).get(); + + // Create mixed elastic distribution + elastic_.distribution = + make_unique(dgroup, *coh_xs, *incoh_xs); } close_group(elastic_group); diff --git a/src/timer.cpp b/src/timer.cpp index 86436758a3..6d692d4fbf 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -26,6 +26,7 @@ Timer time_event_advance_particle; Timer time_event_surface_crossing; Timer time_event_collision; Timer time_event_death; +Timer time_update_src; } // namespace simulation @@ -85,6 +86,7 @@ void reset_timers() simulation::time_event_surface_crossing.reset(); simulation::time_event_collision.reset(); simulation::time_event_death.reset(); + simulation::time_update_src.reset(); } } // namespace openmc diff --git a/src/track_output.cpp b/src/track_output.cpp index 065c2d465c..f4344d50f7 100644 --- a/src/track_output.cpp +++ b/src/track_output.cpp @@ -2,6 +2,7 @@ #include "openmc/constants.h" #include "openmc/hdf5_interface.h" +#include "openmc/message_passing.h" #include "openmc/position.h" #include "openmc/settings.h" #include "openmc/simulation.h" @@ -9,6 +10,7 @@ #include "xtensor/xtensor.hpp" #include +#include #include // for size_t #include @@ -19,52 +21,134 @@ namespace openmc { // Global variables //============================================================================== +hid_t track_file; //! HDF5 identifier for track file +hid_t track_dtype; //! HDF5 identifier for track datatype +int n_tracks_written; //! Number of tracks written + //============================================================================== // Non-member functions //============================================================================== void add_particle_track(Particle& p) { - p.tracks().emplace_back(); + auto& track = p.tracks().emplace_back(); + track.particle = p.type(); } void write_particle_track(Particle& p) { - p.tracks().back().push_back(p.r()); + p.tracks().back().states.push_back(p.get_track_state()); +} + +void open_track_file() +{ + // Open file and write filetype/version -- when MPI is enabled and there is + // more than one rank, each rank writes its own file +#ifdef OPENMC_MPI + std::string filename; + if (mpi::n_procs > 1) { + filename = fmt::format("{}tracks_p{}.h5", settings::path_output, mpi::rank); + } else { + filename = fmt::format("{}tracks.h5", settings::path_output); + } +#else + std::string filename = fmt::format("{}tracks.h5", settings::path_output); +#endif + track_file = file_open(filename, 'w'); + write_attribute(track_file, "filetype", "track"); + write_attribute(track_file, "version", VERSION_TRACK); + + // Create compound type for Position + hid_t postype = H5Tcreate(H5T_COMPOUND, sizeof(struct Position)); + H5Tinsert(postype, "x", HOFFSET(Position, x), H5T_NATIVE_DOUBLE); + H5Tinsert(postype, "y", HOFFSET(Position, y), H5T_NATIVE_DOUBLE); + H5Tinsert(postype, "z", HOFFSET(Position, z), H5T_NATIVE_DOUBLE); + + // Create compound type for TrackState + track_dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct TrackState)); + H5Tinsert(track_dtype, "r", HOFFSET(TrackState, r), postype); + H5Tinsert(track_dtype, "u", HOFFSET(TrackState, u), postype); + H5Tinsert(track_dtype, "E", HOFFSET(TrackState, E), H5T_NATIVE_DOUBLE); + H5Tinsert(track_dtype, "time", HOFFSET(TrackState, time), H5T_NATIVE_DOUBLE); + H5Tinsert(track_dtype, "wgt", HOFFSET(TrackState, wgt), H5T_NATIVE_DOUBLE); + H5Tinsert( + track_dtype, "cell_id", HOFFSET(TrackState, cell_id), H5T_NATIVE_INT); + H5Tinsert(track_dtype, "cell_instance", HOFFSET(TrackState, cell_instance), + H5T_NATIVE_INT); + H5Tinsert(track_dtype, "material_id", HOFFSET(TrackState, material_id), + H5T_NATIVE_INT); + H5Tclose(postype); +} + +void close_track_file() +{ + H5Tclose(track_dtype); + file_close(track_file); + + // Reset number of tracks written + n_tracks_written = 0; +} + +bool check_track_criteria(const Particle& p) +{ + if (settings::write_all_tracks) { + // Increment number of tracks written and get previous value + int n; +#pragma omp atomic capture + n = n_tracks_written++; + + // Indicate that track should be written for this particle + return n < settings::max_tracks; + } + + // Check for match from explicit track identifiers + if (settings::track_identifiers.size() > 0) { + for (const auto& t : settings::track_identifiers) { + if (simulation::current_batch == t[0] && + simulation::current_gen == t[1] && p.id() == t[2]) { + return true; + } + } + } + return false; } void finalize_particle_track(Particle& p) { - std::string filename = - fmt::format("{}track_{}_{}_{}.h5", settings::path_output, - simulation::current_batch, simulation::current_gen, p.id()); - // Determine number of coordinates for each particle - vector n_coords; - for (auto& coords : p.tracks()) { - n_coords.push_back(coords.size()); + vector offsets; + vector particles; + vector tracks; + int offset = 0; + for (auto& track_i : p.tracks()) { + offsets.push_back(offset); + particles.push_back(static_cast(track_i.particle)); + offset += track_i.states.size(); + tracks.insert(tracks.end(), track_i.states.begin(), track_i.states.end()); } + offsets.push_back(offset); #pragma omp critical(FinalizeParticleTrack) { - hid_t file_id = file_open(filename, 'w'); - write_attribute(file_id, "filetype", "track"); - write_attribute(file_id, "version", VERSION_TRACK); - write_attribute(file_id, "n_particles", p.tracks().size()); - write_attribute(file_id, "n_coords", n_coords); - for (auto i = 1; i <= p.tracks().size(); ++i) { - const auto& t {p.tracks()[i - 1]}; - size_t n = t.size(); - xt::xtensor data({n, 3}); - for (int j = 0; j < n; ++j) { - data(j, 0) = t[j].x; - data(j, 1) = t[j].y; - data(j, 2) = t[j].z; - } - std::string name = fmt::format("coordinates_{}", i); - write_dataset(file_id, name.c_str(), data); - } - file_close(file_id); + // Create name for dataset + std::string dset_name = fmt::format("track_{}_{}_{}", + simulation::current_batch, simulation::current_gen, p.id()); + + // Write array of TrackState to file + hsize_t dims[] {static_cast(tracks.size())}; + hid_t dspace = H5Screate_simple(1, dims, nullptr); + hid_t dset = H5Dcreate(track_file, dset_name.c_str(), track_dtype, dspace, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + H5Dwrite(dset, track_dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, tracks.data()); + + // Write attributes + write_attribute(dset, "n_particles", p.tracks().size()); + write_attribute(dset, "offsets", offsets); + write_attribute(dset, "particles", particles); + + // Free resources + H5Dclose(dset); + H5Sclose(dspace); } // Clear particle tracks diff --git a/src/universe.cpp b/src/universe.cpp new file mode 100644 index 0000000000..054f57d060 --- /dev/null +++ b/src/universe.cpp @@ -0,0 +1,277 @@ +#include "openmc/universe.h" + +#include + +#include "openmc/hdf5_interface.h" +#include "openmc/particle.h" + +namespace openmc { + +namespace model { + +std::unordered_map universe_map; +vector> universes; + +} // namespace model + +//============================================================================== +// Universe implementation +//============================================================================== + +void Universe::to_hdf5(hid_t universes_group) const +{ + // Create a group for this universe. + auto group = create_group(universes_group, fmt::format("universe {}", id_)); + + // Write the geometry representation type. + write_string(group, "geom_type", "csg", false); + + // Write the contained cells. + if (cells_.size() > 0) { + vector cell_ids; + for (auto i_cell : cells_) + cell_ids.push_back(model::cells[i_cell]->id_); + write_dataset(group, "cells", cell_ids); + } + + close_group(group); +} + +bool Universe::find_cell(GeometryState& p) const +{ + if (filled_with_triso_base_ != -1) { + bool found = find_cell_in_virtual_lattice(p); + if (found) { + return found; + } + } + const auto& cells { + !partitioner_ ? cells_ : partitioner_->get_cells(p.r_local(), p.u_local())}; + + Position r {p.r_local()}; + Position u {p.u_local()}; + auto surf = p.surface(); + int32_t i_univ = p.lowest_coord().universe(); + + for (auto i_cell : cells) { + if (model::cells[i_cell]->universe_ != i_univ) + continue; + // Check if this cell contains the particle + if (model::cells[i_cell]->contains(r, u, surf)) { + p.lowest_coord().cell() = i_cell; + return true; + } + } + return false; +} +bool Universe::find_cell_in_virtual_lattice(GeometryState& p) const +{ + Cell& c {*model::cells[model::cell_map[filled_with_triso_base_]]}; + vector lat_ind(3); + Position r {p.r_local()}; + lat_ind[0] = std::max( + std::min( + floor((r.x - c.vl_lower_left_[0]) / c.vl_pitch_[0]), c.vl_shape_[0] - 1), + 0); + lat_ind[1] = std::max( + std::min( + floor((r.y - c.vl_lower_left_[1]) / c.vl_pitch_[1]), c.vl_shape_[1] - 1), + 0); + lat_ind[2] = std::max( + std::min( + floor((r.z - c.vl_lower_left_[2]) / c.vl_pitch_[2]), c.vl_shape_[2] - 1), + 0); + + int32_t i_univ = p.lowest_coord().universe(); + for (int token : + c.vl_triso_distribution_[lat_ind[0] + lat_ind[1] * c.vl_shape_[0] + + lat_ind[2] * c.vl_shape_[0] * c.vl_shape_[1]]) { + vector triso_center = model::surfaces[abs(token) - 1]->get_center(); + double triso_radius = model::surfaces[abs(token) - 1]->get_radius(); + if (model::cells + [model::cell_map[model::surfaces[abs(token) - 1]->triso_base_index_]] + ->universe_ != i_univ) + continue; + if (abs(token) == abs(p.surface())) { + if (p.surface() < 0) { + p.lowest_coord().cell() = + model::cell_map[model::surfaces[abs(token) - 1] + ->triso_particle_index_]; + return true; + } else { + p.lowest_coord().cell() = model::cell_map[filled_with_triso_base_]; + return true; + } + } + if (pow(r.x - triso_center[0], 2) + pow(r.y - triso_center[1], 2) + + pow(r.z - triso_center[2], 2) < + pow(triso_radius, 2)) { + p.lowest_coord().cell() = + model::cell_map[model::surfaces[abs(token) - 1]->triso_particle_index_]; + return true; + } + } + if (model::cells[model::cell_map[filled_with_triso_base_]]->universe_ == + i_univ) { + p.lowest_coord().cell() = model::cell_map[filled_with_triso_base_]; + return true; + } + return false; +} + +BoundingBox Universe::bounding_box() const +{ + BoundingBox bbox = {INFTY, -INFTY, INFTY, -INFTY, INFTY, -INFTY}; + if (cells_.size() == 0) { + return {}; + } else { + for (const auto& cell : cells_) { + auto& c = model::cells[cell]; + bbox |= c->bounding_box(); + } + } + return bbox; +} + +//============================================================================== +// UniversePartitioner implementation +//============================================================================== + +UniversePartitioner::UniversePartitioner(const Universe& univ) +{ + // Define an ordered set of surface indices that point to z-planes. Use a + // functor to to order the set by the z0_ values of the corresponding planes. + struct compare_surfs { + bool operator()(const int32_t& i_surf, const int32_t& j_surf) const + { + const auto* surf = model::surfaces[i_surf].get(); + const auto* zplane = dynamic_cast(surf); + double zi = zplane->z0_; + surf = model::surfaces[j_surf].get(); + zplane = dynamic_cast(surf); + double zj = zplane->z0_; + return zi < zj; + } + }; + std::set surf_set; + + // Find all of the z-planes in this universe. A set is used here for the + // O(log(n)) insertions that will ensure entries are not repeated. + for (auto i_cell : univ.cells_) { + for (auto token : model::cells[i_cell]->surfaces()) { + auto i_surf = std::abs(token) - 1; + const auto* surf = model::surfaces[i_surf].get(); + if (const auto* zplane = dynamic_cast(surf)) + surf_set.insert(i_surf); + } + } + + // Populate the surfs_ vector from the ordered set. + surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end()); + + // Populate the partition lists. + partitions_.resize(surfs_.size() + 1); + for (auto i_cell : univ.cells_) { + // It is difficult to determine the bounds of a complex cell, so add complex + // cells to all partitions. + if (!model::cells[i_cell]->is_simple()) { + for (auto& p : partitions_) + p.push_back(i_cell); + continue; + } + + // Find the tokens for bounding z-planes. + int32_t lower_token = 0, upper_token = 0; + double min_z, max_z; + for (auto token : model::cells[i_cell]->surfaces()) { + const auto* surf = model::surfaces[std::abs(token) - 1].get(); + if (const auto* zplane = dynamic_cast(surf)) { + if (lower_token == 0 || zplane->z0_ < min_z) { + lower_token = token; + min_z = zplane->z0_; + } + if (upper_token == 0 || zplane->z0_ > max_z) { + upper_token = token; + max_z = zplane->z0_; + } + } + } + + // If there are no bounding z-planes, add this cell to all partitions. + if (lower_token == 0) { + for (auto& p : partitions_) + p.push_back(i_cell); + continue; + } + + // Find the first partition this cell lies in. If the lower_token indicates + // a negative halfspace, then the cell is unbounded in the lower direction + // and it lies in the first partition onward. Otherwise, it is bounded by + // the positive halfspace given by the lower_token. + int first_partition = 0; + if (lower_token > 0) { + for (int i = 0; i < surfs_.size(); ++i) { + if (lower_token == surfs_[i] + 1) { + first_partition = i + 1; + break; + } + } + } + + // Find the last partition this cell lies in. The logic is analogous to the + // logic for first_partition. + int last_partition = surfs_.size(); + if (upper_token < 0) { + for (int i = first_partition; i < surfs_.size(); ++i) { + if (upper_token == -(surfs_[i] + 1)) { + last_partition = i; + break; + } + } + } + + // Add the cell to all relevant partitions. + for (int i = first_partition; i <= last_partition; ++i) { + partitions_[i].push_back(i_cell); + } + } +} + +const vector& UniversePartitioner::get_cells( + Position r, Direction u) const +{ + // Perform a binary search for the partition containing the given coordinates. + int left = 0; + int middle = (surfs_.size() - 1) / 2; + int right = surfs_.size() - 1; + while (true) { + // Check the sense of the coordinates for the current surface. + const auto& surf = *model::surfaces[surfs_[middle]]; + if (surf.sense(r, u)) { + // The coordinates lie in the positive halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the positive + // side of this surface. + int right_leaf = right - (right - middle) / 2; + if (right_leaf != middle) { + left = middle + 1; + middle = right_leaf; + } else { + return partitions_[middle + 1]; + } + + } else { + // The coordinates lie in the negative halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the negative + // side of this surface. + int left_leaf = left + (middle - left) / 2; + if (left_leaf != middle) { + right = middle - 1; + middle = left_leaf; + } else { + return partitions_[middle]; + } + } + } +} + +} // namespace openmc diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 3ae98fdc38..1deffb8048 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -10,18 +10,16 @@ #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" +#include "openmc/openmp_interface.h" #include "openmc/output.h" #include "openmc/random_lcg.h" #include "openmc/settings.h" #include "openmc/timer.h" #include "openmc/xml_interface.h" -#include -#ifdef _OPENMP -#include -#endif #include "xtensor/xadapt.hpp" #include "xtensor/xview.hpp" +#include #include // for copy #include // for pow, sqrt @@ -97,18 +95,43 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) vector VolumeCalculation::execute() const { + // Check to make sure domain IDs are valid + for (auto uid : domain_ids_) { + switch (domain_type_) { + case TallyDomain::CELL: + if (model::cell_map.find(uid) == model::cell_map.end()) { + throw std::runtime_error {fmt::format( + "Cell {} in volume calculation does not exist in geometry.", uid)}; + } + break; + case TallyDomain::MATERIAL: + if (model::material_map.find(uid) == model::material_map.end()) { + throw std::runtime_error {fmt::format( + "Material {} in volume calculation does not exist in geometry.", + uid)}; + } + break; + case TallyDomain::UNIVERSE: + if (model::universe_map.find(uid) == model::universe_map.end()) { + throw std::runtime_error {fmt::format( + "Universe {} in volume calculation does not exist in geometry.", + uid)}; + } + } + } + // Shared data that is collected from all threads int n = domain_ids_.size(); - vector> master_indices( + vector> master_indices( n); // List of material indices for each domain - vector> master_hits( + vector> master_hits( n); // Number of hits for each material in each domain int iterations = 0; // Divide work over MPI processes - size_t min_samples = n_samples_ / mpi::n_procs; - size_t remainder = n_samples_ % mpi::n_procs; - size_t i_start, i_end; + uint64_t min_samples = n_samples_ / mpi::n_procs; + uint64_t remainder = n_samples_ % mpi::n_procs; + uint64_t i_start, i_end; if (mpi::rank < remainder) { i_start = (min_samples + 1) * mpi::rank; i_end = i_start + min_samples + 1; @@ -123,20 +146,20 @@ vector VolumeCalculation::execute() const #pragma omp parallel { // Variables that are private to each thread - vector> indices(n); - vector> hits(n); + vector> indices(n); + vector> hits(n); Particle p; // Sample locations and count hits #pragma omp for for (size_t i = i_start; i < i_end; i++) { - int64_t id = iterations * n_samples_ + i; + uint64_t id = iterations * n_samples_ + i; uint64_t seed = init_seed(id, STREAM_VOLUME); p.n_coord() = 1; Position xi {prn(&seed), prn(&seed), prn(&seed)}; p.r() = lower_left_ + xi * (upper_right_ - lower_left_); - p.u() = {0.5, 0.5, 0.5}; + p.u() = {1. / std::sqrt(3.), 1. / std::sqrt(3.), 1. / std::sqrt(3.)}; // If this location is not in the geometry at all, move on to next block if (!exhaustive_find_cell(p)) @@ -156,7 +179,7 @@ vector VolumeCalculation::execute() const } else if (domain_type_ == TallyDomain::CELL) { for (int level = 0; level < p.n_coord(); ++level) { for (int i_domain = 0; i_domain < n; i_domain++) { - if (model::cells[p.coord(level).cell]->id_ == + if (model::cells[p.coord(level).cell()]->id_ == domain_ids_[i_domain]) { this->check_hit( p.material(), indices[i_domain], hits[i_domain]); @@ -167,7 +190,7 @@ vector VolumeCalculation::execute() const } else if (domain_type_ == TallyDomain::UNIVERSE) { for (int level = 0; level < p.n_coord(); ++level) { for (int i_domain = 0; i_domain < n; ++i_domain) { - if (model::universes[p.coord(level).universe]->id_ == + if (model::universes[p.coord(level).universe()]->id_ == domain_ids_[i_domain]) { check_hit(p.material(), indices[i_domain], hits[i_domain]); break; @@ -180,37 +203,9 @@ vector VolumeCalculation::execute() const // At this point, each thread has its own pair of index/hits lists and we // now need to reduce them. OpenMP is not nearly smart enough to do this // on its own, so we have to manually reduce them - -#ifdef _OPENMP - int n_threads = omp_get_num_threads(); -#else - int n_threads = 1; -#endif - -#pragma omp for ordered schedule(static) - for (int i = 0; i < n_threads; ++i) { -#pragma omp ordered - for (int i_domain = 0; i_domain < n; ++i_domain) { - for (int j = 0; j < indices[i_domain].size(); ++j) { - // Check if this material has been added to the master list and if - // so, accumulate the number of hits - bool already_added = false; - for (int k = 0; k < master_indices[i_domain].size(); k++) { - if (indices[i_domain][j] == master_indices[i_domain][k]) { - master_hits[i_domain][k] += hits[i_domain][j]; - already_added = true; - break; - } - } - if (!already_added) { - // If we made it here, the material hasn't yet been added to the - // master list, so add entries to the master indices and master - // hits lists - master_indices[i_domain].push_back(indices[i_domain][j]); - master_hits[i_domain].push_back(hits[i_domain][j]); - } - } - } + for (int i_domain = 0; i_domain < n; ++i_domain) { + reduce_indices_hits(indices[i_domain], hits[i_domain], + master_indices[i_domain], master_hits[i_domain]); } } // omp parallel @@ -223,7 +218,14 @@ vector VolumeCalculation::execute() const // bump iteration counter and get total number // of samples at this point iterations++; - size_t total_samples = iterations * n_samples_; + uint64_t total_samples = iterations * n_samples_; + + // warn user if total sample size is greater than what the uin64_t type can + // represent + if (total_samples == std::numeric_limits::max()) { + warning("The number of samples has exceeded the type used to track hits. " + "Volume results may be inaccurate."); + } // reset double trigger_val = -INFTY; @@ -238,18 +240,19 @@ vector VolumeCalculation::execute() const // Create 2D array to store atoms/uncertainty for each nuclide. Later this // is compressed into vectors storing only those nuclides that are // non-zero - auto n_nuc = settings::run_CE ? data::nuclides.size() - : data::mg.nuclides_.size(); + auto n_nuc = + settings::run_CE ? data::nuclides.size() : data::mg.nuclides_.size(); xt::xtensor atoms({n_nuc, 2}, 0.0); #ifdef OPENMC_MPI if (mpi::master) { for (int j = 1; j < mpi::n_procs; j++) { int q; + // retrieve results MPI_Recv( - &q, 1, MPI_INTEGER, j, 2 * j, mpi::intracomm, MPI_STATUS_IGNORE); - vector buffer(2 * q); - MPI_Recv(buffer.data(), 2 * q, MPI_INTEGER, j, 2 * j + 1, + &q, 1, MPI_UINT64_T, j, 2 * j, mpi::intracomm, MPI_STATUS_IGNORE); + vector buffer(2 * q); + MPI_Recv(buffer.data(), 2 * q, MPI_UINT64_T, j, 2 * j + 1, mpi::intracomm, MPI_STATUS_IGNORE); for (int k = 0; k < q; ++k) { bool already_added = false; @@ -268,20 +271,20 @@ vector VolumeCalculation::execute() const } } else { int q = master_indices[i_domain].size(); - vector buffer(2 * q); + vector buffer(2 * q); for (int k = 0; k < q; ++k) { buffer[2 * k] = master_indices[i_domain][k]; buffer[2 * k + 1] = master_hits[i_domain][k]; } - MPI_Send(&q, 1, MPI_INTEGER, 0, 2 * mpi::rank, mpi::intracomm); - MPI_Send(buffer.data(), 2 * q, MPI_INTEGER, 0, 2 * mpi::rank + 1, + MPI_Send(&q, 1, MPI_UINT64_T, 0, 2 * mpi::rank, mpi::intracomm); + MPI_Send(buffer.data(), 2 * q, MPI_UINT64_T, 0, 2 * mpi::rank + 1, mpi::intracomm); } #endif if (mpi::master) { - int total_hits = 0; + size_t total_hits = 0; for (int j = 0; j < master_indices[i_domain].size(); ++j) { total_hits += master_hits[i_domain][j]; double f = @@ -464,7 +467,7 @@ void VolumeCalculation::to_hdf5( } void VolumeCalculation::check_hit( - int i_material, vector& indices, vector& hits) const + int i_material, vector& indices, vector& hits) const { // Check if this material was previously hit and if so, increment count @@ -511,7 +514,13 @@ int openmc_calculate_volumes() // Run volume calculation const auto& vol_calc {model::volume_calcs[i]}; - auto results = vol_calc.execute(); + std::vector results; + try { + results = vol_calc.execute(); + } catch (const std::exception& e) { + set_errmsg(e.what()); + return OPENMC_E_UNASSIGNED; + } if (mpi::master) { std::string domain_type; @@ -526,8 +535,21 @@ int openmc_calculate_volumes() // Display domain volumes for (int j = 0; j < vol_calc.domain_ids_.size(); j++) { - write_message(4, "{}{}: {} +/- {} cm^3", domain_type, - vol_calc.domain_ids_[j], results[j].volume[0], results[j].volume[1]); + std::string region_name {""}; + if (vol_calc.domain_type_ == VolumeCalculation::TallyDomain::CELL) { + int cell_idx = model::cell_map[vol_calc.domain_ids_[j]]; + region_name = model::cells[cell_idx]->name(); + } else if (vol_calc.domain_type_ == + VolumeCalculation::TallyDomain::MATERIAL) { + int mat_idx = model::material_map[vol_calc.domain_ids_[j]]; + region_name = model::materials[mat_idx]->name(); + } + if (region_name.size()) + region_name.insert(0, " "); // prepend space for formatting + + write_message(4, "{}{}{}: {} +/- {} cm^3", domain_type, + vol_calc.domain_ids_[j], region_name, results[j].volume[0], + results[j].volume[1]); } // Write volumes to HDF5 file diff --git a/src/weight_windows.cpp b/src/weight_windows.cpp index e742d48c3f..26762ad18a 100644 --- a/src/weight_windows.cpp +++ b/src/weight_windows.cpp @@ -1,15 +1,38 @@ #include "openmc/weight_windows.h" +#include +#include +#include +#include +#include + +#include "xtensor/xdynamic_view.hpp" +#include "xtensor/xindex_view.hpp" +#include "xtensor/xio.hpp" +#include "xtensor/xmasked_view.hpp" +#include "xtensor/xnoalias.hpp" +#include "xtensor/xview.hpp" + #include "openmc/error.h" #include "openmc/file_utils.h" #include "openmc/hdf5_interface.h" +#include "openmc/mesh.h" +#include "openmc/message_passing.h" +#include "openmc/nuclide.h" +#include "openmc/output.h" #include "openmc/particle.h" #include "openmc/particle_data.h" +#include "openmc/physics_common.h" +#include "openmc/random_ray/flat_source_domain.h" #include "openmc/search.h" +#include "openmc/settings.h" +#include "openmc/tallies/filter_energy.h" +#include "openmc/tallies/filter_mesh.h" +#include "openmc/tallies/filter_particle.h" +#include "openmc/tallies/tally.h" #include "openmc/xml_interface.h" #include -#include namespace openmc { @@ -21,6 +44,7 @@ namespace variance_reduction { std::unordered_map ww_map; openmc::vector> weight_windows; +openmc::vector> weight_windows_generators; } // namespace variance_reduction @@ -30,6 +54,13 @@ openmc::vector> weight_windows; void apply_weight_windows(Particle& p) { + if (!settings::weight_windows_on) + return; + + // WW on photon and neutron only + if (p.type() != ParticleType::neutron && p.type() != ParticleType::photon) + return; + // skip dead or no energy if (p.E() <= 0 || !p.alive()) return; @@ -42,16 +73,31 @@ void apply_weight_windows(Particle& p) if (weight_window.is_valid()) break; } + + // If particle has not yet had its birth weight window value set, set it to + // the current weight window (or 1.0 if not born in a weight window). + if (p.wgt_ww_born() == -1.0) { + if (weight_window.is_valid()) { + p.wgt_ww_born() = + (weight_window.lower_weight + weight_window.upper_weight) / 2; + } else { + p.wgt_ww_born() = 1.0; + } + } + // particle is not in any of the ww domains, do nothing if (!weight_window.is_valid()) return; + // Normalize weight windows based on particle's starting weight + // and the value of the weight window the particle was born in. + weight_window.scale(p.wgt_born() / p.wgt_ww_born()); + // get the paramters double weight = p.wgt(); // first check to see if particle should be killed for weight cutoff if (p.wgt() < weight_window.weight_cutoff) { - p.alive() = false; p.wgt() = 0.0; return; } @@ -73,7 +119,7 @@ void apply_weight_windows(Particle& p) // the window if (weight > weight_window.upper_weight) { // do not further split the particle if above the limit - if (p.n_split() >= settings::max_splits) + if (p.n_split() >= settings::max_history_splits) return; double n_split = std::ceil(weight / weight_window.upper_weight); @@ -85,7 +131,7 @@ void apply_weight_windows(Particle& p) // Create secondaries and divide weight among all particles int i_split = std::round(n_split); for (int l = 0; l < i_split - 1; l++) { - p.create_secondary(weight / n_split, p.u(), p.E(), p.type()); + p.split(weight / n_split); } // remaining weight is applied to current particle p.wgt() = weight / n_split; @@ -94,14 +140,8 @@ void apply_weight_windows(Particle& p) // if the particle weight is below the window, play Russian roulette double weight_survive = std::min(weight * weight_window.max_split, weight_window.survival_weight); - if (weight_survive * prn(p.current_seed()) <= weight) { - p.wgt() = weight_survive; - } else { - p.alive() = false; - p.wgt() = 0.0; - } - // else particle is in the window, continue as normal - } + russian_roulette(p, weight_survive); + } // else particle is in the window, continue as normal } void free_memory_weight_windows() @@ -114,11 +154,18 @@ void free_memory_weight_windows() // WeightWindowSettings implementation //============================================================================== +WeightWindows::WeightWindows(int32_t id) +{ + index_ = variance_reduction::weight_windows.size(); + set_id(id); + set_defaults(); +} + WeightWindows::WeightWindows(pugi::xml_node node) { // Make sure required elements are present const vector required_elems { - "id", "particle_type", "energy_bins", "lower_ww_bounds", "upper_ww_bounds"}; + "id", "particle_type", "lower_ww_bounds", "upper_ww_bounds"}; for (const auto& elem : required_elems) { if (!check_for_node(node, elem.c_str())) { fatal_error(fmt::format("Must specify <{}> for weight windows.", elem)); @@ -135,14 +182,11 @@ WeightWindows::WeightWindows(pugi::xml_node node) // Determine associated mesh int32_t mesh_id = std::stoi(get_node_value(node, "mesh")); - mesh_idx_ = model::mesh_map.at(mesh_id); + set_mesh(model::mesh_map.at(mesh_id)); // energy bounds - energy_bins_ = get_node_array(node, "energy_bins"); - - // read the lower/upper weight bounds - lower_ww_ = get_node_array(node, "lower_ww_bounds"); - upper_ww_ = get_node_array(node, "upper_ww_bounds"); + if (check_for_node(node, "energy_bounds")) + energy_bounds_ = get_node_array(node, "energy_bounds"); // get the survival value - optional if (check_for_node(node, "survival_ratio")) { @@ -176,28 +220,92 @@ WeightWindows::WeightWindows(pugi::xml_node node) fatal_error("weight_cutoff must be less than 1"); } - // make sure that the upper and lower bounds have the same size - if (upper_ww_.size() != lower_ww_.size()) { - fatal_error("The upper and lower weight window lengths do not match."); - } + // read the lower/upper weight bounds + this->set_bounds(get_node_array(node, "lower_ww_bounds"), + get_node_array(node, "upper_ww_bounds")); - // num spatial*energy bins must match num weight bins - int num_spatial_bins = this->mesh().n_bins(); - int num_energy_bins = energy_bins_.size() - 1; - int num_weight_bins = lower_ww_.size(); - if (num_weight_bins != num_spatial_bins * num_energy_bins) { - auto err_msg = - fmt::format("In weight window domain {} the number of spatial " - "energy/spatial bins ({}) does not match the number " - "of weight bins ({})", - id_, num_energy_bins, num_weight_bins); - fatal_error(err_msg); + set_defaults(); +} + +WeightWindows::~WeightWindows() +{ + variance_reduction::ww_map.erase(id()); +} + +WeightWindows* WeightWindows::create(int32_t id) +{ + variance_reduction::weight_windows.push_back(make_unique()); + auto wws = variance_reduction::weight_windows.back().get(); + variance_reduction::ww_map[wws->id()] = + variance_reduction::weight_windows.size() - 1; + return wws; +} + +WeightWindows* WeightWindows::from_hdf5( + hid_t wws_group, const std::string& group_name) +{ + // collect ID from the name of this group + hid_t ww_group = open_group(wws_group, group_name); + + auto wws = WeightWindows::create(); + + std::string particle_type; + read_dataset(ww_group, "particle_type", particle_type); + wws->particle_type_ = openmc::str_to_particle_type(particle_type); + + read_dataset(ww_group, "energy_bounds", wws->energy_bounds_); + + int32_t mesh_id; + read_dataset(ww_group, "mesh", mesh_id); + + if (model::mesh_map.count(mesh_id) == 0) { + fatal_error( + fmt::format("Mesh {} used in weight windows does not exist.", mesh_id)); } + wws->set_mesh(model::mesh_map[mesh_id]); + + wws->lower_ww_ = xt::empty(wws->bounds_size()); + wws->upper_ww_ = xt::empty(wws->bounds_size()); + + read_dataset(ww_group, "lower_ww_bounds", wws->lower_ww_); + read_dataset(ww_group, "upper_ww_bounds", wws->upper_ww_); + read_dataset(ww_group, "survival_ratio", wws->survival_ratio_); + read_dataset(ww_group, "max_lower_bound_ratio", wws->max_lb_ratio_); + read_dataset(ww_group, "max_split", wws->max_split_); + read_dataset(ww_group, "weight_cutoff", wws->weight_cutoff_); + + close_group(ww_group); + + return wws; +} + +void WeightWindows::set_defaults() +{ + // set energy bounds to the min/max energy supported by the data + if (energy_bounds_.size() == 0) { + int p_type = static_cast(particle_type_); + energy_bounds_.push_back(data::energy_min[p_type]); + energy_bounds_.push_back(data::energy_max[p_type]); + } +} + +void WeightWindows::allocate_ww_bounds() +{ + auto shape = bounds_size(); + if (shape[0] * shape[1] == 0) { + auto msg = fmt::format( + "Size of weight window bounds is zero for WeightWindows {}", id()); + warning(msg); + } + lower_ww_ = xt::empty(shape); + lower_ww_.fill(-1); + upper_ww_ = xt::empty(shape); + upper_ww_.fill(-1); } void WeightWindows::set_id(int32_t id) { - Expects(id >= 0 || id == C_NONE); + assert(id >= 0 || id == C_NONE); // Clear entry in mesh map in case one was already assigned if (id_ != C_NONE) { @@ -222,8 +330,45 @@ void WeightWindows::set_id(int32_t id) // Update ID and entry in the mesh map id_ = id; - variance_reduction::ww_map[id] = - variance_reduction::weight_windows.size() - 1; + variance_reduction::ww_map[id] = index_; +} + +void WeightWindows::set_energy_bounds(span bounds) +{ + energy_bounds_.clear(); + energy_bounds_.insert(energy_bounds_.begin(), bounds.begin(), bounds.end()); + // if the mesh is set, allocate space for weight window bounds + if (mesh_idx_ != C_NONE) + allocate_ww_bounds(); +} + +void WeightWindows::set_particle_type(ParticleType p_type) +{ + if (p_type != ParticleType::neutron && p_type != ParticleType::photon) + fatal_error( + fmt::format("Particle type '{}' cannot be applied to weight windows.", + particle_type_to_str(p_type))); + particle_type_ = p_type; +} + +void WeightWindows::set_mesh(int32_t mesh_idx) +{ + if (mesh_idx < 0 || mesh_idx >= model::meshes.size()) + fatal_error(fmt::format("Could not find a mesh for index {}", mesh_idx)); + + mesh_idx_ = mesh_idx; + model::meshes[mesh_idx_]->prepare_for_point_location(); + allocate_ww_bounds(); +} + +void WeightWindows::set_mesh(const std::unique_ptr& mesh) +{ + set_mesh(mesh.get()); +} + +void WeightWindows::set_mesh(const Mesh* mesh) +{ + set_mesh(model::mesh_map[mesh->id_]); } WeightWindow WeightWindows::get_weight_window(const Particle& p) const @@ -235,30 +380,28 @@ WeightWindow WeightWindows::get_weight_window(const Particle& p) const // Get mesh index for particle's position const auto& mesh = this->mesh(); - int ww_index = mesh.get_bin(p.r()); + int mesh_bin = mesh->get_bin(p.r()); // particle is outside the weight window mesh - if (ww_index < 0) + if (mesh_bin < 0) return {}; // particle energy double E = p.E(); // check to make sure energy is in range, expects sorted energy values - if (E < energy_bins_.front() || E > energy_bins_.back()) + if (E < energy_bounds_.front() || E > energy_bounds_.back()) return {}; // get the mesh bin in energy group int energy_bin = - lower_bound_index(energy_bins_.begin(), energy_bins_.end(), E); - - // indices now points to the correct weight for the given energy - ww_index += energy_bin * mesh.n_bins(); + lower_bound_index(energy_bounds_.begin(), energy_bounds_.end(), E); + // mesh_bin += energy_bin * mesh->n_bins(); // Create individual weight window WeightWindow ww; - ww.lower_weight = lower_ww_[ww_index]; - ww.upper_weight = upper_ww_[ww_index]; + ww.lower_weight = lower_ww_(energy_bin, mesh_bin); + ww.upper_weight = upper_ww_(energy_bin, mesh_bin); ww.survival_weight = ww.lower_weight * survival_ratio_; ww.max_lb_ratio = max_lb_ratio_; ww.max_split = max_split_; @@ -266,22 +409,936 @@ WeightWindow WeightWindows::get_weight_window(const Particle& p) const return ww; } +std::array WeightWindows::bounds_size() const +{ + int num_spatial_bins = this->mesh()->n_bins(); + int num_energy_bins = + energy_bounds_.size() > 0 ? energy_bounds_.size() - 1 : 1; + return {num_energy_bins, num_spatial_bins}; +} + +template +void WeightWindows::check_bounds(const T& lower, const T& upper) const +{ + // make sure that the upper and lower bounds have the same size + if (lower.size() != upper.size()) { + auto msg = fmt::format("The upper and lower weight window lengths do not " + "match.\n Lower size: {}\n Upper size: {}", + lower.size(), upper.size()); + fatal_error(msg); + } + this->check_bounds(lower); +} + +template +void WeightWindows::check_bounds(const T& bounds) const +{ + // check that the number of weight window entries is correct + auto dims = this->bounds_size(); + if (bounds.size() != dims[0] * dims[1]) { + auto err_msg = + fmt::format("In weight window domain {} the number of spatial " + "energy/spatial bins ({}) does not match the number " + "of weight bins ({})", + id_, dims, bounds.size()); + fatal_error(err_msg); + } +} + +void WeightWindows::set_bounds(const xt::xtensor& lower_bounds, + const xt::xtensor& upper_bounds) +{ + + this->check_bounds(lower_bounds, upper_bounds); + + // set new weight window values + lower_ww_ = lower_bounds; + upper_ww_ = upper_bounds; +} + +void WeightWindows::set_bounds( + const xt::xtensor& lower_bounds, double ratio) +{ + this->check_bounds(lower_bounds); + + // set new weight window values + lower_ww_ = lower_bounds; + upper_ww_ = lower_bounds; + upper_ww_ *= ratio; +} + +void WeightWindows::set_bounds( + span lower_bounds, span upper_bounds) +{ + check_bounds(lower_bounds, upper_bounds); + auto shape = this->bounds_size(); + lower_ww_ = xt::empty(shape); + upper_ww_ = xt::empty(shape); + + // set new weight window values + xt::view(lower_ww_, xt::all()) = + xt::adapt(lower_bounds.data(), lower_ww_.shape()); + xt::view(upper_ww_, xt::all()) = + xt::adapt(upper_bounds.data(), upper_ww_.shape()); +} + +void WeightWindows::set_bounds(span lower_bounds, double ratio) +{ + this->check_bounds(lower_bounds); + + auto shape = this->bounds_size(); + lower_ww_ = xt::empty(shape); + upper_ww_ = xt::empty(shape); + + // set new weight window values + xt::view(lower_ww_, xt::all()) = + xt::adapt(lower_bounds.data(), lower_ww_.shape()); + xt::view(upper_ww_, xt::all()) = + xt::adapt(lower_bounds.data(), upper_ww_.shape()); + upper_ww_ *= ratio; +} + +void WeightWindows::update_weights(const Tally* tally, const std::string& value, + double threshold, double ratio, WeightWindowUpdateMethod method) +{ + /////////////////////////// + // Setup and checks + /////////////////////////// + this->check_tally_update_compatibility(tally); + + // Dimensions of weight window arrays + int e_bins = lower_ww_.shape()[0]; + int64_t mesh_bins = lower_ww_.shape()[1]; + + // Initialize weight window arrays to -1.0 by default +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + lower_ww_(e, m) = -1.0; + upper_ww_(e, m) = -1.0; + } + } + + // determine which value to use + const std::set allowed_values = {"mean", "rel_err"}; + if (allowed_values.count(value) == 0) { + fatal_error(fmt::format("Invalid value '{}' specified for weight window " + "generation. Must be one of: 'mean' or 'rel_err'", + value)); + } + + // determine the index of the specified score + int score_index = tally->score_index("flux"); + if (score_index == C_NONE) { + fatal_error( + fmt::format("A 'flux' score required for weight window generation " + "is not present on tally {}.", + tally->id())); + } + + /////////////////////////// + // Extract tally data + // + // At the end of this section, the mean and rel_err array + // is a 2D view of tally data (n_e_groups, n_mesh_bins) + // + /////////////////////////// + + // build a shape for a view of the tally results, this will always be + // dimension 5 (3 filter dimensions, 1 score dimension, 1 results dimension) + std::array shape = { + 1, 1, 1, tally->n_scores(), static_cast(TallyResult::SIZE)}; + + // set the shape for the filters applied on the tally + for (int i = 0; i < tally->filters().size(); i++) { + const auto& filter = model::tally_filters[tally->filters(i)]; + shape[i] = filter->n_bins(); + } + + // build the transpose information to re-order data according to filter type + std::array transpose = {0, 1, 2, 3, 4}; + + // track our filter types and where we've added new ones + std::vector filter_types = tally->filter_types(); + + // assign other filter types to dummy positions if needed + if (!tally->has_filter(FilterType::PARTICLE)) + filter_types.push_back(FilterType::PARTICLE); + + if (!tally->has_filter(FilterType::ENERGY)) + filter_types.push_back(FilterType::ENERGY); + + // particle axis mapping + transpose[0] = + std::find(filter_types.begin(), filter_types.end(), FilterType::PARTICLE) - + filter_types.begin(); + + // energy axis mapping + transpose[1] = + std::find(filter_types.begin(), filter_types.end(), FilterType::ENERGY) - + filter_types.begin(); + + // mesh axis mapping + transpose[2] = + std::find(filter_types.begin(), filter_types.end(), FilterType::MESH) - + filter_types.begin(); + + // get a fully reshaped view of the tally according to tally ordering of + // filters + auto tally_values = xt::reshape_view(tally->results(), shape); + + // get a that is (particle, energy, mesh, scores, values) + auto transposed_view = xt::transpose(tally_values, transpose); + + // determine the dimension and index of the particle data + int particle_idx = 0; + if (tally->has_filter(FilterType::PARTICLE)) { + // get the particle filter + auto pf = tally->get_filter(); + const auto& particles = pf->particles(); + + // find the index of the particle that matches these weight windows + auto p_it = + std::find(particles.begin(), particles.end(), this->particle_type_); + // if the particle filter doesn't have particle data for the particle + // used on this weight windows instance, report an error + if (p_it == particles.end()) { + auto msg = fmt::format("Particle type '{}' not present on Filter {} for " + "Tally {} used to update WeightWindows {}", + particle_type_to_str(this->particle_type_), pf->id(), tally->id(), + this->id()); + fatal_error(msg); + } + + // use the index of the particle in the filter to down-select data later + particle_idx = p_it - particles.begin(); + } + + // down-select data based on particle and score + auto sum = xt::dynamic_view( + transposed_view, {particle_idx, xt::all(), xt::all(), score_index, + static_cast(TallyResult::SUM)}); + auto sum_sq = xt::dynamic_view( + transposed_view, {particle_idx, xt::all(), xt::all(), score_index, + static_cast(TallyResult::SUM_SQ)}); + int n = tally->n_realizations_; + + ////////////////////////////////////////////// + // + // Assign new weight windows + // + // Use references to the existing weight window data + // to store and update the values + // + ////////////////////////////////////////////// + + // up to this point the data arrays are views into the tally results (no + // computation has been performed) now we'll switch references to the tally's + // bounds to avoid allocating additional memory + auto& new_bounds = this->lower_ww_; + auto& rel_err = this->upper_ww_; + + // get mesh volumes + auto mesh_vols = this->mesh()->volumes(); + + // Calculate mean (new_bounds) and relative error +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + // Calculate mean + new_bounds(e, m) = sum(e, m) / n; + // Calculate relative error + if (sum(e, m) > 0.0) { + double mean_val = new_bounds(e, m); + double variance = (sum_sq(e, m) / n - mean_val * mean_val) / (n - 1); + rel_err(e, m) = std::sqrt(variance) / mean_val; + } else { + rel_err(e, m) = INFTY; + } + if (value == "rel_err") { + new_bounds(e, m) = 1.0 / rel_err(e, m); + } + } + } + + // Divide by volume of mesh elements +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + new_bounds(e, m) /= mesh_vols[m]; + } + } + + if (method == WeightWindowUpdateMethod::MAGIC) { + // For MAGIC, weight windows are proportional to the forward fluxes. + // We normalize weight windows independently for each energy group. + + // Find group maximum and normalize (per energy group) + for (int e = 0; e < e_bins; e++) { + double group_max = 0.0; + + // Find maximum value across all elements in this energy group +#pragma omp parallel for schedule(static) reduction(max : group_max) + for (int64_t m = 0; m < mesh_bins; m++) { + if (new_bounds(e, m) > group_max) { + group_max = new_bounds(e, m); + } + } + + // Normalize values in this energy group by the maximum value + if (group_max > 0.0) { + double norm_factor = 1.0 / (2.0 * group_max); +#pragma omp parallel for schedule(static) + for (int64_t m = 0; m < mesh_bins; m++) { + new_bounds(e, m) *= norm_factor; + } + } + } + } else { + // For FW-CADIS, weight windows are inversely proportional to the adjoint + // fluxes. We normalize the weight windows across all energy groups. +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + // Take the inverse, but are careful not to divide by zero + if (new_bounds(e, m) != 0.0) { + new_bounds(e, m) = 1.0 / new_bounds(e, m); + } else { + new_bounds(e, m) = 0.0; + } + } + } + + // Find the maximum value across all elements + double max_val = 0.0; +#pragma omp parallel for collapse(2) schedule(static) reduction(max : max_val) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + if (new_bounds(e, m) > max_val) { + max_val = new_bounds(e, m); + } + } + } + + // Parallel normalization + if (max_val > 0.0) { + double norm_factor = 1.0 / (2.0 * max_val); +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + new_bounds(e, m) *= norm_factor; + } + } + } + } + + // Final processing +#pragma omp parallel for collapse(2) schedule(static) + for (int e = 0; e < e_bins; e++) { + for (int64_t m = 0; m < mesh_bins; m++) { + // Values where the mean is zero should be ignored + if (sum(e, m) <= 0.0) { + new_bounds(e, m) = -1.0; + } + // Values where the relative error is higher than the threshold should be + // ignored + else if (rel_err(e, m) > threshold) { + new_bounds(e, m) = -1.0; + } + // Set the upper bounds + upper_ww_(e, m) = ratio * lower_ww_(e, m); + } + } +} + +void WeightWindows::check_tally_update_compatibility(const Tally* tally) +{ + // define the set of allowed filters for the tally + const std::set allowed_filters = { + FilterType::MESH, FilterType::ENERGY, FilterType::PARTICLE}; + + // retrieve a mapping of filter type to filter index for the tally + auto filter_indices = tally->filter_indices(); + + // a mesh filter is required for a tally used to update weight windows + if (!filter_indices.count(FilterType::MESH)) { + fatal_error( + "A mesh filter is required for a tally to update weight window bounds"); + } + + // ensure the mesh filter is using the same mesh as this weight window object + auto mesh_filter = tally->get_filter(); + + // make sure that all of the filters present on the tally are allowed + for (auto filter_pair : filter_indices) { + if (allowed_filters.find(filter_pair.first) == allowed_filters.end()) { + fatal_error(fmt::format("Invalid filter type '{}' found on tally " + "used for weight window generation.", + model::tally_filters[tally->filters(filter_pair.second)]->type_str())); + } + } + + if (mesh_filter->mesh() != mesh_idx_) { + int32_t mesh_filter_id = model::meshes[mesh_filter->mesh()]->id(); + int32_t ww_mesh_id = model::meshes[this->mesh_idx_]->id(); + fatal_error(fmt::format("Mesh filter {} uses a different mesh ({}) than " + "weight window {} mesh ({})", + mesh_filter->id(), mesh_filter_id, id_, ww_mesh_id)); + } + + // if an energy filter exists, make sure the energy grid matches that of this + // weight window object + if (auto energy_filter = tally->get_filter()) { + std::vector filter_bins = energy_filter->bins(); + std::set filter_e_bounds( + energy_filter->bins().begin(), energy_filter->bins().end()); + if (filter_e_bounds.size() != energy_bounds().size()) { + fatal_error( + fmt::format("Energy filter {} does not have the same number of energy " + "bounds ({}) as weight window object {} ({})", + energy_filter->id(), filter_e_bounds.size(), id_, + energy_bounds().size())); + } + + for (auto e : energy_bounds()) { + if (filter_e_bounds.count(e) == 0) { + fatal_error(fmt::format( + "Energy bounds of filter {} and weight windows {} do not match", + energy_filter->id(), id_)); + } + } + } +} + void WeightWindows::to_hdf5(hid_t group) const { - hid_t ww_group = create_group(group, fmt::format("weight_windows {}", id_)); + hid_t ww_group = create_group(group, fmt::format("weight_windows_{}", id())); + write_dataset(ww_group, "mesh", this->mesh()->id()); write_dataset( ww_group, "particle_type", openmc::particle_type_to_str(particle_type_)); - write_dataset(ww_group, "energy_bins", energy_bins_); + write_dataset(ww_group, "energy_bounds", energy_bounds_); write_dataset(ww_group, "lower_ww_bounds", lower_ww_); write_dataset(ww_group, "upper_ww_bounds", upper_ww_); write_dataset(ww_group, "survival_ratio", survival_ratio_); write_dataset(ww_group, "max_lower_bound_ratio", max_lb_ratio_); write_dataset(ww_group, "max_split", max_split_); write_dataset(ww_group, "weight_cutoff", weight_cutoff_); - write_dataset(ww_group, "mesh", this->mesh().id_); close_group(ww_group); } +WeightWindowsGenerator::WeightWindowsGenerator(pugi::xml_node node) +{ + // read information from the XML node + int32_t mesh_id = std::stoi(get_node_value(node, "mesh")); + int32_t mesh_idx = model::mesh_map[mesh_id]; + max_realizations_ = std::stoi(get_node_value(node, "max_realizations")); + + int32_t active_batches = settings::n_batches - settings::n_inactive; + if (max_realizations_ > active_batches) { + auto msg = + fmt::format("The maximum number of specified tally realizations ({}) is " + "greater than the number of active batches ({}).", + max_realizations_, active_batches); + warning(msg); + } + auto tmp_str = get_node_value(node, "particle_type", true, true); + auto particle_type = str_to_particle_type(tmp_str); + + update_interval_ = std::stoi(get_node_value(node, "update_interval")); + on_the_fly_ = get_node_value_bool(node, "on_the_fly"); + + std::vector e_bounds; + if (check_for_node(node, "energy_bounds")) { + e_bounds = get_node_array(node, "energy_bounds"); + } else { + int p_type = static_cast(particle_type); + e_bounds.push_back(data::energy_min[p_type]); + e_bounds.push_back(data::energy_max[p_type]); + } + + // set method + std::string method_string = get_node_value(node, "method"); + if (method_string == "magic") { + method_ = WeightWindowUpdateMethod::MAGIC; + if (settings::solver_type == SolverType::RANDOM_RAY && + FlatSourceDomain::adjoint_) { + fatal_error("Random ray weight window generation with MAGIC cannot be " + "done in adjoint mode."); + } + } else if (method_string == "fw_cadis") { + method_ = WeightWindowUpdateMethod::FW_CADIS; + if (settings::solver_type != SolverType::RANDOM_RAY) { + fatal_error("FW-CADIS can only be run in random ray solver mode."); + } + FlatSourceDomain::adjoint_ = true; + } else { + fatal_error(fmt::format( + "Unknown weight window update method '{}' specified", method_string)); + } + + // parse non-default update parameters if specified + if (check_for_node(node, "update_parameters")) { + pugi::xml_node params_node = node.child("update_parameters"); + if (check_for_node(params_node, "value")) + tally_value_ = get_node_value(params_node, "value"); + if (check_for_node(params_node, "threshold")) + threshold_ = std::stod(get_node_value(params_node, "threshold")); + if (check_for_node(params_node, "ratio")) { + ratio_ = std::stod(get_node_value(params_node, "ratio")); + } + } + + // check update parameter values + if (tally_value_ != "mean" && tally_value_ != "rel_err") { + fatal_error(fmt::format("Unsupported tally value '{}' specified for " + "weight window generation.", + tally_value_)); + } + if (threshold_ <= 0.0) + fatal_error(fmt::format("Invalid relative error threshold '{}' (<= 0.0) " + "specified for weight window generation", + ratio_)); + if (ratio_ <= 1.0) + fatal_error(fmt::format("Invalid weight window ratio '{}' (<= 1.0) " + "specified for weight window generation")); + + // create a matching weight windows object + auto wws = WeightWindows::create(); + ww_idx_ = wws->index(); + wws->set_mesh(mesh_idx); + if (e_bounds.size() > 0) + wws->set_energy_bounds(e_bounds); + wws->set_particle_type(particle_type); + wws->set_defaults(); +} + +void WeightWindowsGenerator::create_tally() +{ + const auto& wws = variance_reduction::weight_windows[ww_idx_]; + + // create a tally based on the WWG information + Tally* ww_tally = Tally::create(); + tally_idx_ = model::tally_map[ww_tally->id()]; + ww_tally->set_scores({"flux"}); + + int32_t mesh_id = wws->mesh()->id(); + int32_t mesh_idx = model::mesh_map.at(mesh_id); + // see if there's already a mesh filter using this mesh + bool found_mesh_filter = false; + for (const auto& f : model::tally_filters) { + if (f->type() == FilterType::MESH) { + const auto* mesh_filter = dynamic_cast(f.get()); + if (mesh_filter->mesh() == mesh_idx && !mesh_filter->translated()) { + ww_tally->add_filter(f.get()); + found_mesh_filter = true; + break; + } + } + } + + if (!found_mesh_filter) { + auto mesh_filter = Filter::create("mesh"); + openmc_mesh_filter_set_mesh(mesh_filter->index(), model::mesh_map[mesh_id]); + ww_tally->add_filter(mesh_filter); + } + + const auto& e_bounds = wws->energy_bounds(); + if (e_bounds.size() > 0) { + auto energy_filter = Filter::create("energy"); + openmc_energy_filter_set_bins( + energy_filter->index(), e_bounds.size(), e_bounds.data()); + ww_tally->add_filter(energy_filter); + } + + // add a particle filter + auto particle_type = wws->particle_type(); + auto particle_filter = Filter::create("particle"); + auto pf = dynamic_cast(particle_filter); + pf->set_particles({&particle_type, 1}); + ww_tally->add_filter(particle_filter); +} + +void WeightWindowsGenerator::update() const +{ + const auto& wws = variance_reduction::weight_windows[ww_idx_]; + + Tally* tally = model::tallies[tally_idx_].get(); + + // if we're beyond the number of max realizations or not at the corrrect + // update interval, skip the update + if (max_realizations_ < tally->n_realizations_ || + tally->n_realizations_ % update_interval_ != 0) + return; + + wws->update_weights(tally, tally_value_, threshold_, ratio_, method_); + + // if we're not doing on the fly generation, reset the tally results once + // we're done with the update + if (!on_the_fly_) + tally->reset(); + + // TODO: deactivate or remove tally once weight window generation is + // complete +} + +//============================================================================== +// Non-member functions +//============================================================================== + +void finalize_variance_reduction() +{ + for (const auto& wwg : variance_reduction::weight_windows_generators) { + wwg->create_tally(); + } +} + +//============================================================================== +// C API +//============================================================================== + +int verify_ww_index(int32_t index) +{ + if (index < 0 || index >= variance_reduction::weight_windows.size()) { + set_errmsg(fmt::format("Index '{}' for weight windows is invalid", index)); + return OPENMC_E_OUT_OF_BOUNDS; + } + return 0; +} + +extern "C" int openmc_get_weight_windows_index(int32_t id, int32_t* idx) +{ + auto it = variance_reduction::ww_map.find(id); + if (it == variance_reduction::ww_map.end()) { + set_errmsg(fmt::format("No weight windows exist with ID={}", id)); + return OPENMC_E_INVALID_ID; + } + + *idx = it->second; + return 0; +} + +extern "C" int openmc_weight_windows_get_id(int32_t index, int32_t* id) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows.at(index); + *id = wws->id(); + return 0; +} + +extern "C" int openmc_weight_windows_set_id(int32_t index, int32_t id) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows.at(index); + wws->set_id(id); + return 0; +} + +extern "C" int openmc_weight_windows_update_magic(int32_t ww_idx, + int32_t tally_idx, const char* value, double threshold, double ratio) +{ + if (int err = verify_ww_index(ww_idx)) + return err; + + if (tally_idx < 0 || tally_idx >= model::tallies.size()) { + set_errmsg(fmt::format("Index '{}' for tally is invalid", tally_idx)); + return OPENMC_E_OUT_OF_BOUNDS; + } + + // get the requested tally + const Tally* tally = model::tallies.at(tally_idx).get(); + + // get the WeightWindows object + const auto& wws = variance_reduction::weight_windows.at(ww_idx); + + wws->update_weights(tally, value, threshold, ratio); + + return 0; +} + +extern "C" int openmc_weight_windows_set_mesh(int32_t ww_idx, int32_t mesh_idx) +{ + if (int err = verify_ww_index(ww_idx)) + return err; + const auto& wws = variance_reduction::weight_windows.at(ww_idx); + wws->set_mesh(mesh_idx); + return 0; +} + +extern "C" int openmc_weight_windows_get_mesh(int32_t ww_idx, int32_t* mesh_idx) +{ + if (int err = verify_ww_index(ww_idx)) + return err; + const auto& wws = variance_reduction::weight_windows.at(ww_idx); + *mesh_idx = model::mesh_map.at(wws->mesh()->id()); + return 0; +} + +extern "C" int openmc_weight_windows_set_energy_bounds( + int32_t ww_idx, double* e_bounds, size_t e_bounds_size) +{ + if (int err = verify_ww_index(ww_idx)) + return err; + const auto& wws = variance_reduction::weight_windows.at(ww_idx); + wws->set_energy_bounds({e_bounds, e_bounds_size}); + return 0; +} + +extern "C" int openmc_weight_windows_get_energy_bounds( + int32_t ww_idx, const double** e_bounds, size_t* e_bounds_size) +{ + if (int err = verify_ww_index(ww_idx)) + return err; + const auto& wws = variance_reduction::weight_windows[ww_idx].get(); + *e_bounds = wws->energy_bounds().data(); + *e_bounds_size = wws->energy_bounds().size(); + return 0; +} + +extern "C" int openmc_weight_windows_set_particle(int32_t index, int particle) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows.at(index); + wws->set_particle_type(static_cast(particle)); + return 0; +} + +extern "C" int openmc_weight_windows_get_particle(int32_t index, int* particle) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows.at(index); + *particle = static_cast(wws->particle_type()); + return 0; +} + +extern "C" int openmc_weight_windows_get_bounds(int32_t index, + const double** lower_bounds, const double** upper_bounds, size_t* size) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows[index]; + *size = wws->lower_ww_bounds().size(); + *lower_bounds = wws->lower_ww_bounds().data(); + *upper_bounds = wws->upper_ww_bounds().data(); + return 0; +} + +extern "C" int openmc_weight_windows_set_bounds(int32_t index, + const double* lower_bounds, const double* upper_bounds, size_t size) +{ + if (int err = verify_ww_index(index)) + return err; + + const auto& wws = variance_reduction::weight_windows[index]; + wws->set_bounds({lower_bounds, size}, {upper_bounds, size}); + return 0; +} + +extern "C" int openmc_weight_windows_get_survival_ratio( + int32_t index, double* ratio) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + *ratio = wws->survival_ratio(); + return 0; +} + +extern "C" int openmc_weight_windows_set_survival_ratio( + int32_t index, double ratio) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + wws->survival_ratio() = ratio; + std::cout << "Survival ratio: " << wws->survival_ratio() << std::endl; + return 0; +} + +extern "C" int openmc_weight_windows_get_max_lower_bound_ratio( + int32_t index, double* lb_ratio) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + *lb_ratio = wws->max_lower_bound_ratio(); + return 0; +} + +extern "C" int openmc_weight_windows_set_max_lower_bound_ratio( + int32_t index, double lb_ratio) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + wws->max_lower_bound_ratio() = lb_ratio; + return 0; +} + +extern "C" int openmc_weight_windows_get_weight_cutoff( + int32_t index, double* cutoff) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + *cutoff = wws->weight_cutoff(); + return 0; +} + +extern "C" int openmc_weight_windows_set_weight_cutoff( + int32_t index, double cutoff) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + wws->weight_cutoff() = cutoff; + return 0; +} + +extern "C" int openmc_weight_windows_get_max_split( + int32_t index, int* max_split) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + *max_split = wws->max_split(); + return 0; +} + +extern "C" int openmc_weight_windows_set_max_split(int32_t index, int max_split) +{ + if (int err = verify_ww_index(index)) + return err; + const auto& wws = variance_reduction::weight_windows[index]; + wws->max_split() = max_split; + return 0; +} + +extern "C" int openmc_extend_weight_windows( + int32_t n, int32_t* index_start, int32_t* index_end) +{ + if (index_start) + *index_start = variance_reduction::weight_windows.size(); + if (index_end) + *index_end = variance_reduction::weight_windows.size() + n - 1; + for (int i = 0; i < n; ++i) + variance_reduction::weight_windows.push_back(make_unique()); + return 0; +} + +extern "C" size_t openmc_weight_windows_size() +{ + return variance_reduction::weight_windows.size(); +} + +extern "C" int openmc_weight_windows_export(const char* filename) +{ + + if (!mpi::master) + return 0; + + std::string name = filename ? filename : "weight_windows.h5"; + + write_message(fmt::format("Exporting weight windows to {}...", name), 5); + + hid_t ww_file = file_open(name, 'w'); + + // Write file type + write_attribute(ww_file, "filetype", "weight_windows"); + + // Write revisiion number for state point file + write_attribute(ww_file, "version", VERSION_WEIGHT_WINDOWS); + + hid_t weight_windows_group = create_group(ww_file, "weight_windows"); + + hid_t mesh_group = create_group(ww_file, "meshes"); + + std::vector mesh_ids; + std::vector ww_ids; + for (const auto& ww : variance_reduction::weight_windows) { + + ww->to_hdf5(weight_windows_group); + ww_ids.push_back(ww->id()); + + // if the mesh has already been written, move on + int32_t mesh_id = ww->mesh()->id(); + if (std::find(mesh_ids.begin(), mesh_ids.end(), mesh_id) != mesh_ids.end()) + continue; + + mesh_ids.push_back(mesh_id); + ww->mesh()->to_hdf5(mesh_group); + } + + write_attribute(mesh_group, "n_meshes", mesh_ids.size()); + write_attribute(mesh_group, "ids", mesh_ids); + close_group(mesh_group); + + write_attribute(weight_windows_group, "n_weight_windows", ww_ids.size()); + write_attribute(weight_windows_group, "ids", ww_ids); + close_group(weight_windows_group); + + file_close(ww_file); + + return 0; +} + +extern "C" int openmc_weight_windows_import(const char* filename) +{ + std::string name = filename ? filename : "weight_windows.h5"; + + if (mpi::master) + write_message(fmt::format("Importing weight windows from {}...", name), 5); + + if (!file_exists(name)) { + set_errmsg(fmt::format("File '{}' does not exist", name)); + } + + hid_t ww_file = file_open(name, 'r'); + + // Check that filetype is correct + std::string filetype; + read_attribute(ww_file, "filetype", filetype); + if (filetype != "weight_windows") { + file_close(ww_file); + set_errmsg(fmt::format("File '{}' is not a weight windows file.", name)); + return OPENMC_E_INVALID_ARGUMENT; + } + + // Check that the file version is compatible + std::array file_version; + read_attribute(ww_file, "version", file_version); + if (file_version[0] != VERSION_WEIGHT_WINDOWS[0]) { + std::string err_msg = + fmt::format("File '{}' has version {} which is incompatible with the " + "expected version ({}).", + name, file_version, VERSION_WEIGHT_WINDOWS); + set_errmsg(err_msg); + return OPENMC_E_INVALID_ARGUMENT; + } + + hid_t weight_windows_group = open_group(ww_file, "weight_windows"); + + std::vector names = group_names(weight_windows_group); + + for (const auto& name : names) { + WeightWindows::from_hdf5(weight_windows_group, name); + } + + close_group(weight_windows_group); + + file_close(ww_file); + + return 0; +} + } // namespace openmc diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index cf3b981e51..840d3f5b87 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -4,6 +4,7 @@ #include "openmc/error.h" #include "openmc/string_utils.h" +#include "openmc/vector.h" namespace openmc { @@ -48,4 +49,29 @@ bool get_node_value_bool(pugi::xml_node node, const char* name) return false; } +vector get_node_position_array( + pugi::xml_node node, const char* name, bool lowercase) +{ + vector coords = get_node_array(node, name, lowercase); + if (coords.size() % 3 != 0) { + fatal_error(fmt::format( + "Incorect number of coordinates in Position array ({}) for \"{}\"", + coords.size(), name)); + } + vector positions; + positions.reserve(coords.size() / 3); + auto it = coords.begin(); + for (size_t i = 0; i < coords.size(); i += 3) { + positions.push_back({coords[i], coords[i + 1], coords[i + 2]}); + } + return positions; +} + +Position get_node_position( + pugi::xml_node node, const char* name, bool lowercase) +{ + vector arr = get_node_array(node, name, lowercase); + return Position(arr); +} + } // namespace openmc diff --git a/tests/chain_ni.xml b/tests/chain_ni.xml new file mode 100644 index 0000000000..fc96d3d8b3 --- /dev/null +++ b/tests/chain_ni.xml @@ -0,0 +1,173 @@ + + + + + + + + + 557.6039 640.4896 655.72 678.8113 5847.93 5858.68 6448.81 6450.12 6499.05 6499.18 126000.0 4.281895544855688e-11 8.113092795419058e-12 2.881383486075155e-13 8.900165070824795e-12 6.680021392530635e-10 1.3098276096378766e-09 7.960187308411459e-11 1.5666266690172673e-10 2.8061406316612144e-14 4.106367573258232e-14 1.0245835494664239e-17 + + + 105210.0 231210.0 1.040592667426837e-17 8.004558980206438e-09 + + + 36.67648 65.20168 562.7812 610.8647 5155.619 5764.491 6377.571 2.257651440763611e-08 2.654244519541021e-09 1.1943434358627441e-08 2.0711244046714524e-10 3.863926291321637e-09 9.499586470057554e-10 5.918783030777615e-11 + + + + + + + + + + + + + + + + + + 780.0 6915.0 6930.0 7649.0 142651.0 189000.0 192343.0 334800.0 382000.0 1099245.0 1291590.0 1481700.0 1.0526870077721752e-12 1.0971242349369013e-11 2.1648606887732874e-11 3.9280951004469605e-12 1.8390802445841403e-09 1.622717862868359e-12 5.553301130705051e-09 4.868153588605077e-10 3.245435725736718e-11 1.018706213911803e-07 7.789045741768123e-08 1.0637817101025909e-10 + + + 750.0 6070.0 83569.95 130946.9 134942.1 141725.4 184634.1 191417.4 273599.0 327091.1 333874.4 465943.0 1091536.0 1283881.0 1565200.0 1.4515384373262843e-10 5.994999523918275e-11 1.4063554811525778e-10 2.3619560003972782e-09 2.703447959538686e-11 2.7218387619845277e-12 4.520385317374064e-11 4.498173915871091e-12 8.16767990977074e-08 8.519268780058884e-13 8.275861100628631e-14 9.574035390923319e-08 1.6299297619569002e-11 8.567950315944936e-12 3.245435725736718e-10 + + + + + + 120340.0 177610.0 297900.0 333000.0 349700.0 440500.0 542600.0 561400.0 603300.0 618400.0 657300.0 686000.0 696900.0 748100.0 769400.0 806300.0 925600.0 945400.0 978000.0 984100.0 989200.0 1027420.0 1097800.0 1205070.0 1275000.0 1285700.0 1381400.0 1403900.0 1538800.0 1618900.0 1645950.0 1659300.0 1837200.0 1879400.0 1889000.0 1899300.0 1972700.0 1999800.0 2011600.0 2177100.0 2230800.0 2484400.0 2754400.0 2920000.0 3191000.0 3204200.0 3239100.0 3364900.0 0.00010275887633317896 3.874515009283797e-05 0.00042956579450755135 4.2956579450755134e-06 3.116457724858706e-06 4.21142935791717e-06 1.263428807375151e-06 1.0949716330584643e-06 1.431885981691838e-06 1.802491765188549e-05 4.2956579450755134e-06 7.749030018567594e-06 2.1899432661169287e-06 1.558228862429353e-05 3.116457724858706e-06 3.7060578349671097e-06 6.569829798350785e-06 2.105714678958585e-06 1.431885981691838e-06 1.1792002202168078e-05 1.1792002202168078e-05 0.0008254401541517653 1.3476573945334946e-05 0.000842285871583434 1.1792002202168078e-05 7.159429908459189e-06 7.749030018567594e-06 2.2741718532752718e-06 5.306400990975635e-06 7.075201321300846e-06 0.00013476573945334945 1.4992688514185127e-05 2.6953147890669893e-06 5.053715229500604e-06 3.4533720734920796e-06 1.431885981691838e-06 1.1792002202168076e-06 2.526857614750302e-06 8.507087302992683e-05 4.042972183600483e-06 2.105714678958585e-06 2.3584004404336153e-06 1.4824231339868439e-05 1.3476573945334947e-06 1.6003431560085245e-06 8.422858715834341e-07 1.0949716330584643e-06 8.422858715834341e-07 + + + 613000.0 738800.1 773300.1 786899.9 873600.1 977699.9 1057900.0 1113610.0 1223500.0 1493500.0 1546600.0 1675000.0 1747100.0 1966500.0 2024800.0 2089000.0 2332110.0 2359120.0 2652590.0 2692270.0 2772900.0 2950510.0 3978000.0 7.969724371709087e-07 1.032441566335041e-06 1.1773456458206606e-05 1.5214928345990077e-06 1.267910695499173e-06 2.535821390998346e-05 1.267910695499173e-06 2.1735611922842964e-05 1.3947017650490904e-05 2.227900222091404e-06 2.173561192284297e-06 2.8980815897123955e-05 1.0867805961421485e-06 9.056504967851236e-05 1.7931879836345447e-05 4.709382583282643e-06 0.00014490407948561978 2.4452563413198337e-06 0.0004890512682639667 3.622601987140494e-07 0.0006882943775566939 0.00028980815897123956 9.056504967851236e-05 + + + + + + 619.9594 707.9362 726.02 741.741 6349.85 6362.71 7015.36 7016.95 7070.49 7070.66 510998.9 810759.2 863951.0 1674725.0 5.276288639112756e-10 1.3715809425619083e-10 3.759818598537813e-12 1.1767757246637665e-10 8.777123989590544e-09 1.719716697150835e-08 1.057271626846154e-09 2.0783510382283326e-09 5.201844116739253e-13 7.595270495013994e-13 3.374000922877054e-08 1.1259402850084962e-07 7.768987966558623e-10 5.854889482044179e-10 + + + 632864.1 1496834.0 2307600.0 1.3699223176071195e-09 1.1185811981783752e-07 8.491254034754873e-13 + + + 40.23492 70.00108 628.9213 675.1537 5583.843 6258.984 6938.274 803647.2 809913.1 810759.2 856839.0 863104.9 863951.0 1667613.0 1673879.0 1674725.0 2.3068291731266982e-07 2.4528685530196402e-08 1.3309519902564132e-07 1.9030055102434398e-09 4.481805021434518e-08 1.1035282426757231e-08 6.934054278993223e-10 3.366561452175404e-11 3.231448617974384e-12 4.640000480603615e-13 1.6314869068937087e-13 1.553797593311725e-14 2.2436836794554425e-15 3.3782714575729324e-14 3.2026247731116077e-15 4.614238015862169e-16 + + + + + + + + + + 751.0021 852.3381 876.89 883.6421 7417.82 7435.78 8222.319 8224.59 8287.88 8288.141 347140.0 826100.0 1173228.0 1332492.0 2158570.0 2505692.0 7.0614888325101086e-15 3.3564685966506987e-15 5.761418570090495e-17 2.3600648888691433e-15 1.3290046971181013e-13 2.5954235427719607e-13 1.6253028424014347e-14 3.186921843254948e-14 1.3932264913028456e-17 2.024904561736029e-17 3.125208966828774e-13 3.1668784197198245e-13 4.160694871171375e-09 4.166220240624728e-09 5.000334346926039e-14 8.333890578210064e-17 + + + 47.97734 78.78671 772.594 818.3802 6497.883 7313.626 8133.5 318151.8 1164895.0 1172220.0 1173228.0 1324159.0 1331484.0 1332492.0 1491392.0 2497359.0 2504684.0 2505692.0 2.430147074908485e-12 3.047352927658078e-13 1.6375715794161468e-12 1.653965175572544e-14 5.18885111875044e-13 1.2976030141139345e-13 8.558901456876446e-15 4.161944954758106e-09 6.241042306757062e-13 6.095415902793419e-14 8.94050113917305e-15 4.736991738545178e-13 4.616171159887578e-14 6.779692031857936e-15 5.000334346926038e-12 6.46709908869101e-21 6.3170890582832295e-22 9.678980517533168e-23 + + + + + + 751.028 852.3382 876.89 883.6886 7417.82 7435.78 8222.319 8224.59 8287.88 8288.141 67415.0 841700.0 909200.0 7.866502846971466e-08 3.756939402646081e-08 6.436568219422988e-10 2.637348005677196e-08 1.4671031864629482e-06 2.8654120987125446e-06 1.794509040062992e-07 3.5187803327436886e-07 1.538314140492697e-10 2.235774236842793e-10 9.880497991981766e-05 9.262966867482906e-07 4.219796017408879e-06 + + + 48.00067 78.83981 772.8435 818.5774 6498.158 7313.756 8133.501 59082.2 66406.9 67415.0 413499.9 833367.2 840691.9 841700.0 900867.2 908191.9 909200.0 1255285.0 2.6847340674754763e-05 3.3502043712197297e-06 1.814135374119958e-05 1.8444716489566865e-07 5.715307736044776e-06 1.430796977323619e-06 9.449890224831685e-08 1.2103610040177662e-05 1.246919266677299e-06 1.902588515965013e-07 5.134423559703298e-06 2.917834563257115e-10 2.862256762052218e-11 4.385088515066407e-12 9.241353278125446e-10 9.030363477255e-11 1.3801262380678854e-11 0.00011155702097900803 + + + + + + 1128900.0 1172900.0 1886300.0 1985000.0 2083000.0 2097000.0 2301800.0 2345900.0 3158200.0 3271000.0 3369500.0 3519000.0 4063100.0 0.0008553051126342747 0.006430865508528381 3.2154327542641905e-05 0.00012218644466203924 2.5723462034113525e-05 7.07395205938122e-05 0.0011318323295009952 0.0001028938481364541 6.430865508528381e-05 1.1254014639924668e-05 2.8938894788377717e-05 6.430865508528381e-06 2.5723462034113525e-05 + + + 1251800.0 1796100.0 1945400.0 2044800.0 2059000.0 2156900.0 2255700.0 3013160.0 4142070.0 5315000.0 2.5415396620531326e-05 0.00010782289475376927 2.9266214290308802e-05 9.318978760861486e-05 2.5415396620531326e-05 0.00018483924814931876 3.080654135821979e-05 0.0019870219176051766 0.005221708760218255 1.925408834888737e-05 + + + + + + 931100.0 1346100.0 0.11552453009332422 0.23104906018664845 + + + 5029800.0 5960900.0 7307000.0 0.11552453009332422 0.11552453009332422 2.079441541679836 + + + + + + 683.2094 778.5527 799.73 813.244 6873.16 6888.41 7606.53 7608.44 7666.77 7666.98 127164.0 161860.0 304100.0 379940.0 510998.9 541900.0 673440.0 696000.0 755300.0 906980.0 1046680.0 1224000.0 1279990.0 1350520.0 1377630.0 1603280.0 1730440.0 1757550.0 1897420.0 1919520.0 2133040.0 2730910.0 2804200.0 3177280.0 1.8638833690911883e-08 6.653293496572297e-09 1.4203876186268797e-10 5.409592966721624e-09 3.0832339673226584e-07 6.028687625463321e-07 3.74301586798566e-08 7.349918311628966e-08 2.4690572137876036e-11 3.5966442331457405e-11 9.014158418349326e-07 1.2284000197554473e-09 1.0604892256881559e-10 3.6233381877678664e-09 4.684800934777033e-06 1.9884172981652923e-10 2.6556417693274235e-09 4.860575617737382e-11 2.916345370642429e-10 3.3140288302754875e-09 7.246676375535733e-09 3.402402932416167e-09 5.2140720263001e-10 1.0604892256881559e-10 4.418705107033983e-06 2.1209784513763118e-10 2.827971268501749e-09 3.110768395351924e-07 1.502359736391554e-09 6.628057660550975e-07 1.546546787461894e-09 1.0737453410092578e-09 5.30244612844078e-09 6.009438945566218e-10 + + + 84790.04 154079.8 457929.9 531290.1 1129120.0 1342650.0 1504620.0 1757390.0 1884550.0 1.124957970946228e-09 3.2450710700371965e-09 1.5738594689680403e-08 1.0762819048956704e-09 1.8388736063544118e-09 6.652395693576253e-07 3.0611837094017556e-07 9.194368031772059e-07 3.488451400289987e-06 + + + 43.92123 75.12743 700.3887 749.9867 6033.611 6776.5 7523.621 119455.1 126238.4 127164.0 154151.1 160934.4 161860.0 665731.1 672514.4 673440.0 747591.1 754374.4 755300.0 1216291.0 1223074.0 1224000.0 1369921.0 1376704.0 1377630.0 1749841.0 1756624.0 1757550.0 1889711.0 1896494.0 1897420.0 1911811.0 1918594.0 1919520.0 2125331.0 2132114.0 2133040.0 7.252641897852818e-06 9.885065183674125e-07 4.453862207006767e-06 5.575973168934215e-08 1.3531437967587472e-06 3.3801747364315905e-07 2.1887831296943824e-08 1.739732899248527e-08 1.7397323584033486e-09 2.636641229198142e-10 1.272622225762379e-11 1.2627950688719498e-12 1.9160584871075314e-13 9.58686478614484e-13 9.268187990163621e-14 1.4090839543995835e-14 8.632381215411233e-14 8.34074667834699e-15 1.2155339619769639e-15 3.6814000810433283e-13 3.538499049712814e-14 5.158725716665081e-15 4.21544467211042e-10 4.0607899933642303e-11 2.1687004665322787e-10 1.822910020070542e-11 1.7482517516525527e-12 6.153697369032366e-11 7.181277376570916e-14 6.8808054292926055e-15 3.195035376537882e-13 3.115187100458958e-11 2.9826259472479385e-12 1.482406852545487e-10 6.031532471101388e-14 5.768616813006974e-15 4.833646927113489e-13 + + + + + + + + + + + + 1072500.0 2.890064045563861e-13 + + + 44.06754 75.28693 701.5928 750.7433 6035.851 6777.615 7523.68 6.689278658453111e-13 8.913674252402247e-14 4.1657845563004787e-13 5.378516121164032e-15 1.2583954438026756e-13 3.16650156158609e-14 2.0701132819599697e-15 + + + 683.3271 778.5529 799.73 813.5109 6873.16 6888.41 7606.53 7608.44 7666.77 7666.98 510998.9 1.7758787515451584e-15 6.581346326607483e-16 1.368589539770362e-17 5.248596182059754e-16 2.920762305855841e-14 5.711006429349971e-14 3.5457762265416177e-15 6.962612245690403e-15 2.338949640535961e-18 3.4071167337635547e-18 2.1386473937172572e-19 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 66945.0 2.1704054025041888e-10 + + + + + + + + + + + + 820.0389 927.6652 951.42 959.5527 7984.67 8005.709 8862.7 8865.34 8933.11 8933.4 366270.0 507900.0 609500.0 770600.0 852700.0 954500.0 1115530.0 1481840.0 1623420.0 1724920.0 6.930967733828076e-11 4.4848940639261224e-11 6.225110448569713e-13 3.0091057755445835e-11 1.4120610720530457e-09 2.7533685191404717e-09 1.7112326402414573e-10 3.3482649978514024e-10 1.9318959861680173e-13 2.7929997279698636e-13 3.6755804457910027e-06 2.2374667416695354e-07 1.181887674027053e-07 7.92135402897521e-08 7.398075516810562e-08 1.3533064969775417e-09 1.1800832653644161e-05 1.804408662636722e-05 3.807302278163484e-07 3.0494506398560603e-07 + + + 56.92597 90.91714 855.942 897.216 6991.356 7880.445 8773.007 357291.1 365173.4 366270.0 412919.9 498921.1 506803.4 507900.0 514460.0 600521.1 608403.4 609500.0 656069.9 761621.1 769503.4 770600.0 1022350.0 1106551.0 1114434.0 1115530.0 1472861.0 1480743.0 1481840.0 1614441.0 1622324.0 1623420.0 1715941.0 1723824.0 1724920.0 2137900.0 2.167548391930537e-08 2.8616659297729836e-09 1.5876524466163717e-08 5.919283450210211e-11 4.8238674145478966e-09 1.1910611683428657e-09 8.251524863747208e-11 6.6528004309537836e-09 6.652800430953784e-10 9.941708721258623e-11 4.2452174979371803e-07 1.968970885650002e-10 1.9600207433178643e-11 2.840463936760989e-12 6.807646077773136e-07 6.748578656939676e-11 6.701302996997783e-12 9.996408692926368e-13 2.172327512457944e-05 2.732867560693677e-11 2.7011821751739386e-12 4.0351373644973695e-13 7.786723266486576e-06 1.953037934211798e-09 1.9247158318161006e-10 3.950919133474779e-11 1.818843931937816e-09 1.7899733933356285e-10 1.4705028396157966e-09 3.076300301948419e-11 3.022997962967563e-12 4.574485034565062e-11 2.213900812679635e-11 2.1742581302894385e-12 5.0020380532292804e-11 4.589424322094249e-05 + + + diff --git a/tests/chain_simple.xml b/tests/chain_simple.xml index c2e50a370f..4e08995a8d 100644 --- a/tests/chain_simple.xml +++ b/tests/chain_simple.xml @@ -1,12 +1,18 @@ - + + + 3696.125 4095.822 4477.27 5097.122 29452.1 29781.3 33566.5 33629.4 33865.1 33878.5 34395.3 34408.0 34486.2 34488.2 112780.0 113150.0 162650.0 165740.0 184490.0 197190.0 220502.0 229720.0 247500.0 254740.0 264260.0 288451.0 290270.0 304910.0 305830.0 326000.0 333600.0 342520.0 361850.0 403030.0 414830.0 417633.0 429930.0 433741.0 451630.0 530800.0 546557.0 575970.0 588280.0 616900.0 649850.0 656090.0 679220.0 684600.0 690130.0 707920.0 785480.0 795500.0 797710.0 807200.0 836804.0 960290.0 961430.0 971960.0 972620.0 995090.0 1038760.0 1096860.0 1101580.0 1124000.0 1131511.0 1151510.0 1159900.0 1169040.0 1225600.0 1240470.0 1254800.0 1260409.0 1315770.0 1334800.0 1343660.0 1367890.0 1441800.0 1448350.0 1457560.0 1502790.0 1521990.0 1543700.0 1566410.0 1678027.0 1706459.0 1791196.0 1830690.0 1845300.0 1927300.0 1948490.0 2045880.0 2112400.0 2151500.0 2189400.0 2255457.0 2408650.0 2466070.0 2477100.0 9.714352819815078e-10 7.460941651551526e-09 6.047882056201745e-09 8.510389107205747e-10 3.7979729633684727e-08 7.033747061198817e-08 6.602815946851458e-09 1.2800909198245071e-08 6.513060244589454e-11 8.894667887850525e-11 1.3843783302275766e-09 2.700005498135763e-09 1.2141115256573815e-11 1.654893875618862e-11 3.7007705885806645e-09 2.0186021392258173e-09 2.859686363903241e-09 9.16781804898392e-09 6.896890642354875e-09 9.588360161322631e-09 5.130613770532286e-07 7.06510748729036e-08 8.410842246774237e-09 6.7286737974193905e-09 5.3829390379355124e-08 9.083709626516178e-07 8.915492781580693e-08 9.251926471451662e-09 2.783988783682273e-08 6.728673797419391e-10 1.093409492080651e-08 2.5232526740322717e-10 5.467047460403255e-08 6.812782219887132e-08 8.83138435911295e-08 1.0345335963532313e-06 8.915492781580693e-08 1.623292553627428e-07 9.251926471451663e-08 9.251926471451662e-09 2.0942997194467853e-06 3.784879011048407e-08 1.513951604419363e-08 1.093409492080651e-08 1.337323917237104e-07 2.186818984161302e-08 1.5980600268871052e-08 6.7286737974193905e-09 3.784879011048407e-08 1.9344937167580748e-07 4.457746390790346e-08 6.7286737974193905e-09 5.0465053480645434e-08 1.3457347594838781e-08 1.9597262434983976e-06 1.0093010696129087e-08 4.289529545854862e-08 2.607361096500014e-07 3.53255374364518e-07 4.541854813258089e-08 2.329803302356464e-06 2.607361096500014e-08 4.7100716581935733e-07 1.059766123093554e-06 6.6193328482113254e-06 4.205421123387119e-10 3.027903208838726e-08 2.565306885266143e-07 1.2616263370161358e-08 2.649415307733885e-07 3.3643368987096953e-09 8.410842246774237e-06 1.934493716758075e-08 9.251926471451662e-09 2.2709274066290446e-08 1.7830985563161385e-07 5.046505348064544e-09 9.251926471451663e-08 2.5400743585258203e-06 3.154065842540339e-07 1.093409492080651e-08 7.569758022096815e-09 3.784879011048407e-07 2.8008104681758214e-06 1.2027504412887161e-06 2.26251656438227e-06 1.6989901338483962e-07 1.6821684493548476e-09 8.663167514177466e-08 1.8503852942903323e-08 2.5568960430193683e-07 2.0186021392258175e-08 6.560456952483906e-09 3.784879011048407e-09 1.7999202408096872e-07 2.800810468175822e-07 2.1027105616935597e-08 4.205421123387119e-10 + - + + + 0.0 10000.0 20000.0 50000.0 100000.0 200000.0 300000.0 400000.0 600000.0 800000.0 1000000.0 1220000.0 1.1612176249914943e-11 0.0 3.1976524142990123e-11 0.0 6.418466676129901e-13 1.894551923065874e-10 5.099949011192198e-13 3.170644340540729e-13 2.756798470867507e-12 6.67627508515641e-14 3.711746246444946e-15 0.0 + @@ -28,6 +34,9 @@ + + 3065.349 12960.11 13197.49 16125.43 19185.05 19590.0 31600.0 34700.0 41400.0 41960.0 51220.0 54100.0 54250.0 64350.0 72700.0 75020.0 76198.0 90330.0 93795.0 96090.0 105278.0 106074.0 106608.0 106771.0 108948.0 109154.0 109160.0 109395.0 109433.0 115450.0 120350.0 136550.0 140760.0 142400.0 143760.0 150930.0 163330.0 173300.0 182610.0 185715.0 194940.0 198900.0 202110.0 205311.0 215280.0 221380.0 228780.0 233500.0 240870.0 246840.0 266450.0 275129.0 275430.0 281420.0 282920.0 289560.0 291650.0 301700.0 317100.0 343500.0 345900.0 356030.0 387820.0 410290.0 428710.0 448400.0 1.4211820389290126e-18 3.695705148646686e-18 4.752472005970374e-19 4.0825252294602915e-18 8.998127221991115e-19 1.9035285506819052e-21 5.305446177665699e-21 1.1547147563154756e-20 9.362552078233586e-21 1.86835843588509e-20 1.0610892355331398e-20 2.736290732002608e-22 4.776811520523089e-21 3.977552295559136e-21 3.432935762018982e-20 1.872510415646717e-20 2.4966805541956234e-21 1.0265454754703584e-18 1.7575878976520235e-18 2.839974130397521e-20 2.1057468800839102e-19 4.1342252420362975e-19 7.098565272539688e-21 8.20050332279016e-21 5.276915360632628e-20 1.0602918581811435e-19 4.806110066826575e-19 2.0521431485853304e-21 2.375669880669523e-21 9.362552078233586e-21 8.267152210184412e-21 3.745020831293435e-21 6.865871524037964e-20 1.5604253463722645e-21 3.420452359248004e-18 2.4966805541956232e-20 1.5853921519142206e-18 1.8725104156467174e-21 1.0610892355331397e-19 1.7851265962498703e-17 1.966135936429053e-19 1.3107572909527022e-20 3.370518748164091e-19 1.5635461970650089e-18 9.050467008959134e-21 3.745020831293434e-20 2.18459548492117e-21 9.050467008959134e-21 2.3406380195583967e-20 1.6540508671546e-20 1.8725104156467174e-21 1.622842360227155e-20 2.18459548492117e-21 1.8725104156467174e-21 1.8725104156467174e-21 2.18459548492117e-21 1.2483402770978116e-20 1.5604253463722645e-21 3.120850692744529e-22 9.362552078233587e-22 1.2483402770978116e-20 1.5604253463722645e-21 1.2483402770978116e-20 9.362552078233587e-22 3.120850692744529e-22 3.120850692744529e-22 + 2.53000e-02 @@ -38,6 +47,9 @@ + + 3061.32 12959.8 13440.07 16150.05 19148.83 49550.0 90330.0 93795.0 105278.0 106074.0 106608.0 106771.0 108948.0 109154.0 109395.0 109433.0 113500.0 5.3130501476983077e-20 1.4936931167066328e-19 1.943509007980312e-20 1.8224649880365157e-19 4.0452400597373603e-20 3.146222282132249e-21 3.300026341588747e-23 5.444173877278485e-23 6.3486292118621375e-24 1.2400176384733938e-23 2.124537722121886e-25 2.4542156071495764e-25 1.5792541400719878e-24 3.1731991718126067e-24 6.141568457919309e-26 7.109808533300877e-26 5.014291762148272e-22 + 2.53000e-02 diff --git a/tests/chain_simple_decay.xml b/tests/chain_simple_decay.xml new file mode 100644 index 0000000000..1c8c5e4eec --- /dev/null +++ b/tests/chain_simple_decay.xml @@ -0,0 +1,66 @@ + + + + + + + 3696.125 4095.822 4477.27 5097.122 29452.1 29781.3 33566.5 33629.4 33865.1 33878.5 34395.3 34408.0 34486.2 34488.2 112780.0 113150.0 162650.0 165740.0 184490.0 197190.0 220502.0 229720.0 247500.0 254740.0 264260.0 288451.0 290270.0 304910.0 305830.0 326000.0 333600.0 342520.0 361850.0 403030.0 414830.0 417633.0 429930.0 433741.0 451630.0 530800.0 546557.0 575970.0 588280.0 616900.0 649850.0 656090.0 679220.0 684600.0 690130.0 707920.0 785480.0 795500.0 797710.0 807200.0 836804.0 960290.0 961430.0 971960.0 972620.0 995090.0 1038760.0 1096860.0 1101580.0 1124000.0 1131511.0 1151510.0 1159900.0 1169040.0 1225600.0 1240470.0 1254800.0 1260409.0 1315770.0 1334800.0 1343660.0 1367890.0 1441800.0 1448350.0 1457560.0 1502790.0 1521990.0 1543700.0 1566410.0 1678027.0 1706459.0 1791196.0 1830690.0 1845300.0 1927300.0 1948490.0 2045880.0 2112400.0 2151500.0 2189400.0 2255457.0 2408650.0 2466070.0 2477100.0 9.714352819815078e-10 7.460941651551526e-09 6.047882056201745e-09 8.510389107205747e-10 3.7979729633684727e-08 7.033747061198817e-08 6.602815946851458e-09 1.2800909198245071e-08 6.513060244589454e-11 8.894667887850525e-11 1.3843783302275766e-09 2.700005498135763e-09 1.2141115256573815e-11 1.654893875618862e-11 3.7007705885806645e-09 2.0186021392258173e-09 2.859686363903241e-09 9.16781804898392e-09 6.896890642354875e-09 9.588360161322631e-09 5.130613770532286e-07 7.06510748729036e-08 8.410842246774237e-09 6.7286737974193905e-09 5.3829390379355124e-08 9.083709626516178e-07 8.915492781580693e-08 9.251926471451662e-09 2.783988783682273e-08 6.728673797419391e-10 1.093409492080651e-08 2.5232526740322717e-10 5.467047460403255e-08 6.812782219887132e-08 8.83138435911295e-08 1.0345335963532313e-06 8.915492781580693e-08 1.623292553627428e-07 9.251926471451663e-08 9.251926471451662e-09 2.0942997194467853e-06 3.784879011048407e-08 1.513951604419363e-08 1.093409492080651e-08 1.337323917237104e-07 2.186818984161302e-08 1.5980600268871052e-08 6.7286737974193905e-09 3.784879011048407e-08 1.9344937167580748e-07 4.457746390790346e-08 6.7286737974193905e-09 5.0465053480645434e-08 1.3457347594838781e-08 1.9597262434983976e-06 1.0093010696129087e-08 4.289529545854862e-08 2.607361096500014e-07 3.53255374364518e-07 4.541854813258089e-08 2.329803302356464e-06 2.607361096500014e-08 4.7100716581935733e-07 1.059766123093554e-06 6.6193328482113254e-06 4.205421123387119e-10 3.027903208838726e-08 2.565306885266143e-07 1.2616263370161358e-08 2.649415307733885e-07 3.3643368987096953e-09 8.410842246774237e-06 1.934493716758075e-08 9.251926471451662e-09 2.2709274066290446e-08 1.7830985563161385e-07 5.046505348064544e-09 9.251926471451663e-08 2.5400743585258203e-06 3.154065842540339e-07 1.093409492080651e-08 7.569758022096815e-09 3.784879011048407e-07 2.8008104681758214e-06 1.2027504412887161e-06 2.26251656438227e-06 1.6989901338483962e-07 1.6821684493548476e-09 8.663167514177466e-08 1.8503852942903323e-08 2.5568960430193683e-07 2.0186021392258175e-08 6.560456952483906e-09 3.784879011048407e-09 1.7999202408096872e-07 2.800810468175822e-07 2.1027105616935597e-08 4.205421123387119e-10 + + + + + + 0.0 10000.0 20000.0 50000.0 100000.0 200000.0 300000.0 400000.0 600000.0 800000.0 1000000.0 1220000.0 1.1612176249914943e-11 0.0 3.1976524142990123e-11 0.0 6.418466676129901e-13 1.894551923065874e-10 5.099949011192198e-13 3.170644340540729e-13 2.756798470867507e-12 6.67627508515641e-14 3.711746246444946e-15 0.0 + + + + + + + + + + + + + + + + + + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 1.093250e-04 2.087260e-04 2.780820e-02 6.759540e-03 2.392300e-02 4.356330e-05 + + + + + + + 3065.349 12960.11 13197.49 16125.43 19185.05 19590.0 31600.0 34700.0 41400.0 41960.0 51220.0 54100.0 54250.0 64350.0 72700.0 75020.0 76198.0 90330.0 93795.0 96090.0 105278.0 106074.0 106608.0 106771.0 108948.0 109154.0 109160.0 109395.0 109433.0 115450.0 120350.0 136550.0 140760.0 142400.0 143760.0 150930.0 163330.0 173300.0 182610.0 185715.0 194940.0 198900.0 202110.0 205311.0 215280.0 221380.0 228780.0 233500.0 240870.0 246840.0 266450.0 275129.0 275430.0 281420.0 282920.0 289560.0 291650.0 301700.0 317100.0 343500.0 345900.0 356030.0 387820.0 410290.0 428710.0 448400.0 1.4211820389290126e-18 3.695705148646686e-18 4.752472005970374e-19 4.0825252294602915e-18 8.998127221991115e-19 1.9035285506819052e-21 5.305446177665699e-21 1.1547147563154756e-20 9.362552078233586e-21 1.86835843588509e-20 1.0610892355331398e-20 2.736290732002608e-22 4.776811520523089e-21 3.977552295559136e-21 3.432935762018982e-20 1.872510415646717e-20 2.4966805541956234e-21 1.0265454754703584e-18 1.7575878976520235e-18 2.839974130397521e-20 2.1057468800839102e-19 4.1342252420362975e-19 7.098565272539688e-21 8.20050332279016e-21 5.276915360632628e-20 1.0602918581811435e-19 4.806110066826575e-19 2.0521431485853304e-21 2.375669880669523e-21 9.362552078233586e-21 8.267152210184412e-21 3.745020831293435e-21 6.865871524037964e-20 1.5604253463722645e-21 3.420452359248004e-18 2.4966805541956232e-20 1.5853921519142206e-18 1.8725104156467174e-21 1.0610892355331397e-19 1.7851265962498703e-17 1.966135936429053e-19 1.3107572909527022e-20 3.370518748164091e-19 1.5635461970650089e-18 9.050467008959134e-21 3.745020831293434e-20 2.18459548492117e-21 9.050467008959134e-21 2.3406380195583967e-20 1.6540508671546e-20 1.8725104156467174e-21 1.622842360227155e-20 2.18459548492117e-21 1.8725104156467174e-21 1.8725104156467174e-21 2.18459548492117e-21 1.2483402770978116e-20 1.5604253463722645e-21 3.120850692744529e-22 9.362552078233587e-22 1.2483402770978116e-20 1.5604253463722645e-21 1.2483402770978116e-20 9.362552078233587e-22 3.120850692744529e-22 3.120850692744529e-22 + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 6.142710e-5 1.483250e-04 0.0292737 0.002566345 0.0219242 4.9097e-6 + + + + + + + 3061.32 12959.8 13440.07 16150.05 19148.83 49550.0 90330.0 93795.0 105278.0 106074.0 106608.0 106771.0 108948.0 109154.0 109395.0 109433.0 113500.0 5.3130501476983077e-20 1.4936931167066328e-19 1.943509007980312e-20 1.8224649880365157e-19 4.0452400597373603e-20 3.146222282132249e-21 3.300026341588747e-23 5.444173877278485e-23 6.3486292118621375e-24 1.2400176384733938e-23 2.124537722121886e-25 2.4542156071495764e-25 1.5792541400719878e-24 3.1731991718126067e-24 6.141568457919309e-26 7.109808533300877e-26 5.014291762148272e-22 + + + 2.53000e-02 + + Gd157 Gd156 I135 Xe135 Xe136 Cs135 + 4.141120e-04 7.605360e-04 0.0135457 0.00026864 0.0024432 3.7100E-07 + + + + diff --git a/tests/conftest.py b/tests/conftest.py index 639d669f3a..fa6718502d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ +import os import pytest +import openmc from tests.regression_tests import config as regression_config @@ -27,3 +29,12 @@ def run_in_tmpdir(tmpdir): yield finally: orig.chdir() + +@pytest.fixture(scope="module") +def endf_data(): + return os.environ['OPENMC_ENDF_DATA'] + +@pytest.fixture(scope='session', autouse=True) +def resolve_paths(): + with openmc.config.patch('resolve_paths', False): + yield diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt new file mode 100644 index 0000000000..8fedc2daa5 --- /dev/null +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAMES + test_distribution + test_file_utils + test_tally + test_interpolate + test_math + test_mcpl_stat_sum + # Add additional unit test files here +) + +foreach(test ${TEST_NAMES}) + add_executable(${test} ${test}.cpp) + target_link_libraries(${test} Catch2::Catch2WithMain libopenmc) + add_test(NAME ${test} COMMAND ${test} WORKING_DIRECTORY ${UNIT_TEST_BIN_OUTPUT_DIR}) +endforeach() diff --git a/tests/cpp_unit_tests/test_distribution.cpp b/tests/cpp_unit_tests/test_distribution.cpp new file mode 100644 index 0000000000..e1a212db55 --- /dev/null +++ b/tests/cpp_unit_tests/test_distribution.cpp @@ -0,0 +1,81 @@ +#include "openmc/distribution.h" +#include "openmc/random_lcg.h" +#include +#include +#include +#include + +TEST_CASE("Test alias method sampling of a discrete distribution") +{ + constexpr int n_samples = 1000000; + double x[5] = {-1.6, 1.1, 20.3, 4.7, 0.9}; + double p[5] = {0.2, 0.1, 0.65, 0.02, 0.03}; + + // Initialize distribution + openmc::Discrete dist(x, p, 5); + uint64_t seed = openmc::init_seed(0, 0); + + // Calculate expected distribution mean + double mean = 0.0; + for (size_t i = 0; i < 5; i++) { + mean += x[i] * p[i]; + } + + // Sample distribution and calculate mean, standard deviation, and number of + // x[0] sampled + double dist_mean = 0.0; + double std = 0.0; + int counter = 0; + + for (size_t i = 0; i < n_samples; i++) { + auto sample = dist.sample(&seed); + std += sample * sample / n_samples; + dist_mean += sample; + + if (sample == x[0]) + counter++; + } + + dist_mean /= n_samples; + std -= dist_mean * dist_mean; + + // Require sampled distribution mean is within 4 standard deviations of the + // expected mean + REQUIRE(std::abs(dist_mean - mean) < 4 * std); + + // Require counter of number of x[0] is within the 95% confidence interval + // assuming a Poisson distribution of 200,000 + REQUIRE(std::abs((double)counter / n_samples - p[0]) < + 1.96 * std::sqrt(p[0] / n_samples)); +} + +TEST_CASE("Test alias sampling method for pugixml constructor") +{ + // XML doc node for Discrete contructor + pugi::xml_document doc; + pugi::xml_node energy = doc.append_child("energy"); + pugi::xml_node parameters = energy.append_child("parameters"); + parameters.append_child(pugi::node_pcdata) + .set_value("800 500000 30000 0.1 0.6 0.3"); + + // Initialize discrete distribution and seed + openmc::Discrete dist(energy); + uint64_t seed = openmc::init_seed(0, 0); + auto sample = dist.sample(&seed); + + // Assertions + REQUIRE(dist.x().size() == 3); + REQUIRE(dist.prob().size() == 3); + REQUIRE(dist.alias().size() == 3); + + openmc::vector correct_x = {800, 500000, 30000}; + openmc::vector correct_prob = {0.3, 1.0, 0.9}; + openmc::vector correct_alias = {1, 0, 1}; + + for (size_t i = 0; i < 3; i++) { + REQUIRE(dist.x()[i] == correct_x[i]); + REQUIRE_THAT( + dist.prob()[i], Catch::Matchers::WithinAbs(correct_prob[i], 1e-12)); + REQUIRE(dist.alias()[i] == correct_alias[i]); + } +} diff --git a/tests/cpp_unit_tests/test_file_utils.cpp b/tests/cpp_unit_tests/test_file_utils.cpp new file mode 100644 index 0000000000..8b0d99d76d --- /dev/null +++ b/tests/cpp_unit_tests/test_file_utils.cpp @@ -0,0 +1,41 @@ +#include "openmc/file_utils.h" +#include + +using namespace openmc; + +TEST_CASE("Test get_file_extension") +{ + REQUIRE(get_file_extension("rememberthealamo.png") == "png"); + REQUIRE(get_file_extension("statepoint.20.h5") == "h5"); + REQUIRE(get_file_extension("wEiRDNaa_ame.h4") == "h4"); + REQUIRE(get_file_extension("has_directory/asdf.20.h5") == "h5"); + REQUIRE(get_file_extension("wasssssup_lol") == ""); + REQUIRE(get_file_extension("has_directory/secret_file") == ""); + REQUIRE(get_file_extension("lovely.dir/extensionless_file") == ""); + REQUIRE(get_file_extension("lovely.dir/statepoint.20.h5") == "h5"); + REQUIRE(get_file_extension("lovely.dir/asdf123.cpp") == "cpp"); +} + +TEST_CASE("Test dir_exists") +{ + // not sure how to test this when running on windows? + REQUIRE(dir_exists("/")); + + // if this exists on your system... you deserve for this test to fail + REQUIRE(!dir_exists("/asdfa/asdfasdf/asdgasodgosuihasjkgh/")); +} + +TEST_CASE("Test file_exists") +{ + // Note: not clear how to portably test where a file should exist. + REQUIRE(!file_exists("./should_not_exist/really_do_not_make_this_please")); +} + +TEST_CASE("Test dir_name") +{ + REQUIRE(dir_name("") == ""); + REQUIRE(dir_name("/") == "/"); + REQUIRE(dir_name("hello") == ""); + REQUIRE(dir_name("hello/world") == "hello"); + REQUIRE(dir_name("/path/to/dir/") == "/path/to/dir"); +} diff --git a/tests/cpp_unit_tests/test_interpolate.cpp b/tests/cpp_unit_tests/test_interpolate.cpp new file mode 100644 index 0000000000..4f19f2b63f --- /dev/null +++ b/tests/cpp_unit_tests/test_interpolate.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include +#include + +#include "openmc/interpolate.h" +#include "openmc/search.h" + +using namespace openmc; + +TEST_CASE("Test Lagranian Interpolation") +{ + std::vector xs {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; + std::vector ys {0.0, 1.0, 1.0, 2.0, 3.0, 3.0, 5.0}; + + // ensure we get data points back at the x values + for (int n = 1; n <= 6; n++) { + for (int i = 0; i < xs.size(); i++) { + double x = xs[i]; + double y = ys[i]; + + size_t idx = lower_bound_index(xs.begin(), xs.end(), x); + idx = std::min(idx, xs.size() - n - 1); + double out = interpolate_lagrangian(xs, ys, idx, x, n); + REQUIRE(out == y); + } + } + + // spot checks based on an independent implementation of Lagrangian + // interpolation + std::map>> checks; + checks[1] = {{0.5, 0.5}, {4.5, 3.0}, {2.5, 1.5}, {5.5, 4.0}}; + checks[2] = {{2.5, 1.5}, {4.5, 2.75}, {4.9999, 3.0}, {4.00001, 3.0}}; + checks[3] = {{2.5592, 1.5}, {4.5, 2.9375}, {4.9999, 3.0}, {4.00001, 3.0}}; + + for (auto check_set : checks) { + int order = check_set.first; + auto checks = check_set.second; + + for (auto check : checks) { + double input = check.first; + double exp_output = check.second; + + size_t idx = lower_bound_index(xs.begin(), xs.end(), input); + idx = std::min(idx, xs.size() - order - 1); + double out = interpolate_lagrangian(xs, ys, idx, input, order); + REQUIRE_THAT(out, Catch::Matchers::WithinAbs(exp_output, 1e-04)); + } + } +} \ No newline at end of file diff --git a/tests/cpp_unit_tests/test_math.cpp b/tests/cpp_unit_tests/test_math.cpp new file mode 100644 index 0000000000..1ad7c4b709 --- /dev/null +++ b/tests/cpp_unit_tests/test_math.cpp @@ -0,0 +1,357 @@ +#include +#include + +#include +#include +#include + +#include "openmc/math_functions.h" +#include "openmc/random_dist.h" +#include "openmc/random_lcg.h" +#include "openmc/wmp.h" + +TEST_CASE("Test t_percentile") +{ + // The reference solutions come from scipy.stats.t.ppf + std::vector> ref_ts { + {-15.894544844102773, -0.32491969623407446, 0.000000000000000, + 0.32491969623407446, 15.894544844102759}, + {-4.848732214442601, -0.2886751346880066, 0.000000000000000, + 0.2886751346880066, 4.848732214442598}, + {-2.756508521909475, -0.2671808657039658, 0.000000000000000, + 0.2671808657039658, 2.7565085219094745}}; + + // Permutations include 1 DoF, 2 DoF, and > 2 DoF + // We will test 5 p-values at 3-DoF values + std::vector test_ps {0.02, 0.4, 0.5, 0.6, 0.98}; + std::vector test_dfs {1, 2, 5}; + + for (int i = 0; i < test_dfs.size(); i++) { + int df = test_dfs[i]; + + std::vector test_ts; + + for (double p : test_ps) { + double test_t = openmc::t_percentile(p, df); + test_ts.push_back(test_t); + } + + // The 5 DoF approximation in openmc.lib.math.t_percentile is off by up to + // 8e-3 from the scipy solution, so test that one separately with looser + // tolerance + double tolerance = (df > 2) ? 1e-2 : 1e-6; + + REQUIRE_THAT( + ref_ts[i], Catch::Matchers::Approx(test_ts).epsilon(tolerance)); + } +} + +TEST_CASE("Test calc_pn") +{ + // The reference solutions come from scipy.special.eval_legendre + std::vector> ref_vals { + {1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1}, + {1, -0.5, -0.125, 0.4375, -0.289062, -0.0898438, 0.323242, -0.223145, + -0.0736389, 0.267899, -0.188229}, + {1, 0, -0.5, -0, 0.375, 0, -0.3125, -0, 0.273438, 0, -0.246094}, + {1, 0.5, -0.125, -0.4375, -0.289062, 0.0898438, 0.323242, 0.223145, + -0.0736389, -0.267899, -0.188229}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; + + int max_order = 10; + std::vector test_xs = {-1.0, -0.5, 0.0, 0.5, 1.0}; + + std::vector> test_vals; + for (double x : test_xs) { + std::vector test_val(max_order + 1); + openmc::calc_pn_c(max_order, x, test_val.data()); + test_vals.push_back(test_val); + } + + for (int i = 0; i < ref_vals.size(); i++) { + REQUIRE_THAT(ref_vals[i], Catch::Matchers::Approx(test_vals[i])); + } +} + +TEST_CASE("Test evaluate_legendre") +{ + // The reference solutions come from numpy.polynomial.legendre.legval + std::vector ref_vals { + 5.5, -0.45597649, -1.35351562, -2.7730999, 60.5}; + + int max_order = 10; + std::vector test_xs = {-1.0, -0.5, 0.0, 0.5, 1.0}; + + // Set the coefficients back to 1s for the test values since + // evaluate legendre incorporates the (2l+1)/2 term on its own + std::vector test_coeffs(max_order + 1, 1.0); + + std::vector test_vals; + for (double x : test_xs) { + test_vals.push_back( + openmc::evaluate_legendre(test_coeffs.size() - 1, test_coeffs.data(), x)); + } + + REQUIRE_THAT(ref_vals, Catch::Matchers::Approx(test_vals)); +} + +TEST_CASE("Test calc_rn") +{ + std::vector ref_vals {1.000000000000000, -0.019833838076210, + 0.980066577841242, -0.197676811654084, 0.006790834062088, + -0.033668438114859, 0.940795745502164, -0.335561350977312, + 0.033500236162691, -0.001831975566765, 0.014882082223994, + -0.046185860057145, 0.883359726009014, -0.460318044571973, + 0.073415616482180, -0.005922278973373, 0.000448625292461, + -0.004750335422039, 0.025089695062177, -0.057224052171859, + 0.809468042300133, -0.570331780454957, 0.123771351522967, + -0.015356543011155, 0.001061098599927, -0.000104097571795, + 0.001319047965347, -0.009263463267120, 0.037043163155191, + -0.066518621473934, 0.721310852552881, -0.662967447756079, + 0.182739660926192, -0.029946258412359, 0.003119841820746, + -0.000190549327031, 0.000023320052630, -0.000338370521658, + 0.002878809439524, -0.015562587450914, 0.050271226423217, + -0.073829294593737, 0.621486505922182, -0.735830327235834, + 0.247995745731425, -0.050309614442385, 0.006809024629381, + -0.000619383085285, 0.000034086826414, -0.000005093626712, + 0.000082405610567, -0.000809532012556, 0.005358034016708, + -0.023740240859138, 0.064242405926477, -0.078969918083157, + 0.512915839160049, -0.787065093668736, 0.316917738015632, + -0.076745744765114, 0.012672942183651, -0.001481838409317, + 0.000120451946983, -0.000006047366709, 0.000001091052697, + -0.000019334294214, 0.000213051604838, -0.001640234119608, + 0.008982263900105, -0.033788039035668, 0.078388909900756, + -0.081820779415058, 0.398746190829636, -0.815478614863816, + 0.386704633068855, -0.109227544713261, 0.021245051959237, + -0.003002428416676, 0.000311416667310, -0.000022954482885, + 0.000001059646310, -0.000000230023931, 0.000004408854505, + -0.000053457925526, 0.000464152759861, -0.002976305522860, + 0.013958017448970, -0.045594791382625, 0.092128969315914, + -0.082334538374971, 0.282248459574595, -0.820599067736528, + 0.454486474163594, -0.147395565311743, 0.033013815809602, + -0.005448090715661, 0.000678450207914, -0.000063467485444, + 0.000004281943868, -0.000000182535754, 0.000000047847775, + -0.000000982664801, 0.000012933320414, -0.000124076425457, + 0.000901739739837, -0.004982323311961, 0.020457776068931, + -0.058948376674391, 0.104888993733747, -0.080538298991650, + 0.166710763818175, -0.802696588503912, 0.517433650833039, + -0.190564076304612, 0.048387190622376, -0.009120081648146, + 0.001318069323039, -0.000147308722683, 0.000012561029621, + -0.000000779794781, 0.000000030722703}; + + int max_order = 10; + + double azi = 0.1; // Longitude + double pol = 0.2; // Latitude + double mu = std::cos(pol); + + std::vector test_uvw {std::sin(pol) * std::cos(azi), + std::sin(pol) * std::sin(azi), std::cos(pol)}; + + std::vector test_vals((max_order + 1) * (max_order + 1), 0); + openmc::calc_rn_c(max_order, test_uvw.data(), test_vals.data()); + + REQUIRE_THAT(ref_vals, Catch::Matchers::Approx(test_vals)); +} + +TEST_CASE("Test calc_zn") +{ + std::vector ref_vals {1.00000000e+00, 2.39712769e-01, 4.38791281e-01, + 2.10367746e-01, -5.00000000e-01, 1.35075576e-01, 1.24686873e-01, + -2.99640962e-01, -5.48489101e-01, 8.84215021e-03, 5.68310892e-02, + -4.20735492e-01, -1.25000000e-01, -2.70151153e-01, -2.60091773e-02, + 1.87022545e-02, -3.42888902e-01, 1.49820481e-01, 2.74244551e-01, + -2.43159131e-02, -2.50357380e-02, 2.20500013e-03, -1.98908812e-01, + 4.07587508e-01, 4.37500000e-01, 2.61708929e-01, 9.10321205e-02, + -1.54686328e-02, -2.74049397e-03, -7.94845816e-02, 4.75368705e-01, + 7.11647284e-02, 1.30266162e-01, 3.37106977e-02, 1.06401886e-01, + -7.31606787e-03, -2.95625975e-03, -1.10250006e-02, 3.55194307e-01, + -1.44627826e-01, -2.89062500e-01, -9.28644588e-02, -1.62557358e-01, + 7.73431638e-02, -2.55329539e-03, -1.90923851e-03, 1.57578403e-02, + 1.72995854e-01, -3.66267690e-01, -1.81657333e-01, -3.32521518e-01, + -2.59738162e-02, -2.31580576e-01, 4.20673902e-02, -4.11710546e-04, + -9.36449487e-04, 1.92156884e-02, 2.82515641e-02, -3.90713738e-01, + -1.69280296e-01, -8.98437500e-02, -1.08693628e-01, 1.78813094e-01, + -1.98191857e-01, 1.65964201e-02, 2.77013853e-04}; + + int n = 10; + double rho = 0.5; + double phi = 0.5; + + int nums = ((n + 1) * (n + 2)) / 2; + + std::vector test_vals(nums, 0); + openmc::calc_zn(n, rho, phi, test_vals.data()); + + REQUIRE_THAT(ref_vals, Catch::Matchers::Approx(test_vals)); +} + +TEST_CASE("Test calc_zn_rad") +{ + std::vector ref_vals {1.00000000e+00, -5.00000000e-01, + -1.25000000e-01, 4.37500000e-01, -2.89062500e-01, -8.98437500e-02}; + + int n = 10; + double rho = 0.5; + + int nums = n / 2 + 1; + std::vector test_vals(nums, 0); + openmc::calc_zn_rad(n, rho, test_vals.data()); + + REQUIRE_THAT(ref_vals, Catch::Matchers::Approx(test_vals)); +} + +TEST_CASE("Test rotate_angle") +{ + std::vector uvw0 {1.0, 0.0, 0.0}; + double phi = 0.0; + + uint64_t prn_seed = 1; + openmc::prn(&prn_seed); + + SECTION("Test rotate_angle mu is 0") + { + std::vector ref_uvw {0.0, 0.0, -1.0}; + + double mu = 0.0; + + std::vector test_uvw(uvw0); + openmc::rotate_angle_c(test_uvw.data(), mu, &phi, &prn_seed); + + REQUIRE_THAT(ref_uvw, Catch::Matchers::Approx(test_uvw)); + } + + SECTION("Test rotate_angle mu is 1") + { + std::vector ref_uvw = {1.0, 0.0, 0.0}; + + double mu = 1.0; + + std::vector test_uvw(uvw0); + openmc::rotate_angle_c(test_uvw.data(), mu, &phi, &prn_seed); + + REQUIRE_THAT(ref_uvw, Catch::Matchers::Approx(test_uvw)); + } + + // Now to test phi is None + SECTION("Test rotate_angle no phi") + { + // When seed = 1, phi will be sampled as 1.9116495709698769 + // The resultant reference is from hand-calculations given the above + std::vector ref_uvw = { + 0.9, -0.422746750548505, 0.10623175090659095}; + + double mu = 0.9; + prn_seed = 1; + + std::vector test_uvw(uvw0); + openmc::rotate_angle_c(test_uvw.data(), mu, NULL, &prn_seed); + + REQUIRE_THAT(ref_uvw, Catch::Matchers::Approx(test_uvw)); + } +} + +TEST_CASE("Test maxwell_spectrum") +{ + double ref_val = 0.27767406743161277; + + double T = 0.5; + uint64_t prn_seed = 1; + + double test_val = openmc::maxwell_spectrum(T, &prn_seed); + + REQUIRE(ref_val == test_val); +} + +TEST_CASE("Test watt_spectrum") +{ + double ref_val = 0.30957476387766697; + + double a = 0.5; + double b = 0.75; + uint64_t prn_seed = 1; + + double test_val = openmc::watt_spectrum(a, b, &prn_seed); + + REQUIRE(ref_val == test_val); +} + +TEST_CASE("Test normal_variate") +{ + + // Generate a series of normally distributed random numbers and test + // whether their mean and standard deviation are close to the expected value + SECTION("Test with non-zero standard deviation") + { + uint64_t seed = 1; + + double mean = 0.0; + double standard_deviation = 1.0; + + int num_samples = 10000; + double sum = 0.0; + double sum_squared_difference = 0.0; + + for (int i = 0; i < num_samples; ++i) { + double sample = openmc::normal_variate(mean, standard_deviation, &seed); + sum += sample; + sum_squared_difference += (sample - mean) * (sample - mean); + } + + double actual_mean = sum / num_samples; + double actual_standard_deviation = + std::sqrt(sum_squared_difference / num_samples); + + REQUIRE_THAT(mean, Catch::Matchers::WithinAbs(actual_mean, 0.1)); + REQUIRE_THAT(standard_deviation, + Catch::Matchers::WithinAbs(actual_standard_deviation, 0.1)); + } + + // When the standard deviation is zero + // the generated random number should always be equal to the mean + SECTION("Test with zero standard deviation") + { + uint64_t seed = 1; + double mean = 5.0; + double standard_deviation = 0.0; + + for (int i = 0; i < 10; ++i) { + double sample = openmc::normal_variate(mean, standard_deviation, &seed); + REQUIRE(sample == mean); + } + } +} + +TEST_CASE("Test broaden_wmp_polynomials") +{ + double test_E = 0.5; + int n = 6; + + // Two branches of the code to worry about, beta > 6 and otherwise + // beta = sqrtE * dopp + SECTION("Test broaden_wmp_polynomials beta > 6") + { + std::vector ref_val { + 2., 1.41421356, 1.0001, 0.70731891, 0.50030001, 0.353907}; + + double test_dopp = 100.0; // approximately U235 at room temperature + + std::vector test_val(n, 0); + openmc::broaden_wmp_polynomials(test_E, test_dopp, n, test_val.data()); + + REQUIRE_THAT(ref_val, Catch::Matchers::Approx(test_val)); + } + + SECTION("Test broaden_wmp_polynomials beta < 6") + { + std::vector ref_val = { + 1.99999885, 1.41421356, 1.04, 0.79195959, 0.6224, 0.50346003}; + + double test_dopp = 5.0; + + std::vector test_val(n, 0); + openmc::broaden_wmp_polynomials(test_E, test_dopp, n, test_val.data()); + + REQUIRE_THAT(ref_val, Catch::Matchers::Approx(test_val)); + } +} diff --git a/tests/cpp_unit_tests/test_mcpl_stat_sum.cpp b/tests/cpp_unit_tests/test_mcpl_stat_sum.cpp new file mode 100644 index 0000000000..909830e035 --- /dev/null +++ b/tests/cpp_unit_tests/test_mcpl_stat_sum.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include + +#include "openmc/bank.h" +#include "openmc/mcpl_interface.h" + +// Test the MCPL stat:sum functionality (issue #3514) +TEST_CASE("MCPL stat:sum field") +{ + // Check if MCPL interface is available + if (!openmc::is_mcpl_interface_available()) { + SKIP("MCPL library not available"); + } + + SECTION("stat:sum field is written to MCPL files") + { + // Create a temporary filename + std::string filename = "test_stat_sum.mcpl"; + + // Create some test particles + std::vector source_bank(100); + std::vector bank_index = {0, 100}; // 100 particles total + + // Initialize test particles + for (int i = 0; i < 100; ++i) { + source_bank[i].particle = openmc::ParticleType::neutron; + source_bank[i].r = {i * 0.1, i * 0.2, i * 0.3}; + source_bank[i].u = {0.0, 0.0, 1.0}; + source_bank[i].E = 2.0e6; // 2 MeV + source_bank[i].time = 0.0; + source_bank[i].wgt = 1.0; + } + + // Write the MCPL file + openmc::write_mcpl_source_point(filename.c_str(), source_bank, bank_index); + + // Verify the file was created + FILE* f = std::fopen(filename.c_str(), "r"); + REQUIRE(f != nullptr); + std::fclose(f); + + // Read the file back to check stat:sum + // Note: This would require mcpl_open_file and checking the header + // Since we can't easily read MCPL headers in C++ without the full MCPL API, + // we rely on the Python test to verify the actual content + + // Clean up + std::remove(filename.c_str()); + } + + SECTION("stat:sum uses correct particle count") + { + std::string filename = "test_count.mcpl"; + + // Test with different particle counts + std::vector test_counts = {1, 10, 100, 1000}; + + for (int count : test_counts) { + std::vector source_bank(count); + std::vector bank_index = {0, count}; + + // Initialize particles + for (int i = 0; i < count; ++i) { + source_bank[i].particle = openmc::ParticleType::neutron; + source_bank[i].r = {0.0, 0.0, 0.0}; + source_bank[i].u = {0.0, 0.0, 1.0}; + source_bank[i].E = 1.0e6; + source_bank[i].time = 0.0; + source_bank[i].wgt = 1.0; + } + + // Write MCPL file + openmc::write_mcpl_source_point( + filename.c_str(), source_bank, bank_index); + + // The stat:sum should equal count (verified by Python test) + // Here we just verify the file was created successfully + FILE* f = std::fopen(filename.c_str(), "r"); + REQUIRE(f != nullptr); + std::fclose(f); + + // Clean up + std::remove(filename.c_str()); + } + } + + SECTION("stat:sum handles empty particle bank") + { + std::string filename = "test_empty.mcpl"; + + // Create empty particle bank + std::vector source_bank; + std::vector bank_index = {0}; + + // This should still create a valid MCPL file with stat:sum = 0 + openmc::write_mcpl_source_point(filename.c_str(), source_bank, bank_index); + + // Verify file was created + FILE* f = std::fopen(filename.c_str(), "r"); + REQUIRE(f != nullptr); + std::fclose(f); + + // Clean up + std::remove(filename.c_str()); + } +} diff --git a/tests/cpp_unit_tests/test_tally.cpp b/tests/cpp_unit_tests/test_tally.cpp new file mode 100644 index 0000000000..964d30cc42 --- /dev/null +++ b/tests/cpp_unit_tests/test_tally.cpp @@ -0,0 +1,55 @@ +#include "openmc/tallies/tally.h" +#include + +using namespace openmc; + +TEST_CASE("Test add/set_filter") +{ + // create a new tally object + Tally* tally = Tally::create(); + + // create a new particle filter + Filter* particle_filter = Filter::create("particle"); + + // add the particle filter to the tally + tally->add_filter(particle_filter); + + // the filter should be added to the tally + REQUIRE(tally->filters().size() == 1); + REQUIRE(model::filter_map[particle_filter->id()] == tally->filters(0)); + + // add the particle filter to the tally again + tally->add_filter(particle_filter); + // the tally should have the same number of filters + REQUIRE(tally->filters().size() == 1); + + // create a cell filter + Filter* cell_filter = Filter::create("cell"); + tally->add_filter(cell_filter); + + // now the size of the filters should have increased + REQUIRE(tally->filters().size() == 2); + REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(1)); + + // if we set the filters explicitly there shouldn't be extra filters hanging + // around + tally->set_filters({&cell_filter, 1}); + + REQUIRE(tally->filters().size() == 1); + REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(0)); + + // set filters again using both filters + std::vector filters = {cell_filter, particle_filter}; + tally->set_filters(filters); + + REQUIRE(tally->filters().size() == 2); + REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(0)); + REQUIRE(model::filter_map[particle_filter->id()] == tally->filters(1)); + + // set filters with a duplicate filter, should only add the filter to the tally once + filters = {cell_filter, cell_filter}; + tally->set_filters(filters); + REQUIRE(tally->filters().size() == 1); + REQUIRE(model::filter_map[cell_filter->id()] == tally->filters(0)); + +} \ No newline at end of file diff --git a/tests/micro_xs_simple.csv b/tests/micro_xs_simple.csv new file mode 100644 index 0000000000..146896aa29 --- /dev/null +++ b/tests/micro_xs_simple.csv @@ -0,0 +1,25 @@ +nuclides,reactions,groups,xs +U234,"(n,gamma)",1,22.23198982200245 +U234,fission,1,0.4962074466374984 +U235,"(n,gamma)",1,10.47900897119712 +U235,fission,1,48.41787337164606 +U238,"(n,gamma)",1,0.8673334105437321 +U238,fission,1,0.1046788058876236 +U236,"(n,gamma)",1,8.651710446071224 +U236,fission,1,0.3194839240001929 +O16,"(n,gamma)",1,7.497851000107522e-05 +O16,fission,1,0.0 +O17,"(n,gamma)",1,0.0004079227797153 +O17,fission,1,0.0 +I135,"(n,gamma)",1,6.842395323713929 +I135,fission,1,0.0 +Xe135,"(n,gamma)",1,227463.8642699061 +Xe135,fission,1,0.0 +Xe136,"(n,gamma)",1,0.0231789603475358 +Xe136,fission,1,0.0 +Cs135,"(n,gamma)",1,2.1721665580713623 +Cs135,fission,1,0.0 +Gd157,"(n,gamma)",1,12786.099392370175 +Gd157,fission,1,0.0 +Gd156,"(n,gamma)",1,3.4006085445846983 +Gd156,fission,1,0.0 diff --git a/tests/regression_tests/__init__.py b/tests/regression_tests/__init__.py index a887448c11..e1cb56f1dd 100644 --- a/tests/regression_tests/__init__.py +++ b/tests/regression_tests/__init__.py @@ -1,3 +1,5 @@ +import pytest + # Test configuration options for regression tests config = { 'event' : False, @@ -8,3 +10,36 @@ config = { 'update': False, 'build_inputs': False } + + +def assert_same_mats(res_ref, res_test): + for mat in res_ref[0].index_mat: + assert mat in res_test[0].index_mat, f"Material {mat} not in new results." + for nuc in res_ref[0].index_nuc: + assert nuc in res_test[0].index_nuc, f"Nuclide {nuc} not in new results." + for mat in res_test[0].index_mat: + assert mat in res_ref[0].index_mat, f"Material {mat} not in old results." + for nuc in res_test[0].index_nuc: + assert nuc in res_ref[0].index_nuc, f"Nuclide {nuc} not in old results." + + +def assert_atoms_equal(res_ref, res_test, tol=1e-5): + for mat in res_test[0].index_mat: + for nuc in res_test[0].index_nuc: + _, y_test = res_test.get_atoms(mat, nuc) + _, y_ref = res_ref.get_atoms(mat, nuc) + assert y_test == pytest.approx(y_ref, rel=tol), \ + f'Atoms not equal for material {mat}, nuclide {nuc}\n' \ + f'y_ref={y_ref}\ny_test={y_test}' + + +def assert_reaction_rates_equal(res_ref, res_test, tol=1e-5): + for reactions in res_test[0].rates: + for mat in reactions.index_mat: + for nuc in reactions.index_nuc: + for rx in reactions.index_rx: + y_test = res_test.get_reaction_rate(mat, nuc, rx)[1] + y_ref = res_ref.get_reaction_rate(mat, nuc, rx)[1] + assert y_test == pytest.approx(y_ref, rel=tol), \ + f'Reaction rate not equal for material {mat}, nuclide '\ + f'{nuc}, {rx}\ny_ref={y_ref}\ny_test={y_test}' diff --git a/tests/regression_tests/adj_cell_rotation/inputs_true.dat b/tests/regression_tests/adj_cell_rotation/inputs_true.dat index 24b00199be..18c0552ceb 100644 --- a/tests/regression_tests/adj_cell_rotation/inputs_true.dat +++ b/tests/regression_tests/adj_cell_rotation/inputs_true.dat @@ -1,38 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 10000 - 10 - 5 - - - -4.0 -4.0 -4.0 4.0 4.0 4.0 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + diff --git a/tests/regression_tests/adj_cell_rotation/results_true.dat b/tests/regression_tests/adj_cell_rotation/results_true.dat index 2bdfe43305..ddb1546b5b 100644 --- a/tests/regression_tests/adj_cell_rotation/results_true.dat +++ b/tests/regression_tests/adj_cell_rotation/results_true.dat @@ -1,2 +1,2 @@ k-combined: -4.381997E-01 1.286263E-03 +4.368327E-01 1.953533E-03 diff --git a/tests/regression_tests/adj_cell_rotation/test.py b/tests/regression_tests/adj_cell_rotation/test.py index 25aada5aad..3fe2405366 100644 --- a/tests/regression_tests/adj_cell_rotation/test.py +++ b/tests/regression_tests/adj_cell_rotation/test.py @@ -24,14 +24,14 @@ def model(): # Create one cell on top of the other. Only one # has a rotation - box = openmc.rectangular_prism(15., 15., 'z', boundary_type='vacuum') + box = openmc.model.RectangularPrism(15., 15., 'z', boundary_type='vacuum') lower_z = openmc.ZPlane(-7.5, boundary_type='vacuum') upper_z = openmc.ZPlane(22.5, boundary_type='vacuum') middle_z = openmc.ZPlane(7.5) - lower_cell = openmc.Cell(fill=univ, region=box & +lower_z & -middle_z) + lower_cell = openmc.Cell(fill=univ, region=-box & +lower_z & -middle_z) lower_cell.rotation = (10, 20, 30) - upper_cell = openmc.Cell(fill=univ, region=box & +middle_z & -upper_z) + upper_cell = openmc.Cell(fill=univ, region=-box & +middle_z & -upper_z) upper_cell.translation = (0, 0, 15) model.geometry = openmc.Geometry(root=[lower_cell, upper_cell]) @@ -40,7 +40,7 @@ def model(): model.settings.inactive = 5 model.settings.batches = 10 source_box = openmc.stats.Box((-4., -4., -4.), (4., 4., 4.)) - model.settings.source = openmc.Source(space=source_box) + model.settings.source = openmc.IndependentSource(space=source_box) return model diff --git a/tests/regression_tests/statepoint_batch/__init__.py b/tests/regression_tests/albedo_box/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from tests/regression_tests/statepoint_batch/__init__.py rename to tests/regression_tests/albedo_box/__init__.py diff --git a/tests/regression_tests/albedo_box/geometry.xml b/tests/regression_tests/albedo_box/geometry.xml new file mode 100644 index 0000000000..7d0e9b5f5d --- /dev/null +++ b/tests/regression_tests/albedo_box/geometry.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/regression_tests/statepoint_batch/materials.xml b/tests/regression_tests/albedo_box/materials.xml similarity index 100% rename from tests/regression_tests/statepoint_batch/materials.xml rename to tests/regression_tests/albedo_box/materials.xml diff --git a/tests/regression_tests/albedo_box/results_true.dat b/tests/regression_tests/albedo_box/results_true.dat new file mode 100644 index 0000000000..dca80abcd8 --- /dev/null +++ b/tests/regression_tests/albedo_box/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.593206E+00 2.925742E-03 diff --git a/tests/regression_tests/statepoint_batch/settings.xml b/tests/regression_tests/albedo_box/settings.xml similarity index 52% rename from tests/regression_tests/statepoint_batch/settings.xml rename to tests/regression_tests/albedo_box/settings.xml index e2f8dad47b..cc66683057 100644 --- a/tests/regression_tests/statepoint_batch/settings.xml +++ b/tests/regression_tests/albedo_box/settings.xml @@ -1,17 +1,9 @@ - - eigenvalue 10 5 1000 - - - -4 -4 -4 4 4 4 - - - diff --git a/tests/regression_tests/albedo_box/test.py b/tests/regression_tests/albedo_box/test.py new file mode 100755 index 0000000000..179f58e5b3 --- /dev/null +++ b/tests/regression_tests/albedo_box/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_albedo_box(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/asymmetric_lattice/inputs_true.dat b/tests/regression_tests/asymmetric_lattice/inputs_true.dat index bbdc79715b..ee3d689076 100644 --- a/tests/regression_tests/asymmetric_lattice/inputs_true.dat +++ b/tests/regression_tests/asymmetric_lattice/inputs_true.dat @@ -1,19 +1,168 @@ - - - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 @@ -31,197 +180,50 @@ 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 - - - 21.42 21.42 - 3 3 - -32.13 -32.13 - + + + 21.42 21.42 + 3 3 + -32.13 -32.13 + 8 7 7 8 8 8 7 7 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -32 -32 0 32 32 32 - - - - - - - 27 - - - 1 - nu-fission - - + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -32 -32 0 32 32 32 + + + true + + + + + + 27 + + + 1 + nu-fission + + + diff --git a/tests/regression_tests/asymmetric_lattice/results_true.dat b/tests/regression_tests/asymmetric_lattice/results_true.dat index 66ee9cdd28..3d6b610a69 100644 --- a/tests/regression_tests/asymmetric_lattice/results_true.dat +++ b/tests/regression_tests/asymmetric_lattice/results_true.dat @@ -1 +1 @@ -73bae264aaca0988fd2ae207722461d161ddbcf9aef083b99a2efc536b09665bbe839d6cae89b32ebab3089a820a2c35ae63a88d5869f0c91b0d6c2f2e090e55 \ No newline at end of file +cc76769636be4f681137598cf366e978d7347425a1dfa1b293d17a28381b2b62595fb7f0d2f126dd06972ff9e79089a18dd53aba45fa2b1f316515b91fe6495a \ No newline at end of file diff --git a/tests/regression_tests/asymmetric_lattice/test.py b/tests/regression_tests/asymmetric_lattice/test.py index 2197a1e7e2..fadf272ffd 100644 --- a/tests/regression_tests/asymmetric_lattice/test.py +++ b/tests/regression_tests/asymmetric_lattice/test.py @@ -54,8 +54,10 @@ class AsymmetricLatticeTestHarness(PyAPITestHarness): self._model.tallies.append(tally) # Specify summary output and correct source sampling box - self._model.settings.source = openmc.Source(space=openmc.stats.Box( - [-32, -32, 0], [32, 32, 32], only_fissionable = True)) + self._model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-32, -32, 0], [32, 32, 32]), + constraints={'fissionable': True} + ) def _get_results(self, hash_output=True): """Digest info in statepoint and summary and return as a string.""" diff --git a/tests/regression_tests/cmfd_feed/geometry.xml b/tests/regression_tests/cmfd_feed/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_feed/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_feed/materials.xml b/tests/regression_tests/cmfd_feed/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_feed/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_feed/model.xml b/tests/regression_tests/cmfd_feed/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_feed/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index 90d8820d0c..1ef9624d4c 100644 --- a/tests/regression_tests/cmfd_feed/results_true.dat +++ b/tests/regression_tests/cmfd_feed/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.159021E+00 8.924006E-03 +1.181723E+00 9.944883E-03 tally 1: -1.140162E+01 -1.306940E+01 -2.093739E+01 -4.404780E+01 -2.914408E+01 -8.521010E+01 -3.483677E+01 -1.216824E+02 -3.778463E+01 -1.429632E+02 -3.810371E+01 -1.455108E+02 -3.465248E+01 -1.207868E+02 -2.862033E+01 -8.218833E+01 -2.086025E+01 -4.365941E+01 -1.130798E+01 -1.286509E+01 +1.169899E+01 +1.373251E+01 +2.142380E+01 +4.605511E+01 +2.968085E+01 +8.838716E+01 +3.561418E+01 +1.271206E+02 +3.777783E+01 +1.428817E+02 +3.805832E+01 +1.450213E+02 +3.439836E+01 +1.184892E+02 +2.852438E+01 +8.161896E+01 +2.088423E+01 +4.376204E+01 +1.076670E+01 +1.168108E+01 tally 2: -2.234393E+01 -2.516414E+01 -1.555024E+01 -1.218205E+01 -4.087743E+01 -8.401702E+01 -2.883717E+01 -4.185393E+01 -5.635166E+01 -1.595225E+02 -3.998857E+01 -8.040398E+01 -6.887126E+01 -2.379185E+02 -4.903103E+01 -1.206174E+02 -7.452051E+01 -2.785675E+02 -5.295380E+01 -1.406900E+02 -7.495422E+01 -2.819070E+02 -5.333191E+01 -1.427474E+02 -6.921815E+01 -2.408568E+02 -4.928246E+01 -1.221076E+02 -5.668548E+01 -1.612556E+02 -4.035856E+01 -8.181159E+01 -4.259952E+01 -9.112630E+01 -3.026717E+01 -4.600625E+01 -2.310563E+01 -2.688378E+01 -1.615934E+01 -1.315528E+01 +2.321241E+01 +2.702156E+01 +1.620912E+01 +1.317752E+01 +4.197404E+01 +8.845008E+01 +2.982666E+01 +4.469221E+01 +5.810089E+01 +1.695857E+02 +4.134123E+01 +8.588866E+01 +6.982488E+01 +2.447068E+02 +4.966939E+01 +1.238763E+02 +7.428421E+01 +2.767613E+02 +5.287955E+01 +1.403163E+02 +7.447402E+01 +2.785012E+02 +5.324628E+01 +1.423393E+02 +6.895164E+01 +2.381937E+02 +4.916366E+01 +1.211701E+02 +5.679253E+01 +1.617881E+02 +4.043125E+01 +8.204061E+01 +4.218618E+01 +8.933666E+01 +2.978592E+01 +4.456592E+01 +2.196426E+01 +2.435867E+01 +1.525576E+01 +1.175879E+01 tally 3: -1.496375E+01 -1.128154E+01 -9.905641E-01 -5.125710E-02 -2.774937E+01 -3.877241E+01 -1.786861E+00 -1.627655E-01 -3.849739E+01 -7.453828E+01 -2.494135E+00 -3.158098E-01 -4.724085E+01 -1.119901E+02 -3.031174E+00 -4.653741E-01 -5.096719E+01 -1.303552E+02 -3.254375E+00 -5.351020E-01 -5.133808E+01 -1.322892E+02 -3.383595E+00 -5.798798E-01 -4.756072E+01 -1.137527E+02 -3.001917E+00 -4.558247E-01 -3.887437E+01 -7.593416E+01 -2.517908E+00 -3.221926E-01 -2.910687E+01 -4.255173E+01 -1.817765E+00 -1.678763E-01 -1.557241E+01 -1.222026E+01 -9.852737E-01 -5.002659E-02 +1.563788E+01 +1.226528E+01 +1.053289E+00 +5.666942E-02 +2.870755E+01 +4.139654E+01 +1.838017E+00 +1.710528E-01 +3.978616E+01 +7.955764E+01 +2.560657E+00 +3.334449E-01 +4.780385E+01 +1.147770E+02 +3.139243E+00 +4.967628E-01 +5.106650E+01 +1.308704E+02 +3.170056E+00 +5.078920E-01 +5.123992E+01 +1.318586E+02 +3.211706E+00 +5.205979E-01 +4.729862E+01 +1.121695E+02 +3.068662E+00 +4.749488E-01 +3.898816E+01 +7.630564E+01 +2.516911E+00 +3.199696E-01 +2.865357E+01 +4.125742E+01 +1.852314E+00 +1.741116E-01 +1.467340E+01 +1.088460E+01 +9.268633E-01 +4.450662E-02 tally 4: -3.047490E+00 -4.661458E-01 +3.029754E+00 +4.613561E-01 0.000000E+00 0.000000E+00 -2.635775E+00 -3.524426E-01 -5.357229E+00 -1.440049E+00 +2.832501E+00 +4.049252E-01 +5.517243E+00 +1.527794E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.357229E+00 -1.440049E+00 -2.635775E+00 -3.524426E-01 -4.982072E+00 -1.251449E+00 -7.228146E+00 -2.620353E+00 +5.517243E+00 +1.527794E+00 +2.832501E+00 +4.049252E-01 +5.117178E+00 +1.316972E+00 +7.333303E+00 +2.701677E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.228146E+00 -2.620353E+00 -4.982072E+00 -1.251449E+00 -7.082265E+00 -2.520047E+00 -8.736529E+00 -3.831244E+00 +7.333303E+00 +2.701677E+00 +5.117178E+00 +1.316972E+00 +7.248464E+00 +2.641591E+00 +8.817788E+00 +3.905530E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.736529E+00 -3.831244E+00 -7.082265E+00 -2.520047E+00 -8.474631E+00 -3.607043E+00 -9.346623E+00 -4.390819E+00 +8.817788E+00 +3.905530E+00 +7.248464E+00 +2.641591E+00 +8.646465E+00 +3.749847E+00 +9.460948E+00 +4.495388E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.346623E+00 -4.390819E+00 -8.474631E+00 -3.607043E+00 -9.496684E+00 -4.522478E+00 -9.532822E+00 -4.559003E+00 +9.460948E+00 +4.495388E+00 +8.646465E+00 +3.749847E+00 +9.379341E+00 +4.415049E+00 +9.278640E+00 +4.320720E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.532822E+00 -4.559003E+00 -9.496684E+00 -4.522478E+00 -9.404949E+00 -4.446260E+00 -8.550930E+00 -3.668401E+00 +9.278640E+00 +4.320720E+00 +9.379341E+00 +4.415049E+00 +9.465746E+00 +4.498591E+00 +8.656146E+00 +3.760545E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.550930E+00 -3.668401E+00 -9.404949E+00 -4.446260E+00 -8.785273E+00 -3.874792E+00 -7.128863E+00 -2.554326E+00 +8.656146E+00 +3.760545E+00 +9.465746E+00 +4.498591E+00 +8.589782E+00 +3.700308E+00 +6.996002E+00 +2.456935E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.128863E+00 -2.554326E+00 -8.785273E+00 -3.874792E+00 -7.408549E+00 -2.755885E+00 -5.094992E+00 -1.305737E+00 +6.996002E+00 +2.456935E+00 +8.589782E+00 +3.700308E+00 +7.352050E+00 +2.714808E+00 +5.105164E+00 +1.312559E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.094992E+00 -1.305737E+00 -7.408549E+00 -2.755885E+00 -5.532149E+00 -1.537289E+00 -2.812344E+00 -3.997146E-01 +5.105164E+00 +1.312559E+00 +7.352050E+00 +2.714808E+00 +5.442756E+00 +1.486776E+00 +2.697305E+00 +3.675580E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.812344E+00 -3.997146E-01 -5.532149E+00 -1.537289E+00 -3.063251E+00 -4.728672E-01 +2.697305E+00 +3.675580E-01 +5.442756E+00 +1.486776E+00 +3.017025E+00 +4.571443E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,144 +345,144 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -1.496000E+01 -1.127586E+01 -2.280081E+00 -2.675609E-01 -2.774503E+01 -3.876011E+01 -3.908836E+00 -7.703029E-01 -3.848706E+01 -7.449836E+01 -5.299924E+00 -1.422782E+00 -4.723172E+01 -1.119459E+02 -6.450156E+00 -2.105590E+00 -5.095931E+01 -1.303132E+02 -7.050681E+00 -2.515092E+00 -5.133412E+01 -1.322694E+02 -6.853429E+00 -2.384127E+00 -4.754621E+01 -1.136848E+02 -6.370026E+00 -2.058896E+00 -3.886829E+01 -7.591042E+01 -5.266816E+00 -1.400495E+00 -2.910277E+01 -4.253981E+01 -4.090844E+00 -8.442500E-01 -1.556949E+01 -1.221526E+01 -2.266123E+00 -2.641551E-01 +1.563588E+01 +1.226217E+01 +2.209027E+00 +2.507131E-01 +2.870034E+01 +4.137550E+01 +3.726620E+00 +7.021948E-01 +3.977762E+01 +7.952285E+01 +5.304975E+00 +1.427333E+00 +4.779747E+01 +1.147456E+02 +6.528302E+00 +2.151715E+00 +5.105366E+01 +1.308037E+02 +6.986782E+00 +2.467487E+00 +5.123380E+01 +1.318264E+02 +6.845633E+00 +2.383542E+00 +4.729296E+01 +1.121433E+02 +6.252695E+00 +1.977351E+00 +3.898236E+01 +7.628315E+01 +5.461495E+00 +1.528579E+00 +2.864576E+01 +4.123538E+01 +3.857301E+00 +7.581323E-01 +1.467047E+01 +1.088031E+01 +2.277024E+00 +2.679801E-01 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.161531E+00 -1.182724E+00 -1.169653E+00 -1.164722E+00 -1.164583E+00 -1.162952E+00 -1.167024E+00 -1.164509E+00 -1.165693E+00 -1.170623E+00 -1.166618E+00 -1.170805E+00 -1.170962E+00 -1.170964E+00 -1.168224E+00 -1.169864E+00 +1.169107E+00 +1.175079E+00 +1.173912E+00 +1.175368E+00 +1.174026E+00 +1.181745E+00 +1.182261E+00 +1.183559E+00 +1.178691E+00 +1.179222E+00 +1.179017E+00 +1.172979E+00 +1.175043E+00 +1.173458E+00 +1.174152E+00 +1.171451E+00 cmfd entropy -3.206619E+00 -3.205815E+00 -3.208678E+00 -3.210820E+00 -3.217023E+00 -3.215014E+00 -3.214592E+00 -3.215913E+00 -3.214998E+00 -3.213644E+00 -3.210755E+00 -3.210496E+00 -3.212488E+00 -3.211553E+00 -3.212999E+00 -3.214052E+00 +3.207640E+00 +3.210547E+00 +3.212218E+00 +3.209573E+00 +3.211619E+00 +3.212126E+00 +3.213163E+00 +3.214288E+00 +3.215737E+00 +3.213677E+00 +3.214925E+00 +3.215612E+00 +3.216708E+00 +3.221454E+00 +3.219048E+00 +3.218387E+00 cmfd balance -4.99833E-03 -5.64677E-03 -3.62795E-03 -3.91962E-03 -3.87172E-03 -2.48450E-03 -3.15554E-03 -2.49335E-03 -2.31973E-03 -2.19156E-03 -2.31352E-03 -2.03401E-03 -1.80242E-03 -1.65868E-03 -1.47543E-03 -1.49706E-03 +4.88208E-03 +4.75139E-03 +3.15783E-03 +3.67091E-03 +2.99797E-03 +2.91060E-03 +2.06576E-03 +1.83482E-03 +1.56292E-03 +1.58659E-03 +2.32986E-03 +1.47376E-03 +1.46673E-03 +1.22627E-03 +1.31963E-03 +1.26456E-03 cmfd dominance ratio -5.283E-01 -5.289E-01 -5.305E-01 -5.327E-01 -5.377E-01 -5.360E-01 -5.353E-01 -4.983E-01 -5.379E-01 -5.370E-01 -5.359E-01 -5.349E-01 -5.364E-01 -5.347E-01 -5.360E-01 -5.378E-01 +5.467E-01 +5.453E-01 +5.458E-01 +5.436E-01 +5.442E-01 +5.406E-01 +5.401E-01 +5.413E-01 +4.995E-01 +5.396E-01 +5.409E-01 +5.414E-01 +5.423E-01 +5.456E-01 +5.442E-01 +5.441E-01 cmfd openmc source comparison -1.291827E-02 -1.027137E-02 -8.738370E-03 -6.854409E-03 -4.188357E-03 -4.941359E-03 -5.139239E-03 -4.244784E-03 -4.240559E-03 -3.375424E-03 -3.716858E-03 -3.595700E-03 -3.626952E-03 -3.999302E-03 -2.431760E-03 -1.673200E-03 +9.587418E-03 +8.150978E-03 +6.677661E-03 +6.334727E-03 +5.153692E-03 +5.082964E-03 +4.633153E-03 +4.037383E-03 +3.528742E-03 +4.559089E-03 +3.517370E-03 +3.306117E-03 +2.913809E-03 +1.906045E-03 +1.932794E-03 +1.711341E-03 cmfd source -4.185460E-02 -7.636314E-02 -1.075536E-01 -1.307167E-01 -1.400879E-01 -1.459944E-01 -1.297413E-01 -1.084649E-01 -7.772031E-02 -4.150306E-02 +4.496492E-02 +7.869674E-02 +1.100280E-01 +1.354045E-01 +1.363339E-01 +1.380533E-01 +1.314512E-01 +1.077480E-01 +7.847306E-02 +3.884630E-02 diff --git a/tests/regression_tests/cmfd_feed/settings.xml b/tests/regression_tests/cmfd_feed/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_feed/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_feed/tallies.xml b/tests/regression_tests/cmfd_feed/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_feed/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index fa22b0fe36..d513dee2aa 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -111,7 +111,7 @@ def test_cmfd_write_matrices(): # Load flux vector from numpy output file flux_np = np.load('fluxvec.npy') # Load flux from data file - flux_dat = np.loadtxt("fluxvec.dat", delimiter='\n') + flux_dat = np.loadtxt("fluxvec.dat") # Compare flux from numpy file, .dat file, and from simulation assert(np.all(np.isclose(flux_np, cmfd_run._phi))) diff --git a/tests/regression_tests/cmfd_feed_2g/results_true.dat b/tests/regression_tests/cmfd_feed_2g/results_true.dat index d27e291e7f..e66eae13c4 100644 --- a/tests/regression_tests/cmfd_feed_2g/results_true.dat +++ b/tests/regression_tests/cmfd_feed_2g/results_true.dat @@ -1,112 +1,112 @@ k-combined: -1.021592E+00 7.184545E-03 +1.027434E+00 6.509170E-03 tally 1: -1.158654E+02 -1.342707E+03 -1.151877E+02 -1.327269E+03 -1.153781E+02 -1.331661E+03 -1.151151E+02 -1.325578E+03 +1.162758E+02 +1.352562E+03 +1.138125E+02 +1.295815E+03 +1.143712E+02 +1.308316E+03 +1.150293E+02 +1.323834E+03 tally 2: -4.299142E+01 -9.258204E+01 -6.324043E+01 -2.003732E+02 -1.860419E+02 -1.731270E+03 -1.037502E+02 -5.383979E+02 -4.229132E+01 -8.952923E+01 -6.264581E+01 -1.965074E+02 -1.838340E+02 -1.690620E+03 -1.029415E+02 -5.299801E+02 -4.314759E+01 -9.337463E+01 -6.404361E+01 -2.056541E+02 -1.836548E+02 -1.687065E+03 -1.028141E+02 -5.287334E+02 -4.256836E+01 -9.079806E+01 -6.336524E+01 -2.012627E+02 -1.837730E+02 -1.689124E+03 -1.021852E+02 -5.222598E+02 +4.284580E+01 +9.207089E+01 +6.335165E+01 +2.014931E+02 +1.894187E+02 +1.818190E+03 +1.033212E+02 +5.340768E+02 +4.282771E+01 +9.186295E+01 +6.295029E+01 +1.983895E+02 +1.834276E+02 +1.684375E+03 +1.022482E+02 +5.228403E+02 +4.330690E+01 +9.402038E+01 +6.395965E+01 +2.053163E+02 +1.851113E+02 +1.714198E+03 +1.030809E+02 +5.314535E+02 +4.337097E+01 +9.426435E+01 +6.417590E+01 +2.063443E+02 +1.846817E+02 +1.706518E+03 +1.027233E+02 +5.279582E+02 tally 3: -5.973628E+01 -1.787876E+02 +5.992726E+01 +1.803120E+02 0.000000E+00 0.000000E+00 -1.724004E-02 -2.766372E-05 -4.379655E+00 -9.682433E-01 -3.484795E+00 -6.104792E-01 +2.172646E-02 +4.414237E-05 +4.181401E+00 +8.912796E-01 +3.536506E+00 +6.287425E-01 0.000000E+00 0.000000E+00 -9.874445E+01 -4.877157E+02 -8.886034E-01 -4.009294E-02 -5.923584E+01 -1.757014E+02 +9.824432E+01 +4.828691E+02 +9.116848E-01 +4.231247E-02 +5.955090E+01 +1.775522E+02 0.000000E+00 0.000000E+00 -1.733168E-02 -3.950365E-05 -4.212697E+00 -8.996477E-01 -3.503046E+00 -6.150657E-01 +1.893222E-02 +3.288000E-05 +4.048183E+00 +8.291130E-01 +3.384041E+00 +5.742363E-01 0.000000E+00 0.000000E+00 -9.780995E+01 -4.784706E+02 -8.648283E-01 -3.899383E-02 -6.057017E+01 -1.839745E+02 +9.734253E+01 +4.738861E+02 +9.157632E-01 +4.329280E-02 +6.045835E+01 +1.835255E+02 0.000000E+00 0.000000E+00 -2.056597E-02 -3.726744E-05 -4.280120E+00 -9.288225E-01 -3.378205E+00 -5.730279E-01 +1.501842E-02 +1.896931E-05 +4.289989E+00 +9.251538E-01 +3.481357E+00 +6.071667E-01 0.000000E+00 0.000000E+00 -9.790474E+01 -4.794523E+02 -9.073765E-01 -4.204720E-02 -5.990874E+01 -1.799224E+02 +9.799829E+01 +4.803591E+02 +8.899390E-01 +4.078750E-02 +6.064531E+01 +1.842746E+02 0.000000E+00 0.000000E+00 -1.881508E-02 -4.239902E-05 -4.206916E+00 -8.926965E-01 -3.478009E+00 -6.067461E-01 +1.495496E-02 +3.082640E-05 +4.321537E+00 +9.371611E-01 +3.453767E+00 +5.983727E-01 0.000000E+00 0.000000E+00 -9.715787E+01 -4.721453E+02 -8.602457E-01 -3.786346E-02 +9.771776E+01 +4.777781E+02 +8.975444E-01 +4.157471E-02 tally 4: 0.000000E+00 0.000000E+00 @@ -116,14 +116,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.735001E+00 -3.821211E+00 -3.708124E+01 -6.880811E+01 -8.713175E+00 -3.807176E+00 -3.703536E+01 -6.862245E+01 +8.840487E+00 +3.915792E+00 +3.700362E+01 +6.851588E+01 +8.756789E+00 +3.844443E+00 +3.672366E+01 +6.747174E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -132,14 +132,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.851322E+00 -3.930766E+00 -3.716154E+01 -6.908449E+01 -8.892499E+00 -3.970458E+00 -3.718644E+01 -6.918810E+01 +8.860460E+00 +3.940908E+00 +3.704658E+01 +6.864069E+01 +8.832046E+00 +3.916611E+00 +3.736239E+01 +6.982147E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -156,14 +156,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.713175E+00 -3.807176E+00 -3.703536E+01 -6.862245E+01 -8.735001E+00 -3.821211E+00 -3.708124E+01 -6.880811E+01 +8.756789E+00 +3.844443E+00 +3.672366E+01 +6.747174E+01 +8.840487E+00 +3.915792E+00 +3.700362E+01 +6.851588E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -180,14 +180,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.915310E+00 -3.982710E+00 -3.682783E+01 -6.786998E+01 -8.800405E+00 -3.881868E+00 -3.692302E+01 -6.819716E+01 +8.892576E+00 +3.964978E+00 +3.703525E+01 +6.860645E+01 +8.824229E+00 +3.906925E+00 +3.685909E+01 +6.796123E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -212,22 +212,22 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.659918E+00 -3.761908E+00 -3.709957E+01 -6.884298E+01 -8.796329E+00 -3.881588E+00 -3.693599E+01 -6.825932E+01 -8.892499E+00 -3.970458E+00 -3.718644E+01 -6.918810E+01 -8.851322E+00 -3.930766E+00 -3.716154E+01 -6.908449E+01 +9.050876E+00 +4.111409E+00 +3.656082E+01 +6.687580E+01 +9.042402E+00 +4.105842E+00 +3.687247E+01 +6.801050E+01 +8.832046E+00 +3.916611E+00 +3.736239E+01 +6.982147E+01 +8.860460E+00 +3.940908E+00 +3.704658E+01 +6.864069E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -252,14 +252,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.796329E+00 -3.881588E+00 -3.693599E+01 -6.825932E+01 -8.659918E+00 -3.761908E+00 -3.709957E+01 -6.884298E+01 +9.042402E+00 +4.105842E+00 +3.687247E+01 +6.801050E+01 +9.050876E+00 +4.111409E+00 +3.656082E+01 +6.687580E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -268,14 +268,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.800405E+00 -3.881868E+00 -3.692302E+01 -6.819716E+01 -8.915310E+00 -3.982710E+00 -3.682783E+01 -6.786998E+01 +8.824229E+00 +3.906925E+00 +3.685909E+01 +6.796123E+01 +8.892576E+00 +3.964978E+00 +3.703525E+01 +6.860645E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -301,133 +301,133 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -5.975352E+01 -1.788900E+02 -1.022213E+02 -5.226556E+02 -1.378440E+01 -9.544964E+00 -4.668065E+01 -1.090372E+02 -5.925317E+01 -1.758044E+02 -1.013043E+02 -5.132587E+02 -1.356187E+01 -9.210736E+00 -4.606346E+01 -1.061497E+02 -6.059074E+01 -1.840993E+02 -1.012721E+02 -5.130091E+02 -1.356441E+01 -9.262377E+00 -4.634250E+01 -1.074283E+02 -5.992755E+01 -1.800342E+02 -1.006299E+02 -5.065012E+02 -1.370402E+01 -9.450980E+00 -4.575433E+01 -1.047310E+02 +5.994898E+01 +1.804403E+02 +1.017670E+02 +5.181130E+02 +1.354160E+01 +9.220935E+00 +4.648971E+01 +1.081636E+02 +5.956983E+01 +1.776668E+02 +1.007226E+02 +5.073592E+02 +1.347883E+01 +9.120344E+00 +4.609907E+01 +1.063127E+02 +6.047337E+01 +1.836168E+02 +1.014777E+02 +5.150557E+02 +1.390508E+01 +9.722899E+00 +4.629251E+01 +1.072027E+02 +6.066027E+01 +1.843661E+02 +1.011648E+02 +5.120629E+02 +1.365982E+01 +9.372236E+00 +4.602994E+01 +1.060579E+02 cmfd indices 2.000000E+00 2.000000E+00 1.000000E+00 2.000000E+00 k cmfd -1.037231E+00 -1.035671E+00 -1.042384E+00 -1.033525E+00 -1.031304E+00 -1.029654E+00 -1.031704E+00 -1.032213E+00 -1.030500E+00 -1.036227E+00 -1.034924E+00 -1.035753E+00 -1.034679E+00 -1.035096E+00 -1.033818E+00 -1.030023E+00 +1.013488E+00 +1.024396E+00 +1.015533E+00 +1.009319E+00 +1.012726E+00 +1.014831E+00 +1.021757E+00 +1.022002E+00 +1.023619E+00 +1.020953E+00 +1.023910E+00 +1.027657E+00 +1.024501E+00 +1.023838E+00 +1.025464E+00 +1.022802E+00 cmfd entropy -1.999702E+00 -1.999790E+00 -1.999713E+00 -1.999852E+00 -1.999820E+00 -1.999667E+00 -1.999553E+00 -1.999649E+00 -1.999398E+00 -1.999527E+00 -1.999648E+00 -1.999607E+00 -1.999533E+00 -1.999684E+00 -1.999714E+00 -1.999812E+00 +1.998974E+00 +1.998742E+00 +1.999128E+00 +1.998952E+00 +1.998951E+00 +1.999439E+00 +1.999626E+00 +1.999826E+00 +1.999513E+00 +1.999451E+00 +1.999514E+00 +1.999590E+00 +1.999563E+00 +1.999604E+00 +1.999742E+00 +1.999736E+00 cmfd balance -7.33587E-04 -1.00987E-03 -8.26985E-04 -5.20809E-04 -6.47932E-04 -9.69990E-04 -8.62860E-04 -4.92175E-04 -5.80764E-04 -4.49167E-04 -4.05541E-04 -4.13811E-04 -4.27271E-04 -3.64944E-04 -3.43522E-04 -2.82842E-04 +9.79896E-04 +4.24873E-04 +8.05696E-04 +1.92071E-03 +3.70731E-04 +2.81424E-04 +8.28991E-04 +6.12217E-04 +5.29185E-04 +4.97799E-04 +3.09154E-04 +1.73703E-04 +2.56689E-04 +2.64938E-04 +1.96305E-04 +1.82702E-04 cmfd dominance ratio -6.259E-03 -6.252E-03 -6.292E-03 -6.347E-03 -6.360E-03 -6.403E-03 -6.375E-03 -6.400E-03 -6.374E-03 -6.343E-03 -6.331E-03 -6.312E-03 -6.305E-03 -6.271E-03 -6.267E-03 -6.265E-03 +6.304E-03 +6.246E-03 +6.159E-03 +6.249E-03 +6.101E-03 +6.155E-03 +6.010E-03 +6.177E-03 +6.349E-03 +6.241E-03 +6.244E-03 +6.249E-03 +6.270E-03 +6.272E-03 +6.278E-03 +6.290E-03 cmfd openmc source comparison -7.908947E-05 -7.452591E-05 -9.249409E-05 -8.223037E-05 -7.355125E-05 -8.926808E-05 -9.363510E-05 -7.628519E-05 -9.019193E-05 -7.130550E-05 -5.947633E-05 -5.744157E-05 -6.093797E-05 -4.505304E-05 -4.430670E-05 -3.019083E-05 +4.046094E-05 +5.979431E-05 +3.836521E-05 +4.577591E-05 +5.012911E-05 +2.114677E-05 +2.074571E-05 +3.042280E-05 +2.408163E-05 +2.434542E-05 +1.190699E-05 +9.499301E-06 +2.354221E-05 +2.937924E-05 +1.889875E-05 +1.913866E-05 cmfd source -2.557606E-01 -2.464707E-01 -2.518098E-01 -2.459589E-01 +2.489706E-01 +2.426801E-01 +2.532142E-01 +2.551351E-01 0.000000E+00 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml b/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_feed_expanding_window/materials.xml b/tests/regression_tests/cmfd_feed_expanding_window/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_feed_expanding_window/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_feed_expanding_window/model.xml b/tests/regression_tests/cmfd_feed_expanding_window/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat b/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat index a235a9f6f0..b39f82f96e 100644 --- a/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat +++ b/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.184725E+00 9.808181E-03 +1.170835E+00 5.423480E-03 tally 1: -1.121111E+01 -1.261033E+01 -2.101271E+01 -4.433294E+01 -2.782926E+01 -7.776546E+01 -3.351044E+01 -1.125084E+02 -3.625691E+01 -1.319666E+02 -3.741881E+01 -1.403776E+02 -3.538049E+01 -1.255798E+02 -3.030311E+01 -9.228152E+01 -2.188389E+01 -4.811272E+01 -1.172417E+01 -1.379179E+01 +1.205100E+01 +1.456707E+01 +2.183882E+01 +4.781179E+01 +2.844010E+01 +8.102358E+01 +3.356334E+01 +1.130832E+02 +3.660829E+01 +1.344973E+02 +3.697740E+01 +1.371500E+02 +3.400119E+01 +1.160196E+02 +2.839868E+01 +8.083199E+01 +2.140398E+01 +4.615447E+01 +1.118179E+01 +1.262942E+01 tally 2: -1.146940E+00 -1.315471E+00 -8.068187E-01 -6.509564E-01 -2.070090E+00 -4.285274E+00 -1.469029E+00 -2.158045E+00 -2.703224E+00 -7.307421E+00 -1.895589E+00 -3.593259E+00 -3.567634E+00 -1.272801E+01 -2.541493E+00 -6.459185E+00 -3.937463E+00 -1.550361E+01 -2.770463E+00 -7.675465E+00 -3.960472E+00 -1.568534E+01 -2.792668E+00 -7.798997E+00 -3.243459E+00 -1.052002E+01 -2.296673E+00 -5.274706E+00 -2.794726E+00 -7.810492E+00 -1.953143E+00 -3.814769E+00 -2.187302E+00 -4.784292E+00 -1.544500E+00 -2.385481E+00 -1.199609E+00 -1.439061E+00 -8.356062E-01 -6.982377E-01 +1.218245E+00 +1.484121E+00 +8.387442E-01 +7.034918E-01 +2.142134E+00 +4.588738E+00 +1.526727E+00 +2.330895E+00 +2.736157E+00 +7.486556E+00 +1.973921E+00 +3.896363E+00 +3.606244E+00 +1.300500E+01 +2.537580E+00 +6.439313E+00 +3.668958E+00 +1.346126E+01 +2.599095E+00 +6.755294E+00 +3.647982E+00 +1.330777E+01 +2.539750E+00 +6.450332E+00 +3.118921E+00 +9.727669E+00 +2.186447E+00 +4.780549E+00 +2.881110E+00 +8.300795E+00 +2.042635E+00 +4.172360E+00 +2.045602E+00 +4.184486E+00 +1.458384E+00 +2.126884E+00 +1.022124E+00 +1.044738E+00 +7.112678E-01 +5.059018E-01 tally 3: -7.817522E-01 -6.111366E-01 -5.930056E-02 -3.516556E-03 -1.426374E+00 -2.034542E+00 -8.539281E-02 -7.291931E-03 -1.815687E+00 -3.296718E+00 -1.221592E-01 -1.492286E-02 -2.447191E+00 -5.988744E+00 -1.624835E-01 -2.640090E-02 -2.670084E+00 -7.129351E+00 -1.838317E-01 -3.379411E-02 -2.683007E+00 -7.198528E+00 -1.719716E-01 -2.957424E-02 -2.215446E+00 -4.908202E+00 -1.707856E-01 -2.916773E-02 -1.872330E+00 -3.505620E+00 -1.209731E-01 -1.463450E-02 -1.484167E+00 -2.202752E+00 -1.114851E-01 -1.242892E-02 -8.018653E-01 -6.429879E-01 -5.692854E-02 -3.240858E-03 +8.048428E-01 +6.477720E-01 +6.603741E-02 +4.360940E-03 +1.466886E+00 +2.151755E+00 +1.002354E-01 +1.004713E-02 +1.909238E+00 +3.645189E+00 +1.202824E-01 +1.446786E-02 +2.443130E+00 +5.968886E+00 +1.627351E-01 +2.648270E-02 +2.492602E+00 +6.213064E+00 +1.910368E-01 +3.649506E-02 +2.437262E+00 +5.940245E+00 +1.568389E-01 +2.459843E-02 +2.104091E+00 +4.427200E+00 +1.450465E-01 +2.103848E-02 +1.965900E+00 +3.864762E+00 +1.367918E-01 +1.871199E-02 +1.402871E+00 +1.968047E+00 +1.061316E-01 +1.126391E-02 +6.832689E-01 +4.668565E-01 +4.599034E-02 +2.115112E-03 tally 4: -1.404203E-01 -1.971786E-02 +1.497312E-01 +2.241943E-02 0.000000E+00 0.000000E+00 -1.383954E-01 -1.915329E-02 -2.626162E-01 -6.896729E-02 +1.535839E-01 +2.358801E-02 +2.882052E-01 +8.306225E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.626162E-01 -6.896729E-02 -1.383954E-01 -1.915329E-02 -2.300607E-01 -5.292793E-02 -3.213893E-01 -1.032911E-01 +2.882052E-01 +8.306225E-02 +1.535839E-01 +2.358801E-02 +2.526805E-01 +6.384743E-02 +3.616220E-01 +1.307705E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.213893E-01 -1.032911E-01 -2.300607E-01 -5.292793E-02 -3.621797E-01 -1.311741E-01 -4.326081E-01 -1.871498E-01 +3.616220E-01 +1.307705E-01 +2.526805E-01 +6.384743E-02 +3.594306E-01 +1.291904E-01 +4.229730E-01 +1.789062E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.326081E-01 -1.871498E-01 -3.621797E-01 -1.311741E-01 -4.274873E-01 -1.827454E-01 -4.701391E-01 -2.210307E-01 +4.229730E-01 +1.789062E-01 +3.594306E-01 +1.291904E-01 +3.973299E-01 +1.578711E-01 +4.255879E-01 +1.811250E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.701391E-01 -2.210307E-01 -4.274873E-01 -1.827454E-01 -4.867763E-01 -2.369512E-01 -5.027339E-01 -2.527413E-01 +4.255879E-01 +1.811250E-01 +3.973299E-01 +1.578711E-01 +4.633933E-01 +2.147333E-01 +4.672837E-01 +2.183540E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.027339E-01 -2.527413E-01 -4.867763E-01 -2.369512E-01 -4.679231E-01 -2.189520E-01 -4.504626E-01 -2.029166E-01 +4.672837E-01 +2.183540E-01 +4.633933E-01 +2.147333E-01 +4.251073E-01 +1.807162E-01 +3.842922E-01 +1.476805E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.504626E-01 -2.029166E-01 -4.679231E-01 -2.189520E-01 -4.340994E-01 -1.884423E-01 -3.622741E-01 -1.312425E-01 +3.842922E-01 +1.476805E-01 +4.251073E-01 +1.807162E-01 +4.045096E-01 +1.636280E-01 +3.192860E-01 +1.019436E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.622741E-01 -1.312425E-01 -4.340994E-01 -1.884423E-01 -3.743415E-01 -1.401316E-01 -2.666952E-01 -7.112632E-02 +3.192860E-01 +1.019436E-01 +4.045096E-01 +1.636280E-01 +3.738326E-01 +1.397508E-01 +2.598153E-01 +6.750398E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.666952E-01 -7.112632E-02 -3.743415E-01 -1.401316E-01 -2.832774E-01 -8.024609E-02 -1.469617E-01 -2.159775E-02 +2.598153E-01 +6.750398E-02 +3.738326E-01 +1.397508E-01 +2.453191E-01 +6.018146E-02 +1.098964E-01 +1.207721E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -1.469617E-01 -2.159775E-02 -2.832774E-01 -8.024609E-02 -1.514998E-01 -2.295220E-02 +1.098964E-01 +1.207721E-02 +2.453191E-01 +6.018146E-02 +1.458094E-01 +2.126039E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,119 +345,119 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -7.817522E-01 -6.111366E-01 -1.254009E-01 -1.572539E-02 -1.426374E+00 -2.034542E+00 -2.208879E-01 -4.879148E-02 -1.815687E+00 -3.296718E+00 -2.598747E-01 -6.753484E-02 -2.446236E+00 -5.984069E+00 -3.067269E-01 -9.408137E-02 -2.670084E+00 -7.129351E+00 -3.358701E-01 -1.128087E-01 -2.682078E+00 -7.193544E+00 -3.021763E-01 -9.131050E-02 -2.215446E+00 -4.908202E+00 -3.092066E-01 -9.560869E-02 -1.871260E+00 -3.501614E+00 -2.400592E-01 -5.762841E-02 -1.483097E+00 -2.199577E+00 -2.197780E-01 -4.830237E-02 -8.009060E-01 -6.414504E-01 -1.023121E-01 -1.046776E-02 +8.048428E-01 +6.477720E-01 +1.018934E-01 +1.038226E-02 +1.466886E+00 +2.151755E+00 +1.414681E-01 +2.001322E-02 +1.909238E+00 +3.645189E+00 +2.450211E-01 +6.003535E-02 +2.443130E+00 +5.968886E+00 +3.360056E-01 +1.128997E-01 +2.492602E+00 +6.213064E+00 +3.266277E-01 +1.066856E-01 +2.437262E+00 +5.940245E+00 +2.878100E-01 +8.283461E-02 +2.104091E+00 +4.427200E+00 +3.440457E-01 +1.183675E-01 +1.965900E+00 +3.864762E+00 +2.880615E-01 +8.297945E-02 +1.401955E+00 +1.965478E+00 +1.646479E-01 +2.710892E-02 +6.832689E-01 +4.668565E-01 +1.147413E-01 +1.316557E-02 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.165553E+00 -1.179705E+00 -1.191818E+00 -1.207372E+00 -1.203745E+00 -1.208055E+00 -1.208191E+00 -1.201797E+00 -1.201945E+00 -1.203753E+00 -1.209025E+00 +1.181376E+00 +1.176656E+00 +1.161939E+00 +1.163552E+00 +1.163035E+00 +1.170382E+00 +1.160597E+00 +1.154301E+00 +1.159007E+00 +1.148290E+00 +1.157088E+00 cmfd entropy -3.217557E+00 -3.209881E+00 -3.204672E+00 -3.212484E+00 -3.217253E+00 -3.216845E+00 -3.219057E+00 -3.217057E+00 -3.223643E+00 -3.230985E+00 -3.230377E+00 +3.246419E+00 +3.246511E+00 +3.252247E+00 +3.240919E+00 +3.237600E+00 +3.233990E+00 +3.234226E+00 +3.229356E+00 +3.224272E+00 +3.225381E+00 +3.226778E+00 cmfd balance -1.65304E-03 -2.29940E-03 -1.63416E-03 -1.39975E-03 -1.91312E-03 -1.62824E-03 -1.95516E-03 -2.02934E-03 -1.92846E-03 -2.33117E-03 -2.29845E-03 +4.18486E-03 +1.72126E-03 +1.10899E-03 +1.88170E-03 +1.31646E-03 +1.34128E-03 +1.57944E-03 +2.11251E-03 +1.79912E-03 +1.86000E-03 +1.47765E-03 cmfd dominance ratio -5.465E-01 -5.481E-01 -5.432E-01 -5.459E-01 -5.463E-01 -5.486E-01 -5.515E-01 -5.493E-01 -5.511E-01 -5.518E-01 -5.499E-01 +5.524E-01 +5.597E-01 +5.622E-01 +5.544E-01 +5.541E-01 +5.519E-01 +5.532E-01 +5.550E-01 +5.484E-01 +5.497E-01 +5.500E-01 cmfd openmc source comparison -6.499546E-03 -3.419761E-03 -4.342514E-03 -7.229618E-03 -9.943057E-03 -1.050086E-02 -1.055060E-02 -6.562146E-03 -7.232860E-03 -4.424331E-03 -2.531323E-03 +1.905464E-03 +4.145126E-03 +2.465876E-03 +2.346755E-03 +1.848120E-03 +3.263822E-03 +3.641639E-03 +4.031509E-03 +4.999010E-03 +6.640746E-03 +5.691414E-03 cmfd source -4.224785E-02 -7.504165E-02 -1.009909E-01 -1.238160E-01 -1.301409E-01 -1.425954E-01 -1.353275E-01 -1.134617E-01 -8.830470E-02 -4.807346E-02 +4.951338E-02 +8.478025E-02 +1.083132E-01 +1.301432E-01 +1.341190E-01 +1.445825E-01 +1.255119E-01 +1.063303E-01 +7.830158E-02 +3.840469E-02 diff --git a/tests/regression_tests/cmfd_feed_expanding_window/settings.xml b/tests/regression_tests/cmfd_feed_expanding_window/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_feed_expanding_window/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml b/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_feed_ng/results_true.dat b/tests/regression_tests/cmfd_feed_ng/results_true.dat index 722cb16c8c..4ea1515f18 100644 --- a/tests/regression_tests/cmfd_feed_ng/results_true.dat +++ b/tests/regression_tests/cmfd_feed_ng/results_true.dat @@ -1,208 +1,208 @@ k-combined: -1.027584E+00 1.502267E-02 +1.008852E+00 9.028695E-03 tally 1: -1.148263E+02 -1.318910E+03 -1.144863E+02 -1.311468E+03 -1.163124E+02 -1.353643E+03 -1.149445E+02 -1.321759E+03 +1.151271E+02 +1.325871E+03 +1.143934E+02 +1.309051E+03 +1.142507E+02 +1.306616E+03 +1.140242E+02 +1.300786E+03 tally 2: -3.397796E+01 -7.265736E+01 -5.019405E+01 -1.585764E+02 -1.053191E+01 -6.978659E+00 -8.739144E+00 -4.800811E+00 -1.368395E+02 -1.172003E+03 -7.370695E+01 -3.398875E+02 -3.374845E+01 -7.161454E+01 -5.011266E+01 -1.580982E+02 -1.014814E+01 -6.486800E+00 -8.590537E+00 -4.639730E+00 -1.379236E+02 -1.198395E+03 -7.277377E+01 -3.313373E+02 -3.554441E+01 -7.913690E+01 -5.224651E+01 -1.709692E+02 -1.050466E+01 -6.961148E+00 -8.866162E+00 -4.951519E+00 -1.388541E+02 -1.206000E+03 -7.399637E+01 -3.423642E+02 -3.479281E+01 -7.602778E+01 -5.139230E+01 -1.659298E+02 -1.026632E+01 -6.621042E+00 -8.649433E+00 -4.697954E+00 -1.402563E+02 -1.235504E+03 -7.374718E+01 -3.400080E+02 +3.403617E+01 +7.260478E+01 +5.031678E+01 +1.588977E+02 +1.003700E+01 +6.373741E+00 +8.514575E+00 +4.571811E+00 +1.413036E+02 +1.264708E+03 +7.321799E+01 +3.353408E+02 +3.354839E+01 +7.052647E+01 +4.895930E+01 +1.501243E+02 +9.972495E+00 +6.271276E+00 +8.436263E+00 +4.481319E+00 +1.353506E+02 +1.146040E+03 +7.309382E+01 +3.341751E+02 +3.389861E+01 +7.205501E+01 +5.005946E+01 +1.571210E+02 +1.041650E+01 +6.810868E+00 +8.839753E+00 +4.897617E+00 +1.344145E+02 +1.130242E+03 +7.270373E+01 +3.307223E+02 +3.347928E+01 +7.040185E+01 +4.940585E+01 +1.535767E+02 +9.898649E+00 +6.175319E+00 +8.406032E+00 +4.442856E+00 +1.374544E+02 +1.182191E+03 +7.334301E+01 +3.365399E+02 tally 3: -4.748159E+01 -1.419494E+02 +4.755532E+01 +1.419592E+02 0.000000E+00 0.000000E+00 -8.132630E-03 -1.326503E-05 +1.628248E-02 +3.680742E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.299481E+00 -6.900644E-01 -2.429296E+00 -3.713869E-01 +3.347160E+00 +7.104290E-01 +2.453669E+00 +3.797470E-01 0.000000E+00 0.000000E+00 -6.175576E+00 -2.403118E+00 +5.925795E+00 +2.220734E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -7.020357E-02 -6.481773E-04 -3.097931E-01 -6.853044E-03 +9.542527E-02 +1.118856E-03 +3.018759E-01 +6.371059E-03 0.000000E+00 0.000000E+00 -2.592969E+00 -4.215508E-01 +2.501316E+00 +3.938401E-01 0.000000E+00 0.000000E+00 -6.972257E+01 -3.041404E+02 -5.792796E-01 -2.191871E-02 -4.745221E+01 -1.418371E+02 +6.926265E+01 +3.001504E+02 +6.765221E-01 +2.991787E-02 +4.605523E+01 +1.328435E+02 0.000000E+00 0.000000E+00 -1.507569E-02 -3.140047E-05 +1.687782E-02 +3.921162E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.307769E+00 -6.924370E-01 -2.473164E+00 -3.840166E-01 +3.481608E+00 +7.705602E-01 +2.439374E+00 +3.779705E-01 0.000000E+00 0.000000E+00 -5.967063E+00 -2.251947E+00 +5.855806E+00 +2.162361E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -9.143833E-02 -8.020544E-04 -3.234880E-01 -7.219562E-03 +1.016677E-01 +9.263706E-04 +3.264878E-01 +7.385168E-03 0.000000E+00 0.000000E+00 -2.477436E+00 -3.882619E-01 +2.519730E+00 +3.986182E-01 0.000000E+00 0.000000E+00 -6.888326E+01 -2.968630E+02 -5.925099E-01 -2.344632E-02 -4.946736E+01 -1.533087E+02 +6.920950E+01 +2.996184E+02 +5.985719E-01 +2.368062E-02 +4.730723E+01 +1.403550E+02 0.000000E+00 0.000000E+00 -1.369119E-02 -3.099298E-05 +6.800415E-03 +1.163614E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.513467E+00 -7.852189E-01 -2.483110E+00 -3.901527E-01 +3.347607E+00 +7.085691E-01 +2.556997E+00 +4.109118E-01 0.000000E+00 0.000000E+00 -6.238898E+00 -2.455700E+00 +6.171063E+00 +2.391770E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -9.032973E-02 -6.677512E-04 -2.816159E-01 -5.349737E-03 +7.942016E-02 +5.628747E-04 +3.456912E-01 +7.940086E-03 0.000000E+00 0.000000E+00 -2.620828E+00 -4.350795E-01 +2.684459E+00 +4.546338E-01 0.000000E+00 0.000000E+00 -6.992910E+01 -3.057653E+02 -5.890670E-01 -2.307978E-02 -4.853121E+01 -1.480132E+02 +6.850588E+01 +2.936078E+02 +6.458806E-01 +2.672541E-02 +4.673301E+01 +1.374202E+02 0.000000E+00 0.000000E+00 -1.719291E-02 -4.141727E-05 +1.504681E-02 +4.202606E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.490129E+00 -7.743548E-01 -2.381968E+00 -3.564896E-01 +3.139138E+00 +6.206027E-01 +2.341735E+00 +3.454035E-01 0.000000E+00 0.000000E+00 -6.151693E+00 -2.382656E+00 +5.923755E+00 +2.216341E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -8.653178E-02 -5.524601E-04 -3.307211E-01 -7.362512E-03 +7.030156E-02 +4.283974E-04 +3.247594E-01 +7.133772E-03 0.000000E+00 0.000000E+00 -2.566722E+00 -4.136929E-01 +2.559691E+00 +4.119674E-01 0.000000E+00 0.000000E+00 -6.968935E+01 -3.036323E+02 -6.295592E-01 -2.609911E-02 +6.938051E+01 +3.012078E+02 +5.159565E-01 +1.730381E-02 tally 4: 0.000000E+00 0.000000E+00 @@ -216,18 +216,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -6.910417E+00 -3.013908E+00 -2.099086E+00 -2.794191E-01 -2.725309E+01 -4.645663E+01 -7.076086E+00 -3.154776E+00 -2.051075E+00 -2.671542E-01 -2.737348E+01 -4.686718E+01 +7.028166E+00 +3.103666E+00 +2.028371E+00 +2.606104E-01 +2.715466E+01 +4.614452E+01 +6.981028E+00 +3.059096E+00 +2.032450E+00 +2.610410E-01 +2.734281E+01 +4.675062E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -240,18 +240,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.215131E+00 -3.265328E+00 -2.066108E+00 -2.704238E-01 -2.755564E+01 -4.753705E+01 -7.022836E+00 -3.102535E+00 -2.076586E+00 -2.741254E-01 -2.756981E+01 -4.756632E+01 +6.969559E+00 +3.054867E+00 +2.042871E+00 +2.624815E-01 +2.766332E+01 +4.787778E+01 +7.022610E+00 +3.098329E+00 +2.109973E+00 +2.824233E-01 +2.733826E+01 +4.676309E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -276,18 +276,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.076086E+00 -3.154776E+00 -2.051075E+00 -2.671542E-01 -2.737348E+01 -4.686718E+01 -6.910417E+00 -3.013908E+00 -2.099086E+00 -2.794191E-01 -2.725309E+01 -4.645663E+01 +6.981028E+00 +3.059096E+00 +2.032450E+00 +2.610410E-01 +2.734281E+01 +4.675062E+01 +7.028166E+00 +3.103666E+00 +2.028371E+00 +2.606104E-01 +2.715466E+01 +4.614452E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -312,18 +312,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.008937E+00 -3.087084E+00 -2.040640E+00 -2.622272E-01 -2.705295E+01 -4.578207E+01 -7.038404E+00 -3.111878E+00 -2.123591E+00 -2.840551E-01 -2.708214E+01 -4.588834E+01 +6.782152E+00 +2.885448E+00 +1.951138E+00 +2.400912E-01 +2.739652E+01 +4.697696E+01 +6.873220E+00 +2.965563E+00 +1.999066E+00 +2.521586E-01 +2.732700E+01 +4.670990E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -360,30 +360,30 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.286308E+00 -3.346248E+00 -2.108252E+00 -2.794781E-01 -2.738387E+01 -4.689407E+01 -7.108444E+00 -3.172810E+00 -2.111474E+00 -2.804633E-01 -2.737249E+01 -4.687424E+01 -7.022836E+00 -3.102535E+00 -2.076586E+00 -2.741254E-01 -2.756981E+01 -4.756632E+01 -7.215131E+00 -3.265328E+00 -2.066108E+00 -2.704238E-01 -2.755564E+01 -4.753705E+01 +6.933695E+00 +3.028826E+00 +2.080336E+00 +2.719620E-01 +2.726925E+01 +4.650782E+01 +6.836291E+00 +2.935998E+00 +2.124868E+00 +2.841370E-01 +2.709685E+01 +4.591604E+01 +7.022610E+00 +3.098329E+00 +2.109973E+00 +2.824233E-01 +2.733826E+01 +4.676309E+01 +6.969559E+00 +3.054867E+00 +2.042871E+00 +2.624815E-01 +2.766332E+01 +4.787778E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -420,18 +420,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.108444E+00 -3.172810E+00 -2.111474E+00 -2.804633E-01 -2.737249E+01 -4.687424E+01 -7.286308E+00 -3.346248E+00 -2.108252E+00 -2.794781E-01 -2.738387E+01 -4.689407E+01 +6.836291E+00 +2.935998E+00 +2.124868E+00 +2.841370E-01 +2.709685E+01 +4.591604E+01 +6.933695E+00 +3.028826E+00 +2.080336E+00 +2.719620E-01 +2.726925E+01 +4.650782E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -444,18 +444,18 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.038404E+00 -3.111878E+00 -2.123591E+00 -2.840551E-01 -2.708214E+01 -4.588834E+01 -7.008937E+00 -3.087084E+00 -2.040640E+00 -2.622272E-01 -2.705295E+01 -4.578207E+01 +6.873220E+00 +2.965563E+00 +1.999066E+00 +2.521586E-01 +2.732700E+01 +4.670990E+01 +6.782152E+00 +2.885448E+00 +1.951138E+00 +2.400912E-01 +2.739652E+01 +4.697696E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -493,124 +493,124 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -4.748972E+01 -1.419968E+02 -8.604872E+00 -4.655596E+00 -7.261430E+01 -3.298767E+02 -1.092092E+01 -7.540081E+00 -3.984923E+00 -1.000404E+00 -3.294804E+01 -6.794642E+01 -4.746728E+01 -1.419250E+02 -8.440227E+00 -4.478243E+00 -7.167598E+01 -3.214310E+02 -1.073385E+01 -7.257545E+00 -3.904158E+00 -9.587510E-01 -3.253492E+01 -6.626202E+01 -4.948105E+01 -1.533940E+02 -8.722008E+00 -4.791509E+00 -7.283154E+01 -3.316829E+02 -1.138387E+01 -8.162117E+00 -4.131397E+00 -1.078045E+00 -3.254336E+01 -6.624925E+01 -4.854840E+01 -1.481182E+02 -8.533661E+00 -4.573316E+00 -7.258180E+01 -3.293419E+02 -1.053673E+01 -7.002045E+00 -3.988151E+00 -1.001884E+00 -3.284213E+01 -6.749116E+01 +4.757160E+01 +1.420561E+02 +8.379464E+00 +4.426892E+00 +7.205748E+01 +3.248178E+02 +1.047742E+01 +6.922945E+00 +3.879420E+00 +9.517577E-01 +3.297227E+01 +6.802001E+01 +4.607211E+01 +1.329402E+02 +8.295180E+00 +4.334618E+00 +7.205259E+01 +3.247324E+02 +1.059307E+01 +7.039382E+00 +3.783039E+00 +9.030658E-01 +3.288052E+01 +6.762048E+01 +4.731403E+01 +1.403940E+02 +8.728060E+00 +4.774750E+00 +7.152809E+01 +3.200993E+02 +1.074636E+01 +7.270116E+00 +4.007351E+00 +1.010562E+00 +3.249303E+01 +6.611829E+01 +4.674806E+01 +1.375015E+02 +8.265491E+00 +4.296947E+00 +7.226174E+01 +3.267127E+02 +1.079228E+01 +7.340408E+00 +3.843888E+00 +9.273339E-01 +3.300391E+01 +6.823122E+01 cmfd indices 2.000000E+00 2.000000E+00 1.000000E+00 3.000000E+00 k cmfd -1.026167E+00 -1.026753E+00 -1.034560E+00 -1.028298E+00 -1.029032E+00 -1.033531E+00 -1.034902E+00 -1.029837E+00 -1.025313E+00 -1.019855E+00 -1.021235E+00 +1.011190E+00 +1.010705E+00 +1.014132E+00 +1.015900E+00 +1.019132E+00 +1.022616E+00 +1.023007E+00 +1.022300E+00 +1.014692E+00 +1.007628E+00 +1.006204E+00 cmfd entropy -1.998818E+00 -1.998853E+00 -1.999048E+00 -1.998864E+00 -1.998999E+00 -1.998789E+00 -1.998670E+00 -1.998822E+00 -1.998782E+00 -1.999027E+00 -1.999420E+00 +1.999167E+00 +1.999076E+00 +1.998507E+00 +1.997924E+00 +1.997814E+00 +1.997752E+00 +1.997882E+00 +1.998074E+00 +1.998109E+00 +1.998301E+00 +1.998581E+00 cmfd balance -1.00064E-03 -6.95941E-04 -5.74967E-04 -4.34776E-04 -4.03288E-04 -4.32457E-04 -4.49075E-04 -3.57342E-04 -3.62891E-04 -2.86133E-04 -2.08678E-04 +9.30124E-04 +2.56632E-04 +3.62598E-04 +4.17543E-04 +5.25720E-04 +4.90208E-04 +3.61304E-04 +2.24090E-04 +1.86602E-04 +1.78395E-04 +6.42497E-05 cmfd dominance ratio -3.759E-03 -3.706E-03 -3.719E-03 -3.796E-03 -3.724E-03 -3.809E-03 -3.792E-03 -3.782E-03 -3.841E-03 -3.866E-03 -3.868E-03 +4.194E-03 +4.234E-03 +4.149E-03 +4.209E-03 +4.185E-03 +4.197E-03 +4.175E-03 +4.105E-03 +4.056E-03 +4.122E-03 +4.113E-03 cmfd openmc source comparison -8.283222E-05 -6.636005E-05 -5.320110E-05 -4.474530E-05 -4.409263E-05 -4.914331E-05 -4.834447E-05 -4.040592E-05 -4.173469E-05 -3.118350E-05 -1.667967E-05 +2.078995E-05 +2.415218E-05 +3.311536E-05 +3.598613E-05 +3.156455E-05 +2.737017E-05 +2.468500E-05 +2.514215E-05 +1.601626E-05 +1.424027E-05 +4.201148E-06 cmfd source -2.416781E-01 -2.442811E-01 -2.566121E-01 -2.574288E-01 +2.558585E-01 +2.597562E-01 +2.529866E-01 +2.313986E-01 0.000000E+00 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/cmfd_feed_rectlin/geometry.xml b/tests/regression_tests/cmfd_feed_rectlin/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_feed_rectlin/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_feed_rectlin/materials.xml b/tests/regression_tests/cmfd_feed_rectlin/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_feed_rectlin/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_feed_rectlin/model.xml b/tests/regression_tests/cmfd_feed_rectlin/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_rectlin/results_true.dat b/tests/regression_tests/cmfd_feed_rectlin/results_true.dat index 1a9c3f4e47..a98f3fbd38 100644 --- a/tests/regression_tests/cmfd_feed_rectlin/results_true.dat +++ b/tests/regression_tests/cmfd_feed_rectlin/results_true.dat @@ -1,149 +1,149 @@ k-combined: -1.167761E+00 4.293426E-03 +1.157362E+00 9.651846E-03 tally 1: -1.202277E+01 -1.452746E+01 -2.195725E+01 -4.835710E+01 -2.862021E+01 -8.215585E+01 -3.453333E+01 -1.196497E+02 -3.756803E+01 -1.414919E+02 -3.739045E+01 -1.403446E+02 -3.361501E+01 -1.134271E+02 -2.819861E+01 -7.968185E+01 -2.111919E+01 -4.472336E+01 -1.129975E+01 -1.280982E+01 +1.160989E+01 +1.351117E+01 +2.127132E+01 +4.540172E+01 +2.903242E+01 +8.450044E+01 +3.443549E+01 +1.188763E+02 +3.678332E+01 +1.355586E+02 +3.760088E+01 +1.418740E+02 +3.433077E+01 +1.181837E+02 +2.861986E+01 +8.231285E+01 +2.182277E+01 +4.792086E+01 +1.138713E+01 +1.304425E+01 tally 2: -8.743037E+00 -3.861220E+00 -6.027264E+00 -1.836570E+00 -3.366159E+01 -5.708871E+01 -2.367086E+01 -2.824484E+01 -2.348709E+01 -2.777633E+01 -1.668756E+01 -1.403629E+01 -5.609969E+01 -1.580739E+02 -3.985682E+01 -7.985715E+01 -3.319543E+01 -5.528370E+01 -2.358360E+01 -2.791829E+01 -7.132407E+01 -2.550603E+02 -5.083603E+01 -1.296049E+02 -3.801086E+01 -7.257141E+01 -2.709688E+01 -3.686995E+01 -3.751307E+01 -7.073318E+01 -2.660859E+01 -3.559134E+01 -7.258292E+01 -2.644619E+02 -5.168044E+01 -1.340982E+02 -3.304423E+01 -5.486256E+01 -2.335526E+01 -2.742989E+01 -5.655005E+01 -1.603926E+02 -4.014572E+01 -8.087797E+01 -2.351019E+01 -2.776367E+01 -1.670083E+01 -1.401374E+01 -3.451886E+01 -5.983149E+01 -2.443300E+01 -2.997375E+01 -8.551177E+00 -3.691263E+00 -5.916124E+00 -1.768556E+00 +8.861425E+00 +3.953963E+00 +6.090757E+00 +1.866235E+00 +3.309260E+01 +5.493482E+01 +2.330765E+01 +2.725350E+01 +2.295343E+01 +2.647375E+01 +1.629166E+01 +1.334081E+01 +5.714234E+01 +1.640293E+02 +4.054051E+01 +8.261123E+01 +3.331786E+01 +5.565082E+01 +2.366345E+01 +2.807637E+01 +7.034800E+01 +2.485209E+02 +5.001286E+01 +1.256087E+02 +3.651223E+01 +6.686058E+01 +2.606851E+01 +3.408881E+01 +3.729833E+01 +6.997492E+01 +2.657970E+01 +3.553107E+01 +7.212382E+01 +2.611253E+02 +5.124647E+01 +1.318756E+02 +3.304529E+01 +5.486643E+01 +2.347504E+01 +2.771353E+01 +5.711252E+01 +1.640619E+02 +4.060288E+01 +8.298231E+01 +2.384078E+01 +2.855909E+01 +1.684046E+01 +1.426180E+01 +3.329487E+01 +5.573544E+01 +2.349871E+01 +2.776487E+01 +8.676886E+00 +3.814132E+00 +5.980976E+00 +1.815143E+00 tally 3: -5.799161E+00 -1.702117E+00 -3.588837E-01 -7.127789E-03 -2.273477E+01 -2.606361E+01 -1.586822E+00 -1.296956E-01 -1.606466E+01 -1.300770E+01 -1.018209E+00 -5.281297E-02 -3.840128E+01 -7.415483E+01 -2.383738E+00 -2.877855E-01 -2.268388E+01 -2.583457E+01 -1.485208E+00 -1.122747E-01 -4.899435E+01 -1.204028E+02 -3.203505E+00 -5.163235E-01 -2.610714E+01 -3.423244E+01 -1.707156E+00 -1.476400E-01 -2.559242E+01 -3.293100E+01 -1.761813E+00 -1.597786E-01 -4.985698E+01 -1.248029E+02 -3.081386E+00 -4.824389E-01 -2.245777E+01 -2.536963E+01 -1.447127E+00 -1.065745E-01 -3.869628E+01 -7.516217E+01 -2.545419E+00 -3.270790E-01 -1.610401E+01 -1.303231E+01 -1.011897E+00 -5.274155E-02 -2.353410E+01 -2.781055E+01 -1.517873E+00 -1.184503E-01 -5.687356E+00 -1.636570E+00 -3.728435E-01 -7.203797E-03 +5.847135E+00 +1.719612E+00 +3.960040E-01 +8.297721E-03 +2.245534E+01 +2.530222E+01 +1.468678E+00 +1.094031E-01 +1.571194E+01 +1.240597E+01 +1.004169E+00 +5.156464E-02 +3.901605E+01 +7.652715E+01 +2.648696E+00 +3.571840E-01 +2.275978E+01 +2.597555E+01 +1.456067E+00 +1.075035E-01 +4.821184E+01 +1.167725E+02 +3.105774E+00 +4.859873E-01 +2.519281E+01 +3.183845E+01 +1.595498E+00 +1.292133E-01 +2.560583E+01 +3.297409E+01 +1.673871E+00 +1.429651E-01 +4.930025E+01 +1.220909E+02 +3.206604E+00 +5.220919E-01 +2.255825E+01 +2.560134E+01 +1.422870E+00 +1.027151E-01 +3.910818E+01 +7.698933E+01 +2.568903E+00 +3.333385E-01 +1.620292E+01 +1.321173E+01 +1.068678E+00 +5.798793E-02 +2.264343E+01 +2.578648E+01 +1.503553E+00 +1.158411E-01 +5.751110E+00 +1.676910E+00 +3.450582E-01 +6.411784E-03 tally 4: -3.060660E+00 -4.703581E-01 +3.051764E+00 +4.671879E-01 0.000000E+00 0.000000E+00 -1.451637E+00 -1.081522E-01 -4.402560E+00 -9.746218E-01 +1.407008E+00 +1.004485E-01 +4.354708E+00 +9.506434E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -160,14 +160,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.402560E+00 -9.746218E-01 -1.451637E+00 -1.081522E-01 -3.903491E+00 -7.713768E-01 -6.448935E+00 -2.089570E+00 +4.354708E+00 +9.506434E-01 +1.407008E+00 +1.004485E-01 +3.852730E+00 +7.498016E-01 +6.382605E+00 +2.043123E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -184,14 +184,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -6.448935E+00 -2.089570E+00 -3.903491E+00 -7.713768E-01 -5.075609E+00 -1.297182E+00 -7.371853E+00 -2.729133E+00 +6.382605E+00 +2.043123E+00 +3.852730E+00 +7.498016E-01 +5.061607E+00 +1.288306E+00 +7.281209E+00 +2.659444E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -208,14 +208,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.371853E+00 -2.729133E+00 -5.075609E+00 -1.297182E+00 -7.015514E+00 -2.477295E+00 -8.723726E+00 -3.828466E+00 +7.281209E+00 +2.659444E+00 +5.061607E+00 +1.288306E+00 +7.096602E+00 +2.527474E+00 +8.632232E+00 +3.736665E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -232,14 +232,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.723726E+00 -3.828466E+00 -7.015514E+00 -2.477295E+00 -7.735344E+00 -3.007547E+00 -9.106625E+00 -4.165807E+00 +8.632232E+00 +3.736665E+00 +7.096602E+00 +2.527474E+00 +7.759456E+00 +3.019026E+00 +8.968687E+00 +4.037738E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -256,14 +256,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.106625E+00 -4.165807E+00 -7.735344E+00 -3.007547E+00 -9.026526E+00 -4.098261E+00 -9.551542E+00 -4.586695E+00 +8.968687E+00 +4.037738E+00 +7.759456E+00 +3.019026E+00 +8.749025E+00 +3.839161E+00 +9.126289E+00 +4.176440E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -280,14 +280,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.551542E+00 -4.586695E+00 -9.026526E+00 -4.098261E+00 -9.344096E+00 -4.384840E+00 -9.405215E+00 -4.441966E+00 +9.126289E+00 +4.176440E+00 +8.749025E+00 +3.839161E+00 +9.259277E+00 +4.304020E+00 +9.165726E+00 +4.216173E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -304,14 +304,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.405215E+00 -4.441966E+00 -9.344096E+00 -4.384840E+00 -9.390313E+00 -4.435716E+00 -9.011690E+00 -4.080913E+00 +9.165726E+00 +4.216173E+00 +9.259277E+00 +4.304020E+00 +9.433925E+00 +4.473551E+00 +8.910566E+00 +3.991458E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -328,14 +328,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.011690E+00 -4.080913E+00 -9.390313E+00 -4.435716E+00 -9.140785E+00 -4.195868E+00 -8.003067E+00 -3.218771E+00 +8.910566E+00 +3.991458E+00 +9.433925E+00 +4.473551E+00 +9.099397E+00 +4.154062E+00 +7.770126E+00 +3.029928E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -352,14 +352,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.003067E+00 -3.218771E+00 -9.140785E+00 -4.195868E+00 -8.629258E+00 -3.736554E+00 -7.133986E+00 -2.557637E+00 +7.770126E+00 +3.029928E+00 +9.099397E+00 +4.154062E+00 +8.575842E+00 +3.694998E+00 +6.934243E+00 +2.418570E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -376,14 +376,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.133986E+00 -2.557637E+00 -8.629258E+00 -3.736554E+00 -7.284136E+00 -2.659936E+00 -5.069993E+00 -1.289638E+00 +6.934243E+00 +2.418570E+00 +8.575842E+00 +3.694998E+00 +7.437526E+00 +2.780629E+00 +5.136923E+00 +1.331553E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -400,14 +400,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.069993E+00 -1.289638E+00 -7.284136E+00 -2.659936E+00 -6.585377E+00 -2.178573E+00 -4.109746E+00 -8.535283E-01 +5.136923E+00 +1.331553E+00 +7.437526E+00 +2.780629E+00 +6.648582E+00 +2.216687E+00 +4.050691E+00 +8.279789E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -424,14 +424,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.109746E+00 -8.535283E-01 -6.585377E+00 -2.178573E+00 -4.394913E+00 -9.694834E-01 -1.455260E+00 -1.073327E-01 +4.050691E+00 +8.279789E-01 +6.648582E+00 +2.216687E+00 +4.390906E+00 +9.686686E-01 +1.391624E+00 +9.861955E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -448,12 +448,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -1.455260E+00 -1.073327E-01 -4.394913E+00 -9.694834E-01 -3.054512E+00 -4.687172E-01 +1.391624E+00 +9.861955E-02 +4.390906E+00 +9.686686E-01 +3.113477E+00 +4.874296E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -473,164 +473,164 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -5.798163E+00 -1.701480E+00 -7.719076E-01 -3.205800E-02 -2.272861E+01 -2.604939E+01 -3.183925E+00 -5.123120E-01 -1.606367E+01 -1.300597E+01 -2.209506E+00 -2.514085E-01 -3.839444E+01 -7.412912E+01 -5.235063E+00 -1.389014E+00 -2.268086E+01 -2.582733E+01 -3.074902E+00 -4.810618E-01 -4.898566E+01 -1.203589E+02 -6.606838E+00 -2.201101E+00 -2.609836E+01 -3.420917E+01 -3.518842E+00 -6.264238E-01 -2.558605E+01 -3.291399E+01 -3.686659E+00 -6.942431E-01 -4.984902E+01 -1.247644E+02 -6.876822E+00 -2.400434E+00 -2.245394E+01 -2.536044E+01 -3.098158E+00 -4.940204E-01 -3.868744E+01 -7.512715E+01 -5.302748E+00 -1.428609E+00 -1.610093E+01 -1.302770E+01 -2.252209E+00 -2.578778E-01 -2.352951E+01 -2.779963E+01 -3.249775E+00 -5.380883E-01 -5.685356E+00 -1.635162E+00 -7.886630E-01 -3.440650E-02 +5.846103E+00 +1.718959E+00 +8.952028E-01 +4.184063E-02 +2.245226E+01 +2.529524E+01 +3.092645E+00 +4.848878E-01 +1.571095E+01 +1.240426E+01 +2.178244E+00 +2.438479E-01 +3.900638E+01 +7.648853E+01 +5.265574E+00 +1.401505E+00 +2.275895E+01 +2.597348E+01 +3.067909E+00 +4.810969E-01 +4.820213E+01 +1.167232E+02 +6.403602E+00 +2.070034E+00 +2.519091E+01 +3.183360E+01 +3.463531E+00 +6.097568E-01 +2.560388E+01 +3.296889E+01 +3.456252E+00 +6.101655E-01 +4.929539E+01 +1.220666E+02 +6.629094E+00 +2.223366E+00 +2.255498E+01 +2.559363E+01 +2.833426E+00 +4.150907E-01 +3.909813E+01 +7.694976E+01 +5.582222E+00 +1.584757E+00 +1.620082E+01 +1.320814E+01 +2.282196E+00 +2.703156E-01 +2.263498E+01 +2.576758E+01 +3.162736E+00 +5.145038E-01 +5.750110E+00 +1.676357E+00 +9.181679E-01 +4.562885E-02 cmfd indices 1.400000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.154550E+00 -1.172635E+00 -1.171962E+00 -1.174888E+00 -1.182656E+00 -1.190779E+00 -1.200964E+00 -1.196775E+00 -1.190049E+00 -1.181514E+00 -1.180749E+00 -1.179370E+00 -1.177279E+00 -1.178924E+00 -1.177106E+00 -1.179987E+00 +1.166740E+00 +1.184008E+00 +1.166534E+00 +1.155559E+00 +1.164960E+00 +1.163229E+00 +1.165897E+00 +1.170104E+00 +1.170207E+00 +1.168091E+00 +1.170940E+00 +1.174589E+00 +1.174609E+00 +1.171505E+00 +1.174456E+00 +1.178370E+00 cmfd entropy -3.598911E+00 -3.600560E+00 -3.599978E+00 -3.601027E+00 -3.599502E+00 -3.598849E+00 -3.601916E+00 -3.605728E+00 -3.607212E+00 -3.612277E+00 -3.615627E+00 -3.618446E+00 -3.615292E+00 -3.612951E+00 -3.610620E+00 -3.607388E+00 +3.594757E+00 +3.587018E+00 +3.590385E+00 +3.595101E+00 +3.592151E+00 +3.600294E+00 +3.602102E+00 +3.604941E+00 +3.605897E+00 +3.604880E+00 +3.601658E+00 +3.602551E+00 +3.600160E+00 +3.604540E+00 +3.604094E+00 +3.602509E+00 cmfd balance -6.80696E-03 -7.03786E-03 -5.33837E-03 -5.39054E-03 -4.82209E-03 -4.46014E-03 -4.48076E-03 -3.31344E-03 -2.71476E-03 -2.03403E-03 -1.68070E-03 -1.38975E-03 -1.30457E-03 -1.29678E-03 -1.34009E-03 -1.43023E-03 +5.52960E-03 +5.42154E-03 +3.62152E-03 +2.92850E-03 +4.08642E-03 +2.07444E-03 +2.03704E-03 +2.06886E-03 +2.09646E-03 +1.94256E-03 +2.02728E-03 +1.89830E-03 +1.83910E-03 +1.48140E-03 +1.47034E-03 +1.64452E-03 cmfd dominance ratio -5.913E-01 -5.946E-01 -5.961E-01 -5.995E-01 -6.024E-01 -6.050E-01 -6.056E-01 -6.062E-01 -6.064E-01 -6.087E-01 -6.117E-01 +6.046E-01 +6.015E-01 +6.059E-01 +6.060E-01 +6.061E-01 +6.109E-01 +6.110E-01 +6.108E-01 6.120E-01 -6.089E-01 -6.074E-01 -6.052E-01 -6.030E-01 +6.124E-01 +6.109E-01 +6.090E-01 +6.116E-01 +6.137E-01 +6.117E-01 +6.131E-01 cmfd openmc source comparison -1.274461E-02 -9.191627E-03 -7.371466E-03 -4.977614E-03 -4.481864E-03 -3.396798E-03 -2.167863E-03 -4.137274E-03 -4.692749E-03 -2.955754E-03 -2.656269E-03 -2.241441E-03 -4.127496E-03 -3.040657E-03 -3.925292E-03 -3.183157E-03 +1.035187E-02 +9.394886E-03 +6.879487E-03 +7.236029E-03 +6.543528E-03 +3.600620E-03 +2.859638E-03 +2.230047E-03 +2.180643E-03 +1.638534E-03 +1.764349E-03 +1.621487E-03 +1.221762E-03 +1.626297E-03 +1.951813E-03 +9.584126E-04 cmfd source -1.525739E-02 -6.776716E-02 -4.352345E-02 -1.020209E-01 -6.379967E-02 -1.378464E-01 -7.334653E-02 -7.562170E-02 -1.318340E-01 -6.145426E-02 -1.073314E-01 -4.229800E-02 -6.258388E-02 -1.531527E-02 +1.677059E-02 +6.229453E-02 +4.278394E-02 +1.134852E-01 +6.231020E-02 +1.327286E-01 +6.808362E-02 +7.130954E-02 +1.362127E-01 +6.048013E-02 +1.094460E-01 +4.546687E-02 +6.403310E-02 +1.459510E-02 diff --git a/tests/regression_tests/cmfd_feed_rectlin/settings.xml b/tests/regression_tests/cmfd_feed_rectlin/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_feed_rectlin/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_feed_rectlin/tallies.xml b/tests/regression_tests/cmfd_feed_rectlin/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_feed_rectlin/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_feed_ref_d/geometry.xml b/tests/regression_tests/cmfd_feed_ref_d/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_feed_ref_d/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_feed_ref_d/materials.xml b/tests/regression_tests/cmfd_feed_ref_d/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_feed_ref_d/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_feed_ref_d/model.xml b/tests/regression_tests/cmfd_feed_ref_d/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_ref_d/results_true.dat b/tests/regression_tests/cmfd_feed_ref_d/results_true.dat index cf1e165b26..34cde1a46e 100644 --- a/tests/regression_tests/cmfd_feed_ref_d/results_true.dat +++ b/tests/regression_tests/cmfd_feed_ref_d/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.184724E+00 9.807415E-03 +1.162249E+00 5.812620E-03 tally 1: -1.121178E+01 -1.261180E+01 -2.101384E+01 -4.433764E+01 -2.783041E+01 -7.777181E+01 -3.351124E+01 -1.125137E+02 -3.625716E+01 -1.319682E+02 -3.741849E+01 -1.403752E+02 -3.537964E+01 -1.255737E+02 -3.030185E+01 -9.227364E+01 -2.188275E+01 -4.810760E+01 -1.172353E+01 -1.379028E+01 +1.153831E+01 +1.338142E+01 +2.155552E+01 +4.659071E+01 +2.813997E+01 +7.941672E+01 +3.270996E+01 +1.073664E+02 +3.639852E+01 +1.329148E+02 +3.729637E+01 +1.393474E+02 +3.443129E+01 +1.186461E+02 +2.832690E+01 +8.040998E+01 +2.177527E+01 +4.771147E+01 +1.146822E+01 +1.328252E+01 tally 2: -1.146903E+00 -1.315387E+00 -8.067939E-01 -6.509164E-01 -2.070041E+00 -4.285068E+00 -1.468994E+00 -2.157944E+00 -2.703198E+00 -7.307278E+00 -1.895572E+00 -3.593192E+00 -3.567627E+00 -1.272796E+01 -2.541486E+00 -6.459152E+00 -3.937479E+00 -1.550374E+01 -2.770473E+00 -7.675520E+00 -3.960493E+00 -1.568551E+01 -2.792683E+00 -7.799079E+00 -3.243496E+00 -1.052027E+01 -2.296698E+00 -5.274821E+00 -2.794771E+00 -7.810744E+00 -1.953175E+00 -3.814893E+00 -2.187333E+00 -4.784426E+00 -1.544523E+00 -2.385551E+00 -1.199628E+00 -1.439107E+00 -8.356207E-01 -6.982620E-01 +1.024353E+00 +1.049299E+00 +6.991057E-01 +4.887487E-01 +2.200432E+00 +4.841901E+00 +1.561655E+00 +2.438768E+00 +2.910400E+00 +8.470426E+00 +2.095155E+00 +4.389674E+00 +3.466006E+00 +1.201320E+01 +2.480456E+00 +6.152662E+00 +3.711781E+00 +1.377732E+01 +2.646019E+00 +7.001418E+00 +3.953648E+00 +1.563133E+01 +2.832759E+00 +8.024524E+00 +3.597870E+00 +1.294467E+01 +2.555396E+00 +6.530048E+00 +2.860871E+00 +8.184585E+00 +2.032282E+00 +4.130169E+00 +2.006740E+00 +4.027007E+00 +1.408150E+00 +1.982886E+00 +1.035163E+00 +1.071562E+00 +7.084068E-01 +5.018402E-01 tally 3: -7.817283E-01 -6.110991E-01 -5.930048E-02 -3.516547E-03 -1.426340E+00 -2.034446E+00 -8.539269E-02 -7.291911E-03 -1.815669E+00 -3.296655E+00 -1.221590E-01 -1.492282E-02 -2.447185E+00 -5.988716E+00 -1.624833E-01 -2.640082E-02 -2.670094E+00 -7.129404E+00 -1.838315E-01 -3.379401E-02 -2.683021E+00 -7.198600E+00 -1.719714E-01 -2.957416E-02 -2.215470E+00 -4.908307E+00 -1.707854E-01 -2.916764E-02 -1.872360E+00 -3.505733E+00 -1.209730E-01 -1.463446E-02 -1.484189E+00 -2.202817E+00 -1.114849E-01 -1.242888E-02 -8.018794E-01 -6.430105E-01 -5.692846E-02 -3.240849E-03 +6.713566E-01 +4.507196E-01 +5.581718E-02 +3.115557E-03 +1.508976E+00 +2.277009E+00 +1.139601E-01 +1.298690E-02 +2.035529E+00 +4.143377E+00 +1.221001E-01 +1.490843E-02 +2.385217E+00 +5.689262E+00 +1.500087E-01 +2.250260E-02 +2.546286E+00 +6.483574E+00 +1.546601E-01 +2.391975E-02 +2.726427E+00 +7.433407E+00 +1.686144E-01 +2.843081E-02 +2.466613E+00 +6.084179E+00 +1.558230E-01 +2.428079E-02 +1.951251E+00 +3.807379E+00 +1.197744E-01 +1.434590E-02 +1.347903E+00 +1.816842E+00 +9.419149E-02 +8.872037E-03 +6.840490E-01 +4.679231E-01 +5.000289E-02 +2.500289E-03 tally 4: -1.404164E-01 -1.971677E-02 +1.561665E-01 +2.438798E-02 0.000000E+00 0.000000E+00 -1.383903E-01 -1.915186E-02 -2.626104E-01 -6.896424E-02 +1.307011E-01 +1.708276E-02 +2.703168E-01 +7.307115E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.626104E-01 -6.896424E-02 -1.383903E-01 -1.915186E-02 -2.300555E-01 -5.292554E-02 -3.213857E-01 -1.032888E-01 +2.703168E-01 +7.307115E-02 +1.307011E-01 +1.708276E-02 +2.637619E-01 +6.957033E-02 +3.685390E-01 +1.358210E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.213857E-01 -1.032888E-01 -2.300555E-01 -5.292554E-02 -3.621759E-01 -1.311714E-01 -4.326073E-01 -1.871490E-01 +3.685390E-01 +1.358210E-01 +2.637619E-01 +6.957033E-02 +3.887017E-01 +1.510890E-01 +4.439407E-01 +1.970834E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.326073E-01 -1.871490E-01 -3.621759E-01 -1.311714E-01 -4.274871E-01 -1.827452E-01 -4.701411E-01 -2.210326E-01 +4.439407E-01 +1.970834E-01 +3.887017E-01 +1.510890E-01 +4.456116E-01 +1.985697E-01 +4.737035E-01 +2.243950E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.701411E-01 -2.210326E-01 -4.274871E-01 -1.827452E-01 -4.867793E-01 -2.369540E-01 -5.027352E-01 -2.527427E-01 +4.737035E-01 +2.243950E-01 +4.456116E-01 +1.985697E-01 +4.760577E-01 +2.266309E-01 +4.703245E-01 +2.212052E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.027352E-01 -2.527427E-01 -4.867793E-01 -2.369540E-01 -4.679247E-01 -2.189535E-01 -4.504680E-01 -2.029214E-01 +4.703245E-01 +2.212052E-01 +4.760577E-01 +2.266309E-01 +4.878056E-01 +2.379543E-01 +4.373120E-01 +1.912418E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.504680E-01 -2.029214E-01 -4.679247E-01 -2.189535E-01 -4.341058E-01 -1.884478E-01 -3.622812E-01 -1.312477E-01 +4.373120E-01 +1.912418E-01 +4.878056E-01 +2.379543E-01 +4.262194E-01 +1.816630E-01 +3.334152E-01 +1.111657E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.622812E-01 -1.312477E-01 -4.341058E-01 -1.884478E-01 -3.743485E-01 -1.401368E-01 -2.666983E-01 -7.112801E-02 +3.334152E-01 +1.111657E-01 +4.262194E-01 +1.816630E-01 +3.560156E-01 +1.267471E-01 +2.409954E-01 +5.807879E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.666983E-01 -7.112801E-02 -3.743485E-01 -1.401368E-01 -2.832798E-01 -8.024744E-02 -1.469655E-01 -2.159885E-02 +2.409954E-01 +5.807879E-02 +3.560156E-01 +1.267471E-01 +2.646501E-01 +7.003965E-02 +1.327244E-01 +1.761576E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -1.469655E-01 -2.159885E-02 -2.832798E-01 -8.024744E-02 -1.515017E-01 -2.295275E-02 +1.327244E-01 +1.761576E-02 +2.646501E-01 +7.003965E-02 +1.480567E-01 +2.192079E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,119 +345,119 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -7.817283E-01 -6.110991E-01 -1.253966E-01 -1.572431E-02 -1.426340E+00 -2.034446E+00 -2.208824E-01 -4.878905E-02 -1.815669E+00 -3.296655E+00 -2.598719E-01 -6.753342E-02 -2.446230E+00 -5.984040E+00 -3.067264E-01 -9.408108E-02 -2.670094E+00 -7.129404E+00 -3.358723E-01 -1.128102E-01 -2.682092E+00 -7.193615E+00 -3.021784E-01 -9.131179E-02 -2.215470E+00 -4.908307E+00 -3.092112E-01 -9.561158E-02 -1.871290E+00 -3.501727E+00 -2.400626E-01 -5.763006E-02 -1.483119E+00 -2.199642E+00 -2.197799E-01 -4.830323E-02 -8.009200E-01 -6.414728E-01 -1.023113E-01 -1.046761E-02 +6.713566E-01 +4.507196E-01 +9.805793E-02 +9.615358E-03 +1.508976E+00 +2.277009E+00 +1.968348E-01 +3.874394E-02 +2.032573E+00 +4.131353E+00 +2.120922E-01 +4.498309E-02 +2.385217E+00 +5.689262E+00 +2.863031E-01 +8.196946E-02 +2.545200E+00 +6.478041E+00 +3.278920E-01 +1.075131E-01 +2.726427E+00 +7.433407E+00 +3.770758E-01 +1.421861E-01 +2.466613E+00 +6.084179E+00 +3.593230E-01 +1.291130E-01 +1.951251E+00 +3.807379E+00 +2.474112E-01 +6.121229E-02 +1.347903E+00 +1.816842E+00 +2.127109E-01 +4.524594E-02 +6.823620E-01 +4.656179E-01 +1.249863E-01 +1.562159E-02 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.165539E+00 -1.179703E+00 -1.191810E+00 -1.207373E+00 -1.203749E+00 -1.208059E+00 -1.208191E+00 -1.201767E+00 -1.201921E+00 -1.203758E+00 -1.209018E+00 +1.181365E+00 +1.176693E+00 +1.161946E+00 +1.163565E+00 +1.163043E+00 +1.169908E+00 +1.149155E+00 +1.142379E+00 +1.152957E+00 +1.137602E+00 +1.141883E+00 cmfd entropy -3.217563E+00 -3.209882E+00 -3.204676E+00 -3.212483E+00 -3.217256E+00 -3.216849E+00 -3.219070E+00 -3.217085E+00 -3.223663E+00 -3.230980E+00 -3.230377E+00 +3.246422E+00 +3.246496E+00 +3.252238E+00 +3.240920E+00 +3.237606E+00 +3.234301E+00 +3.234103E+00 +3.229918E+00 +3.226983E+00 +3.221321E+00 +3.223622E+00 cmfd balance -1.65304E-03 -2.29951E-03 -1.63426E-03 -1.40012E-03 -1.91396E-03 -1.62948E-03 -1.95633E-03 -2.03045E-03 -1.93004E-03 -2.33201E-03 -2.29894E-03 +4.18486E-03 +1.72126E-03 +1.10906E-03 +1.88158E-03 +1.31626E-03 +1.30818E-03 +1.77315E-03 +2.16148E-03 +1.67789E-03 +2.31333E-03 +1.94932E-03 cmfd dominance ratio -5.460E-01 -5.473E-01 -5.415E-01 -5.446E-01 -5.451E-01 +5.505E-01 +5.580E-01 +5.610E-01 +5.526E-01 +5.519E-01 +5.499E-01 +5.504E-01 +5.504E-01 5.475E-01 -5.502E-01 -5.474E-01 -5.498E-01 -5.502E-01 -5.483E-01 +5.465E-01 +5.477E-01 cmfd openmc source comparison -6.484315E-03 -3.411419E-03 -4.321857E-03 -7.194272E-03 -9.907101E-03 -1.046661E-02 -1.050754E-02 -6.523687E-03 -7.212189E-03 -4.425062E-03 -2.540177E-03 +1.902234E-03 +4.110960E-03 +2.452031E-03 +2.337951E-03 +1.838979E-03 +3.138637E-03 +2.684401E-03 +2.912891E-03 +2.823494E-03 +6.391584E-03 +5.904139E-03 cmfd source -4.224343E-02 -7.503541E-02 -1.009853E-01 -1.238103E-01 -1.301388E-01 -1.425912E-01 -1.353339E-01 -1.134730E-01 -8.830826E-02 -4.808031E-02 +4.488002E-02 +8.895136E-02 +1.085930E-01 +1.229651E-01 +1.330479E-01 +1.497140E-01 +1.309102E-01 +1.028556E-01 +7.738878E-02 +4.069397E-02 diff --git a/tests/regression_tests/cmfd_feed_ref_d/settings.xml b/tests/regression_tests/cmfd_feed_ref_d/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_feed_ref_d/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_feed_ref_d/tallies.xml b/tests/regression_tests/cmfd_feed_ref_d/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_feed_ref_d/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml b/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_feed_rolling_window/materials.xml b/tests/regression_tests/cmfd_feed_rolling_window/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_feed_rolling_window/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_feed_rolling_window/model.xml b/tests/regression_tests/cmfd_feed_rolling_window/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat b/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat index a065289ec2..2e31b35bab 100644 --- a/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat +++ b/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.169526E+00 5.973537E-03 +1.158333E+00 1.402684E-02 tally 1: -1.115884E+01 -1.253283E+01 -2.176964E+01 -4.753045E+01 -2.988209E+01 -8.948736E+01 -3.426842E+01 -1.176956E+02 -3.817775E+01 -1.464502E+02 -3.802079E+01 -1.449155E+02 -3.407399E+01 -1.166067E+02 -2.938579E+01 -8.673128E+01 -2.126809E+01 -4.540856E+01 -1.105934E+01 -1.229825E+01 +1.169478E+01 +1.373162E+01 +2.192038E+01 +4.844559E+01 +2.913292E+01 +8.542569E+01 +3.446069E+01 +1.201782E+02 +3.624088E+01 +1.320213E+02 +3.569791E+01 +1.278170E+02 +3.340601E+01 +1.119165E+02 +2.908648E+01 +8.514603E+01 +2.175458E+01 +4.767916E+01 +1.171268E+01 +1.378033E+01 tally 2: -1.064631E+00 -1.133439E+00 -7.584662E-01 -5.752710E-01 -1.898252E+00 -3.603361E+00 -1.330629E+00 -1.770573E+00 -2.737585E+00 -7.494369E+00 -1.949437E+00 -3.800303E+00 -3.313845E+00 -1.098157E+01 -2.356303E+00 -5.552166E+00 -3.735566E+00 -1.395445E+01 -2.657859E+00 -7.064213E+00 -4.052274E+00 -1.642093E+01 -2.864885E+00 -8.207568E+00 -3.385112E+00 -1.145899E+01 -2.358075E+00 -5.560519E+00 -2.776429E+00 -7.708560E+00 -1.960468E+00 -3.843434E+00 -2.240243E+00 -5.018688E+00 -1.549641E+00 -2.401386E+00 -1.092006E+00 -1.192478E+00 -7.441585E-01 -5.537719E-01 +1.132414E+00 +1.282361E+00 +7.822980E-01 +6.119901E-01 +2.124428E+00 +4.513196E+00 +1.490832E+00 +2.222581E+00 +3.158472E+00 +9.975946E+00 +2.221665E+00 +4.935795E+00 +3.994786E+00 +1.595831E+01 +2.828109E+00 +7.998199E+00 +3.491035E+00 +1.218732E+01 +2.461223E+00 +6.057617E+00 +3.461784E+00 +1.198395E+01 +2.473337E+00 +6.117398E+00 +3.027132E+00 +9.163527E+00 +2.150455E+00 +4.624458E+00 +2.471217E+00 +6.106911E+00 +1.737168E+00 +3.017752E+00 +1.880969E+00 +3.538044E+00 +1.316574E+00 +1.733367E+00 +1.233103E+00 +1.520543E+00 +8.543318E-01 +7.298828E-01 tally 3: -7.295798E-01 -5.322866E-01 -4.986135E-02 -2.486155E-03 -1.280099E+00 -1.638654E+00 -9.022531E-02 -8.140606E-03 -1.859202E+00 -3.456630E+00 -1.234662E-01 -1.524390E-02 -2.274313E+00 -5.172502E+00 -1.234662E-01 -1.524390E-02 -2.548554E+00 -6.495129E+00 -1.531456E-01 -2.345357E-02 -2.773126E+00 -7.690228E+00 -1.733276E-01 -3.004244E-02 -2.270798E+00 -5.156524E+00 -1.673917E-01 -2.801998E-02 -1.887286E+00 -3.561849E+00 -1.507712E-01 -2.273196E-02 -1.480414E+00 -2.191625E+00 -1.127816E-01 -1.271970E-02 -7.126366E-01 -5.078509E-01 -6.054593E-02 -3.665809E-03 +7.540113E-01 +5.685330E-01 +6.367610E-02 +4.054646E-03 +1.436984E+00 +2.064922E+00 +8.961822E-02 +8.031425E-03 +2.132240E+00 +4.546449E+00 +1.356065E-01 +1.838913E-02 +2.726356E+00 +7.433016E+00 +1.780572E-01 +3.170438E-02 +2.379700E+00 +5.662970E+00 +1.544735E-01 +2.386206E-02 +2.383471E+00 +5.680933E+00 +1.568319E-01 +2.459624E-02 +2.047271E+00 +4.191318E+00 +1.662654E-01 +2.764418E-02 +1.673107E+00 +2.799287E+00 +1.132020E-01 +1.281468E-02 +1.271576E+00 +1.616906E+00 +7.546797E-02 +5.695415E-03 +8.249906E-01 +6.806094E-01 +5.660098E-02 +3.203671E-03 tally 4: -1.416041E-01 -2.005172E-02 +1.551630E-01 +2.407556E-02 0.000000E+00 0.000000E+00 -1.118431E-01 -1.250887E-02 -2.419761E-01 -5.855243E-02 +1.487992E-01 +2.214121E-02 +2.878091E-01 +8.283409E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.419761E-01 -5.855243E-02 -1.118431E-01 -1.250887E-02 -2.383966E-01 -5.683292E-02 -3.454391E-01 -1.193281E-01 +2.878091E-01 +8.283409E-02 +1.487992E-01 +2.214121E-02 +2.936596E-01 +8.623595E-02 +3.954149E-01 +1.563529E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.454391E-01 -1.193281E-01 -2.383966E-01 -5.683292E-02 -3.449676E-01 -1.190027E-01 -4.324309E-01 -1.869965E-01 +3.954149E-01 +1.563529E-01 +2.936596E-01 +8.623595E-02 +3.991153E-01 +1.592930E-01 +4.758410E-01 +2.264247E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.324309E-01 -1.869965E-01 -3.449676E-01 -1.190027E-01 -4.410209E-01 -1.944994E-01 -4.785286E-01 -2.289896E-01 +4.758410E-01 +2.264247E-01 +3.991153E-01 +1.592930E-01 +4.850882E-01 +2.353106E-01 +5.210840E-01 +2.715285E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.785286E-01 -2.289896E-01 -4.410209E-01 -1.944994E-01 -5.038795E-01 -2.538945E-01 -5.028063E-01 -2.528142E-01 +5.210840E-01 +2.715285E-01 +4.850882E-01 +2.353106E-01 +4.790245E-01 +2.294645E-01 +4.570092E-01 +2.088574E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.028063E-01 -2.528142E-01 -5.038795E-01 -2.538945E-01 -4.780368E-01 -2.285192E-01 -4.303602E-01 -1.852099E-01 +4.570092E-01 +2.088574E-01 +4.790245E-01 +2.294645E-01 +4.505886E-01 +2.030301E-01 +3.884038E-01 +1.508575E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.303602E-01 -1.852099E-01 -4.780368E-01 -2.285192E-01 -4.183673E-01 -1.750312E-01 -3.317104E-01 -1.100318E-01 +3.884038E-01 +1.508575E-01 +4.505886E-01 +2.030301E-01 +3.986999E-01 +1.589616E-01 +3.220889E-01 +1.037412E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -3.317104E-01 -1.100318E-01 -4.183673E-01 -1.750312E-01 -3.797671E-01 -1.442230E-01 -2.636003E-01 -6.948514E-02 +3.220889E-01 +1.037412E-01 +3.986999E-01 +1.589616E-01 +3.319083E-01 +1.101631E-01 +2.101261E-01 +4.415297E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.636003E-01 -6.948514E-02 -3.797671E-01 -1.442230E-01 -2.686240E-01 -7.215888E-02 -1.324766E-01 -1.755005E-02 +2.101261E-01 +4.415297E-02 +3.319083E-01 +1.101631E-01 +2.795459E-01 +7.814588E-02 +1.306499E-01 +1.706938E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -1.324766E-01 -1.755005E-02 -2.686240E-01 -7.215888E-02 -1.444839E-01 -2.087560E-02 +1.306499E-01 +1.706938E-02 +2.795459E-01 +7.814588E-02 +1.585507E-01 +2.513833E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,119 +345,119 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -7.285713E-01 -5.308161E-01 -1.056136E-01 -1.115423E-02 -1.280099E+00 -1.638654E+00 -1.717497E-01 -2.949797E-02 -1.859202E+00 -3.456630E+00 -2.285372E-01 -5.222927E-02 -2.274313E+00 -5.172502E+00 -2.636808E-01 -6.952757E-02 -2.547588E+00 -6.490203E+00 -2.914026E-01 -8.491545E-02 -2.773126E+00 -7.690228E+00 -3.908576E-01 -1.527696E-01 -2.269831E+00 -5.152135E+00 -3.279256E-01 -1.075352E-01 -1.886272E+00 -3.558024E+00 -2.679083E-01 -7.177487E-02 -1.479436E+00 -2.188731E+00 -2.362981E-01 -5.583681E-02 -7.126366E-01 -5.078509E-01 -1.015892E-01 -1.032038E-02 +7.530237E-01 +5.670447E-01 +1.178984E-01 +1.390004E-02 +1.436984E+00 +2.064922E+00 +1.458395E-01 +2.126916E-02 +2.132240E+00 +4.546449E+00 +2.950109E-01 +8.703141E-02 +2.726356E+00 +7.433016E+00 +3.397288E-01 +1.154157E-01 +2.379700E+00 +5.662970E+00 +3.113206E-01 +9.692053E-02 +2.383471E+00 +5.680933E+00 +3.455611E-01 +1.194125E-01 +2.047271E+00 +4.191318E+00 +2.910986E-01 +8.473841E-02 +1.673107E+00 +2.799287E+00 +2.381996E-01 +5.673907E-02 +1.270672E+00 +1.614606E+00 +1.795241E-01 +3.222889E-02 +8.249906E-01 +6.806094E-01 +1.201397E-01 +1.443354E-02 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.188625E+00 -1.172261E+00 -1.172154E+00 -1.176234E+00 -1.161072E+00 -1.174217E+00 -1.176208E+00 -1.183067E+00 -1.208406E+00 -1.220367E+00 -1.208889E+00 +1.184474E+00 +1.188170E+00 +1.165127E+00 +1.135010E+00 +1.145439E+00 +1.158451E+00 +1.154420E+00 +1.179615E+00 +1.197843E+00 +1.181252E+00 +1.186316E+00 cmfd entropy -3.216548E+00 -3.213244E+00 -3.220132E+00 -3.216556E+00 -3.218184E+00 -3.208592E+00 -3.220641E+00 -3.217076E+00 -3.216257E+00 -3.212578E+00 -3.230102E+00 +3.243654E+00 +3.244091E+00 +3.249203E+00 +3.249952E+00 +3.245538E+00 +3.240838E+00 +3.238919E+00 +3.223131E+00 +3.216002E+00 +3.220324E+00 +3.224804E+00 cmfd balance -1.65186E-03 -1.76715E-03 -2.25997E-03 -2.27924E-03 -1.67354E-03 -1.72671E-03 -1.79744E-03 -1.99065E-03 -2.56417E-03 -2.45718E-03 -3.26245E-03 +4.21104E-03 +1.38052E-03 +1.34642E-03 +2.39255E-03 +2.07426E-03 +1.27927E-03 +2.26249E-03 +2.72103E-03 +2.71504E-03 +2.19156E-03 +1.91989E-03 cmfd dominance ratio -5.445E-01 -5.466E-01 -5.539E-01 -5.547E-01 -5.569E-01 -5.518E-01 -5.553E-01 -5.544E-01 -5.474E-01 -5.355E-01 -5.460E-01 +5.520E-01 +5.535E-01 +5.628E-01 +5.696E-01 +5.723E-01 +5.651E-01 +5.648E-01 +5.555E-01 +5.448E-01 +5.488E-01 +5.484E-01 cmfd openmc source comparison -5.662096E-03 -3.660183E-03 -4.806643E-03 -2.475327E-03 -6.166988E-03 -2.205088E-03 -4.010491E-03 -6.270883E-03 -3.070427E-03 -5.679208E-03 -1.488803E-02 +1.713810E-03 +2.429503E-03 +4.526209E-03 +7.978149E-03 +3.320012E-03 +3.880041E-03 +1.580215E-02 +1.663452E-02 +1.878103E-02 +7.436342E-03 +3.724478E-03 cmfd source -4.111004E-02 -7.023296E-02 -1.023250E-01 -1.185886E-01 -1.386562E-01 -1.392823E-01 -1.290017E-01 -1.147469E-01 -9.733704E-02 -4.871933E-02 +4.688167E-02 +9.066303E-02 +1.150679E-01 +1.430247E-01 +1.370466E-01 +1.267615E-01 +1.244218E-01 +1.048774E-01 +7.083441E-02 +4.042108E-02 diff --git a/tests/regression_tests/cmfd_feed_rolling_window/settings.xml b/tests/regression_tests/cmfd_feed_rolling_window/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_feed_rolling_window/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml b/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_nofeed/geometry.xml b/tests/regression_tests/cmfd_nofeed/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_nofeed/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_nofeed/materials.xml b/tests/regression_tests/cmfd_nofeed/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_nofeed/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_nofeed/model.xml b/tests/regression_tests/cmfd_nofeed/model.xml new file mode 100644 index 0000000000..b3fe853b26 --- /dev/null +++ b/tests/regression_tests/cmfd_nofeed/model.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_nofeed/results_true.dat b/tests/regression_tests/cmfd_nofeed/results_true.dat index dac35871c6..ea1a0230b9 100644 --- a/tests/regression_tests/cmfd_nofeed/results_true.dat +++ b/tests/regression_tests/cmfd_nofeed/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.170405E+00 1.487119E-02 +1.169143E+00 7.248013E-03 tally 1: -1.172722E+01 -1.378635E+01 -2.122139E+01 -4.511390E+01 -2.936481E+01 -8.649521E+01 -3.553909E+01 -1.265901E+02 -3.862903E+01 -1.497314E+02 -3.683895E+01 -1.358679E+02 -3.373597E+01 -1.140439E+02 -2.810435E+01 -7.932253E+01 -2.098314E+01 -4.409819E+01 -1.105096E+01 -1.224007E+01 +1.115130E+01 +1.249933E+01 +2.147608E+01 +4.643964E+01 +2.923697E+01 +8.598273E+01 +3.439175E+01 +1.189653E+02 +3.729169E+01 +1.395456E+02 +3.709975E+01 +1.380839E+02 +3.415420E+01 +1.168226E+02 +2.895696E+01 +8.419764E+01 +2.140382E+01 +4.646784E+01 +1.125483E+01 +1.275812E+01 tally 2: -2.275408E+01 -2.603315E+01 -1.590700E+01 -1.273395E+01 -4.112322E+01 -8.498758E+01 -2.911800E+01 -4.264070E+01 -5.707453E+01 -1.638375E+02 -4.069300E+01 -8.336155E+01 -6.915962E+01 -2.399628E+02 -4.926800E+01 -1.218497E+02 -7.521027E+01 -2.842789E+02 -5.357900E+01 -1.443468E+02 -7.387401E+01 -2.738660E+02 -5.263400E+01 -1.390611E+02 -6.871266E+01 -2.368771E+02 -4.902000E+01 -1.205569E+02 -5.632046E+01 -1.592677E+02 -3.999300E+01 -8.035469E+01 -4.291289E+01 -9.245946E+01 -3.046000E+01 -4.661878E+01 -2.273666E+01 -2.598211E+01 -1.590800E+01 -1.271433E+01 +2.275147E+01 +2.604974E+01 +1.587800E+01 +1.271197E+01 +4.191959E+01 +8.846441E+01 +2.966500E+01 +4.430669E+01 +5.699815E+01 +1.637359E+02 +4.037600E+01 +8.219353E+01 +6.656851E+01 +2.228703E+02 +4.718000E+01 +1.119941E+02 +7.334215E+01 +2.701014E+02 +5.223500E+01 +1.370726E+02 +7.394394E+01 +2.748295E+02 +5.273400E+01 +1.397334E+02 +6.931604E+01 +2.406234E+02 +4.949000E+01 +1.226886E+02 +5.799672E+01 +1.687992E+02 +4.142300E+01 +8.612116E+01 +4.320102E+01 +9.402138E+01 +3.068300E+01 +4.749148E+01 +2.295257E+01 +2.657057E+01 +1.606200E+01 +1.301453E+01 tally 3: -1.529800E+01 -1.178010E+01 -1.016076E+00 -5.280687E-02 -2.803900E+01 -3.955010E+01 -1.861024E+00 -1.765351E-01 -3.919400E+01 -7.734526E+01 -2.540695E+00 -3.268959E-01 -4.749400E+01 -1.132677E+02 -3.087604E+00 -4.837647E-01 -5.156500E+01 -1.337335E+02 -3.371014E+00 -5.734875E-01 -5.070500E+01 -1.290569E+02 -3.292766E+00 -5.489854E-01 -4.723400E+01 -1.119247E+02 -2.949932E+00 -4.381745E-01 -3.847900E+01 -7.441953E+01 -2.522868E+00 -3.218962E-01 -2.933900E+01 -4.325593E+01 -1.818016E+00 -1.672765E-01 -1.534700E+01 -1.183573E+01 -9.564025E-01 -4.735079E-02 +1.528200E+01 +1.177982E+01 +1.040687E+00 +5.586386E-02 +2.857900E+01 +4.113603E+01 +1.871515E+00 +1.774689E-01 +3.888800E+01 +7.626262E+01 +2.534433E+00 +3.274088E-01 +4.541400E+01 +1.037867E+02 +2.926509E+00 +4.319780E-01 +5.034500E+01 +1.273517E+02 +3.215813E+00 +5.215716E-01 +5.076100E+01 +1.295218E+02 +3.194424E+00 +5.165993E-01 +4.768100E+01 +1.138949E+02 +3.058255E+00 +4.732131E-01 +3.995800E+01 +8.015691E+01 +2.454286E+00 +3.044948E-01 +2.957000E+01 +4.412743E+01 +1.938963E+00 +1.908501E-01 +1.544000E+01 +1.202869E+01 +1.038073E+00 +5.487827E-02 tally 4: -3.092000E+00 -4.809100E-01 +3.086000E+00 +4.780160E-01 0.000000E+00 0.000000E+00 -2.628000E+00 -3.509980E-01 -5.388000E+00 -1.458946E+00 +2.739000E+00 +3.790990E-01 +5.478000E+00 +1.505928E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.388000E+00 -1.458946E+00 -2.628000E+00 -3.509980E-01 -5.063000E+00 -1.292417E+00 -7.312000E+00 -2.686738E+00 +5.478000E+00 +1.505928E+00 +2.739000E+00 +3.790990E-01 +5.094000E+00 +1.310476E+00 +7.282000E+00 +2.669810E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.312000E+00 -2.686738E+00 -5.063000E+00 -1.292417E+00 -7.115000E+00 -2.542363E+00 -8.719000E+00 -3.819081E+00 +7.282000E+00 +2.669810E+00 +5.094000E+00 +1.310476E+00 +6.987000E+00 +2.461137E+00 +8.487000E+00 +3.624153E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.719000E+00 -3.819081E+00 -7.115000E+00 -2.542363E+00 -8.483000E+00 -3.615549E+00 -9.255000E+00 -4.303287E+00 +8.487000E+00 +3.624153E+00 +6.987000E+00 +2.461137E+00 +8.250000E+00 +3.421824E+00 +9.022000E+00 +4.088536E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.255000E+00 -4.303287E+00 -8.483000E+00 -3.615549E+00 -9.375000E+00 -4.416751E+00 -9.330000E+00 -4.375230E+00 +9.022000E+00 +4.088536E+00 +8.250000E+00 +3.421824E+00 +9.300000E+00 +4.344142E+00 +9.262000E+00 +4.308946E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.330000E+00 -4.375230E+00 -9.375000E+00 -4.416751E+00 -9.346000E+00 -4.379220E+00 -8.458000E+00 -3.585930E+00 +9.262000E+00 +4.308946E+00 +9.300000E+00 +4.344142E+00 +9.267000E+00 +4.310941E+00 +8.487000E+00 +3.613257E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.458000E+00 -3.585930E+00 -9.346000E+00 -4.379220E+00 -8.671000E+00 -3.770383E+00 -7.062000E+00 -2.505966E+00 +8.487000E+00 +3.613257E+00 +9.267000E+00 +4.310941E+00 +8.682000E+00 +3.778194E+00 +7.123000E+00 +2.544345E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.062000E+00 -2.505966E+00 -8.671000E+00 -3.770383E+00 -7.279000E+00 -2.663587E+00 -4.994000E+00 -1.261468E+00 +7.123000E+00 +2.544345E+00 +8.682000E+00 +3.778194E+00 +7.421000E+00 +2.773897E+00 +5.198000E+00 +1.369216E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -4.994000E+00 -1.261468E+00 -7.279000E+00 -2.663587E+00 -5.492000E+00 -1.515002E+00 -2.772000E+00 -3.896040E-01 +5.198000E+00 +1.369216E+00 +7.421000E+00 +2.773897E+00 +5.567000E+00 +1.561013E+00 +2.763000E+00 +3.882750E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.772000E+00 -3.896040E-01 -5.492000E+00 -1.515002E+00 -3.022000E+00 -4.608940E-01 +2.763000E+00 +3.882750E-01 +5.567000E+00 +1.561013E+00 +3.106000E+00 +4.853380E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,144 +345,144 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -1.529600E+01 -1.177689E+01 -2.229624E+00 -2.514699E-01 -2.803300E+01 -3.953329E+01 -3.889856E+00 -7.678274E-01 -3.918900E+01 -7.732686E+01 -5.211188E+00 -1.378173E+00 -4.748700E+01 -1.132339E+02 -6.595911E+00 -2.211528E+00 -5.155000E+01 -1.336522E+02 -6.673103E+00 -2.248462E+00 -5.069800E+01 -1.290196E+02 -6.907470E+00 -2.412919E+00 -4.722500E+01 -1.118824E+02 -6.200070E+00 -1.954544E+00 -3.847200E+01 -7.439145E+01 -5.385095E+00 -1.462192E+00 -2.933600E+01 -4.324744E+01 -4.014057E+00 -8.142088E-01 -1.534200E+01 -1.182781E+01 -2.076271E+00 -2.273431E-01 +1.527700E+01 +1.177204E+01 +2.285003E+00 +2.661382E-01 +2.857200E+01 +4.111506E+01 +4.126099E+00 +8.737996E-01 +3.887800E+01 +7.622094E+01 +5.121343E+00 +1.333837E+00 +4.540600E+01 +1.037492E+02 +6.160114E+00 +1.913665E+00 +5.033400E+01 +1.272949E+02 +6.859861E+00 +2.384476E+00 +5.075800E+01 +1.295055E+02 +6.929393E+00 +2.443364E+00 +4.767500E+01 +1.138658E+02 +6.385463E+00 +2.080293E+00 +3.995300E+01 +8.013651E+01 +5.620641E+00 +1.603844E+00 +2.956200E+01 +4.410395E+01 +3.952701E+00 +7.877105E-01 +1.543500E+01 +1.202080E+01 +2.203583E+00 +2.512936E-01 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.161531E+00 -1.181707E+00 -1.176063E+00 -1.168433E+00 -1.173170E+00 -1.174671E+00 -1.176457E+00 -1.177540E+00 -1.180826E+00 -1.174158E+00 -1.171305E+00 -1.172618E+00 -1.170171E+00 -1.173235E+00 -1.172599E+00 -1.177343E+00 +1.169107E+00 +1.173852E+00 +1.181921E+00 +1.187733E+00 +1.185766E+00 +1.177020E+00 +1.181200E+00 +1.180088E+00 +1.180676E+00 +1.174948E+00 +1.174167E+00 +1.174935E+00 +1.169912E+00 +1.169058E+00 +1.170366E+00 +1.169774E+00 cmfd entropy -3.206619E+00 -3.207385E+00 -3.208853E+00 -3.210825E+00 -3.214820E+00 -3.213490E+00 -3.212717E+00 -3.210590E+00 -3.211948E+00 -3.212156E+00 -3.212900E+00 -3.212275E+00 -3.212766E+00 -3.214205E+00 -3.212717E+00 -3.214169E+00 +3.207640E+00 +3.212075E+00 +3.215463E+00 +3.219545E+00 +3.225225E+00 +3.227103E+00 +3.229048E+00 +3.228263E+00 +3.229077E+00 +3.229932E+00 +3.229351E+00 +3.228195E+00 +3.228493E+00 +3.227823E+00 +3.225830E+00 +3.227270E+00 cmfd balance -4.99833E-03 -5.81289E-03 -3.45193E-03 -2.84521E-03 -3.72331E-03 -3.07942E-03 -2.58676E-03 -2.19354E-03 -2.11522E-03 -2.02671E-03 -1.71439E-03 -1.62909E-03 -1.43261E-03 -1.26201E-03 -1.40167E-03 -1.26141E-03 +4.88208E-03 +4.63702E-03 +3.41158E-03 +2.99755E-03 +2.78360E-03 +3.31542E-03 +2.64344E-03 +2.04609E-03 +1.84340E-03 +1.65450E-03 +1.70816E-03 +1.69952E-03 +1.51417E-03 +1.32738E-03 +1.41435E-03 +1.01462E-03 cmfd dominance ratio -5.283E-01 -5.304E-01 -5.310E-01 -5.324E-01 -5.372E-01 -5.369E-01 -5.367E-01 -5.341E-01 -5.353E-01 -5.375E-01 -5.379E-01 -5.372E-01 -5.379E-01 -5.393E-01 -5.384E-01 -5.382E-01 +5.467E-01 +5.468E-01 +5.448E-01 +5.457E-01 +5.457E-01 +5.485E-01 +5.500E-01 +5.497E-01 +5.495E-01 +5.499E-01 +5.502E-01 +5.485E-01 +5.492E-01 +5.483E-01 +5.473E-01 +5.466E-01 cmfd openmc source comparison -1.291827E-02 -9.488059E-03 -8.538280E-03 -6.820006E-03 -4.300032E-03 -5.486871E-03 -4.493389E-03 -4.913340E-03 -5.132196E-03 -3.342331E-03 -3.094995E-03 -3.553279E-03 -3.284811E-03 -2.492272E-03 -3.062765E-03 -2.632092E-03 +9.587418E-03 +7.785087E-03 +6.798967E-03 +5.947641E-03 +4.980801E-03 +4.272665E-03 +4.073759E-03 +4.305612E-03 +3.572759E-03 +3.785830E-03 +3.766828E-03 +3.495462E-03 +3.017281E-03 +2.857633E-03 +2.858606E-03 +2.449010E-03 cmfd source -4.280100E-02 -7.944783E-02 -1.091569E-01 -1.329540E-01 -1.451697E-01 -1.413526E-01 -1.258514E-01 -1.067698E-01 -7.656551E-02 -3.993138E-02 +4.390084E-02 +7.966902E-02 +1.087889E-01 +1.263915E-01 +1.394331E-01 +1.383156E-01 +1.319970E-01 +1.051339E-01 +8.248244E-02 +4.388774E-02 diff --git a/tests/regression_tests/cmfd_nofeed/settings.xml b/tests/regression_tests/cmfd_nofeed/settings.xml deleted file mode 100644 index 24b0b6ab50..0000000000 --- a/tests/regression_tests/cmfd_nofeed/settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - diff --git a/tests/regression_tests/cmfd_nofeed/tallies.xml b/tests/regression_tests/cmfd_nofeed/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_nofeed/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/cmfd_restart/geometry.xml b/tests/regression_tests/cmfd_restart/geometry.xml deleted file mode 100644 index 73ea679c4c..0000000000 --- a/tests/regression_tests/cmfd_restart/geometry.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - 0 - -1 2 -3 4 -5 6 - 1 - - - - - x-plane - 10 - vacuum - - - x-plane - -10 - vacuum - - - y-plane - 1 - reflective - - - y-plane - -1 - reflective - - - z-plane - 1 - reflective - - - z-plane - -1 - reflective - - - diff --git a/tests/regression_tests/cmfd_restart/materials.xml b/tests/regression_tests/cmfd_restart/materials.xml deleted file mode 100644 index 70580e3a8d..0000000000 --- a/tests/regression_tests/cmfd_restart/materials.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/tests/regression_tests/cmfd_restart/model.xml b/tests/regression_tests/cmfd_restart/model.xml new file mode 100644 index 0000000000..487e9b66dd --- /dev/null +++ b/tests/regression_tests/cmfd_restart/model.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 10 + + + -10.0 -1.0 -1.0 10.0 1.0 1.0 + + + + 15 20 + + 10 + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + + 1 + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_restart/results_true.dat b/tests/regression_tests/cmfd_restart/results_true.dat index 90d8820d0c..1ef9624d4c 100644 --- a/tests/regression_tests/cmfd_restart/results_true.dat +++ b/tests/regression_tests/cmfd_restart/results_true.dat @@ -1,117 +1,117 @@ k-combined: -1.159021E+00 8.924006E-03 +1.181723E+00 9.944883E-03 tally 1: -1.140162E+01 -1.306940E+01 -2.093739E+01 -4.404780E+01 -2.914408E+01 -8.521010E+01 -3.483677E+01 -1.216824E+02 -3.778463E+01 -1.429632E+02 -3.810371E+01 -1.455108E+02 -3.465248E+01 -1.207868E+02 -2.862033E+01 -8.218833E+01 -2.086025E+01 -4.365941E+01 -1.130798E+01 -1.286509E+01 +1.169899E+01 +1.373251E+01 +2.142380E+01 +4.605511E+01 +2.968085E+01 +8.838716E+01 +3.561418E+01 +1.271206E+02 +3.777783E+01 +1.428817E+02 +3.805832E+01 +1.450213E+02 +3.439836E+01 +1.184892E+02 +2.852438E+01 +8.161896E+01 +2.088423E+01 +4.376204E+01 +1.076670E+01 +1.168108E+01 tally 2: -2.234393E+01 -2.516414E+01 -1.555024E+01 -1.218205E+01 -4.087743E+01 -8.401702E+01 -2.883717E+01 -4.185393E+01 -5.635166E+01 -1.595225E+02 -3.998857E+01 -8.040398E+01 -6.887126E+01 -2.379185E+02 -4.903103E+01 -1.206174E+02 -7.452051E+01 -2.785675E+02 -5.295380E+01 -1.406900E+02 -7.495422E+01 -2.819070E+02 -5.333191E+01 -1.427474E+02 -6.921815E+01 -2.408568E+02 -4.928246E+01 -1.221076E+02 -5.668548E+01 -1.612556E+02 -4.035856E+01 -8.181159E+01 -4.259952E+01 -9.112630E+01 -3.026717E+01 -4.600625E+01 -2.310563E+01 -2.688378E+01 -1.615934E+01 -1.315528E+01 +2.321241E+01 +2.702156E+01 +1.620912E+01 +1.317752E+01 +4.197404E+01 +8.845008E+01 +2.982666E+01 +4.469221E+01 +5.810089E+01 +1.695857E+02 +4.134123E+01 +8.588866E+01 +6.982488E+01 +2.447068E+02 +4.966939E+01 +1.238763E+02 +7.428421E+01 +2.767613E+02 +5.287955E+01 +1.403163E+02 +7.447402E+01 +2.785012E+02 +5.324628E+01 +1.423393E+02 +6.895164E+01 +2.381937E+02 +4.916366E+01 +1.211701E+02 +5.679253E+01 +1.617881E+02 +4.043125E+01 +8.204061E+01 +4.218618E+01 +8.933666E+01 +2.978592E+01 +4.456592E+01 +2.196426E+01 +2.435867E+01 +1.525576E+01 +1.175879E+01 tally 3: -1.496375E+01 -1.128154E+01 -9.905641E-01 -5.125710E-02 -2.774937E+01 -3.877241E+01 -1.786861E+00 -1.627655E-01 -3.849739E+01 -7.453828E+01 -2.494135E+00 -3.158098E-01 -4.724085E+01 -1.119901E+02 -3.031174E+00 -4.653741E-01 -5.096719E+01 -1.303552E+02 -3.254375E+00 -5.351020E-01 -5.133808E+01 -1.322892E+02 -3.383595E+00 -5.798798E-01 -4.756072E+01 -1.137527E+02 -3.001917E+00 -4.558247E-01 -3.887437E+01 -7.593416E+01 -2.517908E+00 -3.221926E-01 -2.910687E+01 -4.255173E+01 -1.817765E+00 -1.678763E-01 -1.557241E+01 -1.222026E+01 -9.852737E-01 -5.002659E-02 +1.563788E+01 +1.226528E+01 +1.053289E+00 +5.666942E-02 +2.870755E+01 +4.139654E+01 +1.838017E+00 +1.710528E-01 +3.978616E+01 +7.955764E+01 +2.560657E+00 +3.334449E-01 +4.780385E+01 +1.147770E+02 +3.139243E+00 +4.967628E-01 +5.106650E+01 +1.308704E+02 +3.170056E+00 +5.078920E-01 +5.123992E+01 +1.318586E+02 +3.211706E+00 +5.205979E-01 +4.729862E+01 +1.121695E+02 +3.068662E+00 +4.749488E-01 +3.898816E+01 +7.630564E+01 +2.516911E+00 +3.199696E-01 +2.865357E+01 +4.125742E+01 +1.852314E+00 +1.741116E-01 +1.467340E+01 +1.088460E+01 +9.268633E-01 +4.450662E-02 tally 4: -3.047490E+00 -4.661458E-01 +3.029754E+00 +4.613561E-01 0.000000E+00 0.000000E+00 -2.635775E+00 -3.524426E-01 -5.357229E+00 -1.440049E+00 +2.832501E+00 +4.049252E-01 +5.517243E+00 +1.527794E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -128,14 +128,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.357229E+00 -1.440049E+00 -2.635775E+00 -3.524426E-01 -4.982072E+00 -1.251449E+00 -7.228146E+00 -2.620353E+00 +5.517243E+00 +1.527794E+00 +2.832501E+00 +4.049252E-01 +5.117178E+00 +1.316972E+00 +7.333303E+00 +2.701677E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -152,14 +152,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.228146E+00 -2.620353E+00 -4.982072E+00 -1.251449E+00 -7.082265E+00 -2.520047E+00 -8.736529E+00 -3.831244E+00 +7.333303E+00 +2.701677E+00 +5.117178E+00 +1.316972E+00 +7.248464E+00 +2.641591E+00 +8.817788E+00 +3.905530E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -176,14 +176,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.736529E+00 -3.831244E+00 -7.082265E+00 -2.520047E+00 -8.474631E+00 -3.607043E+00 -9.346623E+00 -4.390819E+00 +8.817788E+00 +3.905530E+00 +7.248464E+00 +2.641591E+00 +8.646465E+00 +3.749847E+00 +9.460948E+00 +4.495388E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -200,14 +200,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.346623E+00 -4.390819E+00 -8.474631E+00 -3.607043E+00 -9.496684E+00 -4.522478E+00 -9.532822E+00 -4.559003E+00 +9.460948E+00 +4.495388E+00 +8.646465E+00 +3.749847E+00 +9.379341E+00 +4.415049E+00 +9.278640E+00 +4.320720E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -224,14 +224,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -9.532822E+00 -4.559003E+00 -9.496684E+00 -4.522478E+00 -9.404949E+00 -4.446260E+00 -8.550930E+00 -3.668401E+00 +9.278640E+00 +4.320720E+00 +9.379341E+00 +4.415049E+00 +9.465746E+00 +4.498591E+00 +8.656146E+00 +3.760545E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -248,14 +248,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -8.550930E+00 -3.668401E+00 -9.404949E+00 -4.446260E+00 -8.785273E+00 -3.874792E+00 -7.128863E+00 -2.554326E+00 +8.656146E+00 +3.760545E+00 +9.465746E+00 +4.498591E+00 +8.589782E+00 +3.700308E+00 +6.996002E+00 +2.456935E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -272,14 +272,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.128863E+00 -2.554326E+00 -8.785273E+00 -3.874792E+00 -7.408549E+00 -2.755885E+00 -5.094992E+00 -1.305737E+00 +6.996002E+00 +2.456935E+00 +8.589782E+00 +3.700308E+00 +7.352050E+00 +2.714808E+00 +5.105164E+00 +1.312559E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -296,14 +296,14 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -5.094992E+00 -1.305737E+00 -7.408549E+00 -2.755885E+00 -5.532149E+00 -1.537289E+00 -2.812344E+00 -3.997146E-01 +5.105164E+00 +1.312559E+00 +7.352050E+00 +2.714808E+00 +5.442756E+00 +1.486776E+00 +2.697305E+00 +3.675580E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -320,12 +320,12 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -2.812344E+00 -3.997146E-01 -5.532149E+00 -1.537289E+00 -3.063251E+00 -4.728672E-01 +2.697305E+00 +3.675580E-01 +5.442756E+00 +1.486776E+00 +3.017025E+00 +4.571443E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -345,144 +345,144 @@ tally 4: 0.000000E+00 0.000000E+00 tally 5: -1.496000E+01 -1.127586E+01 -2.280081E+00 -2.675609E-01 -2.774503E+01 -3.876011E+01 -3.908836E+00 -7.703029E-01 -3.848706E+01 -7.449836E+01 -5.299924E+00 -1.422782E+00 -4.723172E+01 -1.119459E+02 -6.450156E+00 -2.105590E+00 -5.095931E+01 -1.303132E+02 -7.050681E+00 -2.515092E+00 -5.133412E+01 -1.322694E+02 -6.853429E+00 -2.384127E+00 -4.754621E+01 -1.136848E+02 -6.370026E+00 -2.058896E+00 -3.886829E+01 -7.591042E+01 -5.266816E+00 -1.400495E+00 -2.910277E+01 -4.253981E+01 -4.090844E+00 -8.442500E-01 -1.556949E+01 -1.221526E+01 -2.266123E+00 -2.641551E-01 +1.563588E+01 +1.226217E+01 +2.209027E+00 +2.507131E-01 +2.870034E+01 +4.137550E+01 +3.726620E+00 +7.021948E-01 +3.977762E+01 +7.952285E+01 +5.304975E+00 +1.427333E+00 +4.779747E+01 +1.147456E+02 +6.528302E+00 +2.151715E+00 +5.105366E+01 +1.308037E+02 +6.986782E+00 +2.467487E+00 +5.123380E+01 +1.318264E+02 +6.845633E+00 +2.383542E+00 +4.729296E+01 +1.121433E+02 +6.252695E+00 +1.977351E+00 +3.898236E+01 +7.628315E+01 +5.461495E+00 +1.528579E+00 +2.864576E+01 +4.123538E+01 +3.857301E+00 +7.581323E-01 +1.467047E+01 +1.088031E+01 +2.277024E+00 +2.679801E-01 cmfd indices 1.000000E+01 1.000000E+00 1.000000E+00 1.000000E+00 k cmfd -1.161531E+00 -1.182724E+00 -1.169653E+00 -1.164722E+00 -1.164583E+00 -1.162952E+00 -1.167024E+00 -1.164509E+00 -1.165693E+00 -1.170623E+00 -1.166618E+00 -1.170805E+00 -1.170962E+00 -1.170964E+00 -1.168224E+00 -1.169864E+00 +1.169107E+00 +1.175079E+00 +1.173912E+00 +1.175368E+00 +1.174026E+00 +1.181745E+00 +1.182261E+00 +1.183559E+00 +1.178691E+00 +1.179222E+00 +1.179017E+00 +1.172979E+00 +1.175043E+00 +1.173458E+00 +1.174152E+00 +1.171451E+00 cmfd entropy -3.206619E+00 -3.205815E+00 -3.208678E+00 -3.210820E+00 -3.217023E+00 -3.215014E+00 -3.214592E+00 -3.215913E+00 -3.214998E+00 -3.213644E+00 -3.210755E+00 -3.210496E+00 -3.212488E+00 -3.211553E+00 -3.212999E+00 -3.214052E+00 +3.207640E+00 +3.210547E+00 +3.212218E+00 +3.209573E+00 +3.211619E+00 +3.212126E+00 +3.213163E+00 +3.214288E+00 +3.215737E+00 +3.213677E+00 +3.214925E+00 +3.215612E+00 +3.216708E+00 +3.221454E+00 +3.219048E+00 +3.218387E+00 cmfd balance -4.99833E-03 -5.64677E-03 -3.62795E-03 -3.91962E-03 -3.87172E-03 -2.48450E-03 -3.15554E-03 -2.49335E-03 -2.31973E-03 -2.19156E-03 -2.31352E-03 -2.03401E-03 -1.80242E-03 -1.65868E-03 -1.47543E-03 -1.49706E-03 +4.88208E-03 +4.75139E-03 +3.15783E-03 +3.67091E-03 +2.99797E-03 +2.91060E-03 +2.06576E-03 +1.83482E-03 +1.56292E-03 +1.58659E-03 +2.32986E-03 +1.47376E-03 +1.46673E-03 +1.22627E-03 +1.31963E-03 +1.26456E-03 cmfd dominance ratio -5.283E-01 -5.289E-01 -5.305E-01 -5.327E-01 -5.377E-01 -5.360E-01 -5.353E-01 -4.983E-01 -5.379E-01 -5.370E-01 -5.359E-01 -5.349E-01 -5.364E-01 -5.347E-01 -5.360E-01 -5.378E-01 +5.467E-01 +5.453E-01 +5.458E-01 +5.436E-01 +5.442E-01 +5.406E-01 +5.401E-01 +5.413E-01 +4.995E-01 +5.396E-01 +5.409E-01 +5.414E-01 +5.423E-01 +5.456E-01 +5.442E-01 +5.441E-01 cmfd openmc source comparison -1.291827E-02 -1.027137E-02 -8.738370E-03 -6.854409E-03 -4.188357E-03 -4.941359E-03 -5.139239E-03 -4.244784E-03 -4.240559E-03 -3.375424E-03 -3.716858E-03 -3.595700E-03 -3.626952E-03 -3.999302E-03 -2.431760E-03 -1.673200E-03 +9.587418E-03 +8.150978E-03 +6.677661E-03 +6.334727E-03 +5.153692E-03 +5.082964E-03 +4.633153E-03 +4.037383E-03 +3.528742E-03 +4.559089E-03 +3.517370E-03 +3.306117E-03 +2.913809E-03 +1.906045E-03 +1.932794E-03 +1.711341E-03 cmfd source -4.185460E-02 -7.636314E-02 -1.075536E-01 -1.307167E-01 -1.400879E-01 -1.459944E-01 -1.297413E-01 -1.084649E-01 -7.772031E-02 -4.150306E-02 +4.496492E-02 +7.869674E-02 +1.100280E-01 +1.354045E-01 +1.363339E-01 +1.380533E-01 +1.314512E-01 +1.077480E-01 +7.847306E-02 +3.884630E-02 diff --git a/tests/regression_tests/cmfd_restart/settings.xml b/tests/regression_tests/cmfd_restart/settings.xml deleted file mode 100644 index ba5495911f..0000000000 --- a/tests/regression_tests/cmfd_restart/settings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - eigenvalue - 20 - 10 - 1000 - - - - - box - -10 -1 -1 10 1 1 - - - - - - 10 1 1 - -10.0 -1.0 -1.0 - 10.0 1.0 1.0 - - 10 - - - - diff --git a/tests/regression_tests/cmfd_restart/tallies.xml b/tests/regression_tests/cmfd_restart/tallies.xml deleted file mode 100644 index c869711147..0000000000 --- a/tests/regression_tests/cmfd_restart/tallies.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - regular - -10 -1 -1 - 10 1 1 - 10 1 1 - - - - mesh - 1 - - - - 1 - flux - - - diff --git a/tests/regression_tests/complex_cell/geometry.xml b/tests/regression_tests/complex_cell/geometry.xml index a695396e01..638c7c9b83 100644 --- a/tests/regression_tests/complex_cell/geometry.xml +++ b/tests/regression_tests/complex_cell/geometry.xml @@ -19,7 +19,7 @@ - + diff --git a/tests/regression_tests/complex_cell/results_true.dat b/tests/regression_tests/complex_cell/results_true.dat index 927a7a519a..ddedb07ff0 100644 --- a/tests/regression_tests/complex_cell/results_true.dat +++ b/tests/regression_tests/complex_cell/results_true.dat @@ -1,11 +1,11 @@ k-combined: -2.490321E-01 1.083676E-03 +2.603220E-01 1.429366E-03 tally 1: -2.617769E+00 -1.371478E+00 -2.716496E+00 -1.476169E+00 -1.005297E+00 -2.026100E-01 -1.075286E-01 -2.316593E-03 +2.624819E+00 +1.378200E+00 +2.730035E+00 +1.492361E+00 +1.013707E+00 +2.055807E-01 +1.123257E-01 +2.530233E-03 diff --git a/tests/regression_tests/confidence_intervals/results_true.dat b/tests/regression_tests/confidence_intervals/results_true.dat index cb6c6f00a2..8ca2566249 100644 --- a/tests/regression_tests/confidence_intervals/results_true.dat +++ b/tests/regression_tests/confidence_intervals/results_true.dat @@ -1,5 +1,5 @@ k-combined: -2.679617E-01 1.158917E-02 +2.850178E-01 9.646334E-03 tally 1: -6.693704E+01 -5.643483E+02 +6.234169E+01 +4.884167E+02 diff --git a/tests/regression_tests/conftest.py b/tests/regression_tests/conftest.py index b4a7644762..1cdf414e76 100644 --- a/tests/regression_tests/conftest.py +++ b/tests/regression_tests/conftest.py @@ -1,12 +1,12 @@ import numpy as np import openmc -from pkg_resources import parse_version +from packaging.version import parse import pytest @pytest.fixture(scope='module', autouse=True) def numpy_version_requirement(): - assert parse_version(np.__version__) >= parse_version("1.14"), \ + assert parse(np.__version__) >= parse("1.14"), \ "Regression tests require NumPy 1.14 or greater" diff --git a/tests/regression_tests/cpp_driver/driver.cpp b/tests/regression_tests/cpp_driver/driver.cpp index 48ed7f3171..a99c97b64e 100644 --- a/tests/regression_tests/cpp_driver/driver.cpp +++ b/tests/regression_tests/cpp_driver/driver.cpp @@ -15,14 +15,16 @@ using namespace openmc; -int main(int argc, char** argv) { +int main(int argc, char** argv) +{ #ifdef OPENMC_MPI MPI_Comm world {MPI_COMM_WORLD}; int err = openmc_init(argc, argv, &world); #else int err = openmc_init(argc, argv, nullptr); #endif - if (err) fatal_error(openmc_err_msg); + if (err) + fatal_error(openmc_err_msg); // create a new cell filter auto cell_filter = Filter::create(); @@ -30,7 +32,7 @@ int main(int argc, char** argv) { // add all cells to the cell filter std::vector cell_indices; for (auto& entry : openmc::model::cell_map) { - cell_indices.push_back(entry.second); + cell_indices.push_back(entry.second); } // enable distribcells offsets for all cells prepare_distribcell(&cell_indices); @@ -39,7 +41,6 @@ int main(int argc, char** argv) { std::sort(cell_indices.begin(), cell_indices.end()); cell_filter->set_cells(cell_indices); - // create a new tally auto tally = Tally::create(); std::vector filters = {cell_filter}; @@ -60,14 +61,19 @@ int main(int argc, char** argv) { } } - // set a higher temperature for only one of the lattice cells (ID is 4 in the model) + // set a higher temperature for only one of the lattice cells (ID is 4 in the + // model) model::cells[model::cell_map[4]]->set_temperature(400.0, 3, true); + // set the density of another lattice cell to 2 + model::cells[model::cell_map[4]]->set_density(2.0, 2, true); + // the summary file will be used to check that // temperatures were set correctly so clear // error output can be provided #ifdef OPENMC_MPI - if (openmc::mpi::master) openmc::write_summary(); + if (openmc::mpi::master) + openmc::write_summary(); #else openmc::write_summary(); #endif diff --git a/tests/regression_tests/cpp_driver/inputs_true.dat b/tests/regression_tests/cpp_driver/inputs_true.dat index faa3fa9b1a..fd450428a9 100644 --- a/tests/regression_tests/cpp_driver/inputs_true.dat +++ b/tests/regression_tests/cpp_driver/inputs_true.dat @@ -1,45 +1,45 @@ - - - - - - - - 4.0 4.0 - 2 2 - -4.0 -4.0 - + + + + + + + + + + + + + + + + + + + + + + + + 4.0 4.0 + 2 2 + -4.0 -4.0 + 2 2 2 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 1 - + + + + + + + + + + eigenvalue + 100 + 10 + 1 + + diff --git a/tests/regression_tests/cpp_driver/results_true.dat b/tests/regression_tests/cpp_driver/results_true.dat index e44b45054a..09f188db6b 100644 --- a/tests/regression_tests/cpp_driver/results_true.dat +++ b/tests/regression_tests/cpp_driver/results_true.dat @@ -1,13 +1,13 @@ k-combined: -1.902610E+00 1.901530E-02 +1.874924E+00 2.180236E-02 tally 1: -9.580351E+01 -1.031580E+03 -2.745984E+01 -8.430494E+01 -9.422158E+01 -9.919885E+02 -2.174849E+02 -5.292948E+03 -2.174849E+02 -5.292948E+03 +9.484447E+01 +1.002269E+03 +2.746252E+01 +8.406603E+01 +9.833099E+01 +1.076376E+03 +2.206380E+02 +5.417609E+03 +2.206380E+02 +5.417609E+03 diff --git a/tests/regression_tests/cpp_driver/test.py b/tests/regression_tests/cpp_driver/test.py index 0726e4c6ec..b80e82ee0e 100644 --- a/tests/regression_tests/cpp_driver/test.py +++ b/tests/regression_tests/cpp_driver/test.py @@ -20,7 +20,7 @@ def cpp_driver(request): openmc_dir = Path(str(request.config.rootdir)) / 'build' with open('CMakeLists.txt', 'w') as f: f.write(textwrap.dedent(""" - cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_cpp_driver CXX) add_executable(cpp_driver driver.cpp) find_package(OpenMC REQUIRED HINTS {}) @@ -33,12 +33,14 @@ def cpp_driver(request): os.chdir(str(local_builddir)) if config['mpi']: - os.environ['CXX'] = 'mpicxx' + mpi_arg = "On" + else: + mpi_arg = "Off" try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) + subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) subprocess.run(['make'], check=True) os.chdir(os.path.pardir) @@ -46,8 +48,8 @@ def cpp_driver(request): finally: # Remove local build directory when test is complete - shutil.rmtree('build') - os.remove('CMakeLists.txt') + shutil.rmtree(request.node.path.parent / 'build') + os.remove(request.node.path.parent / 'CMakeLists.txt') @pytest.fixture @@ -90,10 +92,9 @@ def model(): lattice.pitch = (4.0, 4.0) lattice.lower_left = (-4.0, -4.0) lattice.universes = [[extra_univ, extra_univ], [extra_univ, extra_univ]] - lattice_region = openmc.model.rectangular_prism(8.0, - 8.0, - boundary_type='reflective') - lattice_cell = openmc.Cell(fill=lattice, region=lattice_region) + lattice_prism = openmc.model.RectangularPrism( + 8.0, 8.0, boundary_type='reflective') + lattice_cell = openmc.Cell(fill=lattice, region=-lattice_prism) model.geometry = openmc.Geometry([lattice_cell]) diff --git a/tests/regression_tests/create_fission_neutrons/inputs_true.dat b/tests/regression_tests/create_fission_neutrons/inputs_true.dat index b0ca896476..47e8c38307 100644 --- a/tests/regression_tests/create_fission_neutrons/inputs_true.dat +++ b/tests/regression_tests/create_fission_neutrons/inputs_true.dat @@ -1,37 +1,36 @@ - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - -1 -1 -1 1 1 1 - - - - false - - - - - flux - - + + + + + + + + + + + + + + + + + + + fixed source + 100 + 10 + + + -1 -1 -1 1 1 1 + + + + false + + + + flux + + + diff --git a/tests/regression_tests/create_fission_neutrons/test.py b/tests/regression_tests/create_fission_neutrons/test.py index f1c1d072cb..0ca4a48b29 100755 --- a/tests/regression_tests/create_fission_neutrons/test.py +++ b/tests/regression_tests/create_fission_neutrons/test.py @@ -4,14 +4,14 @@ from tests.testing_harness import PyAPITestHarness class CreateFissionNeutronsTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Material is composed of H-1 and U-235 mat = openmc.Material(material_id=1, name='mat') mat.set_density('atom/b-cm', 0.069335) mat.add_nuclide('H1', 40.0) mat.add_nuclide('U235', 1.0) - materials_file = openmc.Materials([mat]) - materials_file.export_to_xml() + self._model.materials = openmc.Materials([mat]) # Cell is box with reflective boundary x1 = openmc.XPlane(surface_id=1, x0=-1) @@ -27,8 +27,7 @@ class CreateFissionNeutronsTestHarness(PyAPITestHarness): box.fill = mat root = openmc.Universe(universe_id=0, name='root universe') root.add_cell(box) - geometry = openmc.Geometry(root) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root) # Set the running parameters settings_file = openmc.Settings() @@ -39,16 +38,16 @@ class CreateFissionNeutronsTestHarness(PyAPITestHarness): bounds = [-1, -1, -1, 1, 1, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) watt_dist = openmc.stats.Watt() - settings_file.source = openmc.source.Source(space=uniform_dist, - energy=watt_dist) - settings_file.export_to_xml() + settings_file.source = openmc.IndependentSource(space=uniform_dist, + energy=watt_dist) + self._model.settings = settings_file # Create tallies tallies = openmc.Tallies() tally = openmc.Tally(1) tally.scores = ['flux'] tallies.append(tally) - tallies.export_to_xml() + self._model.tallies = tallies def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -66,5 +65,6 @@ class CreateFissionNeutronsTestHarness(PyAPITestHarness): def test_create_fission_neutrons(): - harness = CreateFissionNeutronsTestHarness('statepoint.10.h5') + harness = CreateFissionNeutronsTestHarness('statepoint.10.h5', + model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/dagmc/external/__init__.py b/tests/regression_tests/dagmc/external/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/dagmc/external/dagmc.h5m b/tests/regression_tests/dagmc/external/dagmc.h5m new file mode 120000 index 0000000000..92c41719c5 --- /dev/null +++ b/tests/regression_tests/dagmc/external/dagmc.h5m @@ -0,0 +1 @@ +../legacy/dagmc.h5m \ No newline at end of file diff --git a/tests/regression_tests/dagmc/external/inputs_true.dat b/tests/regression_tests/dagmc/external/inputs_true.dat new file mode 100644 index 0000000000..8a6c6fe74a --- /dev/null +++ b/tests/regression_tests/dagmc/external/inputs_true.dat @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + 293 + + + + 1 + + + 1 + total + + + diff --git a/tests/regression_tests/dagmc/external/main.cpp b/tests/regression_tests/dagmc/external/main.cpp new file mode 100644 index 0000000000..e78ab03fa2 --- /dev/null +++ b/tests/regression_tests/dagmc/external/main.cpp @@ -0,0 +1,121 @@ +#include "openmc/capi.h" +#include "openmc/cross_sections.h" +#include "openmc/dagmc.h" +#include "openmc/error.h" +#include "openmc/geometry.h" +#include "openmc/geometry_aux.h" +#include "openmc/material.h" +#include "openmc/message_passing.h" +#include "openmc/nuclide.h" +#include + +int main(int argc, char* argv[]) +{ + using namespace openmc; + int openmc_err; + + // Initialise OpenMC +#ifdef OPENMC_MPI + MPI_Comm world = MPI_COMM_WORLD; + openmc_err = openmc_init(argc, argv, &world); +#else + openmc_err = openmc_init(argc, argv, nullptr); +#endif + if (openmc_err == -1) { + // This happens for the -h and -v flags + return EXIT_SUCCESS; + } else if (openmc_err) { + fatal_error(openmc_err_msg); + } + + // Create DAGMC ptr + std::string filename = "dagmc.h5m"; + std::shared_ptr dag_ptr = std::make_shared(); + moab::ErrorCode rval = dag_ptr->load_file(filename.c_str()); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to load file"); + } + + // Initialize acceleration data structures + rval = dag_ptr->init_OBBTree(); + if (rval != moab::MB_SUCCESS) { + fatal_error("Failed to initialize OBB tree"); + } + + // Get rid of existing geometry + std::unordered_map nuclide_map_copy = + openmc::data::nuclide_map; + openmc::data::nuclides.clear(); + openmc::data::nuclide_map = nuclide_map_copy; + openmc::model::surfaces.clear(); + openmc::model::surface_map.clear(); + openmc::model::cells.clear(); + openmc::model::cell_map.clear(); + openmc::model::universes.clear(); + openmc::model::universe_map.clear(); + + // Update materials (emulate an external program) + for (auto& mat_ptr : openmc::model::materials) { + mat_ptr->set_temperature(300); + } + + // Create new DAGMC universe + openmc::model::universes.push_back( + std::make_unique(dag_ptr)); + model::universe_map[model::universes.back()->id_] = + model::universes.size() - 1; + + // Add cells to universes + openmc::populate_universes(); + + // Make sure implicit complement appears last + auto dag_univ = dynamic_cast(model::universes.back().get()); + int n = dag_univ->cells_.size(); + for (int i = 0; i < n - 1; ++i) { + if (dag_univ->cells_[i] == dag_univ->implicit_complement_idx()) { + fatal_error("Implicit complement cell should appear last in vector of " + "cells for DAGMC universe."); + } + } + if (dag_univ->cells_.back() != dag_univ->implicit_complement_idx()) { + fatal_error( + "Last cell in DAGMC universe is not an implicit complement cell."); + } + + // Set root universe + openmc::model::root_universe = openmc::find_root_universe(); + openmc::check_dagmc_root_univ(); + + // Final geometry setup and assign temperatures + openmc::finalize_geometry(); + + // Finalize cross sections having assigned temperatures + openmc::finalize_cross_sections(); + + // Check that we correctly assigned cell temperatures with non-void fill + for (auto& cell_ptr : openmc::model::cells) { + if (cell_ptr->material_.front() != openmc::C_NONE && + cell_ptr->temperature() != 300) { + fatal_error("Failed to set cell temperature"); + } + } + + // Finalize cell densities + openmc::finalize_cell_densities(); + + // Run OpenMC + openmc_err = openmc_run(); + if (openmc_err) + fatal_error(openmc_err_msg); + + // Deallocate memory + openmc_err = openmc_finalize(); + if (openmc_err) + fatal_error(openmc_err_msg); + +#ifdef OPENMC_MPI + MPI_Finalize(); +#endif + + return EXIT_SUCCESS; +} diff --git a/tests/regression_tests/dagmc/external/results_true.dat b/tests/regression_tests/dagmc/external/results_true.dat new file mode 100644 index 0000000000..9a6b481b7f --- /dev/null +++ b/tests/regression_tests/dagmc/external/results_true.dat @@ -0,0 +1,5 @@ +k-combined: +1.083415E+00 5.991738E-02 +tally 1: +8.862860E+00 +1.602117E+01 diff --git a/tests/regression_tests/dagmc/external/test.py b/tests/regression_tests/dagmc/external/test.py new file mode 100644 index 0000000000..3580bfa11e --- /dev/null +++ b/tests/regression_tests/dagmc/external/test.py @@ -0,0 +1,128 @@ +from pathlib import Path +import os +import shutil +import subprocess +import textwrap + +import openmc +import openmc.lib +import pytest + +from tests.regression_tests import config +from tests.testing_harness import PyAPITestHarness + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), + reason="DAGMC is not enabled.") + +# Test that an external DAGMC instance can be passed in through the C API + +@pytest.fixture +def cpp_driver(request): + """Compile the external source""" + + # Get build directory and write CMakeLists.txt file + openmc_dir = Path(str(request.config.rootdir)) / 'build' + with open('CMakeLists.txt', 'w') as f: + f.write(textwrap.dedent(""" + cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + project(openmc_cpp_driver CXX) + add_executable(main main.cpp) + find_package(OpenMC REQUIRED HINTS {}) + target_link_libraries(main OpenMC::libopenmc) + target_compile_features(main PUBLIC cxx_std_14) + set(CMAKE_CXX_FLAGS "-pedantic-errors") + add_compile_definitions(OPENMC_DAGMC_ENABLED=1) + """.format(openmc_dir))) + + # Create temporary build directory and change to there + local_builddir = Path('build') + local_builddir.mkdir(exist_ok=True) + os.chdir(local_builddir) + + mpi_arg = "On" if config['mpi'] else "Off" + + try: + # Run cmake/make to build the shared libary + subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) + subprocess.run(['make'], check=True) + os.chdir(os.path.pardir) + + yield "./build/main" + + finally: + # Remove local build directory when test is complete + shutil.rmtree('build') + os.remove('CMakeLists.txt') + +@pytest.fixture +def model(): + model = openmc.model.Model() + + # Settings + model.settings.batches = 5 + model.settings.inactive = 0 + model.settings.particles = 100 + source_box = openmc.stats.Box([-4, -4, -4], + [ 4, 4, 4]) + source = openmc.IndependentSource(space=source_box) + model.settings.source = source + model.settings.temperature['default'] = 293 + + # Geometry + dag_univ = openmc.DAGMCUniverse("dagmc.h5m") + model.geometry = openmc.Geometry(dag_univ) + + # Tallies + tally = openmc.Tally() + tally.scores = ['total'] + tally.filters = [openmc.CellFilter(1)] + model.tallies = [tally] + + # Materials + u235 = openmc.Material(name="no-void fuel") + u235.add_nuclide('U235', 1.0, 'ao') + u235.set_density('g/cc', 11) + u235.id = 40 + water = openmc.Material(name="water") + water.add_nuclide('H1', 2.0, 'ao') + water.add_nuclide('O16', 1.0, 'ao') + water.set_density('g/cc', 1.0) + water.add_s_alpha_beta('c_H_in_H2O') + water.id = 41 + mats = openmc.Materials([u235, water]) + model.materials = mats + + return model + +class ExternalDAGMCTest(PyAPITestHarness): + def __init__(self, executable, statepoint_name, model): + super().__init__(statepoint_name, model) + self.executable = executable + + def _run_openmc(self): + """ + Just test if results generated with the external C++ API are + self-consistent with the internal python API. + We generate the "truth" results with the python API but + the main test produces the results file by running the + executable compiled from main.cpp. This future-proofs + the test - we only care that the two routes are equivalent. + """ + if config['update']: + # Generate the results file with internal python API + openmc.run(openmc_exec=config['exe'], event_based=config['event']) + elif config['mpi']: + mpi_args = [config['mpiexec'], '-n', config['mpi_np']] + # Run main cpp executable with MPI + openmc.run(openmc_exec=self.executable, + mpi_args=mpi_args, + event_based=config['event']) + else: + # Run main cpp executable + openmc.run(openmc_exec=self.executable, + event_based=config['event']) + +def test_external_dagmc(cpp_driver, model): + harness = ExternalDAGMCTest(cpp_driver, 'statepoint.5.h5', model) + harness.main() diff --git a/tests/regression_tests/dagmc/legacy/inputs_true.dat b/tests/regression_tests/dagmc/legacy/inputs_true.dat index 2f56410466..ad2f8e54d8 100644 --- a/tests/regression_tests/dagmc/legacy/inputs_true.dat +++ b/tests/regression_tests/dagmc/legacy/inputs_true.dat @@ -1,39 +1,38 @@ - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 5 - 0 - - - -4 -4 -4 4 4 4 - - - - - - - 1 - - - 1 - total - - + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + + + + 1 + + + 1 + total + + + diff --git a/tests/regression_tests/dagmc/legacy/results_true.dat b/tests/regression_tests/dagmc/legacy/results_true.dat index e5127c8272..9a6b481b7f 100644 --- a/tests/regression_tests/dagmc/legacy/results_true.dat +++ b/tests/regression_tests/dagmc/legacy/results_true.dat @@ -1,5 +1,5 @@ k-combined: -8.426936E-01 5.715847E-02 +1.083415E+00 5.991738E-02 tally 1: -8.093843E+00 -1.328829E+01 +8.862860E+00 +1.602117E+01 diff --git a/tests/regression_tests/dagmc/legacy/test.py b/tests/regression_tests/dagmc/legacy/test.py index e0fccb9344..b6b1e376b0 100644 --- a/tests/regression_tests/dagmc/legacy/test.py +++ b/tests/regression_tests/dagmc/legacy/test.py @@ -1,8 +1,13 @@ +from pathlib import Path + import openmc import openmc.lib +import h5py +import numpy as np import pytest -from tests.testing_harness import PyAPITestHarness + +from tests.testing_harness import PyAPITestHarness, config pytestmark = pytest.mark.skipif( not openmc.lib._dagmc_enabled(), @@ -10,8 +15,9 @@ pytestmark = pytest.mark.skipif( @pytest.fixture def model(): + openmc.reset_auto_ids() - model = openmc.model.Model() + model = openmc.Model() # settings model.settings.batches = 5 @@ -20,14 +26,12 @@ def model(): source_box = openmc.stats.Box([-4, -4, -4], [ 4, 4, 4]) - source = openmc.Source(space=source_box) + source = openmc.IndependentSource(space=source_box) model.settings.source = source - model.settings.dagmc = True - # geometry - dag_univ = openmc.DAGMCUniverse("dagmc.h5m") + dag_univ = openmc.DAGMCUniverse(Path("dagmc.h5m")) model.geometry = openmc.Geometry(dag_univ) # tally @@ -54,6 +58,50 @@ def model(): return model + +def test_missing_material_id(model): + # remove the last material, which is identified by ID in the DAGMC file + model.materials = model.materials[:-1] + with pytest.raises(RuntimeError) as exec_info: + model.run() + exp_error_msg = "Material with name/ID '41' not found for volume (cell) 3" + assert exp_error_msg in str(exec_info.value) + + +def test_missing_material_name(model): + # remove the first material, which is identified by name in the DAGMC file + model.materials = model.materials[1:] + with pytest.raises(RuntimeError) as exec_info: + model.run() + exp_error_msg = "Material with name/ID 'no-void fuel' not found for volume (cell) 1" + assert exp_error_msg in str(exec_info.value) + + +def test_surf_source(model): + # create a surface source read on this model to ensure + # particles are being generated correctly + n = 100 + model.settings.surf_source_write = {'surface_ids': [1], 'max_particles': n} + + # If running in MPI mode, setup proper keyword arguments for run() + kwargs = {'openmc_exec': config['exe']} + if config['mpi']: + kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + model.run(**kwargs) + + with h5py.File('surface_source.h5') as fh: + assert fh.attrs['filetype'] == b'source' + arr = fh['source_bank'][...] + expected_size = n * int(config['mpi_np']) if config['mpi'] else n + assert arr.size == expected_size + + # check that all particles are on surface 1 (radius = 7) + xs = arr[:]['r']['x'] + ys = arr[:]['r']['y'] + rad = np.sqrt(xs**2 + ys**2) + assert np.allclose(rad, 7.0) + + def test_dagmc(model): harness = PyAPITestHarness('statepoint.5.h5', model) - harness.main() + harness.main() \ No newline at end of file diff --git a/tests/regression_tests/dagmc/refl/inputs_true.dat b/tests/regression_tests/dagmc/refl/inputs_true.dat index 938916ece1..58cb9e66f2 100644 --- a/tests/regression_tests/dagmc/refl/inputs_true.dat +++ b/tests/regression_tests/dagmc/refl/inputs_true.dat @@ -1,29 +1,28 @@ - - - - - - - - - eigenvalue - 100 - 5 - 0 - - - -4 -4 -4 4 4 4 - - - - - - - 2 - - - 1 - total - - + + + + + + + + eigenvalue + 100 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + + + + 2 + + + 1 + total + + + diff --git a/tests/regression_tests/dagmc/refl/results_true.dat b/tests/regression_tests/dagmc/refl/results_true.dat index bd1169835d..b49a4a7a49 100644 --- a/tests/regression_tests/dagmc/refl/results_true.dat +++ b/tests/regression_tests/dagmc/refl/results_true.dat @@ -1,5 +1,5 @@ k-combined: -2.130286E+00 2.412252E-02 +2.047107E+00 8.605767E-02 tally 1: -1.177815E+01 -2.806871E+01 +1.145034E+01 +2.636875E+01 diff --git a/tests/regression_tests/dagmc/refl/test.py b/tests/regression_tests/dagmc/refl/test.py index 3fe41345cf..a13acc0256 100644 --- a/tests/regression_tests/dagmc/refl/test.py +++ b/tests/regression_tests/dagmc/refl/test.py @@ -6,40 +6,33 @@ import pytest from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._uwuw_enabled(), + reason="UWUW is not enabled.") class UWUWTest(PyAPITestHarness): - - def _build_inputs(self): - model = openmc.model.Model() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # settings - model.settings.batches = 5 - model.settings.inactive = 0 - model.settings.particles = 100 + self._model.settings.batches = 5 + self._model.settings.inactive = 0 + self._model.settings.particles = 100 - source = openmc.Source(space=Box([-4, -4, -4], - [ 4, 4, 4])) - model.settings.source = source - - model.settings.dagmc = True - - model.settings.export_to_xml() + source = openmc.IndependentSource(space=Box([-4, -4, -4], + [ 4, 4, 4])) + self._model.settings.source = source # geometry dag_univ = openmc.DAGMCUniverse("dagmc.h5m", auto_geom_ids=True) - model.geometry = openmc.Geometry(dag_univ) + self._model.geometry = openmc.Geometry(dag_univ) # tally tally = openmc.Tally() tally.scores = ['total'] tally.filters = [openmc.CellFilter(2)] - model.tallies = [tally] + self._model.tallies = [tally] - model.tallies.export_to_xml() - model.export_to_xml() def test_refl(): - harness = UWUWTest('statepoint.5.h5') + harness = UWUWTest('statepoint.5.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/dagmc/universes/inputs_true.dat b/tests/regression_tests/dagmc/universes/inputs_true.dat index 4443f9a35d..be1a17383c 100644 --- a/tests/regression_tests/dagmc/universes/inputs_true.dat +++ b/tests/regression_tests/dagmc/universes/inputs_true.dat @@ -1,55 +1,72 @@ - - - - - 24.0 24.0 - 2 2 - -24.0 -24.0 - -9 9 -9 9 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 2 - -

false - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 24.0 24.0 + 2 2 + -24.0 -24.0 + +1 1 +1 1 + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -10.0 -10.0 -24.0 10.0 10.0 24.0 + + + true + + + + false + + + + + 4 0 4 1 4 2 4 3 4 4 + + + 1 + scatter + + + diff --git a/tests/regression_tests/dagmc/universes/results_true.dat b/tests/regression_tests/dagmc/universes/results_true.dat index 0dc1789d45..aacb7d1ab5 100644 --- a/tests/regression_tests/dagmc/universes/results_true.dat +++ b/tests/regression_tests/dagmc/universes/results_true.dat @@ -1,2 +1,13 @@ k-combined: -9.436168E-01 2.905559E-02 +9.719586E-01 3.630894E-02 +tally 1: +4.463288E+00 +4.136647E+00 +4.769631E+00 +4.622840E+00 +4.315273E+00 +3.871129E+00 +4.091804E+00 +3.582192E+00 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/dagmc/universes/test.py b/tests/regression_tests/dagmc/universes/test.py index 01963986c5..d68c6b11cf 100644 --- a/tests/regression_tests/dagmc/universes/test.py +++ b/tests/regression_tests/dagmc/universes/test.py @@ -11,70 +11,87 @@ pytestmark = pytest.mark.skipif( reason="DAGMC CAD geometry is not enabled.") -class DAGMCUniverseTest(PyAPITestHarness): +@pytest.fixture +def pin_lattice_model(): + ### MATERIALS ### + fuel = openmc.Material(name='no-void fuel') + fuel.set_density('g/cc', 10.29769) + fuel.add_nuclide('U234', 0.93120485) + fuel.add_nuclide('U235', 0.00055815) + fuel.add_nuclide('U238', 0.022408) + fuel.add_nuclide('O16', 0.045829) - def _build_inputs(self): - model = openmc.model.Model() + cladding = openmc.Material(name='clad') + cladding.set_density('g/cc', 6.55) + cladding.add_nuclide('Zr90', 0.021827) + cladding.add_nuclide('Zr91', 0.00476) + cladding.add_nuclide('Zr92', 0.0072758) + cladding.add_nuclide('Zr94', 0.0073734) + cladding.add_nuclide('Zr96', 0.0011879) - ### MATERIALS ### - fuel = openmc.Material(name='no-void fuel') - fuel.set_density('g/cc', 10.29769) - fuel.add_nuclide('U234', 0.93120485) - fuel.add_nuclide('U235', 0.00055815) - fuel.add_nuclide('U238', 0.022408) - fuel.add_nuclide('O16', 0.045829) + water = openmc.Material(name='water') + water.set_density('g/cc', 0.740582) + water.add_nuclide('H1', 0.049457) + water.add_nuclide('O16', 0.024672) + water.add_nuclide('B10', 8.0042e-06) + water.add_nuclide('B11', 3.2218e-05) + water.add_s_alpha_beta('c_H_in_H2O') - cladding = openmc.Material(name='clad') - cladding.set_density('g/cc', 6.55) - cladding.add_nuclide('Zr90', 0.021827) - cladding.add_nuclide('Zr91', 0.00476) - cladding.add_nuclide('Zr92', 0.0072758) - cladding.add_nuclide('Zr94', 0.0073734) - cladding.add_nuclide('Zr96', 0.0011879) + model = openmc.Model() + model.materials = openmc.Materials([fuel, cladding, water]) - water = openmc.Material(name='water') - water.set_density('g/cc', 0.740582) - water.add_nuclide('H1', 0.049457) - water.add_nuclide('O16', 0.024672) - water.add_nuclide('B10', 8.0042e-06) - water.add_nuclide('B11', 3.2218e-05) - water.add_s_alpha_beta('c_H_in_H2O') + ### GEOMETRY ### + # create the DAGMC universe + pincell_univ = openmc.DAGMCUniverse(filename='dagmc.h5m', auto_geom_ids=True) - model.materials = openmc.Materials([fuel, cladding, water]) + # creates another DAGMC universe, this time with within a bounded cell + bound_pincell_universe = openmc.DAGMCUniverse(filename='dagmc.h5m').bounded_universe() + # uses the bound_dag_cell as the root argument to test the type checks in openmc.Geometry + bound_pincell_geometry = openmc.Geometry(root=bound_pincell_universe) + # assigns the bound_dag_geometry to the model to test the type checks in model.Geometry setter + model.geometry = bound_pincell_geometry - ### GEOMETRY ### - # create the DAGMC universe - pincell_univ = openmc.DAGMCUniverse(filename='dagmc.h5m', auto_geom_ids=True) + # create a 2 x 2 lattice using the DAGMC pincell + pitch = np.asarray((24.0, 24.0)) + lattice = openmc.RectLattice() + lattice.pitch = pitch + lattice.universes = [[pincell_univ] * 2] * 2 + lattice.lower_left = -pitch - # create a 2 x 2 lattice using the DAGMC pincell - pitch = np.asarray((24.0, 24.0)) - lattice = openmc.RectLattice() - lattice.pitch = pitch - lattice.universes = [[pincell_univ] * 2] * 2 - lattice.lower_left = -pitch + left = openmc.XPlane(x0=-pitch[0], name='left', boundary_type='reflective') + right = openmc.XPlane(x0=pitch[0], name='right', boundary_type='reflective') + front = openmc.YPlane(y0=-pitch[1], name='front', boundary_type='reflective') + back = openmc.YPlane(y0=pitch[1], name='back', boundary_type='reflective') + # clip the DAGMC geometry at +/- 10 cm w/ CSG planes + bottom = openmc.ZPlane(z0=-10.0, name='bottom', boundary_type='reflective') + top = openmc.ZPlane(z0=10.0, name='top', boundary_type='reflective') - left = openmc.XPlane(x0=-pitch[0], name='left', boundary_type='reflective') - right = openmc.XPlane(x0=pitch[0], name='right', boundary_type='reflective') - front = openmc.YPlane(y0=-pitch[1], name='front', boundary_type='reflective') - back = openmc.YPlane(y0=pitch[1], name='back', boundary_type='reflective') - # clip the DAGMC geometry at +/- 10 cm w/ CSG planes - bottom = openmc.ZPlane(z0=-10.0, name='bottom', boundary_type='reflective') - top = openmc.ZPlane(z0=10.0, name='top', boundary_type='reflective') + bounding_region = +left & -right & +front & -back & +bottom & -top + bounding_cell = openmc.Cell(fill=lattice, region=bounding_region) - bounding_region = +left & -right & +front & -back & +bottom & -top - bounding_cell = openmc.Cell(fill=lattice, region=bounding_region) + model.geometry = openmc.Geometry([bounding_cell]) - model.geometry = openmc.Geometry([bounding_cell]) + # add a cell instance tally + tally = openmc.Tally(name='cell instance tally') + # using scattering + cell_instance_filter = openmc.CellInstanceFilter(((4, 0), (4, 1), (4, 2), (4, 3), (4, 4))) + tally.filters = [cell_instance_filter] + tally.scores = ['scatter'] + model.tallies = [tally] - # settings - model.settings.particles = 100 - model.settings.batches = 10 - model.settings.inactive = 2 - model.settings.output = {'summary' : False} + # settings + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.output = {'summary' : False} + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box((-10., -10., -24.), (10., 10., 24.)), + constraints={'fissionable': True}, + ) - model.export_to_xml() + return model -def test_univ(): - harness = DAGMCUniverseTest('statepoint.10.h5') +def test_univ(pin_lattice_model): + harness = PyAPITestHarness('statepoint.10.h5', model=pin_lattice_model) harness.main() diff --git a/tests/regression_tests/dagmc/uwuw/inputs_true.dat b/tests/regression_tests/dagmc/uwuw/inputs_true.dat index 7d533c5db2..01082cb238 100644 --- a/tests/regression_tests/dagmc/uwuw/inputs_true.dat +++ b/tests/regression_tests/dagmc/uwuw/inputs_true.dat @@ -1,29 +1,28 @@ - - - - - - - - - eigenvalue - 100 - 5 - 0 - - - -4 -4 -4 4 4 4 - - - - - - - 1 - - - 1 - total - - + + + + + + + + eigenvalue + 100 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + + + + 1 + + + 1 + total + + + diff --git a/tests/regression_tests/dagmc/uwuw/test.py b/tests/regression_tests/dagmc/uwuw/test.py index 5c9777d481..bea464cfab 100644 --- a/tests/regression_tests/dagmc/uwuw/test.py +++ b/tests/regression_tests/dagmc/uwuw/test.py @@ -6,39 +6,33 @@ import pytest from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._dagmc_enabled(), - reason="DAGMC CAD geometry is not enabled.") + not openmc.lib._uwuw_enabled(), + reason="UWUW is not enabled.") class UWUWTest(PyAPITestHarness): - - def _build_inputs(self): - model = openmc.model.Model() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # settings - model.settings.batches = 5 - model.settings.inactive = 0 - model.settings.particles = 100 + self._model.settings.batches = 5 + self._model.settings.inactive = 0 + self._model.settings.particles = 100 - source = openmc.Source(space=Box([-4, -4, -4], - [ 4, 4, 4])) - model.settings.source = source - - model.settings.dagmc = True - - model.settings.export_to_xml() + source = openmc.IndependentSource(space=Box([-4, -4, -4], + [ 4, 4, 4])) + self._model.settings.source = source # geometry dag_univ = openmc.DAGMCUniverse("dagmc.h5m") - model.geometry = openmc.Geometry(root=dag_univ) + self._model.geometry = openmc.Geometry(root=dag_univ) # tally tally = openmc.Tally() tally.scores = ['total'] tally.filters = [openmc.CellFilter(1)] - model.tallies = [tally] + self._model.tallies = [tally] - model.export_to_xml() def test_uwuw(): - harness = UWUWTest('statepoint.5.h5') + harness = UWUWTest('statepoint.5.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/density/results_true.dat b/tests/regression_tests/density/results_true.dat index 17d0730dfd..42dc0c19f3 100644 --- a/tests/regression_tests/density/results_true.dat +++ b/tests/regression_tests/density/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.097336E+00 2.305434E-02 +1.082191E+00 3.064029E-02 diff --git a/tests/regression_tests/deplete/last_step_reference_materials.xml b/tests/regression_tests/deplete/last_step_reference_materials.xml deleted file mode 100644 index fc9fd0cf5b..0000000000 --- a/tests/regression_tests/deplete/last_step_reference_materials.xml +++ /dev/null @@ -1,1057 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/regression_tests/deplete/test_reference.h5 b/tests/regression_tests/deplete/test_reference.h5 deleted file mode 100644 index d478d628ef..0000000000 Binary files a/tests/regression_tests/deplete/test_reference.h5 and /dev/null differ diff --git a/tests/regression_tests/deplete_decay_only/__init__.py b/tests/regression_tests/deplete_decay_only/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/deplete_decay_only/test.py b/tests/regression_tests/deplete_decay_only/test.py new file mode 100644 index 0000000000..4345b86b89 --- /dev/null +++ b/tests/regression_tests/deplete_decay_only/test.py @@ -0,0 +1,103 @@ +""" Transport-free depletion test suite """ + +from pathlib import Path +import shutil + +import numpy as np +import pytest +import openmc +import openmc.deplete +from openmc.deplete import CoupledOperator, IndependentOperator, MicroXS + + +@pytest.fixture(scope="module") +def model(): + fuel = openmc.Material(name="uo2") + fuel.add_element("U", 1, percent_type="ao", enrichment=4.25) + fuel.add_element("O", 2) + fuel.add_nuclide("Xe135_m1", 1) + fuel.add_nuclide("Cs135_m1", 1) + fuel.set_density("g/cc", 10.4) + + clad = openmc.Material(name="clad") + clad.add_element("Zr", 1) + clad.set_density("g/cc", 6) + + water = openmc.Material(name="water") + water.add_element("O", 1) + water.add_element("H", 2) + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + + radii = [0.42, 0.45] + fuel.volume = np.pi * radii[0] ** 2 + + materials = openmc.Materials([fuel, clad, water]) + + pin_surfaces = [openmc.ZCylinder(r=r) for r in radii] + pin_univ = openmc.model.pin(pin_surfaces, materials) + bound_box = openmc.model.RectangularPrism(1.24, 1.24, boundary_type="reflective") + root_cell = openmc.Cell(fill=pin_univ, region=-bound_box) + geometry = openmc.Geometry([root_cell]) + + settings = openmc.Settings() + settings.particles = 1000 + settings.inactive = 5 + settings.batches = 10 + + return openmc.Model(geometry, materials, settings) + +@pytest.fixture(scope="module") +def micro_xs(): + micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv' + return MicroXS.from_csv(micro_xs_file) + + +@pytest.fixture(scope="module") +def chain_file(): + return Path(__file__).parents[2] / 'chain_simple_decay.xml' + + +@pytest.mark.parametrize("operator_type", ["coupled", "independent"]) +def test_decay_only(run_in_tmpdir, operator_type, model, micro_xs, chain_file): + """Transport free system test suite. + + """ + # Create operator + if operator_type == "coupled": + op = CoupledOperator(model, chain_file=chain_file) + else: + op = IndependentOperator(openmc.Materials([model.materials[0]]), + [1e15], + [micro_xs], + chain_file) + + # Power and timesteps + dt = [917.4, 2262.6] # one Xe135_m1 half life and one Cs135_m1 half life + + # Perform simulation using the predictor algorithm + openmc.deplete.PredictorIntegrator(op, + dt, + power=0.0, + timestep_units='s').integrate() + + # Get path to test and reference results + path_test = op.output_dir / 'depletion_results.h5' + + # Load the reference/test results + res_test = openmc.deplete.Results(path_test) + + _, xe135m1_atoms = res_test.get_atoms('1', 'Xe135_m1') + _, xe135_atoms = res_test.get_atoms('1', 'Xe135') + _, cs135m1_atoms = res_test.get_atoms('1', 'Cs135_m1') + _, cs135_atoms = res_test.get_atoms('1', 'Cs135') + + tol = 1.0e-14 + assert xe135m1_atoms[0] == pytest.approx(xe135m1_atoms[1] * 2, rel=tol) + + # WARNING: this is generally not true as Xe135_m1 has two + # decay modes, and Xe135 will also decay, but we've modified the depletion chain so + # that Xe135_m1 only decays to Xe135, and that Xe135 has has no decay modes + assert xe135_atoms[1] == pytest.approx(xe135m1_atoms[1], rel=tol) + assert cs135m1_atoms[0] == pytest.approx(cs135m1_atoms[2] * 2, rel=tol) + assert cs135_atoms[2] == pytest.approx(cs135m1_atoms[2], rel=tol) diff --git a/tests/regression_tests/deplete_no_transport/__init__.py b/tests/regression_tests/deplete_no_transport/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/deplete_no_transport/test.py b/tests/regression_tests/deplete_no_transport/test.py new file mode 100644 index 0000000000..63ae584e11 --- /dev/null +++ b/tests/regression_tests/deplete_no_transport/test.py @@ -0,0 +1,190 @@ +""" Transport-free depletion test suite """ + +from pathlib import Path +import shutil + +import numpy as np +import pytest +import openmc +import openmc.deplete +from openmc.deplete import IndependentOperator, MicroXS + +from tests.regression_tests import config, assert_atoms_equal, \ + assert_reaction_rates_equal, assert_same_mats + + +@pytest.fixture(scope="module") +def fuel(): + fuel = openmc.Material(name="uo2") + fuel.add_element("U", 1, percent_type="ao", enrichment=4.25) + fuel.add_element("O", 2) + fuel.set_density("g/cc", 10.4) + fuel.depletable = True + + fuel.volume = np.pi * 0.42 ** 2 + + return fuel + +@pytest.fixture(scope="module") +def micro_xs(): + micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv' + return MicroXS.from_csv(micro_xs_file) + + +@pytest.fixture(scope="module") +def chain_file(): + return Path(__file__).parents[2] / 'chain_simple.xml' + + +neutron_per_cm2_sec = 1164719970082145.0 + + +@pytest.mark.parametrize("multiproc, from_nuclides, normalization_mode, power, source_rate", [ + (True, True, 'source-rate', None, 1.0), + (False, True, 'source-rate', None, 1.0), + (True, True, 'fission-q', 174, None), + (False, True, 'fission-q', 174, None), + (True, False, 'source-rate', None, 1.0), + (False, False, 'source-rate', None, 1.0), + (True, False, 'fission-q', 174, None), + (False, False, 'fission-q', 174, None)]) +def test_against_self(run_in_tmpdir, + fuel, + micro_xs, + chain_file, + multiproc, + from_nuclides, + normalization_mode, + power, + source_rate): + """Transport free system test suite. + + Runs an OpenMC transport-free depletion calculation and verifies + that the outputs match a reference file. + + """ + # Create operator + flux = neutron_per_cm2_sec * fuel.volume + op = _create_operator(from_nuclides, + fuel, + flux, + micro_xs, + chain_file, + normalization_mode) + + # Power and timesteps + dt = [360] # single step + + # Perform simulation using the predictor algorithm + openmc.deplete.pool.USE_MULTIPROCESSING = multiproc + openmc.deplete.PredictorIntegrator(op, + dt, + power=power, + source_rates=source_rate, + timestep_units='s').integrate() + + # Get path to test and reference results + path_test = op.output_dir / 'depletion_results.h5' + if power is None: + ref_path = 'test_reference_source_rate.h5' + else: + ref_path = 'test_reference_fission_q.h5' + path_reference = Path(__file__).with_name(ref_path) + + # If updating results, do so and return + if config['update']: + shutil.copyfile(str(path_test), str(path_reference)) + return + + # Load the reference/test results + res_test = openmc.deplete.Results(path_test) + res_ref = openmc.deplete.Results(path_reference) + + # Assert same mats + assert_same_mats(res_ref, res_test) + + tol = 1.0e-14 + assert_atoms_equal(res_ref, res_test, tol) + assert_reaction_rates_equal(res_ref, res_test, tol) + + +@pytest.mark.parametrize("multiproc, dt, time_units, time_type, atom_tol, rx_tol ", [ + (True, 360, 's', 'minutes', 2.0e-3, 3.0e-2), + (False, 360, 's', 'minutes', 2.0e-3, 3.0e-2), + (True, 4, 'h', 'hours', 2.0e-3, 6.0e-2), + (False, 4, 'h', 'hours', 2.0e-3, 6.0e-2), + (True, 5, 'd', 'days', 2.0e-3, 5.0e-2), + (False, 5, 'd', 'days', 2.0e-3, 5.0e-2), + (True, 100, 'd', 'months', 4.0e-3, 9.0e-2), + (False, 100, 'd', 'months', 4.0e-3, 9.0e-2)]) +def test_against_coupled(run_in_tmpdir, + fuel, + micro_xs, + chain_file, + multiproc, + dt, + time_units, + time_type, + atom_tol, + rx_tol): + # Create operator + flux = neutron_per_cm2_sec * fuel.volume + op = _create_operator(False, fuel, flux, micro_xs, chain_file, 'fission-q') + + # Power and timesteps + dt = [dt] # single step + + # Perform simulation using the predictor algorithm + openmc.deplete.pool.USE_MULTIPROCESSING = multiproc + openmc.deplete.PredictorIntegrator( + op, dt, power=174, timestep_units=time_units).integrate() + + # Get path to test and reference results + path_test = op.output_dir / 'depletion_results.h5' + + ref_path = f'test_reference_coupled_{time_type}.h5' + path_reference = Path(__file__).with_name(ref_path) + + # If updating results, do so and return + if config['update']: + shutil.copyfile(str(path_test), str(path_reference)) + return + + # Load the reference/test results + res_test = openmc.deplete.Results(path_test) + res_ref = openmc.deplete.Results(path_reference) + + # Assert same mats + assert_same_mats(res_test, res_ref) + + assert_atoms_equal(res_ref, res_test, atom_tol) + assert_reaction_rates_equal(res_ref, res_test, rx_tol) + + +def _create_operator(from_nuclides, + fuel, + flux, + micro_xs, + chain_file, + normalization_mode): + if from_nuclides: + nuclides = {} + for nuc, dens in fuel.get_nuclide_atom_densities().items(): + nuclides[nuc] = dens + + openmc.reset_auto_ids() + op = IndependentOperator.from_nuclides(fuel.volume, + nuclides, + flux, + micro_xs, + chain_file, + normalization_mode=normalization_mode) + + else: + op = IndependentOperator(openmc.Materials([fuel]), + [flux], + [micro_xs], + chain_file, + normalization_mode=normalization_mode) + + return op diff --git a/tests/regression_tests/deplete_no_transport/test_reference_coupled_days.h5 b/tests/regression_tests/deplete_no_transport/test_reference_coupled_days.h5 new file mode 100644 index 0000000000..9757c9791b Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_coupled_days.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_coupled_hours.h5 b/tests/regression_tests/deplete_no_transport/test_reference_coupled_hours.h5 new file mode 100644 index 0000000000..08d4d3eedc Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_coupled_hours.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_coupled_minutes.h5 b/tests/regression_tests/deplete_no_transport/test_reference_coupled_minutes.h5 new file mode 100644 index 0000000000..41b5235bb6 Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_coupled_minutes.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_coupled_months.h5 b/tests/regression_tests/deplete_no_transport/test_reference_coupled_months.h5 new file mode 100644 index 0000000000..53d80aff0f Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_coupled_months.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 b/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 new file mode 100644 index 0000000000..fe695692d6 Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_fission_q.h5 differ diff --git a/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 b/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 new file mode 100644 index 0000000000..c9c7e9e363 Binary files /dev/null and b/tests/regression_tests/deplete_no_transport/test_reference_source_rate.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/__init__.py b/tests/regression_tests/deplete_with_transfer_rates/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_ext_source.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_ext_source.h5 new file mode 100644 index 0000000000..3ed38f3d6c Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_ext_source.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_feed.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_feed.h5 new file mode 100644 index 0000000000..a147022322 Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_feed.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_removal.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_removal.h5 new file mode 100644 index 0000000000..3d48105d7b Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_removal.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_transfer.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_transfer.h5 new file mode 100644 index 0000000000..97855478ca Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_depletion_with_transfer.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_feed.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_feed.h5 new file mode 100644 index 0000000000..70a4a3d67a Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_feed.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_removal.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_removal.h5 new file mode 100644 index 0000000000..b41218a9b2 Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_only_removal.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_ext_source.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_ext_source.h5 new file mode 100644 index 0000000000..71bfdae112 Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_ext_source.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_transfer.h5 b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_transfer.h5 new file mode 100644 index 0000000000..ccf92b3991 Binary files /dev/null and b/tests/regression_tests/deplete_with_transfer_rates/ref_no_depletion_with_transfer.h5 differ diff --git a/tests/regression_tests/deplete_with_transfer_rates/test.py b/tests/regression_tests/deplete_with_transfer_rates/test.py new file mode 100644 index 0000000000..9784a07f6b --- /dev/null +++ b/tests/regression_tests/deplete_with_transfer_rates/test.py @@ -0,0 +1,122 @@ +""" ExternalRates depletion test suite """ + +from pathlib import Path +import shutil + +import numpy as np +import pytest +import openmc +import openmc.deplete +from openmc.deplete import CoupledOperator + +from tests.regression_tests import config, assert_reaction_rates_equal, \ + assert_atoms_equal + + +@pytest.fixture +def model(): + openmc.reset_auto_ids() + f = openmc.Material(name="f") + f.add_element("U", 1, percent_type="ao", enrichment=4.25) + f.add_element("O", 2) + f.set_density("g/cc", 10.4) + + w = openmc.Material(name="w") + w.add_element("O", 1) + w.add_element("H", 2) + w.set_density("g/cc", 1.0) + w.depletable = True + + radii = [0.42, 0.45] + f.volume = np.pi * radii[0] ** 2 + w.volume = np.pi * (radii[1]**2 - radii[0]**2) + + materials = openmc.Materials([f, w]) + + surf_f = openmc.Sphere(r=radii[0]) + surf_w = openmc.Sphere(r=radii[1], boundary_type='reflective') + cell_f = openmc.Cell(fill=f, region=-surf_f) + cell_w = openmc.Cell(fill=w, region=+surf_f & -surf_w) + geometry = openmc.Geometry([cell_f, cell_w]) + + settings = openmc.Settings() + settings.particles = 150 + settings.inactive = 0 + settings.batches = 10 + + return openmc.Model(geometry, materials, settings) + + +@pytest.mark.parametrize("rate, dest_mat, power, ref_result", [ + (1e-5, None, 0.0, 'no_depletion_only_removal'), + (-1e-5, None, 0.0, 'no_depletion_only_feed'), + (1e-5, None, 174.0, 'depletion_with_removal'), + (-1e-5, None, 174.0, 'depletion_with_feed'), + (-1e-5, 'w', 0.0, 'no_depletion_with_transfer'), + (1e-5, 'w', 174.0, 'depletion_with_transfer'), + ]) +def test_transfer_rates(run_in_tmpdir, model, rate, dest_mat, power, ref_result): + """Tests transfer_rates depletion class with transfer rates""" + + chain_file = Path(__file__).parents[2] / 'chain_simple.xml' + + transfer_elements = ['Xe'] + + op = CoupledOperator(model, chain_file) + op.round_number = True + integrator = openmc.deplete.PredictorIntegrator( + op, [1], power, timestep_units = 'd') + integrator.add_transfer_rate('f', transfer_elements, rate, + destination_material=dest_mat) + integrator.integrate() + + # Get path to test and reference results + path_test = op.output_dir / 'depletion_results.h5' + path_reference = Path(__file__).with_name(f'ref_{ref_result}.h5') + + # If updating results, do so and return + if config['update']: + shutil.copyfile(str(path_test), str(path_reference)) + return + + # Load the reference/test results + res_ref = openmc.deplete.Results(path_reference) + res_test = openmc.deplete.Results(path_test) + + assert_atoms_equal(res_ref, res_test) + assert_reaction_rates_equal(res_ref, res_test) + + +@pytest.mark.parametrize("rate, power, ref_result", [ + (1e-1, 0.0, 'no_depletion_with_ext_source'), + (1e-1, 174., 'depletion_with_ext_source'), +]) +def test_external_source_rates(run_in_tmpdir, model, rate, power, ref_result): + """Tests external_rates depletion class with external source rates""" + + chain_file = Path(__file__).parents[2] / 'chain_simple.xml' + + external_source_vector = {'U': 1} + + op = CoupledOperator(model, chain_file) + op.round_number = True + integrator = openmc.deplete.PredictorIntegrator( + op, [1], power, timestep_units='d') + integrator.add_external_source_rate('f', external_source_vector, rate) + integrator.integrate() + + # Get path to test and reference results + path_test = op.output_dir / 'depletion_results.h5' + path_reference = Path(__file__).with_name(f'ref_{ref_result}.h5') + + # If updating results, do so and return + if config['update']: + shutil.copyfile(str(path_test), str(path_reference)) + return + + # Load the reference/test results + res_ref = openmc.deplete.Results(path_reference) + res_test = openmc.deplete.Results(path_test) + + assert_atoms_equal(res_ref, res_test) + assert_reaction_rates_equal(res_ref, res_test) diff --git a/tests/regression_tests/deplete_with_transport/__init__.py b/tests/regression_tests/deplete_with_transport/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/deplete/example_geometry.py b/tests/regression_tests/deplete_with_transport/example_geometry.py similarity index 100% rename from tests/regression_tests/deplete/example_geometry.py rename to tests/regression_tests/deplete_with_transport/example_geometry.py diff --git a/tests/regression_tests/deplete_with_transport/last_step_reference_materials.xml b/tests/regression_tests/deplete_with_transport/last_step_reference_materials.xml new file mode 100644 index 0000000000..2dca559925 --- /dev/null +++ b/tests/regression_tests/deplete_with_transport/last_step_reference_materials.xml @@ -0,0 +1,1049 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/regression_tests/deplete/test.py b/tests/regression_tests/deplete_with_transport/test.py similarity index 72% rename from tests/regression_tests/deplete/test.py rename to tests/regression_tests/deplete_with_transport/test.py index fccd504931..0f7ebf00f3 100644 --- a/tests/regression_tests/deplete/test.py +++ b/tests/regression_tests/deplete_with_transport/test.py @@ -3,6 +3,7 @@ from math import floor import shutil from pathlib import Path +from collections import defaultdict from difflib import unified_diff import numpy as np @@ -11,8 +12,8 @@ import openmc from openmc.data import JOULE_PER_EV import openmc.deplete -from tests.regression_tests import config -from example_geometry import generate_problem +from tests.regression_tests import config, assert_atoms_equal +from .example_geometry import generate_problem @pytest.fixture(scope="module") @@ -45,7 +46,7 @@ def test_full(run_in_tmpdir, problem, multiproc): settings.batches = 10 settings.inactive = 0 space = openmc.stats.Box(lower_left, upper_right) - settings.source = openmc.Source(space=space) + settings.source = openmc.IndependentSource(space=space) settings.seed = 1 settings.verbosity = 1 @@ -53,7 +54,7 @@ def test_full(run_in_tmpdir, problem, multiproc): # Create operator chain_file = Path(__file__).parents[2] / 'chain_simple.xml' - op = openmc.deplete.Operator(model, chain_file) + op = openmc.deplete.CoupledOperator(model, chain_file) op.round_number = True # Power and timesteps @@ -77,54 +78,41 @@ def test_full(run_in_tmpdir, problem, multiproc): return # Load the reference/test results - res_test = openmc.deplete.ResultsList.from_hdf5(path_test) - res_ref = openmc.deplete.ResultsList.from_hdf5(path_reference) + res_test = openmc.deplete.Results(path_test) + res_ref = openmc.deplete.Results(path_reference) # Assert same mats - for mat in res_ref[0].mat_to_ind: - assert mat in res_test[0].mat_to_ind, \ + for mat in res_ref[0].index_mat: + assert mat in res_test[0].index_mat, \ "Material {} not in new results.".format(mat) - for nuc in res_ref[0].nuc_to_ind: - assert nuc in res_test[0].nuc_to_ind, \ + for nuc in res_ref[0].index_nuc: + assert nuc in res_test[0].index_nuc, \ "Nuclide {} not in new results.".format(nuc) - for mat in res_test[0].mat_to_ind: - assert mat in res_ref[0].mat_to_ind, \ + for mat in res_test[0].index_mat: + assert mat in res_ref[0].index_mat, \ "Material {} not in old results.".format(mat) - for nuc in res_test[0].nuc_to_ind: - assert nuc in res_ref[0].nuc_to_ind, \ + for nuc in res_test[0].index_nuc: + assert nuc in res_ref[0].index_nuc, \ "Nuclide {} not in old results.".format(nuc) - tol = 1.0e-6 - for mat in res_test[0].mat_to_ind: - for nuc in res_test[0].nuc_to_ind: - _, y_test = res_test.get_atoms(mat, nuc) - _, y_old = res_ref.get_atoms(mat, nuc) - - # Test each point - correct = True - for i, ref in enumerate(y_old): - if ref != y_test[i]: - if ref != 0.0: - correct = np.abs(y_test[i] - ref) / ref <= tol - else: - correct = False - - assert correct, "Discrepancy in mat {} and nuc {}\n{}\n{}".format( - mat, nuc, y_old, y_test) + assert_atoms_equal(res_ref, res_test, tol=1e-6) # Compare statepoint files with depletion results - t_test, k_test = res_test.get_eigenvalue() - t_ref, k_ref = res_ref.get_eigenvalue() + t_test, k_test = res_test.get_keff() + t_ref, k_ref = res_ref.get_keff() k_state = np.empty_like(k_ref) n_tallies = np.empty(N + 1, dtype=int) # Get statepoint files for all BOS points and EOL + runtimes = defaultdict(list) for n in range(N + 1): - statepoint = openmc.StatePoint("openmc_simulation_n{}.h5".format(n)) - k_n = statepoint.k_combined + statepoint = openmc.StatePoint(f"openmc_simulation_n{n}.h5") + for measure, time in statepoint.runtime.items(): + runtimes[measure].append(time) + k_n = statepoint.keff k_state[n] = [k_n.nominal_value, k_n.std_dev] n_tallies[n] = len(statepoint.tallies) # Look for exact match pulling from statepoint and depletion_results @@ -134,12 +122,27 @@ def test_full(run_in_tmpdir, problem, multiproc): # Check that no additional tallies are loaded from the files assert np.all(n_tallies == 0) + # Convert values in runtimes to arrays + runtimes = {k: np.array(v) for k, v in runtimes.items()} + + # Check that runtimes are qualitatively correct + assert runtimes['reading cross sections'][0] != 0 + assert runtimes['total initialization'][0] != 0 + assert np.all(runtimes['reading cross sections'][1:] == 0) + assert np.all(runtimes['total initialization'][1:] == 0) + assert np.all(runtimes['inactive batches'] == 0) + del runtimes['reading cross sections'] + del runtimes['total initialization'] + del runtimes['inactive batches'] + for measure, times in runtimes.items(): + assert np.all(times != 0) + def test_depletion_results_to_material(run_in_tmpdir, problem): """Checks openmc.Materials objects can be created from depletion results""" # Load the reference/test results path_reference = Path(__file__).with_name('test_reference.h5') - res_ref = openmc.deplete.ResultsList.from_hdf5(path_reference) + res_ref = openmc.deplete.Results(path_reference) # Firstly need to export materials.xml file for the initial simulation state geometry, lower_left, upper_right = problem diff --git a/tests/regression_tests/deplete_with_transport/test_reference.h5 b/tests/regression_tests/deplete_with_transport/test_reference.h5 new file mode 100644 index 0000000000..cc616e7910 Binary files /dev/null and b/tests/regression_tests/deplete_with_transport/test_reference.h5 differ diff --git a/tests/regression_tests/diff_tally/inputs_true.dat b/tests/regression_tests/diff_tally/inputs_true.dat index 909475f962..19356b64ad 100644 --- a/tests/regression_tests/diff_tally/inputs_true.dat +++ b/tests/regression_tests/diff_tally/inputs_true.dat @@ -1,38 +1,187 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 @@ -50,12 +199,12 @@ 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 @@ -73,12 +222,12 @@ 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 @@ -100,12 +249,12 @@ 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 @@ -127,313 +276,163 @@ 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 3 - 0 - - - -160 -160 -183 160 160 183 - - - true - - - - - 1 3 - - - 0.0 0.625 20000000.0 - - - 1 - flux - 1 - - - 1 - flux - 2 - - - 1 - flux - 3 - - - 1 - flux - 4 - - - 1 - flux - 5 - - - 1 - total U235 - total absorption scatter fission nu-fission - 1 - - - 1 - total U235 - total absorption scatter fission nu-fission - 2 - - - 1 - total U235 - total absorption scatter fission nu-fission - 3 - - - 1 - total U235 - total absorption scatter fission nu-fission - 4 - - - 1 - total U235 - total absorption scatter fission nu-fission - 5 - - - 1 - absorption - analog - 1 - - - 1 - absorption - analog - 2 - - - 1 - absorption - analog - 3 - - - 1 - absorption - analog - 4 - - - 1 - absorption - analog - 5 - - - 1 2 - total U235 - nu-fission scatter - 1 - - - 1 2 - total U235 - nu-fission scatter - 2 - - - 1 2 - U235 - nu-fission scatter - 3 - - - 1 2 - U235 - nu-fission scatter - 4 - - - 1 2 - U235 - nu-fission scatter - 5 - - - - - - - + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 3 + 0 + + + -160 -160 -183 160 160 183 + + + true + + + + 1 3 + + + 0.0 0.625 20000000.0 + + + 1 + flux + 1 + + + 1 + flux + 2 + + + 1 + flux + 3 + + + 1 + flux + 4 + + + 1 + flux + 5 + + + 1 + total U235 + total absorption scatter fission nu-fission + 1 + + + 1 + total U235 + total absorption scatter fission nu-fission + 2 + + + 1 + total U235 + total absorption scatter fission nu-fission + 3 + + + 1 + total U235 + total absorption scatter fission nu-fission + 4 + + + 1 + total U235 + total absorption scatter fission nu-fission + 5 + + + 1 + absorption + analog + 1 + + + 1 + absorption + analog + 2 + + + 1 + absorption + analog + 3 + + + 1 + absorption + analog + 4 + + + 1 + absorption + analog + 5 + + + 1 2 + total U235 + nu-fission scatter + 1 + + + 1 2 + total U235 + nu-fission scatter + 2 + + + 1 2 + U235 + nu-fission scatter + 3 + + + 1 2 + U235 + nu-fission scatter + 4 + + + 1 2 + U235 + nu-fission scatter + 5 + + + + + + + + diff --git a/tests/regression_tests/diff_tally/results_true.dat b/tests/regression_tests/diff_tally/results_true.dat index 12157932fb..a88081b7c1 100644 --- a/tests/regression_tests/diff_tally/results_true.dat +++ b/tests/regression_tests/diff_tally/results_true.dat @@ -1,27 +1,27 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. -3,,density,flux,-8.9862333e+00,2.7508460e+00 -3,,density,flux,-1.9778752e+01,3.5926341e+00 -1,,density,flux,-2.6861000e-01,3.8449483e-02 -1,,density,flux,-3.7643515e-01,1.8991066e-01 -1,O16,nuclide_density,flux,-2.5795663e+00,1.6620453e+01 -1,O16,nuclide_density,flux,7.2861106e+00,1.5941849e+01 -1,U235,nuclide_density,flux,-2.2849358e+03,3.9506163e+02 -1,U235,nuclide_density,flux,-2.7845978e+03,5.6693457e+02 -1,,temperature,flux,-1.8666344e-04,1.1088646e-04 -1,,temperature,flux,9.2264971e-05,8.7268407e-05 -3,,density,total,-4.1447663e+00,1.1453915e+00 -3,,density,absorption,-6.0174688e-01,1.0525787e-01 -3,,density,scatter,-3.5430194e+00,1.0499557e+00 -3,,density,fission,-2.8547461e-01,6.7402688e-02 -3,,density,nu-fission,-6.9988357e-01,1.6360007e-01 -3,,density,total,-3.7715161e-01,1.0166405e-01 -3,,density,absorption,-3.3327322e-01,8.9912373e-02 -3,,density,scatter,-4.3878393e-02,1.3801869e-02 -3,,density,fission,-2.7256167e-01,7.1483193e-02 -3,,density,nu-fission,-6.6439453e-01,1.7417261e-01 -3,,density,total,-2.3838461e+00,4.2331432e+00 -3,,density,absorption,-5.7877951e-02,5.3522562e-02 -3,,density,scatter,-2.3259682e+00,4.1799423e+00 +3,,density,flux,-9.2822822e+00,1.6880315e+00 +3,,density,flux,-2.0591270e+01,3.0043477e+00 +1,,density,flux,-2.9765141e-01,5.2949290e-02 +1,,density,flux,-4.0095723e-01,1.1716168e-01 +1,O16,nuclide_density,flux,-1.4245069e+01,6.3028710e+00 +1,O16,nuclide_density,flux,-2.5098326e+01,5.7525657e+00 +1,U235,nuclide_density,flux,-2.1513560e+03,6.7234014e+02 +1,U235,nuclide_density,flux,-2.4222736e+03,7.7715656e+02 +1,,temperature,flux,-1.1242141e-04,8.1692839e-05 +1,,temperature,flux,6.1357500e-06,7.4812624e-05 +3,,density,total,-4.2880614e+00,7.0021512e-01 +3,,density,absorption,-5.0324695e-01,4.9079615e-02 +3,,density,scatter,-3.7848144e+00,6.5868388e-01 +3,,density,fission,-2.7732455e-01,6.4647487e-02 +3,,density,nu-fission,-6.7990282e-01,1.5692707e-01 +3,,density,total,-3.8043572e-01,9.6222860e-02 +3,,density,absorption,-3.3518262e-01,8.6810436e-02 +3,,density,scatter,-4.5253106e-02,9.5205809e-03 +3,,density,fission,-2.6400028e-01,6.8175997e-02 +3,,density,nu-fission,-6.4353897e-01,1.6613026e-01 +3,,density,total,-3.2070285e-01,2.8328990e+00 +3,,density,absorption,2.5921007e-02,2.2141808e-02 +3,,density,scatter,-3.4662385e-01,2.8171749e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,total,0.0000000e+00,0.0000000e+00 @@ -29,19 +29,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,fission,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,total,4.0520294e-01,3.3719992e-02 -1,,density,absorption,1.3915754e-02,1.3515385e-02 -1,,density,scatter,3.9128718e-01,2.2403876e-02 -1,,density,fission,2.4781454e-04,1.0189025e-02 -1,,density,nu-fission,1.2555927e-03,2.4804599e-02 -1,,density,total,5.1306892e-03,1.1784483e-02 -1,,density,absorption,6.1064429e-04,1.1572255e-02 -1,,density,scatter,4.5200449e-03,3.2228853e-04 -1,,density,fission,-1.5830325e-03,1.0334068e-02 -1,,density,nu-fission,-3.8226434e-03,2.5181192e-02 -1,,density,total,-6.6601129e-01,2.0465955e-01 -1,,density,absorption,-1.5334818e-02,4.0664980e-03 -1,,density,scatter,-6.5067647e-01,2.0153765e-01 +1,,density,total,4.0321800e-01,3.5365140e-02 +1,,density,absorption,4.8426976e-03,8.3784362e-03 +1,,density,scatter,3.9837530e-01,2.9076664e-02 +1,,density,fission,-5.4793062e-03,7.1720201e-03 +1,,density,nu-fission,-1.2481616e-02,1.7542625e-02 +1,,density,total,-3.6063021e-03,7.1950060e-03 +1,,density,absorption,-8.2666439e-03,6.9274342e-03 +1,,density,scatter,4.6603417e-03,4.1803496e-04 +1,,density,fission,-7.6315741e-03,7.1774359e-03 +1,,density,nu-fission,-1.8551295e-02,1.7493106e-02 +1,,density,total,-6.2852725e-01,1.9776006e-01 +1,,density,absorption,-1.4850233e-02,4.3086611e-03 +1,,density,scatter,-6.1367701e-01,1.9363188e-01 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,total,0.0000000e+00,0.0000000e+00 @@ -49,19 +49,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,fission,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,total,4.3895711e+01,8.6765744e+00 -1,O16,nuclide_density,absorption,-7.3377277e-01,1.1652783e+00 -1,O16,nuclide_density,scatter,4.4629484e+01,7.5113743e+00 -1,O16,nuclide_density,fission,-1.0525946e+00,6.2358336e-01 -1,O16,nuclide_density,nu-fission,-2.5950001e+00,1.5266115e+00 -1,O16,nuclide_density,total,-1.0686783e+00,8.1661764e-01 -1,O16,nuclide_density,absorption,-1.0549848e+00,7.2501792e-01 -1,O16,nuclide_density,scatter,-1.3693500e-02,9.5152724e-02 -1,O16,nuclide_density,fission,-9.8026040e-01,6.2874064e-01 -1,O16,nuclide_density,nu-fission,-2.3899304e+00,1.5323042e+00 -1,O16,nuclide_density,total,4.3744802e+00,1.8346830e+01 -1,O16,nuclide_density,absorption,-7.6879459e-03,1.9447931e-01 -1,O16,nuclide_density,scatter,4.3821681e+00,1.8168908e+01 +1,O16,nuclide_density,total,4.2608989e+01,2.6322158e+00 +1,O16,nuclide_density,absorption,-5.9877963e-01,4.2139216e-01 +1,O16,nuclide_density,scatter,4.3207769e+01,2.5312922e+00 +1,O16,nuclide_density,fission,-9.2838585e-01,4.3505041e-01 +1,O16,nuclide_density,nu-fission,-2.2834786e+00,1.0634300e+00 +1,O16,nuclide_density,total,-1.1195412e+00,3.3033730e-01 +1,O16,nuclide_density,absorption,-1.0650267e+00,3.2780477e-01 +1,O16,nuclide_density,scatter,-5.4514540e-02,2.4911965e-02 +1,O16,nuclide_density,fission,-8.6787386e-01,4.4270048e-01 +1,O16,nuclide_density,nu-fission,-2.1159252e+00,1.0787495e+00 +1,O16,nuclide_density,total,-3.1004750e+01,8.0776339e+00 +1,O16,nuclide_density,absorption,-5.0354237e-01,2.6144390e-01 +1,O16,nuclide_density,scatter,-3.0501208e+01,7.8176703e+00 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -69,19 +69,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,total,-6.4251150e+02,2.3106826e+02 -1,U235,nuclide_density,absorption,1.5311660e+02,8.7788828e+01 -1,U235,nuclide_density,scatter,-7.9562811e+02,1.5432248e+02 -1,U235,nuclide_density,fission,2.3667219e+02,6.4506235e+01 -1,U235,nuclide_density,nu-fission,5.7770896e+02,1.5738708e+02 -1,U235,nuclide_density,total,4.1238071e+02,7.5208565e+01 -1,U235,nuclide_density,absorption,3.0507276e+02,7.3015316e+01 -1,U235,nuclide_density,scatter,1.0730795e+02,3.4182980e+00 -1,U235,nuclide_density,fission,2.3734641e+02,6.4221163e+01 -1,U235,nuclide_density,nu-fission,5.7953503e+02,1.5657365e+02 -1,U235,nuclide_density,total,-5.5174298e+03,1.1185254e+03 -1,U235,nuclide_density,absorption,-1.3416339e+02,3.2272881e+01 -1,U235,nuclide_density,scatter,-5.3832664e+03,1.0864829e+03 +1,U235,nuclide_density,total,-5.3578869e+02,3.4044374e+02 +1,U235,nuclide_density,absorption,2.0769087e+02,1.0873543e+02 +1,U235,nuclide_density,scatter,-7.4347956e+02,2.5966153e+02 +1,U235,nuclide_density,fission,2.8982867e+02,8.5348425e+01 +1,U235,nuclide_density,nu-fission,7.0772332e+02,2.0847449e+02 +1,U235,nuclide_density,total,4.8356107e+02,1.0276621e+02 +1,U235,nuclide_density,absorption,3.6683548e+02,9.6924085e+01 +1,U235,nuclide_density,scatter,1.1672558e+02,6.1773010e+00 +1,U235,nuclide_density,fission,2.8941699e+02,8.4475937e+01 +1,U235,nuclide_density,nu-fission,7.0639136e+02,2.0594285e+02 +1,U235,nuclide_density,total,-4.6777522e+03,1.5037171e+03 +1,U235,nuclide_density,absorption,-1.1585081e+02,3.8299177e+01 +1,U235,nuclide_density,scatter,-4.5619014e+03,1.4660078e+03 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,total,0.0000000e+00,0.0000000e+00 @@ -89,19 +89,19 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,total,3.0892324e-05,5.5277596e-05 -1,,temperature,absorption,2.2324596e-05,2.6209954e-05 -1,,temperature,scatter,8.5677277e-06,6.2111314e-05 -1,,temperature,fission,-1.7715856e-05,1.2803586e-05 -1,,temperature,nu-fission,-4.3163971e-05,3.1203107e-05 -1,,temperature,total,-2.3895766e-05,1.6226810e-05 -1,,temperature,absorption,-2.2388970e-05,1.6503185e-05 -1,,temperature,scatter,-1.5067967e-06,8.6753589e-07 -1,,temperature,fission,-1.7806576e-05,1.2760443e-05 -1,,temperature,nu-fission,-4.3390060e-05,3.1095620e-05 -1,,temperature,total,1.3388075e-04,1.1361483e-04 -1,,temperature,absorption,1.3825063e-06,1.3898795e-06 -1,,temperature,scatter,1.3249824e-04,1.1224449e-04 +1,,temperature,total,8.2134941e-05,1.3941434e-05 +1,,temperature,absorption,4.1502002e-05,3.5967068e-05 +1,,temperature,scatter,4.0632939e-05,2.6595574e-05 +1,,temperature,fission,3.1768469e-06,2.1582455e-05 +1,,temperature,nu-fission,7.7368345e-06,5.2593375e-05 +1,,temperature,total,1.0834099e-06,2.6707333e-05 +1,,temperature,absorption,1.6240165e-06,2.6844372e-05 +1,,temperature,scatter,-5.4060665e-07,2.5905412e-07 +1,,temperature,fission,3.1674458e-06,2.1588836e-05 +1,,temperature,nu-fission,7.7134913e-06,5.2609321e-05 +1,,temperature,total,9.5175179e-05,1.0549922e-04 +1,,temperature,absorption,4.3553888e-06,2.9261613e-06 +1,,temperature,scatter,9.0819790e-05,1.0362538e-04 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,total,0.0000000e+00,0.0000000e+00 @@ -109,68 +109,68 @@ d_material,d_nuclide,d_variable,score,mean,std. dev. 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,fission,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,absorption,-4.3471777e-01,1.4851761e-01 -3,,density,absorption,4.8155520e-02,1.3781656e-01 -1,,density,absorption,9.4994508e-03,2.4533642e-03 -1,,density,absorption,-1.0306958e-02,3.9360517e-03 -1,O16,nuclide_density,absorption,-1.6745789e+00,6.7608522e-01 -1,O16,nuclide_density,absorption,1.2254539e+00,7.5421132e-01 -1,U235,nuclide_density,absorption,7.1093314e+01,1.0278274e+02 -1,U235,nuclide_density,absorption,-1.3158317e+02,1.0057325e+01 -1,,temperature,absorption,-2.8254564e-05,2.2125234e-05 -1,,temperature,absorption,1.3248119e-05,8.9856151e-06 +3,,density,absorption,-5.4897740e-01,8.6529144e-02 +3,,density,absorption,1.3410763e-01,5.6235628e-02 +1,,density,absorption,2.9551566e-02,1.5215007e-02 +1,,density,absorption,-8.6382033e-03,5.5181454e-03 +1,O16,nuclide_density,absorption,2.7111116e-01,9.6922067e-01 +1,O16,nuclide_density,absorption,-7.3785997e-01,7.7252792e-01 +1,U235,nuclide_density,absorption,2.7361252e+02,1.6168134e+02 +1,U235,nuclide_density,absorption,-9.6885408e+01,1.9390948e+01 +1,,temperature,absorption,1.0054736e-05,4.2876538e-05 +1,,temperature,absorption,-1.8280609e-06,1.0133065e-05 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,-5.9591692e-01,1.8102644e-01 +3,,density,scatter,-5.4273849e-01,1.8081153e-01 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,-1.0186140e-04,6.5335918e-04 -3,,density,nu-fission,-7.7216010e-01,6.6235203e-02 -3,,density,scatter,-3.1141316e+00,8.7438594e-01 -3,,density,nu-fission,-7.4472975e-01,6.8932536e-02 -3,,density,scatter,-1.0613299e-02,1.4747505e-02 +3,,density,scatter,1.3451827e-02,7.2870304e-03 +3,,density,nu-fission,-8.4200508e-01,3.4267126e-01 +3,,density,scatter,-3.1963455e+00,5.0369564e-01 +3,,density,nu-fission,-8.2934702e-01,3.4526131e-01 +3,,density,scatter,-7.0221960e-02,6.8067882e-02 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,-2.3938461e+00,2.0442591e+00 +3,,density,scatter,1.1368387e+00,1.2091778e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 -3,,density,scatter,-3.8155549e-02,2.1549075e+00 +3,,density,scatter,-1.5916491e+00,2.3551913e+00 3,,density,nu-fission,0.0000000e+00,0.0000000e+00 3,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,9.6114352e-03,3.0773624e-02 +1,,density,scatter,-1.8655633e-03,2.4061808e-02 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-4.8445916e-04,4.1692636e-04 -1,,density,nu-fission,-2.0032982e-02,1.5628598e-02 -1,,density,scatter,3.8609205e-01,2.0651677e-02 -1,,density,nu-fission,-2.4325475e-02,1.6087936e-02 -1,,density,scatter,2.1127093e-03,1.1144847e-03 +1,,density,scatter,-1.7672807e-04,7.4702879e-04 +1,,density,nu-fission,4.4506944e-03,3.1478180e-02 +1,,density,scatter,3.7553200e-01,2.3420342e-02 +1,,density,nu-fission,-1.3567324e-03,3.0610901e-02 +1,,density,scatter,6.9330560e-03,4.6649873e-03 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-5.6312059e-01,1.2644279e-01 +1,,density,scatter,-5.1560927e-01,1.4846253e-01 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 -1,,density,scatter,-9.2583739e-02,1.3802271e-01 +1,,density,scatter,-1.0427977e-01,6.6396271e-02 1,,density,nu-fission,0.0000000e+00,0.0000000e+00 1,,density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,O16,nuclide_density,scatter,2.8010197e-02,9.9675258e-02 -1,O16,nuclide_density,nu-fission,-2.7133439e+00,1.9638343e+00 -1,O16,nuclide_density,scatter,-5.8699167e-02,2.9596632e-01 +1,O16,nuclide_density,scatter,4.6587151e-02,9.0232672e-02 +1,O16,nuclide_density,nu-fission,-2.1162226e+00,2.4773842e+00 +1,O16,nuclide_density,scatter,6.6533325e-01,2.4693113e-01 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,O16,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 -1,U235,nuclide_density,scatter,1.0007013e+01,5.9794002e+00 -1,U235,nuclide_density,nu-fission,4.3209054e+02,1.5648214e+02 -1,U235,nuclide_density,scatter,4.6100250e+01,2.3766583e+01 +1,U235,nuclide_density,scatter,2.8979849e+01,2.0235325e+01 +1,U235,nuclide_density,nu-fission,8.1210341e+02,2.9252040e+02 +1,U235,nuclide_density,scatter,1.0307302e+02,4.4919960e+01 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,nu-fission,0.0000000e+00,0.0000000e+00 1,U235,nuclide_density,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 -1,,temperature,scatter,1.9350898e-06,2.0685006e-06 -1,,temperature,nu-fission,-6.5233572e-05,6.4075926e-05 -1,,temperature,scatter,-7.6629325e-07,7.6629325e-07 +1,,temperature,scatter,-3.3782154e-06,3.8240314e-06 +1,,temperature,nu-fission,-6.8920903e-05,4.4384761e-05 +1,,temperature,scatter,1.6325729e-07,2.3270228e-06 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 1,,temperature,scatter,0.0000000e+00,0.0000000e+00 1,,temperature,nu-fission,0.0000000e+00,0.0000000e+00 diff --git a/tests/regression_tests/diff_tally/test.py b/tests/regression_tests/diff_tally/test.py index b688e6d233..89460df915 100644 --- a/tests/regression_tests/diff_tally/test.py +++ b/tests/regression_tests/diff_tally/test.py @@ -16,7 +16,7 @@ class DiffTallyTestHarness(PyAPITestHarness): self._model.settings.batches = 3 self._model.settings.inactive = 0 self._model.settings.particles = 100 - self._model.settings.source = openmc.Source(space=openmc.stats.Box( + self._model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-160, -160, -183], [160, 160, 183])) self._model.settings.temperature['multipole'] = True @@ -103,9 +103,8 @@ class DiffTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(statepoint) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('d_material', 'd_nuclide', 'd_variable', 'score', 'mean', diff --git a/tests/regression_tests/distribmat/inputs_true.dat b/tests/regression_tests/distribmat/inputs_true.dat index a1c1e83116..35b3b5b9fb 100644 --- a/tests/regression_tests/distribmat/inputs_true.dat +++ b/tests/regression_tests/distribmat/inputs_true.dat @@ -1,62 +1,61 @@ - - - - - - - 2.0 2.0 - 1 - 2 2 - -2.0 -2.0 - + + + + + + + + + + + + + + + + + + + + + + + 2.0 2.0 + 1 + 2 2 + -2.0 -2.0 + 11 11 11 11 - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - -1 -1 -1 1 1 1 - - - - - - - 0 0 0 - 7 7 - 400 400 - - - 0 0 0 - 7 7 - 400 400 - - + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + -1 -1 -1 1 1 1 + + + + + + 400 400 + 0 0 0 + 7 7 + + + 400 400 + 0 0 0 + 7 7 + + + diff --git a/tests/regression_tests/distribmat/results_true.dat b/tests/regression_tests/distribmat/results_true.dat index a33cbec0f6..166fd4662d 100644 --- a/tests/regression_tests/distribmat/results_true.dat +++ b/tests/regression_tests/distribmat/results_true.dat @@ -1,10 +1,10 @@ k-combined: -1.234870E+00 1.724266E-02 +1.246391E+00 1.414798E-02 Cell ID = 11 Name = Fill = [2, None, 3, 2] - Region = -9 + Region = -1 Rotation = None Translation = None Volume = None diff --git a/tests/regression_tests/distribmat/test.py b/tests/regression_tests/distribmat/test.py index 33f64ba031..02f7e773e5 100644 --- a/tests/regression_tests/distribmat/test.py +++ b/tests/regression_tests/distribmat/test.py @@ -4,7 +4,8 @@ from tests.testing_harness import TestHarness, PyAPITestHarness class DistribmatTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) #################### # Materials #################### @@ -22,8 +23,8 @@ class DistribmatTestHarness(PyAPITestHarness): light_fuel.set_density('g/cc', 2.0) light_fuel.add_nuclide('U235', 1.0) - mats_file = openmc.Materials([moderator, dense_fuel, light_fuel]) - mats_file.export_to_xml() + self._model.materials = openmc.Materials([moderator, dense_fuel, + light_fuel]) #################### # Geometry @@ -54,8 +55,7 @@ class DistribmatTestHarness(PyAPITestHarness): c101.region = +x0 & -x1 & +y0 & -y1 root_univ = openmc.Universe(universe_id=0, cells=[c101]) - geometry = openmc.Geometry(root_univ) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root_univ) #################### # Settings @@ -65,9 +65,9 @@ class DistribmatTestHarness(PyAPITestHarness): sets_file.batches = 5 sets_file.inactive = 0 sets_file.particles = 1000 - sets_file.source = openmc.Source(space=openmc.stats.Box( + sets_file.source = openmc.IndependentSource(space=openmc.stats.Box( [-1, -1, -1], [1, 1, 1])) - sets_file.export_to_xml() + self._model.settings = sets_file #################### # Plots @@ -89,8 +89,7 @@ class DistribmatTestHarness(PyAPITestHarness): plot2.width = (7, 7) plot2.pixels = (400, 400) - plots = openmc.Plots([plot1, plot2]) - plots.export_to_xml() + self._model.plots = openmc.Plots([plot1, plot2]) def _get_results(self): outstr = super()._get_results() @@ -100,5 +99,5 @@ class DistribmatTestHarness(PyAPITestHarness): def test_distribmat(): - harness = DistribmatTestHarness('statepoint.5.h5') + harness = DistribmatTestHarness('statepoint.5.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/eigenvalue_genperbatch/inputs_true.dat b/tests/regression_tests/eigenvalue_genperbatch/inputs_true.dat index 7348663e8d..15b810a626 100644 --- a/tests/regression_tests/eigenvalue_genperbatch/inputs_true.dat +++ b/tests/regression_tests/eigenvalue_genperbatch/inputs_true.dat @@ -1,31 +1,30 @@ - - - - - - - - - - - - - - eigenvalue - 1000 - 7 - 3 - 3 - - - -4.0 -4.0 -4.0 4.0 4.0 4.0 - - - - - - - flux - - + + + + + + + + + + + + + eigenvalue + 1000 + 7 + 3 + 3 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + + + flux + + + diff --git a/tests/regression_tests/eigenvalue_genperbatch/results_true.dat b/tests/regression_tests/eigenvalue_genperbatch/results_true.dat index 91d1c393a0..d171c5e87f 100644 --- a/tests/regression_tests/eigenvalue_genperbatch/results_true.dat +++ b/tests/regression_tests/eigenvalue_genperbatch/results_true.dat @@ -1,5 +1,5 @@ k-combined: -3.029569E-01 9.632511E-05 +2.975937E-01 1.293390E-03 tally 1: -3.226370E+01 -2.602467E+02 +3.173222E+01 +2.517683E+02 diff --git a/tests/regression_tests/eigenvalue_genperbatch/test.py b/tests/regression_tests/eigenvalue_genperbatch/test.py index 7e21c8d367..17c6dff7f9 100644 --- a/tests/regression_tests/eigenvalue_genperbatch/test.py +++ b/tests/regression_tests/eigenvalue_genperbatch/test.py @@ -22,7 +22,7 @@ def model(): model.settings.batches = 7 model.settings.generations_per_batch = 3 space = openmc.stats.Box((-4.0, -4.0, -4.0), (4.0, 4.0, 4.)) - model.settings.source = openmc.Source(space=space) + model.settings.source = openmc.IndependentSource(space=space) t = openmc.Tally() t.scores = ['flux'] diff --git a/tests/regression_tests/eigenvalue_no_inactive/results_true.dat b/tests/regression_tests/eigenvalue_no_inactive/results_true.dat index 80d463a3c8..3ba7485a96 100644 --- a/tests/regression_tests/eigenvalue_no_inactive/results_true.dat +++ b/tests/regression_tests/eigenvalue_no_inactive/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.066848E-01 7.260987E-03 +3.072780E-01 6.882841E-03 diff --git a/tests/regression_tests/electron_heating/__init__.py b/tests/regression_tests/electron_heating/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/electron_heating/inputs_true.dat b/tests/regression_tests/electron_heating/inputs_true.dat new file mode 100644 index 0000000000..ec8e5a8376 --- /dev/null +++ b/tests/regression_tests/electron_heating/inputs_true.dat @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 10000000.0 1.0 + + + + 1000.0 + + + + + heating + + + diff --git a/tests/regression_tests/electron_heating/results_true.dat b/tests/regression_tests/electron_heating/results_true.dat new file mode 100644 index 0000000000..4f54ceaa4d --- /dev/null +++ b/tests/regression_tests/electron_heating/results_true.dat @@ -0,0 +1,3 @@ +tally 1: +1.000000E+07 +1.000000E+14 diff --git a/tests/regression_tests/electron_heating/test.py b/tests/regression_tests/electron_heating/test.py new file mode 100644 index 0000000000..e7a58560c4 --- /dev/null +++ b/tests/regression_tests/electron_heating/test.py @@ -0,0 +1,40 @@ +import pytest +import openmc + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def water_model(): + # Define materals and geometry + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cc", 1.0) + sphere = openmc.Sphere(r=1.0, boundary_type="reflective") + sph = openmc.Cell(fill=water, region=-sphere) + geometry = openmc.Geometry([sph]) + source = openmc.IndependentSource( + energy=openmc.stats.delta_function(10.0e6), + particle="electron" + ) + + # Define settings + settings = openmc.Settings() + settings.particles = 10000 + settings.batches = 1 + settings.cutoff = {"energy_photon": 1000.0} + settings.run_mode = "fixed source" + settings.source = source + + # Define tallies + tally = openmc.Tally() + tally.scores = ["heating"] + tallies = openmc.Tallies([tally]) + + return openmc.Model(geometry=geometry, settings=settings, tallies=tallies) + + +def test_electron_heating_calc(water_model): + harness = PyAPITestHarness("statepoint.1.h5", water_model) + harness.main() diff --git a/tests/regression_tests/energy_cutoff/inputs_true.dat b/tests/regression_tests/energy_cutoff/inputs_true.dat index bb02369dc5..557cb7a3b1 100644 --- a/tests/regression_tests/energy_cutoff/inputs_true.dat +++ b/tests/regression_tests/energy_cutoff/inputs_true.dat @@ -1,42 +1,41 @@ - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - -1 -1 -1 1 1 1 - - - - - 4.0 - - - - - - 0.0 4.0 - - - 1 - flux - - + + + + + + + + + + + + + + + + + + fixed source + 100 + 10 + + + -1 -1 -1 1 1 1 + + + + + 4.0 + + + + + 0.0 4.0 + + + 1 + flux + + + diff --git a/tests/regression_tests/energy_cutoff/test.py b/tests/regression_tests/energy_cutoff/test.py index d76edd9a08..9aca802fa3 100755 --- a/tests/regression_tests/energy_cutoff/test.py +++ b/tests/regression_tests/energy_cutoff/test.py @@ -4,7 +4,8 @@ from tests.testing_harness import PyAPITestHarness class EnergyCutoffTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Set energy cutoff energy_cutoff = 4.0 @@ -12,8 +13,7 @@ class EnergyCutoffTestHarness(PyAPITestHarness): mat = openmc.Material(material_id=1, name='mat') mat.set_density('atom/b-cm', 0.069335) mat.add_nuclide('H1', 40.0) - materials_file = openmc.Materials([mat]) - materials_file.export_to_xml() + self._model.materials = openmc.Materials([mat]) # Cell is box with reflective boundary x1 = openmc.XPlane(surface_id=1, x0=-1) @@ -29,8 +29,7 @@ class EnergyCutoffTestHarness(PyAPITestHarness): box.fill = mat root = openmc.Universe(universe_id=0, name='root universe') root.add_cell(box) - geometry = openmc.Geometry(root) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root) # Set the running parameters settings_file = openmc.Settings() @@ -41,9 +40,9 @@ class EnergyCutoffTestHarness(PyAPITestHarness): bounds = [-1, -1, -1, 1, 1, 1] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) watt_dist = openmc.stats.Watt() - settings_file.source = openmc.source.Source(space=uniform_dist, - energy=watt_dist) - settings_file.export_to_xml() + settings_file.source = openmc.IndependentSource(space=uniform_dist, + energy=watt_dist) + self._model.settings = settings_file # Tally flux under energy cutoff tallies = openmc.Tallies() @@ -52,7 +51,7 @@ class EnergyCutoffTestHarness(PyAPITestHarness): energy_filter = openmc.filter.EnergyFilter((0.0, energy_cutoff)) tally.filters = [energy_filter] tallies.append(tally) - tallies.export_to_xml() + self._model.tallies = tallies def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -70,5 +69,5 @@ class EnergyCutoffTestHarness(PyAPITestHarness): def test_energy_cutoff(): - harness = EnergyCutoffTestHarness('statepoint.10.h5') + harness = EnergyCutoffTestHarness('statepoint.10.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/energy_grid/results_true.dat b/tests/regression_tests/energy_grid/results_true.dat index b06742c1ba..3a042d882b 100644 --- a/tests/regression_tests/energy_grid/results_true.dat +++ b/tests/regression_tests/energy_grid/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.317249E-01 1.079825E-02 +3.218009E-01 4.687417E-03 diff --git a/tests/regression_tests/energy_laws/inputs_true.dat b/tests/regression_tests/energy_laws/inputs_true.dat index 2320b21d5b..8c5191217d 100644 --- a/tests/regression_tests/energy_laws/inputs_true.dat +++ b/tests/regression_tests/energy_laws/inputs_true.dat @@ -1,23 +1,23 @@ - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/energy_laws/results_true.dat b/tests/regression_tests/energy_laws/results_true.dat index 534a299b56..7802786838 100644 --- a/tests/regression_tests/energy_laws/results_true.dat +++ b/tests/regression_tests/energy_laws/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.444000E+00 1.044626E-02 +2.458770E+00 9.422203E-03 diff --git a/tests/regression_tests/entropy/results_true.dat b/tests/regression_tests/entropy/results_true.dat index 864905fa7c..92d7f091df 100644 --- a/tests/regression_tests/entropy/results_true.dat +++ b/tests/regression_tests/entropy/results_true.dat @@ -1,13 +1,13 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 entropy: 7.688862E+00 -8.184416E+00 -8.363034E+00 -8.174200E+00 -8.243373E+00 -8.298547E+00 -8.267346E+00 -8.253347E+00 -8.329508E+00 -8.363320E+00 +8.226316E+00 +8.308355E+00 +8.243413E+00 +8.369345E+00 +8.304865E+00 +8.230689E+00 +8.338304E+00 +8.270630E+00 +8.386598E+00 diff --git a/tests/regression_tests/entropy/test.py b/tests/regression_tests/entropy/test.py index 10a11e3008..af1fbd56a3 100644 --- a/tests/regression_tests/entropy/test.py +++ b/tests/regression_tests/entropy/test.py @@ -14,7 +14,7 @@ class EntropyTestHarness(TestHarness): with StatePoint(statepoint) as sp: # Write out k-combined. outstr = 'k-combined:\n' - outstr += '{:12.6E} {:12.6E}\n'.format(sp.k_combined.n, sp.k_combined.s) + outstr += '{:12.6E} {:12.6E}\n'.format(sp.keff.n, sp.keff.s) # Write out entropy data. outstr += 'entropy:\n' diff --git a/tests/regression_tests/external_moab/inputs_true.dat b/tests/regression_tests/external_moab/inputs_true.dat index af259eee47..ed035f897a 100644 --- a/tests/regression_tests/external_moab/inputs_true.dat +++ b/tests/regression_tests/external_moab/inputs_true.dat @@ -1,73 +1,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - 0.0 0.0 0.0 - - - - 15000000.0 1.0 - - - - - - - test_mesh_tets.h5m - - - 1 - - - 1 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 100 + 10 + + + 0.0 0.0 0.0 + + + + 15000000.0 1.0 + + + + + + test_mesh_tets.h5m + + + 1 + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/external_moab/test.py b/tests/regression_tests/external_moab/test.py index caff6b5dbd..2d64eb14bb 100644 --- a/tests/regression_tests/external_moab/test.py +++ b/tests/regression_tests/external_moab/test.py @@ -32,15 +32,14 @@ def cpp_driver(request): openmc_dir = Path(str(request.config.rootdir)) / 'build' with open('CMakeLists.txt', 'w') as f: f.write(textwrap.dedent(""" - cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_cpp_driver CXX) add_executable(main main.cpp) find_package(OpenMC REQUIRED HINTS {}) target_link_libraries(main OpenMC::libopenmc) - set_target_properties(main PROPERTIES CXX_STANDARD - 14 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) + target_compile_features(main PUBLIC cxx_std_14) set(CMAKE_CXX_FLAGS "-pedantic-errors") - add_compile_definitions(DAGMC=1) + add_compile_definitions(OPENMC_DAGMC_ENABLED=1) """.format(openmc_dir))) # Create temporary build directory and change to there @@ -49,12 +48,14 @@ def cpp_driver(request): os.chdir(str(local_builddir)) if config['mpi']: - os.environ['CXX'] = 'mpicxx' + mpi_arg = "On" + else: + mpi_arg = "Off" try: print("Building driver") # Run cmake/make to build the shared libary - subprocess.run(['cmake', os.path.pardir], check=True) + subprocess.run(['cmake', os.path.pardir, f'-DOPENMC_USE_MPI={mpi_arg}'], check=True) subprocess.run(['make'], check=True) os.chdir(os.path.pardir) @@ -161,8 +162,6 @@ def test_external_mesh(cpp_driver): water_mat.set_density("atom/b-cm", 0.07416) materials.append(water_mat) - materials.export_to_xml() - # Geometry fuel_min_x = openmc.XPlane(-5.0, name="minimum x") fuel_max_x = openmc.XPlane(5.0, name="maximum x") @@ -259,7 +258,7 @@ def test_external_mesh(cpp_driver): space = openmc.stats.Point() angle = openmc.stats.Monodirectional((-1.0, 0.0, 0.0)) energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) - source = openmc.Source(space=space, energy=energy, angle=angle) + source = openmc.IndependentSource(space=space, energy=energy, angle=angle) settings.source = source model = openmc.model.Model(geometry=geometry, diff --git a/tests/regression_tests/filter_cellfrom/__init__.py b/tests/regression_tests/filter_cellfrom/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/filter_cellfrom/inputs_true.dat b/tests/regression_tests/filter_cellfrom/inputs_true.dat new file mode 100644 index 0000000000..20d0d69d4c --- /dev/null +++ b/tests/regression_tests/filter_cellfrom/inputs_true.dat @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 2000 + 15 + 5 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + 1 + + + + 1 + + + 1 + + + 2 + + + 3 + + + 4 + + + 2 + + + 3 + + + 4 + + + 5 1 + total + + + 5 2 + total + + + 5 3 + total + + + 5 4 + total + + + 6 1 + total + + + 6 2 + total + + + 6 3 + total + + + 6 4 + total + + + 7 1 + total + + + 7 2 + total + + + 7 3 + total + + + 7 4 + total + + + 8 1 + total + + + 8 2 + total + + + 8 3 + total + + + 8 4 + total + + + total + + + diff --git a/tests/regression_tests/filter_cellfrom/results_true.dat b/tests/regression_tests/filter_cellfrom/results_true.dat new file mode 100644 index 0000000000..408a4965b1 --- /dev/null +++ b/tests/regression_tests/filter_cellfrom/results_true.dat @@ -0,0 +1,53 @@ +k-combined: +9.035025E-02 2.654309E-03 +tally 1: +5.994069E+00 +3.594398E+00 +tally 2: +4.559707E-04 +2.080739E-08 +tally 3: +5.994525E+00 +3.594945E+00 +tally 4: +0.000000E+00 +0.000000E+00 +tally 5: +1.473892E+00 +2.187509E-01 +tally 6: +5.223875E-05 +2.992861E-10 +tally 7: +1.473945E+00 +2.187663E-01 +tally 8: +1.885798E+01 +3.558423E+01 +tally 9: +7.467961E+00 +5.580255E+00 +tally 10: +5.082094E-04 +2.584432E-08 +tally 11: +7.468470E+00 +5.581014E+00 +tally 12: +1.885798E+01 +3.558423E+01 +tally 13: +0.000000E+00 +0.000000E+00 +tally 14: +2.739543E-04 +7.600983E-09 +tally 15: +2.739543E-04 +7.600983E-09 +tally 16: +7.881296E+01 +6.221087E+02 +tally 17: +1.051397E+02 +1.106292E+03 diff --git a/tests/regression_tests/filter_cellfrom/test.py b/tests/regression_tests/filter_cellfrom/test.py new file mode 100644 index 0000000000..5559b4c817 --- /dev/null +++ b/tests/regression_tests/filter_cellfrom/test.py @@ -0,0 +1,316 @@ +"""This test ensures that the CellFromFilter works correctly even if the level of +coordinates (number of encapsulated universes) is different in the cell from +where the particle originates compared to the cell where the particle is going. + +A matrix of reaction rates based on where the particle is coming from and +where it goes to is calculated and compared to the total reaction rate of the problem. +The components of this matrix are also compared to other components using symmetric +properties. + +TODO: + +- Test with a lattice, +- Test with mesh, +- Test with reflective boundary conditions, +- Test with periodic boundary conditions. + +""" + +from numpy.testing import assert_allclose, assert_equal +import numpy as np +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +RTOL = 1.0e-7 +ATOL = 0.0 + + +@pytest.fixture +def model(): + """Cylindrical core contained in a first box which is contained in a larger box. + A lower universe is used to describe the interior of the first box which + contains the core and its surrounding space.""" + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + fuel = openmc.Material() + fuel.add_nuclide("U234", 0.0004524) + fuel.add_nuclide("U235", 0.0506068) + fuel.add_nuclide("U238", 0.9487090) + fuel.add_nuclide("U236", 0.0002318) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cm3", 10.97) + + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + + air = openmc.Material() + air.add_element("O", 0.2) + air.add_element("N", 0.8) + air.set_density("g/cm3", 0.001225) + + # ============================================================================= + # Geometry + # ============================================================================= + + # ----------------------------------------------------------------------------- + # Cylindrical core + # ----------------------------------------------------------------------------- + + # Parameters + core_radius = 2.0 + core_height = 4.0 + + # Surfaces + core_cylinder = openmc.ZCylinder(r=core_radius) + core_lower_plane = openmc.ZPlane(z0=-core_height / 2.0) + core_upper_plane = openmc.ZPlane(z0=core_height / 2.0) + + # Region + core_region = -core_cylinder & +core_lower_plane & -core_upper_plane + + # Cells + core = openmc.Cell(fill=fuel, region=core_region) + outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane + outside_core = openmc.Cell(fill=air, region=outside_core_region) + + # Universe + inside_box1_universe = openmc.Universe(cells=[core, outside_core]) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 4.1 + + # Surfaces + box1_rpp = openmc.model.RectangularParallelepiped( + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + ) + + # Cell + box1 = openmc.Cell(fill=inside_box1_universe, region=-box1_rpp) + + # ----------------------------------------------------------------------------- + # Box 2 + # ----------------------------------------------------------------------------- + + # Parameters + box2_size = 12 + + # Surfaces + box2_rpp = openmc.model.RectangularParallelepiped( + -box2_size / 2.0, box2_size / 2.0, + -box2_size / 2.0, box2_size / 2.0, + -box2_size / 2.0, box2_size / 2.0, + boundary_type="vacuum" + ) + + # Cell + box2 = openmc.Cell(fill=water, region=-box2_rpp & +box1_rpp) + + # Register geometry + model.geometry = openmc.Geometry([box1, box2]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 2000 + model.settings.batches = 15 + model.settings.inactive = 5 + model.settings.seed = 1 + + bounds = [ + -core_radius, + -core_radius, + -core_height / 2.0, + core_radius, + core_radius, + core_height / 2.0, + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource( + space=distribution, constraints={'fissionable': True}) + + # ============================================================================= + # Tallies + # ============================================================================= + + in_core_filter = openmc.CellFilter([core]) + in_outside_core_filter = openmc.CellFilter([outside_core]) + in_box1_filter = openmc.CellFilter([box1]) + in_box2_filter = openmc.CellFilter([box2]) + + from_core_filter = openmc.CellFromFilter([core]) + from_outside_core_filter = openmc.CellFromFilter([outside_core]) + from_box1_filter = openmc.CellFromFilter([box1]) + from_box2_filter = openmc.CellFromFilter([box2]) + + t1_1 = openmc.Tally(name="total from 1 in 1") + t1_1.filters = [from_core_filter, in_core_filter] + t1_1.scores = ["total"] + + t1_2 = openmc.Tally(name="total from 1 in 2") + t1_2.filters = [from_core_filter, in_outside_core_filter] + t1_2.scores = ["total"] + + t1_3 = openmc.Tally(name="total from 1 in 3") + t1_3.filters = [from_core_filter, in_box1_filter] + t1_3.scores = ["total"] + + t1_4 = openmc.Tally(name="total from 1 in 4") + t1_4.filters = [from_core_filter, in_box2_filter] + t1_4.scores = ["total"] + + t2_1 = openmc.Tally(name="total from 2 in 1") + t2_1.filters = [from_outside_core_filter, in_core_filter] + t2_1.scores = ["total"] + + t2_2 = openmc.Tally(name="total from 2 in 2") + t2_2.filters = [from_outside_core_filter, in_outside_core_filter] + t2_2.scores = ["total"] + + t2_3 = openmc.Tally(name="total from 2 in 3") + t2_3.filters = [from_outside_core_filter, in_box1_filter] + t2_3.scores = ["total"] + + t2_4 = openmc.Tally(name="total from 2 in 4") + t2_4.filters = [from_outside_core_filter, in_box2_filter] + t2_4.scores = ["total"] + + t3_1 = openmc.Tally(name="total from 3 in 1") + t3_1.filters = [from_box1_filter, in_core_filter] + t3_1.scores = ["total"] + + t3_2 = openmc.Tally(name="total from 3 in 2") + t3_2.filters = [from_box1_filter, in_outside_core_filter] + t3_2.scores = ["total"] + + t3_3 = openmc.Tally(name="total from 3 in 3") + t3_3.filters = [from_box1_filter, in_box1_filter] + t3_3.scores = ["total"] + + t3_4 = openmc.Tally(name="total from 3 in 4") + t3_4.filters = [from_box1_filter, in_box2_filter] + t3_4.scores = ["total"] + + t4_1 = openmc.Tally(name="total from 4 in 1") + t4_1.filters = [from_box2_filter, in_core_filter] + t4_1.scores = ["total"] + + t4_2 = openmc.Tally(name="total from 4 in 2") + t4_2.filters = [from_box2_filter, in_outside_core_filter] + t4_2.scores = ["total"] + + t4_3 = openmc.Tally(name="total from 4 in 3") + t4_3.filters = [from_box2_filter, in_box1_filter] + t4_3.scores = ["total"] + + t4_4 = openmc.Tally(name="total from 4 in 4") + t4_4.filters = [from_box2_filter, in_box2_filter] + t4_4.scores = ["total"] + + tglobal = openmc.Tally(name="total") + tglobal.scores = ["total"] + + model.tallies += [ + t1_1, + t1_2, + t1_3, + t1_4, + t2_1, + t2_2, + t2_3, + t2_4, + t3_1, + t3_2, + t3_3, + t3_4, + t4_1, + t4_2, + t4_3, + t4_4, + tglobal, + ] + return model + + +class CellFromFilterTest(PyAPITestHarness): + + def _compare_results(self): + """Additional unit tests on the tally results to check + consistency of CellFromFilter.""" + with openmc.StatePoint(self.statepoint_name) as sp: + + t1_1 = sp.get_tally(name="total from 1 in 1").mean + t1_2 = sp.get_tally(name="total from 1 in 2").mean + t1_3 = sp.get_tally(name="total from 1 in 3").mean + t1_4 = sp.get_tally(name="total from 1 in 4").mean + + t2_1 = sp.get_tally(name="total from 2 in 1").mean + t2_2 = sp.get_tally(name="total from 2 in 2").mean + t2_3 = sp.get_tally(name="total from 2 in 3").mean + t2_4 = sp.get_tally(name="total from 2 in 4").mean + + t3_1 = sp.get_tally(name="total from 3 in 1").mean + t3_2 = sp.get_tally(name="total from 3 in 2").mean + t3_3 = sp.get_tally(name="total from 3 in 3").mean + t3_4 = sp.get_tally(name="total from 3 in 4").mean + + t4_1 = sp.get_tally(name="total from 4 in 1").mean + t4_2 = sp.get_tally(name="total from 4 in 2").mean + t4_3 = sp.get_tally(name="total from 4 in 3").mean + t4_4 = sp.get_tally(name="total from 4 in 4").mean + + tglobal = sp.get_tally(name="total").mean + + # From 1 and 2 is equivalent to from 3 + assert_allclose(t1_1 + t2_1, t3_1, rtol=RTOL, atol=ATOL) + assert_allclose(t1_2 + t2_2, t3_2, rtol=RTOL, atol=ATOL) + assert_allclose(t1_3 + t2_3, t3_3, rtol=RTOL, atol=ATOL) + assert_allclose(t1_4 + t2_4, t3_4, rtol=RTOL, atol=ATOL) + + # In 1 and 2 equivalent to in 3 + assert_allclose(t1_1 + t1_2, t1_3, rtol=RTOL, atol=ATOL) + assert_allclose(t2_1 + t2_2, t2_3, rtol=RTOL, atol=ATOL) + assert_allclose(t3_1 + t3_2, t3_3, rtol=RTOL, atol=ATOL) + assert_allclose(t4_1 + t4_2, t4_3, rtol=RTOL, atol=ATOL) + + # Comparison to global from 3 + assert_allclose(t3_3 + t3_4 + t4_3 + t4_4, tglobal, rtol=RTOL, atol=ATOL) + + # Comparison to global from 1 and 2 + t_from_1_wo_3 = t1_1 + t1_2 + t1_4 + t_from_2_wo_3 = t2_1 + t2_2 + t2_4 + t_from_4_wo_3 = t4_1 + t4_2 + t4_4 + assert_allclose( + t_from_1_wo_3 + t_from_2_wo_3 + t_from_4_wo_3, + tglobal, + rtol=RTOL, + atol=ATOL, + ) + + # 1 cannot contribute to 4 and 4 cannot contribute to 1 by symmetry + assert_equal(t1_4, np.zeros_like(t1_4)) + assert_equal(t4_1, np.zeros_like(t4_1)) + + return super()._compare_results() + + +def test_filter_cellfrom(model): + harness = CellFromFilterTest("statepoint.15.h5", model) + harness.main() diff --git a/tests/regression_tests/filter_cellinstance/inputs_true.dat b/tests/regression_tests/filter_cellinstance/inputs_true.dat index f9d3ca58d4..2677d7ad2e 100644 --- a/tests/regression_tests/filter_cellinstance/inputs_true.dat +++ b/tests/regression_tests/filter_cellinstance/inputs_true.dat @@ -1,65 +1,64 @@ - - - - - - - - - 2 2 - 4 4 - -4 -4 - + + + + + + + + + + + + + + + + + + + + 2 2 + 4 4 + -4 -4 + 2 3 3 3 3 2 3 3 3 3 2 3 3 3 3 2 - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - 0.0 0.0 0.0 - - - - - - - 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 4 10 4 11 2 0 2 1 2 2 2 3 3 0 3 1 3 2 3 3 - - - 3 3 3 2 3 1 3 0 2 3 2 2 2 1 2 0 4 11 4 10 4 9 4 8 4 7 4 6 4 5 4 4 4 3 4 2 4 1 4 0 - - - 1 - total - - - 2 - total - - + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + 0.0 0.0 0.0 + + + + + + 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 4 10 4 11 2 0 2 1 2 2 2 3 3 0 3 1 3 2 3 3 + + + 3 3 3 2 3 1 3 0 2 3 2 2 2 1 2 0 4 11 4 10 4 9 4 8 4 7 4 6 4 5 4 4 4 3 4 2 4 1 4 0 + + + 1 + total + + + 2 + total + + + diff --git a/tests/regression_tests/filter_cellinstance/results_true.dat b/tests/regression_tests/filter_cellinstance/results_true.dat index c6832c6e1a..1529865954 100644 --- a/tests/regression_tests/filter_cellinstance/results_true.dat +++ b/tests/regression_tests/filter_cellinstance/results_true.dat @@ -1,84 +1,84 @@ k-combined: -1.045340E+00 4.712608E-02 +1.097679E+00 8.074294E-03 tally 1: -7.410456E-02 -1.481523E-03 -1.225401E-01 -3.736978E-03 -1.383468E-01 -4.249085E-03 -1.197994E-01 -3.155257E-03 -2.966893E-01 -2.045198E-02 -1.570258E-01 -5.313506E-03 -1.655546E-01 -6.093224E-03 -2.749756E-01 -1.582780E-02 -1.184686E-01 -3.543719E-03 -1.354860E-01 -3.990719E-03 -1.299309E-01 -3.988554E-03 -6.794395E-02 -1.135006E-03 -1.061333E+01 -2.508486E+01 -2.767786E+01 -1.781264E+02 -2.898957E+01 -2.015188E+02 -9.951936E+00 -2.345815E+01 -1.061333E+01 -2.508486E+01 -2.767786E+01 -1.781264E+02 -2.898957E+01 -2.015188E+02 -9.951936E+00 -2.345815E+01 +7.125168E-02 +1.412152E-03 +1.254059E-01 +4.145686E-03 +1.609454E-01 +6.174116E-03 +1.444278E-01 +4.523981E-03 +3.363588E-01 +2.342988E-02 +1.751677E-01 +7.108034E-03 +1.384074E-01 +3.949804E-03 +2.856450E-01 +1.824185E-02 +9.680810E-02 +2.256338E-03 +1.691663E-01 +6.456530E-03 +1.160968E-01 +3.233815E-03 +7.334131E-02 +1.456688E-03 +1.168548E+01 +3.210924E+01 +2.860162E+01 +1.876786E+02 +2.857267E+01 +1.996553E+02 +1.050245E+01 +2.569953E+01 +1.168548E+01 +3.210924E+01 +2.860162E+01 +1.876786E+02 +2.857267E+01 +1.996553E+02 +1.050245E+01 +2.569953E+01 tally 2: -9.951936E+00 -2.345815E+01 -2.898957E+01 -2.015188E+02 -2.767786E+01 -1.781264E+02 -1.061333E+01 -2.508486E+01 -9.951936E+00 -2.345815E+01 -2.898957E+01 -2.015188E+02 -2.767786E+01 -1.781264E+02 -1.061333E+01 -2.508486E+01 -6.794395E-02 -1.135006E-03 -1.299309E-01 -3.988554E-03 -1.354860E-01 -3.990719E-03 -1.184686E-01 -3.543719E-03 -2.749756E-01 -1.582780E-02 -1.655546E-01 -6.093224E-03 -1.570258E-01 -5.313506E-03 -2.966893E-01 -2.045198E-02 -1.197994E-01 -3.155257E-03 -1.383468E-01 -4.249085E-03 -1.225401E-01 -3.736978E-03 -7.410456E-02 -1.481523E-03 +1.050245E+01 +2.569953E+01 +2.857267E+01 +1.996553E+02 +2.860162E+01 +1.876786E+02 +1.168548E+01 +3.210924E+01 +1.050245E+01 +2.569953E+01 +2.857267E+01 +1.996553E+02 +2.860162E+01 +1.876786E+02 +1.168548E+01 +3.210924E+01 +7.334131E-02 +1.456688E-03 +1.160968E-01 +3.233815E-03 +1.691663E-01 +6.456530E-03 +9.680810E-02 +2.256338E-03 +2.856450E-01 +1.824185E-02 +1.384074E-01 +3.949804E-03 +1.751677E-01 +7.108034E-03 +3.363588E-01 +2.342988E-02 +1.444278E-01 +4.523981E-03 +1.609454E-01 +6.174116E-03 +1.254059E-01 +4.145686E-03 +7.125168E-02 +1.412152E-03 diff --git a/tests/regression_tests/filter_cellinstance/test.py b/tests/regression_tests/filter_cellinstance/test.py index 3f5fa54b26..61f17d88a6 100644 --- a/tests/regression_tests/filter_cellinstance/test.py +++ b/tests/regression_tests/filter_cellinstance/test.py @@ -60,8 +60,8 @@ def model(): [u3, u3, u2, u3], [u3, u3, u3, u2] ] - box = openmc.model.rectangular_prism(8.0, 8.0, boundary_type='reflective') - main_cell = openmc.Cell(fill=lat, region=box) + box = openmc.model.RectangularPrism(8.0, 8.0, boundary_type='reflective') + main_cell = openmc.Cell(fill=lat, region=-box) model.geometry.root_universe = openmc.Universe(cells=[main_cell]) model.geometry.determine_paths() @@ -69,7 +69,7 @@ def model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) instances = ([(c4, i) for i in range(c4.num_instances)] + [(c2, i) for i in range(c2.num_instances)] + diff --git a/tests/regression_tests/filter_distribcell/case-3/results_true.dat b/tests/regression_tests/filter_distribcell/case-3/results_true.dat index c12903feda..77f2a2a873 100644 --- a/tests/regression_tests/filter_distribcell/case-3/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-3/results_true.dat @@ -1 +1 @@ -d3ecf354b33d09064f43816325f33ec1229d1258255ee8413e83f7ba96ca921b8f4be738c10af99c5376bfe2b6be45976595634efcafddec0d9296eb45ffed3e \ No newline at end of file +93c1efbc586874a715982d26609e9e79232de25a0b73093a00938d658440644f0ee6bb823902a6ef1b7a2a855e67b2da0a0e767ef550f9ffa4dea723e35af6f5 \ No newline at end of file diff --git a/tests/regression_tests/filter_distribcell/case-4/geometry.xml b/tests/regression_tests/filter_distribcell/case-4/geometry.xml deleted file mode 100644 index c835218bc0..0000000000 --- a/tests/regression_tests/filter_distribcell/case-4/geometry.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - 1.0 - 3 -
0.0 0.0
- - 1 -1 1 - 1 -1 1 - 1 -
- - - - - -
diff --git a/tests/regression_tests/filter_distribcell/case-4/materials.xml b/tests/regression_tests/filter_distribcell/case-4/materials.xml deleted file mode 100644 index 2eb744fe64..0000000000 --- a/tests/regression_tests/filter_distribcell/case-4/materials.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/tests/regression_tests/filter_distribcell/case-4/model.xml b/tests/regression_tests/filter_distribcell/case-4/model.xml new file mode 100644 index 0000000000..8694219194 --- /dev/null +++ b/tests/regression_tests/filter_distribcell/case-4/model.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 + 3 +
0.0 0.0
+ + 1 +1 1 + 1 +1 1 + 1 +
+ + + + + +
+ + eigenvalue + 1000 + 1 + 0 + + + -1.0 -1.0 -1.0 1.0 1.0 1.0 + + + + + + 101 + + + 1 + total + + +
diff --git a/tests/regression_tests/filter_distribcell/case-4/results_true.dat b/tests/regression_tests/filter_distribcell/case-4/results_true.dat index 228456c58b..ed84838ee4 100644 --- a/tests/regression_tests/filter_distribcell/case-4/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-4/results_true.dat @@ -1,12 +1,12 @@ k-combined: -1.068596E-01 INF +1.069692E-01 INF tally 1: 1.812612E-02 3.285561E-04 2.870442E-02 8.239436E-04 -1.807689E-02 -3.267740E-04 +1.824058E-02 +3.327188E-04 2.660796E-02 7.079835E-04 1.961572E-02 diff --git a/tests/regression_tests/filter_distribcell/case-4/settings.xml b/tests/regression_tests/filter_distribcell/case-4/settings.xml deleted file mode 100644 index f3f0779bc9..0000000000 --- a/tests/regression_tests/filter_distribcell/case-4/settings.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - eigenvalue - 1000 - 1 - 0 - - - -1 -1 -1 1 1 1 - - - diff --git a/tests/regression_tests/filter_distribcell/case-4/tallies.xml b/tests/regression_tests/filter_distribcell/case-4/tallies.xml deleted file mode 100644 index b923c030b8..0000000000 --- a/tests/regression_tests/filter_distribcell/case-4/tallies.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - distribcell - 101 - - - - 1 - total - - - diff --git a/tests/regression_tests/filter_energyfun/inputs_true.dat b/tests/regression_tests/filter_energyfun/inputs_true.dat index 0f506f3f5f..b7a70290fe 100644 --- a/tests/regression_tests/filter_energyfun/inputs_true.dat +++ b/tests/regression_tests/filter_energyfun/inputs_true.dat @@ -1,35 +1,105 @@ - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 1e-05 0.369 1000.0 100000.0 600000.0 1000000.0 2000000.0 4000000.0 30000000.0 - 0.1 0.1 0.1333 0.158 0.18467 0.25618 0.4297 0.48 0.48 - - - Am241 - (n,gamma) - - - 1 - Am241 - (n,gamma) - - + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 1e-05 0.369 1000.0 100000.0 600000.0 1000000.0 2000000.0 4000000.0 30000000.0 + 0.1 0.1 0.1333 0.158 0.18467 0.25618 0.4297 0.48 0.48 + linear-linear + + + 1e-05 0.369 1000.0 100000.0 600000.0 1000000.0 2000000.0 4000000.0 30000000.0 + 0.1 0.1 0.1333 0.158 0.18467 0.25618 0.4297 0.48 0.48 + log-log + + + 1e-05 0.369 1000.0 100000.0 600000.0 1000000.0 2000000.0 4000000.0 30000000.0 + 0.1 0.1 0.1333 0.158 0.18467 0.25618 0.4297 0.48 0.48 + linear-log + + + 1e-05 0.369 1000.0 100000.0 600000.0 1000000.0 2000000.0 4000000.0 30000000.0 + 0.1 0.1 0.1333 0.158 0.18467 0.25618 0.4297 0.48 0.48 + log-linear + + + 0.0 5000000.0 10000000.0 15000000.0 + 0.2 0.7 0.7 0.2 + linear-linear + + + 0.0 5000000.0 10000000.0 15000000.0 + 0.2 0.7 0.7 0.2 + quadratic + + + 0.0 5000000.0 10000000.0 15000000.0 + 0.2 0.7 0.7 0.2 + cubic + + + 0.0 5000000.0 10000000.0 15000000.0 + 0.2 0.7 0.7 0.2 + histogram + + + Am241 + (n,gamma) + + + 1 + Am241 + (n,gamma) + + + 3 + Am241 + (n,gamma) + + + 4 + Am241 + (n,gamma) + + + 5 + Am241 + (n,gamma) + + + 6 + Am241 + (n,gamma) + + + 7 + Am241 + (n,gamma) + + + 8 + Am241 + (n,gamma) + + + 9 + Am241 + (n,gamma) + + + diff --git a/tests/regression_tests/filter_energyfun/results_true.dat b/tests/regression_tests/filter_energyfun/results_true.dat index a1fda93a81..d64de58a69 100644 --- a/tests/regression_tests/filter_energyfun/results_true.dat +++ b/tests/regression_tests/filter_energyfun/results_true.dat @@ -1,2 +1,10 @@ energyfunction nuclide score mean std. dev. -0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.74e-01 6.83e-03 +0 448ee8dfd19c4f Am241 ((n,gamma) / (n,gamma)) 1.74e-01 5.44e-03 + energyfunction nuclide score mean std. dev. +0 37e006ae6b2e74 Am241 (n,gamma) 8.35e-02 1.83e-03 + energyfunction nuclide score mean std. dev. +0 b4e2ac84068d2d Am241 (n,gamma) 8.39e-02 1.84e-03 + energyfunction nuclide score mean std. dev. +0 dacf88242512ea Am241 (n,gamma) 8.14e-02 1.78e-03 + energyfunction nuclide score mean std. dev. +0 fe168c70d9e078 Am241 (n,gamma) 1.09e-01 2.41e-03 diff --git a/tests/regression_tests/filter_energyfun/test.py b/tests/regression_tests/filter_energyfun/test.py index f61f05d785..295b8ebd8f 100644 --- a/tests/regression_tests/filter_energyfun/test.py +++ b/tests/regression_tests/filter_energyfun/test.py @@ -28,20 +28,66 @@ def model(): # Make an EnergyFunctionFilter directly from the x and y lists. filt1 = openmc.EnergyFunctionFilter(x, y) + # check interpolatoin property setter + assert filt1.interpolation == 'linear-linear' + + with pytest.raises(ValueError): + filt1.interpolation = '🥏' + # Also make a filter with the .from_tabulated1d constructor. Make sure # the filters are identical. tab1d = openmc.data.Tabulated1D(x, y) filt2 = openmc.EnergyFunctionFilter.from_tabulated1d(tab1d) assert filt1 == filt2, 'Error with the .from_tabulated1d constructor' + filt3 = openmc.EnergyFunctionFilter(x, y) + filt3.interpolation = 'log-log' + + filt4 = openmc.EnergyFunctionFilter(x, y) + filt4.interpolation = 'linear-log' + + filt5 = openmc.EnergyFunctionFilter(x, y) + filt5.interpolation = 'log-linear' + + # define a trapezoidal function for comparison + x = [0.0, 5e6, 1e7, 1.5e7] + y = [0.2, 0.7, 0.7, 0.2] + + filt6 = openmc.EnergyFunctionFilter(x, y) + + filt7 = openmc.EnergyFunctionFilter(x, y) + filt7.interpolation = 'quadratic' + + filt8 = openmc.EnergyFunctionFilter(x, y) + filt8.interpolation = 'cubic' + + filt9 = openmc.EnergyFunctionFilter(x, y) + filt9.interpolation = 'histogram' + + filters = [filt1, filt3, filt4, filt5, filt6, filt7, filt8, filt9] # Make tallies - tallies = [openmc.Tally(), openmc.Tally()] + tallies = [openmc.Tally() for _ in range(len(filters) + 1)] for t in tallies: t.scores = ['(n,gamma)'] t.nuclides = ['Am241'] - tallies[1].filters = [filt1] + + for t, f in zip(tallies[1:], filters): + t.filters = [f] + model.tallies.extend(tallies) + interpolation_vals = \ + list(openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES.keys()) + for i_val in interpolation_vals: + # breakpoint here is fake and unused + t1d = openmc.data.Tabulated1D(x, + y, + breakpoints=[1], + interpolation=[i_val]) + f = openmc.EnergyFunctionFilter.from_tabulated1d(t1d) + assert f.interpolation == \ + openmc.EnergyFunctionFilter.INTERPOLATION_SCHEMES[i_val] + return model @@ -50,13 +96,74 @@ class FilterEnergyFunHarness(PyAPITestHarness): # Read the statepoint file. sp = openmc.StatePoint(self._sp_name) + dataframes_string = "" # Use tally arithmetic to compute the branching ratio. br_tally = sp.tallies[2] / sp.tallies[1] + dataframes_string += br_tally.get_pandas_dataframe().to_string() + '\n' + + for t_id in (3, 4, 5, 6): + ef_tally = sp.tallies[t_id] + dataframes_string += ef_tally.get_pandas_dataframe().to_string() + '\n' # Output the tally in a Pandas DataFrame. - return br_tally.get_pandas_dataframe().to_string() + '\n' + return dataframes_string + def _compare_results(self): + super()._compare_results() + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + + # statepoint file round-trip checks + + # linear-linear interpolation tally + sp_lin_lin_tally = sp.get_tally(id=2) + sp_lin_lin_filt = sp_lin_lin_tally.find_filter(openmc.EnergyFunctionFilter) + + model_lin_lin_tally = self._model.tallies[1] + model_lin_lin_filt = model_lin_lin_tally.find_filter(openmc.EnergyFunctionFilter) + + assert sp_lin_lin_filt.interpolation == 'linear-linear' + assert all(sp_lin_lin_filt.energy == model_lin_lin_filt.energy) + assert all(sp_lin_lin_filt.y == model_lin_lin_filt.y) + + # log-log interpolation tally + sp_log_log_tally = sp.get_tally(id=3) + sp_log_log_filt = sp_log_log_tally.find_filter(openmc.EnergyFunctionFilter) + + model_log_log_tally = self._model.tallies[2] + model_log_log_filt = model_log_log_tally.find_filter(openmc.EnergyFunctionFilter) + + assert sp_log_log_filt.interpolation == 'log-log' + assert all(sp_log_log_filt.energy == model_log_log_filt.energy) + assert all(sp_log_log_filt.y == model_log_log_filt.y) + + # because the values of y are monotonically increasing, + # we expect the log-log tally to have a higher value + assert all(sp_lin_lin_tally.mean < sp_log_log_tally.mean) + + sp_lin_log_tally = self._model.tallies[3] + sp_lin_log_filt = sp_lin_log_tally.find_filter(openmc.EnergyFunctionFilter) + assert sp_lin_log_filt.interpolation == 'linear-log' + + sp_log_lin_tally = self._model.tallies[4] + sp_log_lin_filt = sp_log_lin_tally.find_filter(openmc.EnergyFunctionFilter) + assert sp_log_lin_filt.interpolation == 'log-linear' + + # check that the cubic interpolation provides a higher value + # than linear-linear + contrived_lin_lin_tally = sp.get_tally(id=6) + contrived_quadratic_tally = sp.get_tally(id=7) + contrived_cubic_tally = sp.get_tally(id=8) + + assert all(contrived_lin_lin_tally.mean < contrived_quadratic_tally.mean) + assert all(contrived_lin_lin_tally.mean < contrived_cubic_tally.mean) + + # check that the histogram tally is less than the quadratic/cubic interpolations + histogram_tally = sp.get_tally(id=9) + assert all(histogram_tally.mean < contrived_quadratic_tally.mean) + assert all(histogram_tally.mean < contrived_cubic_tally.mean) def test_filter_energyfun(model): harness = FilterEnergyFunHarness('statepoint.5.h5', model) - harness.main() + harness.main() \ No newline at end of file diff --git a/tests/regression_tests/filter_mesh/inputs_true.dat b/tests/regression_tests/filter_mesh/inputs_true.dat index d0c481d604..10f70a720f 100644 --- a/tests/regression_tests/filter_mesh/inputs_true.dat +++ b/tests/regression_tests/filter_mesh/inputs_true.dat @@ -1,150 +1,151 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 5 - -7.5 - 7.5 - - - 5 5 - -7.5 -7.5 - 7.5 7.5 - - - 5 5 5 - -7.5 -7.5 -7.5 - 7.5 7.5 7.5 - - - -7.5 -6.617647058823529 -5.735294117647059 -4.852941176470589 -3.9705882352941178 -3.0882352941176467 -2.2058823529411766 -1.3235294117647065 -0.4411764705882355 0.4411764705882355 1.3235294117647065 2.2058823529411757 3.0882352941176467 3.9705882352941178 4.852941176470587 5.735294117647058 6.617647058823529 7.5 - -7.5 -6.617647058823529 -5.735294117647059 -4.852941176470589 -3.9705882352941178 -3.0882352941176467 -2.2058823529411766 -1.3235294117647065 -0.4411764705882355 0.4411764705882355 1.3235294117647065 2.2058823529411757 3.0882352941176467 3.9705882352941178 4.852941176470587 5.735294117647058 6.617647058823529 7.5 - 1.0 1.223224374241637 1.4962778697388448 1.8302835609029084 2.2388474634702153 2.7386127875258306 3.3499379133114306 4.09772570775871 5.012437964687018 6.131336292779302 7.500000000000001 - - - 0.0 0.4411764705882353 0.8823529411764706 1.3235294117647058 1.7647058823529411 2.2058823529411766 2.6470588235294117 3.0882352941176467 3.5294117647058822 3.9705882352941178 4.411764705882353 4.852941176470588 5.294117647058823 5.735294117647059 6.1764705882352935 6.617647058823529 7.0588235294117645 7.5 - 0.0 0.3490658503988659 0.6981317007977318 1.0471975511965976 1.3962634015954636 1.7453292519943295 2.0943951023931953 2.443460952792061 2.792526803190927 3.141592653589793 3.490658503988659 3.839724354387525 4.1887902047863905 4.537856055185257 4.886921905584122 5.235987755982989 5.585053606381854 5.93411945678072 6.283185307179586 - -7.5 -6.5625 -5.625 -4.6875 -3.75 -2.8125 -1.875 -0.9375 0.0 0.9375 1.875 2.8125 3.75 4.6875 5.625 6.5625 7.5 - - - 0.0 0.4411764705882353 0.8823529411764706 1.3235294117647058 1.7647058823529411 2.2058823529411766 2.6470588235294117 3.0882352941176467 3.5294117647058822 3.9705882352941178 4.411764705882353 4.852941176470588 5.294117647058823 5.735294117647059 6.1764705882352935 6.617647058823529 7.0588235294117645 7.5 - 0.0 0.39269908169872414 0.7853981633974483 1.1780972450961724 1.5707963267948966 1.9634954084936207 2.356194490192345 2.748893571891069 3.141592653589793 - 0.0 0.3490658503988659 0.6981317007977318 1.0471975511965976 1.3962634015954636 1.7453292519943295 2.0943951023931953 2.443460952792061 2.792526803190927 3.141592653589793 3.490658503988659 3.839724354387525 4.1887902047863905 4.537856055185257 4.886921905584122 5.235987755982989 5.585053606381854 5.93411945678072 6.283185307179586 - - - 1 - - - 1 - - - 2 - - - 2 - - - 3 - - - 3 - - - 4 - - - 4 - - - 5 - - - 5 - - - 6 - - - 6 - - - 1 - total - - - 7 - current - - - 2 - total - - - 8 - current - - - 3 - total - - - 9 - current - - - 4 - total - - - 10 - current - - - 5 - total - - - 11 - current - - - 6 - total - - - 12 - current - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 5 + -7.5 + 7.5 + + + 5 5 + -7.5 -7.5 + 7.5 7.5 + + + 5 5 5 + -7.5 -7.5 -7.5 + 7.5 7.5 7.5 + + + -7.5 -6.617647058823529 -5.735294117647059 -4.852941176470589 -3.9705882352941178 -3.0882352941176467 -2.2058823529411766 -1.3235294117647065 -0.4411764705882355 0.4411764705882355 1.3235294117647065 2.2058823529411757 3.0882352941176467 3.9705882352941178 4.852941176470587 5.735294117647058 6.617647058823529 7.5 + -7.5 -6.617647058823529 -5.735294117647059 -4.852941176470589 -3.9705882352941178 -3.0882352941176467 -2.2058823529411766 -1.3235294117647065 -0.4411764705882355 0.4411764705882355 1.3235294117647065 2.2058823529411757 3.0882352941176467 3.9705882352941178 4.852941176470587 5.735294117647058 6.617647058823529 7.5 + 1.0 1.223224374241637 1.4962778697388448 1.8302835609029084 2.2388474634702153 2.7386127875258306 3.3499379133114306 4.09772570775871 5.012437964687018 6.131336292779302 7.500000000000001 + + + 0.0 0.4411764705882353 0.8823529411764706 1.3235294117647058 1.7647058823529411 2.2058823529411766 2.6470588235294117 3.0882352941176467 3.5294117647058822 3.9705882352941178 4.411764705882353 4.852941176470588 5.294117647058823 5.735294117647059 6.1764705882352935 6.617647058823529 7.0588235294117645 7.5 + 0.0 0.3490658503988659 0.6981317007977318 1.0471975511965976 1.3962634015954636 1.7453292519943295 2.0943951023931953 2.443460952792061 2.792526803190927 3.141592653589793 3.490658503988659 3.839724354387525 4.1887902047863905 4.537856055185257 4.886921905584122 5.235987755982989 5.585053606381854 5.93411945678072 6.283185307179586 + 0.0 0.9375 1.875 2.8125 3.75 4.6875 5.625 6.5625 7.5 8.4375 9.375 10.3125 11.25 12.1875 13.125 14.0625 15.0 + 0.0 0.0 -7.5 + + + 0.0 0.4411764705882353 0.8823529411764706 1.3235294117647058 1.7647058823529411 2.2058823529411766 2.6470588235294117 3.0882352941176467 3.5294117647058822 3.9705882352941178 4.411764705882353 4.852941176470588 5.294117647058823 5.735294117647059 6.1764705882352935 6.617647058823529 7.0588235294117645 7.5 + 0.0 0.39269908169872414 0.7853981633974483 1.1780972450961724 1.5707963267948966 1.9634954084936207 2.356194490192345 2.748893571891069 3.141592653589793 + 0.0 0.3490658503988659 0.6981317007977318 1.0471975511965976 1.3962634015954636 1.7453292519943295 2.0943951023931953 2.443460952792061 2.792526803190927 3.141592653589793 3.490658503988659 3.839724354387525 4.1887902047863905 4.537856055185257 4.886921905584122 5.235987755982989 5.585053606381854 5.93411945678072 6.283185307179586 + 0.0 0.0 0.0 + + + 1 + + + 1 + + + 2 + + + 2 + + + 3 + + + 3 + + + 4 + + + 4 + + + 5 + + + 5 + + + 6 + + + 6 + + + 1 + total + + + 7 + current + + + 2 + total + + + 8 + current + + + 3 + total + + + 9 + current + + + 4 + total + + + 10 + current + + + 5 + total + + + 11 + current + + + 6 + total + + + 12 + current + + + diff --git a/tests/regression_tests/filter_mesh/results_true.dat b/tests/regression_tests/filter_mesh/results_true.dat index 97735130e1..34e99c17e2 100644 --- a/tests/regression_tests/filter_mesh/results_true.dat +++ b/tests/regression_tests/filter_mesh/results_true.dat @@ -1 +1 @@ -6e02d01fc115c54e4c60477a9f963149bc83c82e6a7bdb2d29125b131546150c59de5ac3aae5b9469b89b343ecf2574f6c949b918ccb43231e8666ee2b7c1b7c \ No newline at end of file +e07ed2bc8893c69721abf61b123336f1f6128a3bee6ec63b84d1f549f31707a74a6ce885091ccc0eac6b7f16f7cab39ede4784584c08825829e108de878ea5fb \ No newline at end of file diff --git a/tests/regression_tests/filter_mesh/test.py b/tests/regression_tests/filter_mesh/test.py index fc24b1b9bc..165ba2a0c0 100644 --- a/tests/regression_tests/filter_mesh/test.py +++ b/tests/regression_tests/filter_mesh/test.py @@ -19,12 +19,12 @@ def model(): zr.add_nuclide('Zr90', 1.0) model.materials.extend([fuel, zr]) - box1 = openmc.model.rectangular_prism(10.0, 10.0) - box2 = openmc.model.rectangular_prism(20.0, 20.0, boundary_type='reflective') + box1 = openmc.model.RectangularPrism(10.0, 10.0) + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') - cell1 = openmc.Cell(fill=fuel, region=box1 & +bottom & -top) - cell2 = openmc.Cell(fill=zr, region=~box1 & box2 & +bottom & -top) + cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) + cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) model.settings.batches = 5 @@ -46,21 +46,44 @@ def model(): mesh_3d.dimension = [5, 5, 5] mesh_3d.lower_left = [-7.5, -7.5, -7.5] mesh_3d.upper_right = [7.5, 7.5, 7.5] + dx = dy = dz = 15 / 5 + reg_mesh_exp_vols = np.full(mesh_3d.dimension, dx*dy*dz) + np.testing.assert_equal(mesh_3d.volumes, reg_mesh_exp_vols) recti_mesh = openmc.RectilinearMesh() recti_mesh.x_grid = np.linspace(-7.5, 7.5, 18) recti_mesh.y_grid = np.linspace(-7.5, 7.5, 18) recti_mesh.z_grid = np.logspace(0, np.log10(7.5), 11) + dx = dy = 15 / 17 + dz = np.diff(np.logspace(0, np.log10(7.5), 11)) + dxdy = np.full(recti_mesh.dimension[:2], dx*dy) + recti_mesh_exp_vols = np.multiply.outer(dxdy, dz) + np.testing.assert_allclose(recti_mesh.volumes, recti_mesh_exp_vols) - cyl_mesh = openmc.CylindricalMesh() - cyl_mesh.r_grid = np.linspace(0, 7.5, 18) - cyl_mesh.phi_grid = np.linspace(0, 2*pi, 19) - cyl_mesh.z_grid = np.linspace(-7.5, 7.5, 17) + cyl_mesh = openmc.CylindricalMesh( + origin=(0, 0, -7.5), + r_grid=np.linspace(0, 7.5, 18), + phi_grid=np.linspace(0, 2*pi, 19), + z_grid=np.linspace(0, 15, 17), + ) + dr = 0.5 * np.diff(np.linspace(0, 7.5, 18)**2) + dp = np.full(cyl_mesh.dimension[1], 2*pi / 18) + dz = np.full(cyl_mesh.dimension[2], 15 / 16) + drdp = np.outer(dr, dp) + cyl_mesh_exp_vols = np.multiply.outer(drdp, dz) + np.testing.assert_allclose(cyl_mesh.volumes, cyl_mesh_exp_vols) - sph_mesh = openmc.SphericalMesh() - sph_mesh.r_grid = np.linspace(0, 7.5, 18) - sph_mesh.theta_grid = np.linspace(0, pi, 9) - sph_mesh.phi_grid = np.linspace(0, 2*pi, 19) + sph_mesh = openmc.SphericalMesh( + r_grid=np.linspace(0, 7.5, 18), + theta_grid=np.linspace(0, pi, 9), + phi_grid=np.linspace(0, 2*pi, 19) + ) + dr = np.diff(np.linspace(0, 7.5, 18)**3) / 3 + dt = np.diff(-np.cos(np.linspace(0, pi, 9))) + dp = np.full(sph_mesh.dimension[2], 2*pi / 18) + drdt = np.outer(dr, dt) + sph_mesh_exp_vols = np.multiply.outer(drdt, dp) + np.testing.assert_allclose(sph_mesh.volumes, sph_mesh_exp_vols) # Create filters reg_filters = [ diff --git a/tests/regression_tests/filter_meshborn/__init__.py b/tests/regression_tests/filter_meshborn/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/filter_meshborn/inputs_true.dat b/tests/regression_tests/filter_meshborn/inputs_true.dat new file mode 100644 index 0000000000..a94646ec8d --- /dev/null +++ b/tests/regression_tests/filter_meshborn/inputs_true.dat @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + fixed source + 2000 + 8 + + + 0.0 -10.0 -10.0 10.0 10.0 10.0 + + + + + + 2 2 1 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + 1 + + + 1 + + + 1 2 + scatter + + + 1 + scatter + + + 2 + scatter + + + scatter + + + diff --git a/tests/regression_tests/filter_meshborn/results_true.dat b/tests/regression_tests/filter_meshborn/results_true.dat new file mode 100644 index 0000000000..62f2707ffc --- /dev/null +++ b/tests/regression_tests/filter_meshborn/results_true.dat @@ -0,0 +1,54 @@ +tally 1: +0.000000E+00 +0.000000E+00 +2.631246E+01 +8.845079E+01 +0.000000E+00 +0.000000E+00 +2.450265E+00 +9.462266E-01 +0.000000E+00 +0.000000E+00 +3.878380E+02 +1.881752E+04 +0.000000E+00 +0.000000E+00 +2.932956E+01 +1.091674E+02 +0.000000E+00 +0.000000E+00 +1.837753E+00 +5.195343E-01 +0.000000E+00 +0.000000E+00 +2.944919E+01 +1.095819E+02 +0.000000E+00 +0.000000E+00 +2.921731E+01 +1.097387E+02 +0.000000E+00 +0.000000E+00 +4.019442E+02 +2.021184E+04 +tally 2: +2.876273E+01 +1.060244E+02 +4.171676E+02 +2.176683E+04 +3.128695E+01 +1.238772E+02 +4.311615E+02 +2.325871E+04 +tally 3: +0.000000E+00 +0.000000E+00 +4.452055E+02 +2.478148E+04 +0.000000E+00 +0.000000E+00 +4.631732E+02 +2.683862E+04 +tally 4: +9.083787E+02 +1.031695E+05 diff --git a/tests/regression_tests/filter_meshborn/test.py b/tests/regression_tests/filter_meshborn/test.py new file mode 100644 index 0000000000..ff4adbc9f0 --- /dev/null +++ b/tests/regression_tests/filter_meshborn/test.py @@ -0,0 +1,107 @@ +"""Test the meshborn filter using a fixed source calculation on a H1 sphere. + +""" + +from numpy.testing import assert_allclose +import numpy as np +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +RTOL = 1.0e-7 +ATOL = 0.0 + + +@pytest.fixture +def model(): + """Sphere of H1 with one hemisphere containing the source (x>0) and one + hemisphere with no source (x<0). + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # Materials + h1 = openmc.Material() + h1.add_nuclide("H1", 1.0) + h1.set_density("g/cm3", 1.0) + model.materials = openmc.Materials([h1]) + + # Core geometry + r = 10.0 + sphere = openmc.Sphere(r=r, boundary_type="reflective") + core = openmc.Cell(fill=h1, region=-sphere) + model.geometry = openmc.Geometry([core]) + + # Settings + model.settings.run_mode = 'fixed source' + model.settings.particles = 2000 + model.settings.batches = 8 + distribution = openmc.stats.Box((0., -r, -r), (r, r, r)) + model.settings.source = openmc.IndependentSource(space=distribution) + + # Tallies + mesh = openmc.RegularMesh() + mesh.dimension = (2, 2, 1) + mesh.lower_left = (-r, -r, -r) + mesh.upper_right = (r, r, r) + + f_1 = openmc.MeshFilter(mesh) + f_2 = openmc.MeshBornFilter(mesh) + + t_1 = openmc.Tally(name="scatter") + t_1.filters = [f_1, f_2] + t_1.scores = ["scatter"] + + t_2 = openmc.Tally(name="scatter-mesh") + t_2.filters = [f_1] + t_2.scores = ["scatter"] + + t_3 = openmc.Tally(name="scatter-meshborn") + t_3.filters = [f_2] + t_3.scores = ["scatter"] + + t_4 = openmc.Tally(name="scatter-total") + t_4.scores = ["scatter"] + + model.tallies = [t_1, t_2, t_3, t_4] + + return model + + +class MeshBornFilterTest(PyAPITestHarness): + + def _compare_results(self): + """Additional unit tests on the tally results to check consistency.""" + with openmc.StatePoint(self.statepoint_name) as sp: + + t1 = sp.get_tally(name="scatter").mean.reshape(4, 4) + t2 = sp.get_tally(name="scatter-mesh").mean.reshape(4) + t3 = sp.get_tally(name="scatter-meshborn").mean.reshape(4) + t4 = sp.get_tally(name="scatter-total").mean.reshape(1) + + # Consistency between mesh+meshborn matrix tally and meshborn tally + for i in range(4): + assert_allclose(t1[:, i].sum(), t3[i], rtol=RTOL, atol=ATOL) + + # Consistency between mesh+meshborn matrix tally and mesh tally + for i in range(4): + assert_allclose(t1[i, :].sum(), t2[i], rtol=RTOL, atol=ATOL) + + # Mesh cells in x<0 do not contribute to meshborn + assert_allclose(t1[:, 0].sum(), np.zeros(4), rtol=RTOL, atol=ATOL) + assert_allclose(t1[:, 2].sum(), np.zeros(4), rtol=RTOL, atol=ATOL) + + # Consistency with total scattering + assert_allclose(t1.sum(), t4, rtol=RTOL, atol=ATOL) + assert_allclose(t2.sum(), t4, rtol=RTOL, atol=ATOL) + assert_allclose(t3.sum(), t4, rtol=RTOL, atol=ATOL) + + super()._compare_results() + + +def test_filter_meshborn(model): + harness = MeshBornFilterTest("statepoint.8.h5", model) + harness.main() diff --git a/tests/regression_tests/filter_musurface/__init__.py b/tests/regression_tests/filter_musurface/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/filter_musurface/inputs_true.dat b/tests/regression_tests/filter_musurface/inputs_true.dat new file mode 100644 index 0000000000..6db8543c2d --- /dev/null +++ b/tests/regression_tests/filter_musurface/inputs_true.dat @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 1 + + + -1.0 -0.5 0.0 0.5 1.0 + + + 1 2 + current + + + diff --git a/tests/regression_tests/filter_musurface/results_true.dat b/tests/regression_tests/filter_musurface/results_true.dat new file mode 100644 index 0000000000..4cdd7dbf50 --- /dev/null +++ b/tests/regression_tests/filter_musurface/results_true.dat @@ -0,0 +1,11 @@ +k-combined: +1.202075E-01 1.113188E-02 +tally 1: +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.230000E-01 +1.791510E-01 +3.869000E+00 +3.002523E+00 diff --git a/tests/regression_tests/filter_musurface/test.py b/tests/regression_tests/filter_musurface/test.py new file mode 100644 index 0000000000..f2ec96b495 --- /dev/null +++ b/tests/regression_tests/filter_musurface/test.py @@ -0,0 +1,43 @@ +import numpy as np +from math import pi + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.Model() + fuel = openmc.Material() + fuel.set_density('g/cm3', 10.0) + fuel.add_nuclide('U235', 1.0) + zr = openmc.Material() + zr.set_density('g/cm3', 1.0) + zr.add_nuclide('Zr90', 1.0) + + cyl1 = openmc.ZCylinder(r=1.0) + cyl2 = openmc.ZCylinder(r=3.0, boundary_type='vacuum') + cell1 = openmc.Cell(fill=fuel, region=-cyl1) + cell2 = openmc.Cell(fill=zr, region=+cyl1 & -cyl2) + model.geometry = openmc.Geometry([cell1, cell2]) + + model.settings.batches = 5 + model.settings.inactive = 0 + model.settings.particles = 1000 + + # Create a tally for current through the first surface binned by mu + surf_filter = openmc.SurfaceFilter([cyl1]) + mu_filter = openmc.MuSurfaceFilter([-1.0, -0.5, 0.0, 0.5, 1.0]) + tally = openmc.Tally() + tally.filters = [surf_filter, mu_filter] + tally.scores = ['current'] + model.tallies.append(tally) + + return model + + +def test_filter_musurface(model): + harness = PyAPITestHarness('statepoint.5.h5', model) + harness.main() diff --git a/tests/regression_tests/filter_translations/inputs_true.dat b/tests/regression_tests/filter_translations/inputs_true.dat index d38bec39f1..41ae9b6dc1 100644 --- a/tests/regression_tests/filter_translations/inputs_true.dat +++ b/tests/regression_tests/filter_translations/inputs_true.dat @@ -1,84 +1,83 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 3 4 5 - -9 -9 -9 - 9 9 9 - - - -9.0 0.0 9.0 - -9.0 -3.0 3.0 9.0 - -9.0 -4.5 0.0 4.5 9.0 - - - 3 4 5 - -19 -4 -9 - -1 14 9 - - - -19.0 -10.0 -1.0 - -4.0 2.0 8.0 14.0 - -9.0 -4.5 0.0 4.5 9.0 - - - 1 - - - 2 - - - 3 - - - 4 - - - 1 - total - - - 2 - total - - - 3 - total - - - 4 - total - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 3 4 5 + -9 -9 -9 + 9 9 9 + + + -9.0 0.0 9.0 + -9.0 -3.0 3.0 9.0 + -9.0 -4.5 0.0 4.5 9.0 + + + 3 4 5 + -19 -4 -9 + -1 14 9 + + + -19.0 -10.0 -1.0 + -4.0 2.0 8.0 14.0 + -9.0 -4.5 0.0 4.5 9.0 + + + 1 + + + 2 + + + 3 + + + 4 + + + 1 + total + + + 2 + total + + + 3 + total + + + 4 + total + + + diff --git a/tests/regression_tests/filter_translations/results_true.dat b/tests/regression_tests/filter_translations/results_true.dat index e5e620b97d..a63586d2dd 100644 --- a/tests/regression_tests/filter_translations/results_true.dat +++ b/tests/regression_tests/filter_translations/results_true.dat @@ -1,342 +1,342 @@ k-combined: -7.952381E-01 3.714273E-02 +7.729082E-01 3.775399E-02 tally 1: -5.340506E-02 -5.744901E-04 -8.160966E-02 -1.360702E-03 -5.306458E-02 -5.712302E-04 -1.263577E-01 -3.302526E-03 -3.858930E-01 -3.008104E-02 -1.511449E-01 -4.656419E-03 -1.279399E-01 -3.429368E-03 -3.676349E-01 -2.739091E-02 -1.410822E-01 -4.100718E-03 -5.379722E-02 -5.997669E-04 -8.933947E-02 -1.635151E-03 -5.212766E-02 -5.497167E-04 -7.332108E-02 -1.098591E-03 -1.040477E-01 -2.211291E-03 -5.326449E-02 -5.881665E-04 -1.952299E-01 -7.733668E-03 -5.683168E-01 -6.600345E-02 -1.866765E-01 -7.004663E-03 -2.262997E-01 -1.033597E-02 -6.280081E-01 -7.932045E-02 -1.861170E-01 -6.952815E-03 -7.317076E-02 -1.109977E-03 -1.145829E-01 -2.646373E-03 -6.567032E-02 -8.836880E-04 -6.614241E-02 -8.902720E-04 -1.083575E-01 -2.356190E-03 -6.479323E-02 -8.604886E-04 -2.202887E-01 -9.846484E-03 -9.405324E-01 -2.263338E-01 -1.993991E-01 -7.984693E-03 -2.310168E-01 -1.084962E-02 -9.617014E-01 -2.432501E-01 -2.127786E-01 -9.085201E-03 -7.772444E-02 -1.217267E-03 -1.170744E-01 -2.791253E-03 -7.480464E-02 -1.126824E-03 -6.321842E-02 -8.234272E-04 -1.002360E-01 -2.026069E-03 -6.532157E-02 -8.622865E-04 -1.934126E-01 -7.639724E-03 -5.583838E-01 -6.274792E-02 -1.914812E-01 -7.440011E-03 -1.808706E-01 -6.774850E-03 -6.259735E-01 -7.874410E-02 -1.959623E-01 -7.771208E-03 -6.301362E-02 -8.178688E-04 -1.062362E-01 -2.292485E-03 -5.916939E-02 -7.078944E-04 -5.265976E-02 -5.634399E-04 -6.747063E-02 -9.141711E-04 -4.478697E-02 -4.077583E-04 -1.277486E-01 -3.308655E-03 -3.585674E-01 -2.641571E-02 -1.192882E-01 -2.901276E-03 -1.457696E-01 -4.299462E-03 -3.467377E-01 -2.463831E-02 -1.305729E-01 -3.605534E-03 -5.067715E-02 -5.273379E-04 -7.374940E-02 -1.102912E-03 -5.226497E-02 -5.649644E-04 +5.296804E-02 +5.661701E-04 +8.356446E-02 +1.412139E-03 +5.041335E-02 +5.143568E-04 +1.299348E-01 +3.467618E-03 +3.929702E-01 +3.147038E-02 +1.379707E-01 +3.888484E-03 +1.405034E-01 +4.473799E-03 +3.785796E-01 +2.940585E-02 +1.422010E-01 +4.113723E-03 +5.647073E-02 +6.735251E-04 +7.911154E-02 +1.329137E-03 +5.160755E-02 +5.361448E-04 +6.669424E-02 +9.090832E-04 +1.008621E-01 +2.134534E-03 +6.808932E-02 +9.355993E-04 +1.873006E-01 +7.135961E-03 +6.221575E-01 +7.819842E-02 +1.856653E-01 +6.954762E-03 +2.014929E-01 +8.327845E-03 +5.853251E-01 +6.945708E-02 +1.709645E-01 +5.917124E-03 +7.214913E-02 +1.058962E-03 +1.027720E-01 +2.138475E-03 +6.099853E-02 +7.493941E-04 +6.892071E-02 +9.630680E-04 +1.035459E-01 +2.173883E-03 +6.973870E-02 +9.904237E-04 +2.125703E-01 +9.112659E-03 +9.012205E-01 +2.163546E-01 +2.066426E-01 +8.617414E-03 +2.258950E-01 +1.039607E-02 +9.476792E-01 +2.350708E-01 +2.225585E-01 +1.017898E-02 +7.111503E-02 +1.036847E-03 +1.117012E-01 +2.530040E-03 +6.870474E-02 +9.551035E-04 +5.738897E-02 +6.699030E-04 +9.522335E-02 +1.835769E-03 +6.570917E-02 +8.656870E-04 +1.945592E-01 +7.593336E-03 +5.514753E-01 +6.122981E-02 +2.144202E-01 +9.421739E-03 +1.971631E-01 +7.944046E-03 +6.088996E-01 +7.442954E-02 +1.965447E-01 +7.765628E-03 +7.005494E-02 +1.012891E-03 +1.010633E-01 +2.084095E-03 +6.145926E-02 +7.694351E-04 +4.999479E-02 +5.129164E-04 +7.238243E-02 +1.062921E-03 +4.902309E-02 +4.852193E-04 +1.324655E-01 +3.642431E-03 +3.305312E-01 +2.265726E-02 +1.332993E-01 +3.728385E-03 +1.547469E-01 +4.894837E-03 +3.625944E-01 +2.747313E-02 +1.435761E-01 +4.334405E-03 +5.789603E-02 +7.065383E-04 +7.589559E-02 +1.205386E-03 +5.210018E-02 +5.790843E-04 tally 2: -2.442367E-01 -1.209459E-02 -2.543262E-01 -1.314598E-02 -5.639744E-01 -6.449959E-02 -6.415410E-01 -8.269091E-02 -2.631124E-01 -1.406544E-02 -2.513955E-01 -1.285616E-02 -3.570608E-01 -2.559924E-02 -2.978502E-01 -1.787722E-02 -1.142500E+00 -2.799696E-01 -1.072656E+00 -2.465813E-01 -3.720711E-01 -2.776168E-02 -3.520288E-01 -2.482313E-02 -3.406093E-01 -2.332649E-02 -3.186893E-01 -2.046998E-02 -1.039053E+00 -2.337911E-01 -1.055863E+00 -2.442418E-01 -3.636093E-01 -2.679467E-02 -3.514845E-01 -2.481131E-02 -2.356811E-01 -1.112305E-02 -2.424590E-01 -1.205945E-02 -5.851380E-01 -6.974618E-02 -5.788717E-01 -6.882684E-02 -2.430058E-01 -1.205907E-02 -2.390704E-01 -1.167330E-02 +2.572693E-01 +1.338921E-02 +2.567576E-01 +1.333558E-02 +6.127899E-01 +7.781914E-02 +6.163075E-01 +7.600273E-02 +2.566545E-01 +1.372553E-02 +2.509744E-01 +1.286275E-02 +3.366720E-01 +2.282560E-02 +3.216175E-01 +2.103222E-02 +1.118187E+00 +2.697988E-01 +1.050003E+00 +2.391098E-01 +3.407360E-01 +2.332214E-02 +3.150094E-01 +2.006040E-02 +3.130223E-01 +2.001074E-02 +3.238651E-01 +2.101351E-02 +1.061186E+00 +2.372828E-01 +1.099461E+00 +2.597134E-01 +3.445524E-01 +2.400393E-02 +3.429055E-01 +2.372392E-02 +2.299776E-01 +1.064851E-02 +2.208901E-01 +9.829109E-03 +6.015657E-01 +7.399276E-02 +5.851229E-01 +7.165716E-02 +2.583609E-01 +1.364046E-02 +2.456380E-01 +1.257735E-02 tally 3: -5.340506E-02 -5.744901E-04 -8.160966E-02 -1.360702E-03 -5.306458E-02 -5.712302E-04 -1.263577E-01 -3.302526E-03 -3.858930E-01 -3.008104E-02 -1.511449E-01 -4.656419E-03 -1.279399E-01 -3.429368E-03 -3.676349E-01 -2.739091E-02 -1.410822E-01 -4.100718E-03 -5.379722E-02 -5.997669E-04 -8.933947E-02 -1.635151E-03 -5.212766E-02 -5.497167E-04 -7.332108E-02 -1.098591E-03 -1.040477E-01 -2.211291E-03 -5.326449E-02 -5.881665E-04 -1.952299E-01 -7.733668E-03 -5.683168E-01 -6.600345E-02 -1.866765E-01 -7.004663E-03 -2.262997E-01 -1.033597E-02 -6.280081E-01 -7.932045E-02 -1.861170E-01 -6.952815E-03 -7.317076E-02 -1.109977E-03 -1.145829E-01 -2.646373E-03 -6.567032E-02 -8.836880E-04 -6.614241E-02 -8.902720E-04 -1.083575E-01 -2.356190E-03 -6.479323E-02 -8.604886E-04 -2.202887E-01 -9.846484E-03 -9.405324E-01 -2.263338E-01 -1.993991E-01 -7.984693E-03 -2.310168E-01 -1.084962E-02 -9.617014E-01 -2.432501E-01 -2.127786E-01 -9.085201E-03 -7.772444E-02 -1.217267E-03 -1.170744E-01 -2.791253E-03 -7.480464E-02 -1.126824E-03 -6.321842E-02 -8.234272E-04 -1.002360E-01 -2.026069E-03 -6.532157E-02 -8.622865E-04 -1.934126E-01 -7.639724E-03 -5.583838E-01 -6.274792E-02 -1.914812E-01 -7.440011E-03 -1.808706E-01 -6.774850E-03 -6.259735E-01 -7.874410E-02 -1.959623E-01 -7.771208E-03 -6.301362E-02 -8.178688E-04 -1.062362E-01 -2.292485E-03 -5.916939E-02 -7.078944E-04 -5.265976E-02 -5.634399E-04 -6.747063E-02 -9.141711E-04 -4.478697E-02 -4.077583E-04 -1.277486E-01 -3.308655E-03 -3.585674E-01 -2.641571E-02 -1.192882E-01 -2.901276E-03 -1.457696E-01 -4.299462E-03 -3.467377E-01 -2.463831E-02 -1.305729E-01 -3.605534E-03 -5.067715E-02 -5.273379E-04 -7.374940E-02 -1.102912E-03 -5.226497E-02 -5.649644E-04 +5.296804E-02 +5.661701E-04 +8.356446E-02 +1.412139E-03 +5.041335E-02 +5.143568E-04 +1.299348E-01 +3.467618E-03 +3.929702E-01 +3.147038E-02 +1.379707E-01 +3.888484E-03 +1.405034E-01 +4.473799E-03 +3.785796E-01 +2.940585E-02 +1.422010E-01 +4.113723E-03 +5.647073E-02 +6.735251E-04 +7.911154E-02 +1.329137E-03 +5.160755E-02 +5.361448E-04 +6.669424E-02 +9.090832E-04 +1.008621E-01 +2.134534E-03 +6.808932E-02 +9.355993E-04 +1.873006E-01 +7.135961E-03 +6.221575E-01 +7.819842E-02 +1.856653E-01 +6.954762E-03 +2.014929E-01 +8.327845E-03 +5.853251E-01 +6.945708E-02 +1.709645E-01 +5.917124E-03 +7.214913E-02 +1.058962E-03 +1.027720E-01 +2.138475E-03 +6.099853E-02 +7.493941E-04 +6.892071E-02 +9.630680E-04 +1.035459E-01 +2.173883E-03 +6.973870E-02 +9.904237E-04 +2.125703E-01 +9.112659E-03 +9.012205E-01 +2.163546E-01 +2.066426E-01 +8.617414E-03 +2.258950E-01 +1.039607E-02 +9.476792E-01 +2.350708E-01 +2.225585E-01 +1.017898E-02 +7.111503E-02 +1.036847E-03 +1.117012E-01 +2.530040E-03 +6.870474E-02 +9.551035E-04 +5.738897E-02 +6.699030E-04 +9.522335E-02 +1.835769E-03 +6.570917E-02 +8.656870E-04 +1.945592E-01 +7.593336E-03 +5.514753E-01 +6.122981E-02 +2.144202E-01 +9.421739E-03 +1.971631E-01 +7.944046E-03 +6.088996E-01 +7.442954E-02 +1.965447E-01 +7.765628E-03 +7.005494E-02 +1.012891E-03 +1.010633E-01 +2.084095E-03 +6.145926E-02 +7.694351E-04 +4.999479E-02 +5.129164E-04 +7.238243E-02 +1.062921E-03 +4.902309E-02 +4.852193E-04 +1.324655E-01 +3.642431E-03 +3.305312E-01 +2.265726E-02 +1.332993E-01 +3.728385E-03 +1.547469E-01 +4.894837E-03 +3.625944E-01 +2.747313E-02 +1.435761E-01 +4.334405E-03 +5.789603E-02 +7.065383E-04 +7.589559E-02 +1.205386E-03 +5.210018E-02 +5.790843E-04 tally 4: -2.442367E-01 -1.209459E-02 -2.543262E-01 -1.314598E-02 -5.639744E-01 -6.449959E-02 -6.415410E-01 -8.269091E-02 -2.631124E-01 -1.406544E-02 -2.513955E-01 -1.285616E-02 -3.570608E-01 -2.559924E-02 -2.978502E-01 -1.787722E-02 -1.142500E+00 -2.799696E-01 -1.072656E+00 -2.465813E-01 -3.720711E-01 -2.776168E-02 -3.520288E-01 -2.482313E-02 -3.406093E-01 -2.332649E-02 -3.186893E-01 -2.046998E-02 -1.039053E+00 -2.337911E-01 -1.055863E+00 -2.442418E-01 -3.636093E-01 -2.679467E-02 -3.514845E-01 -2.481131E-02 -2.356811E-01 -1.112305E-02 -2.424590E-01 -1.205945E-02 -5.851380E-01 -6.974618E-02 -5.788717E-01 -6.882684E-02 -2.430058E-01 -1.205907E-02 -2.390704E-01 -1.167330E-02 +2.572693E-01 +1.338921E-02 +2.567576E-01 +1.333558E-02 +6.127899E-01 +7.781914E-02 +6.163075E-01 +7.600273E-02 +2.566545E-01 +1.372553E-02 +2.509744E-01 +1.286275E-02 +3.366720E-01 +2.282560E-02 +3.216175E-01 +2.103222E-02 +1.118187E+00 +2.697988E-01 +1.050003E+00 +2.391098E-01 +3.407360E-01 +2.332214E-02 +3.150094E-01 +2.006040E-02 +3.130223E-01 +2.001074E-02 +3.238651E-01 +2.101351E-02 +1.061186E+00 +2.372828E-01 +1.099461E+00 +2.597134E-01 +3.445524E-01 +2.400393E-02 +3.429055E-01 +2.372392E-02 +2.299776E-01 +1.064851E-02 +2.208901E-01 +9.829109E-03 +6.015657E-01 +7.399276E-02 +5.851229E-01 +7.165716E-02 +2.583609E-01 +1.364046E-02 +2.456380E-01 +1.257735E-02 diff --git a/tests/regression_tests/filter_translations/test.py b/tests/regression_tests/filter_translations/test.py index d61667b014..4f0fe7141b 100644 --- a/tests/regression_tests/filter_translations/test.py +++ b/tests/regression_tests/filter_translations/test.py @@ -19,12 +19,12 @@ def model(): zr.add_nuclide('Zr90', 1.0) model.materials.extend([fuel, zr]) - box1 = openmc.model.rectangular_prism(10.0, 10.0) - box2 = openmc.model.rectangular_prism(20.0, 20.0, boundary_type='reflective') + box1 = openmc.model.RectangularPrism(10.0, 10.0) + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') - cell1 = openmc.Cell(fill=fuel, region=box1 & +bottom & -top) - cell2 = openmc.Cell(fill=zr, region=~box1 & box2 & +bottom & -top) + cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) + cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) model.settings.batches = 5 diff --git a/tests/regression_tests/fixed_source/inputs_true.dat b/tests/regression_tests/fixed_source/inputs_true.dat index f1aebb3b2b..a367417750 100644 --- a/tests/regression_tests/fixed_source/inputs_true.dat +++ b/tests/regression_tests/fixed_source/inputs_true.dat @@ -1,31 +1,30 @@ - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - 0.0 0.0 0.0 - - - 294 - - - - - flux - - + + + + + + + + + + + + + + fixed source + 100 + 10 + + + 0.0 0.0 0.0 + + + 294 + + + + flux + + + diff --git a/tests/regression_tests/fixed_source/test.py b/tests/regression_tests/fixed_source/test.py index 74908f87cd..b9ee251253 100644 --- a/tests/regression_tests/fixed_source/test.py +++ b/tests/regression_tests/fixed_source/test.py @@ -48,8 +48,8 @@ def test_fixed_source(): model.settings.batches = 10 model.settings.particles = 100 model.settings.temperature = {'default': 294} - model.settings.source = openmc.Source(space=openmc.stats.Point(), - strength=10.0) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point(), + strength=10.0) tally = openmc.Tally() tally.scores = ['flux'] diff --git a/tests/regression_tests/ifp/__init__.py b/tests/regression_tests/ifp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/ifp/groupwise/__init__.py b/tests/regression_tests/ifp/groupwise/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/ifp/groupwise/inputs_true.dat b/tests/regression_tests/ifp/groupwise/inputs_true.dat new file mode 100644 index 0000000000..6d7e20717b --- /dev/null +++ b/tests/regression_tests/ifp/groupwise/inputs_true.dat @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 5 + + + -10.0 -10.0 -10.0 10.0 10.0 10.0 + + + true + + + 5 + + + + 1 2 3 4 5 6 + + + ifp-time-numerator + + + 1 + ifp-beta-numerator + + + ifp-denominator + + + diff --git a/tests/regression_tests/ifp/groupwise/results_true.dat b/tests/regression_tests/ifp/groupwise/results_true.dat new file mode 100644 index 0000000000..ea66a8de3c --- /dev/null +++ b/tests/regression_tests/ifp/groupwise/results_true.dat @@ -0,0 +1,21 @@ +k-combined: +1.006559E+00 5.389391E-03 +tally 1: +9.109384E-08 +5.667165E-16 +tally 2: +3.000000E-03 +9.000000E-06 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.370000E-04 +2.800000E-02 +2.220000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 3: +1.489000E+01 +1.480036E+01 diff --git a/tests/regression_tests/ifp/groupwise/test.py b/tests/regression_tests/ifp/groupwise/test.py new file mode 100644 index 0000000000..a1a0ebefb8 --- /dev/null +++ b/tests/regression_tests/ifp/groupwise/test.py @@ -0,0 +1,40 @@ +"""Test the Iterated Fission Probability (IFP) method to compute adjoint-weighted +kinetics parameters using dedicated tallies.""" + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + +@pytest.fixture() +def ifp_model(): + # Material + material = openmc.Material(name="core") + material.add_nuclide("U235", 1.0) + material.set_density('g/cm3', 16.0) + + # Geometry + radius = 10.0 + sphere = openmc.Sphere(r=radius, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + geometry = openmc.Geometry([cell]) + + # Settings + settings = openmc.Settings() + settings.particles = 1000 + settings.batches = 20 + settings.inactive = 5 + settings.ifp_n_generation = 5 + + model = openmc.Model(settings=settings, geometry=geometry) + + space = openmc.stats.Box(*cell.bounding_box) + model.settings.source = openmc.IndependentSource( + space=space, constraints={'fissionable': True}) + model.add_kinetics_parameters_tallies(num_groups=6) + return model + + +def test_iterated_fission_probability(ifp_model): + harness = PyAPITestHarness("statepoint.20.h5", model=ifp_model) + harness.main() diff --git a/tests/regression_tests/ifp/total/__init__.py b/tests/regression_tests/ifp/total/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/ifp/total/inputs_true.dat b/tests/regression_tests/ifp/total/inputs_true.dat new file mode 100644 index 0000000000..2d69b29abd --- /dev/null +++ b/tests/regression_tests/ifp/total/inputs_true.dat @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 5 + + + -10.0 -10.0 -10.0 10.0 10.0 10.0 + + + true + + + 5 + + + + ifp-time-numerator ifp-beta-numerator ifp-denominator + + + diff --git a/tests/regression_tests/ifp/total/results_true.dat b/tests/regression_tests/ifp/total/results_true.dat new file mode 100644 index 0000000000..466ca1f015 --- /dev/null +++ b/tests/regression_tests/ifp/total/results_true.dat @@ -0,0 +1,9 @@ +k-combined: +1.006559E+00 5.389391E-03 +tally 1: +9.109384E-08 +5.667165E-16 +5.200000E-02 +5.420000E-04 +1.489000E+01 +1.480036E+01 diff --git a/tests/regression_tests/ifp/total/test.py b/tests/regression_tests/ifp/total/test.py new file mode 100644 index 0000000000..18b89cfc0b --- /dev/null +++ b/tests/regression_tests/ifp/total/test.py @@ -0,0 +1,44 @@ +"""Test the Iterated Fission Probability (IFP) method to compute adjoint-weighted +kinetics parameters using dedicated tallies.""" + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + +@pytest.fixture() +def ifp_model(): + model = openmc.Model() + + # Material + material = openmc.Material(name="core") + material.add_nuclide("U235", 1.0) + material.set_density('g/cm3', 16.0) + + # Geometry + radius = 10.0 + sphere = openmc.Sphere(r=radius, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + model.geometry = openmc.Geometry([cell]) + + # Settings + model.settings.particles = 1000 + model.settings.batches = 20 + model.settings.inactive = 5 + model.settings.ifp_n_generation = 5 + + space = openmc.stats.Box(*cell.bounding_box) + model.settings.source = openmc.IndependentSource( + space=space, constraints={'fissionable': True}) + + # Tally IFP scores + tally = openmc.Tally(name="ifp-scores") + tally.scores = ["ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"] + model.tallies = [tally] + + return model + + +def test_iterated_fission_probability(ifp_model): + harness = PyAPITestHarness("statepoint.20.h5", model=ifp_model) + harness.main() diff --git a/tests/regression_tests/infinite_cell/geometry.xml b/tests/regression_tests/infinite_cell/geometry.xml deleted file mode 100644 index 90bd2233be..0000000000 --- a/tests/regression_tests/infinite_cell/geometry.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - 11 12 - 12 11 - - - - - - - diff --git a/tests/regression_tests/infinite_cell/model.xml b/tests/regression_tests/infinite_cell/model.xml new file mode 100644 index 0000000000..0f741a86d9 --- /dev/null +++ b/tests/regression_tests/infinite_cell/model.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + 2.0 2.0 + 12 + 2 2 + -2.0 -2.0 + +11 12 +12 11 + + + + + eigenvalue + 1000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + diff --git a/tests/regression_tests/infinite_cell/results_true.dat b/tests/regression_tests/infinite_cell/results_true.dat index b73c733aea..4cdbcaf887 100644 --- a/tests/regression_tests/infinite_cell/results_true.dat +++ b/tests/regression_tests/infinite_cell/results_true.dat @@ -1,2 +1,2 @@ k-combined: -9.537627E-02 2.349145E-03 +9.603664E-02 1.050772E-03 diff --git a/tests/regression_tests/iso_in_lab/inputs_true.dat b/tests/regression_tests/iso_in_lab/inputs_true.dat index 2a302ada67..adfcf7e51a 100644 --- a/tests/regression_tests/iso_in_lab/inputs_true.dat +++ b/tests/regression_tests/iso_in_lab/inputs_true.dat @@ -1,38 +1,199 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + U234 U235 U238 Xe135 O16 + + + + + + + + + Zr90 Zr91 Zr92 Zr94 Zr96 + + + + + + + + + H1 O16 B10 B11 + + + + + + + + + H1 O16 B10 B11 + + + + + + + + + + + + + + Fe54 Fe56 Fe57 Fe58 Ni58 Ni60 Mn55 Cr52 C0 Cu63 + + + + + + + + + + + + + + + + H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 + + + + + + + + + + + + + + + + H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 + + + + + + + + + + + + + + + + H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 + + + + + + + + + + + + + + + + H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 + + + + + + + + + + + + + + + + H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 + + + + + + + + + + + + + + H1 O16 B10 B11 Zr90 Zr91 Zr92 Zr94 Zr96 + + + + + + + + + + + + + + H1 O16 B10 B11 Zr90 Zr91 Zr92 Zr94 Zr96 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 @@ -50,12 +211,12 @@ 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 @@ -73,12 +234,12 @@ 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 @@ -100,12 +261,12 @@ 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 @@ -127,195 +288,34 @@ 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - U234 U235 U238 Xe135 O16 - - - - - - - - - Zr90 Zr91 Zr92 Zr94 Zr96 - - - - - - - - - H1 O16 B10 B11 - - - - - - - - - H1 O16 B10 B11 - - - - - - - - - - - - - - Fe54 Fe56 Fe57 Fe58 Ni58 Ni60 Mn55 Cr52 C0 Cu63 - - - - - - - - - - - - - - - - H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 - - - - - - - - - - - - - - - - H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 - - - - - - - - - - - - - - - - H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 - - - - - - - - - - - - - - - - H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 - - - - - - - - - - - - - - - - H1 O16 B10 B11 Fe54 Fe56 Fe57 Fe58 Ni58 Mn55 Cr52 - - - - - - - - - - - - - - H1 O16 B10 B11 Zr90 Zr91 Zr92 Zr94 Zr96 - - - - - - - - - - - - - - H1 O16 B10 B11 Zr90 Zr91 Zr92 Zr94 Zr96 - - - - - eigenvalue - 100 - 10 - 5 - - - -160 -160 -183 160 160 183 - - - + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -160 -160 -183 160 160 183 + + + + diff --git a/tests/regression_tests/iso_in_lab/results_true.dat b/tests/regression_tests/iso_in_lab/results_true.dat index ce9cf316f6..0378ec86dc 100644 --- a/tests/regression_tests/iso_in_lab/results_true.dat +++ b/tests/regression_tests/iso_in_lab/results_true.dat @@ -1,2 +1,2 @@ k-combined: -9.050918E-01 3.355799E-02 +9.365837E-01 5.366122E-02 diff --git a/tests/regression_tests/lattice/results_true.dat b/tests/regression_tests/lattice/results_true.dat index ff0ff626f5..dca84bd6a5 100644 --- a/tests/regression_tests/lattice/results_true.dat +++ b/tests/regression_tests/lattice/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.068493E+00 1.318070E-01 +9.182679E-01 5.270201E-02 diff --git a/tests/regression_tests/lattice_distribmat/False/inputs_true.dat b/tests/regression_tests/lattice_distribmat/False/inputs_true.dat new file mode 100644 index 0000000000..7839890571 --- /dev/null +++ b/tests/regression_tests/lattice_distribmat/False/inputs_true.dat @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 1.0 + 1 + 1 1 + -1.0 -1.0 + +1 + + + 1.0 1.0 + 1 + 1 1 + -1.0 0 + +1 + + + 1.0 1.0 + 1 + 1 1 + 0 -1.0 + +1 + + + 1.0 1.0 + 1 + 1 1 + 0 0 + +1 + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/lattice_distribmat/False/results_true.dat b/tests/regression_tests/lattice_distribmat/False/results_true.dat new file mode 100644 index 0000000000..9ebbb43b83 --- /dev/null +++ b/tests/regression_tests/lattice_distribmat/False/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.848895E+00 1.480242E-02 diff --git a/tests/regression_tests/lattice_distribmat/True/inputs_true.dat b/tests/regression_tests/lattice_distribmat/True/inputs_true.dat new file mode 100644 index 0000000000..aec3a54008 --- /dev/null +++ b/tests/regression_tests/lattice_distribmat/True/inputs_true.dat @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 1.0 + 8 + 1 1 + -1.0 -1.0 + +8 + + + 1.0 1.0 + 8 + 1 1 + -1.0 0 + +8 + + + 1.0 1.0 + 8 + 1 1 + 0 -1.0 + +8 + + + 1.0 1.0 + 8 + 1 1 + 0 0 + +8 + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/lattice_distribmat/True/results_true.dat b/tests/regression_tests/lattice_distribmat/True/results_true.dat new file mode 100644 index 0000000000..9ebbb43b83 --- /dev/null +++ b/tests/regression_tests/lattice_distribmat/True/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.848895E+00 1.480242E-02 diff --git a/tests/regression_tests/lattice_distribmat/__init__.py b/tests/regression_tests/lattice_distribmat/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/lattice_distribmat/test.py b/tests/regression_tests/lattice_distribmat/test.py new file mode 100644 index 0000000000..4d0b6e1560 --- /dev/null +++ b/tests/regression_tests/lattice_distribmat/test.py @@ -0,0 +1,83 @@ +import numpy as np +import openmc +from openmc.utility_funcs import change_directory +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.model.Model() + + uo2 = openmc.Material(name='UO2') + uo2.set_density('g/cm3', 10.0) + uo2.add_nuclide('U235', 1.0) + uo2.add_nuclide('O16', 2.0) + water = openmc.Material(name='light water') + water.add_nuclide('H1', 2.0) + water.add_nuclide('O16', 1.0) + water.set_density('g/cm3', 1.0) + water.add_s_alpha_beta('c_H_in_H2O') + model.materials.extend([uo2, water]) + + cyl = openmc.ZCylinder(r=0.4) + pin = openmc.model.pin([cyl], [uo2, water]) + d = 1.0 + + lattice00 = openmc.RectLattice() + lattice00.lower_left = (-d, -d) + lattice00.pitch = (d, d) + lattice00.outer = pin + lattice00.universes = [[pin]] + box00 = openmc.model.RectangularPrism(d, d, origin=(-d/2,-d/2)) + + lattice01 = openmc.RectLattice() + lattice01.lower_left = (-d, 0) + lattice01.pitch = (d, d) + lattice01.outer = pin + lattice01.universes = [[pin]] + box01 = openmc.model.RectangularPrism(d, d, origin=(-d/2,d/2)) + + lattice10 = openmc.RectLattice() + lattice10.lower_left = (0, -d) + lattice10.pitch = (d, d) + lattice10.outer = pin + lattice10.universes = [[pin]] + box10 = openmc.model.RectangularPrism(d, d, origin=(d/2,-d/2)) + + lattice11 = openmc.RectLattice() + lattice11.lower_left = (0, 0) + lattice11.pitch = (d, d) + lattice11.outer = pin + lattice11.universes = [[pin]] + box11 = openmc.model.RectangularPrism(d, d, origin=(d/2,d/2)) + + + cell00 = openmc.Cell(fill=lattice00, region = -box00) + cell01 = openmc.Cell(fill=lattice01, region = -box01) + cell10 = openmc.Cell(fill=lattice10, region = -box10) + cell11 = openmc.Cell(fill=lattice11, region = -box11) + + univ = openmc.Universe(cells=[cell00, cell01, cell10, cell11]) + + box = openmc.model.RectangularPrism(2*d, 2*d, boundary_type='reflective') + + main_cell = openmc.Cell(fill=univ, region=-box) + model.geometry = openmc.Geometry([main_cell]) + model.geometry.merge_surfaces = True + + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 1000 + + return model + +@pytest.mark.parametrize("distribmat", [False, True]) +def test_lattice(model, distribmat): + with change_directory(str(distribmat)): + openmc.reset_auto_ids() + if distribmat: + model.differentiate_mats(depletable_only=False) + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/lattice_distribrho/__init__.py b/tests/regression_tests/lattice_distribrho/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/lattice_distribrho/inputs_true.dat b/tests/regression_tests/lattice_distribrho/inputs_true.dat new file mode 100644 index 0000000000..5031bea6e2 --- /dev/null +++ b/tests/regression_tests/lattice_distribrho/inputs_true.dat @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + 1.0 1.0 + 2 2 + -1.0 -1.0 + +1 1 +1 1 + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/lattice_distribrho/results_true.dat b/tests/regression_tests/lattice_distribrho/results_true.dat new file mode 100644 index 0000000000..f7f3da8e65 --- /dev/null +++ b/tests/regression_tests/lattice_distribrho/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.900249E+00 8.157834E-03 diff --git a/tests/regression_tests/lattice_distribrho/test.py b/tests/regression_tests/lattice_distribrho/test.py new file mode 100644 index 0000000000..ec94fe96b8 --- /dev/null +++ b/tests/regression_tests/lattice_distribrho/test.py @@ -0,0 +1,51 @@ +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.Model() + + uo2 = openmc.Material(name='UO2') + uo2.set_density('g/cm3', 10.0) + uo2.add_nuclide('U235', 1.0) + uo2.add_nuclide('O16', 2.0) + water = openmc.Material(name='light water') + water.add_nuclide('H1', 2.0) + water.add_nuclide('O16', 1.0) + water.set_density('g/cm3', 1.0) + water.add_s_alpha_beta('c_H_in_H2O') + model.materials.extend([uo2, water]) + + cyl = openmc.ZCylinder(r=0.4) + pin = openmc.model.pin([cyl], [uo2, water]) + d = 1.0 + + lattice = openmc.RectLattice() + lattice.lower_left = (-d, -d) + lattice.pitch = (d, d) + lattice.universes = [[pin, pin], + [pin, pin]] + box = openmc.model.RectangularPrism( + 2.0 * d, 2.0 * d, + origin=(0.0, 0.0), + boundary_type='reflective' + ) + + pin.cells[1].density = [10.0, 20.0, 10.0, 20.0] + + model.geometry = openmc.Geometry([openmc.Cell(fill=lattice, region=-box)]) + model.geometry.merge_surfaces = True + + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 1000 + + return model + + +def test_lattice_checkerboard(model): + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/lattice_hex/results_true.dat b/tests/regression_tests/lattice_hex/results_true.dat index 3d53292abd..46db641ebe 100644 --- a/tests/regression_tests/lattice_hex/results_true.dat +++ b/tests/regression_tests/lattice_hex/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.522694E-01 1.422929E-02 +2.595598E-01 9.089294E-03 diff --git a/tests/regression_tests/lattice_hex_coincident/inputs_true.dat b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat index 0388a4f555..fafc03da1e 100644 --- a/tests/regression_tests/lattice_hex_coincident/inputs_true.dat +++ b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat @@ -1,81 +1,81 @@ - - - - - - - - - - - 1.4 - 11 -
0.0 0.0
- - 10 -10 10 - 9 -10 10 - 10 -
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 2 - - - -0.9899494936611666 -0.9899494936611666 0.0 0.9899494936611666 0.9899494936611666 10.0 - - - - false - - 22 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.4 + 3 +
0.0 0.0
+ + 2 +2 2 + 1 +2 2 + 2 +
+ + + + + + + + + + + + +
+ + eigenvalue + 1000 + 5 + 2 + + + -0.9899494936611666 -0.9899494936611666 0.0 0.9899494936611666 0.9899494936611666 10.0 + + + + false + + 22 + +
diff --git a/tests/regression_tests/lattice_hex_coincident/results_true.dat b/tests/regression_tests/lattice_hex_coincident/results_true.dat index 94115e6eee..c798b66e40 100644 --- a/tests/regression_tests/lattice_hex_coincident/results_true.dat +++ b/tests/regression_tests/lattice_hex_coincident/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.901983E+00 1.772999E-02 +1.931086E+00 5.968486E-02 diff --git a/tests/regression_tests/lattice_hex_coincident/test.py b/tests/regression_tests/lattice_hex_coincident/test.py index 30bc470f26..f971098c06 100644 --- a/tests/regression_tests/lattice_hex_coincident/test.py +++ b/tests/regression_tests/lattice_hex_coincident/test.py @@ -6,7 +6,8 @@ from tests.testing_harness import PyAPITestHarness class HexLatticeCoincidentTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) materials = openmc.Materials() fuel_mat = openmc.Material() @@ -40,7 +41,7 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): zirc.add_nuclide('Zr96', 1.131E-03, 'ao') materials.append(zirc) - materials.export_to_xml() + self._model.materials = materials ### Geometry ### pin_rad = 0.7 # cm @@ -103,10 +104,10 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): inf_mat_univ = openmc.Universe(cells=[inf_mat,]) # a hex surface for the core to go inside of - hexprism = openmc.model.hexagonal_prism(edge_length=edge_length, - origin=(0.0, 0.0), - boundary_type = 'reflective', - orientation='x') + hexprism = openmc.model.HexagonalPrism(edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type = 'reflective', + orientation='x') pincell_only_lattice = openmc.HexLattice(name="regular fuel assembly") pincell_only_lattice.center = (0., 0.) @@ -119,20 +120,19 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): pincell_only_lattice.universes = [ring1, ring0] pincell_only_cell = openmc.Cell(name="container cell") - pincell_only_cell.region = hexprism & +fuel_btm & -fuel_top + pincell_only_cell.region = -hexprism & +fuel_btm & -fuel_top pincell_only_cell.fill = pincell_only_lattice root_univ = openmc.Universe(name="root universe", cells=[pincell_only_cell,]) - geom = openmc.Geometry(root_univ) - geom.export_to_xml() + self._model.geometry = openmc.Geometry(root_univ) ### Settings ### settings = openmc.Settings() settings.run_mode = 'eigenvalue' - source = openmc.Source() + source = openmc.IndependentSource() corner_dist = sqrt(2) * pin_rad ll = [-corner_dist, -corner_dist, 0.0] ur = [corner_dist, corner_dist, 10.0] @@ -144,8 +144,9 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): settings.inactive = 2 settings.particles = 1000 settings.seed = 22 - settings.export_to_xml() + self._model.settings = settings def test_lattice_hex_coincident_surf(): - harness = HexLatticeCoincidentTestHarness('statepoint.5.h5') + harness = HexLatticeCoincidentTestHarness('statepoint.5.h5', + model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/lattice_hex_x/inputs_true.dat b/tests/regression_tests/lattice_hex_x/inputs_true.dat index b528a97b7c..b6536c6a8b 100644 --- a/tests/regression_tests/lattice_hex_x/inputs_true.dat +++ b/tests/regression_tests/lattice_hex_x/inputs_true.dat @@ -1,23 +1,53 @@ - - - - - - - - - - - - - - - - 1.235 5.0 - 4 -
0.0 0.0 5.0
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.235 5.0 + 4 +
0.0 0.0 5.0
+ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 @@ -60,64 +90,34 @@ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -
- - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - -13.62546635287517 -13.62546635287517 0.0 13.62546635287517 13.62546635287517 10.0 - - - 22 - +
+ + + + + + + + + + + + + + + + +
+ + eigenvalue + 1000 + 10 + 5 + + + -13.62546635287517 -13.62546635287517 0.0 13.62546635287517 13.62546635287517 10.0 + + + 22 + + diff --git a/tests/regression_tests/lattice_hex_x/results_true.dat b/tests/regression_tests/lattice_hex_x/results_true.dat index b1177911ea..44f947283c 100644 --- a/tests/regression_tests/lattice_hex_x/results_true.dat +++ b/tests/regression_tests/lattice_hex_x/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.345955E+00 3.225468E-02 +1.326294E+00 1.193578E-02 diff --git a/tests/regression_tests/lattice_hex_x/test.py b/tests/regression_tests/lattice_hex_x/test.py index ecb27c9c2f..dd5c53d0c0 100644 --- a/tests/regression_tests/lattice_hex_x/test.py +++ b/tests/regression_tests/lattice_hex_x/test.py @@ -4,8 +4,8 @@ import numpy as np class HexLatticeOXTestHarness(PyAPITestHarness): - - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) materials = openmc.Materials() fuel_mat = openmc.Material(material_id=1, name="UO2") @@ -35,7 +35,7 @@ class HexLatticeOXTestHarness(PyAPITestHarness): zirc.add_element('Zr', 4.23e-2) materials.append(zirc) - materials.export_to_xml() + self._model.materials = materials # Geometry # @@ -139,11 +139,11 @@ class HexLatticeOXTestHarness(PyAPITestHarness): # a hex surface for the core to go inside of - hexprism = openmc.model.hexagonal_prism(edge_length=edge_length, - origin=(0.0, 0.0), - boundary_type='reflective', - orientation='x') - region = hexprism & +fuel_bottom & -fuel_top + hexprism = openmc.model.HexagonalPrism(edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type='reflective', + orientation='x') + region = -hexprism & +fuel_bottom & -fuel_top inf_mat = openmc.Cell(cell_id=12) inf_mat.fill = coolant @@ -165,7 +165,7 @@ class HexLatticeOXTestHarness(PyAPITestHarness): (4, 21), (5, 20), (4, 27), (5, 25), (4, 33)] for i, j in channels: universes[i][j] = abs_ch_univ - lattice = openmc.HexLattice(name="regular fuel assembly") + lattice = openmc.HexLattice(lattice_id=6, name="regular fuel assembly") lattice.orientation = "x" lattice.center = (0., 0., length/2.0) lattice.pitch = (assembly_pitch, length/2.0) @@ -180,15 +180,14 @@ class HexLatticeOXTestHarness(PyAPITestHarness): root_univ = openmc.Universe(universe_id=5, name="root universe", cells=[assembly_cell]) - geom = openmc.Geometry(root_univ) - geom.export_to_xml() + self._model.geometry = openmc.Geometry(root_univ) # Settings # settings = openmc.Settings() settings.run_mode = 'eigenvalue' - source = openmc.Source() + source = openmc.IndependentSource() ll = [-edge_length, -edge_length, 0.0] ur = [edge_length, edge_length, 10.0] source.space = openmc.stats.Box(ll, ur) @@ -198,9 +197,9 @@ class HexLatticeOXTestHarness(PyAPITestHarness): settings.inactive = 5 settings.particles = 1000 settings.seed = 22 - settings.export_to_xml() + self._model.settings = settings def test_lattice_hex_ox_surf(): - harness = HexLatticeOXTestHarness('statepoint.10.h5') + harness = HexLatticeOXTestHarness('statepoint.10.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/lattice_multiple/inputs_true.dat b/tests/regression_tests/lattice_multiple/inputs_true.dat index c92e841bc9..06abef5821 100644 --- a/tests/regression_tests/lattice_multiple/inputs_true.dat +++ b/tests/regression_tests/lattice_multiple/inputs_true.dat @@ -1,53 +1,53 @@ - - - - - - - - - 1.2 1.2 - 1 - 2 2 - -1.2 -1.2 - + + + + + + + + + + + + + + + + + + + + + + + 1.2 1.2 + 1 + 2 2 + -1.2 -1.2 + 2 1 1 1 - - - 2.4 2.4 - 2 2 - -2.4 -2.4 - + + + 2.4 2.4 + 2 2 + -2.4 -2.4 + 4 4 4 4 - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/lattice_multiple/results_true.dat b/tests/regression_tests/lattice_multiple/results_true.dat index 88b911e781..0866932d2a 100644 --- a/tests/regression_tests/lattice_multiple/results_true.dat +++ b/tests/regression_tests/lattice_multiple/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.859909E+00 1.768384E-02 +1.843982E+00 5.815875E-03 diff --git a/tests/regression_tests/lattice_multiple/test.py b/tests/regression_tests/lattice_multiple/test.py index c287c01027..10d9e50bc6 100644 --- a/tests/regression_tests/lattice_multiple/test.py +++ b/tests/regression_tests/lattice_multiple/test.py @@ -42,8 +42,8 @@ def model(): lattice.pitch = (2*d, 2*d) lattice.universes = np.full((2, 2), inner_univ) - box = openmc.model.rectangular_prism(4*d, 4*d, boundary_type='reflective') - main_cell = openmc.Cell(fill=lattice, region=box) + box = openmc.model.RectangularPrism(4*d, 4*d, boundary_type='reflective') + main_cell = openmc.Cell(fill=lattice, region=-box) model.geometry = openmc.Geometry([main_cell]) model.settings.batches = 10 diff --git a/tests/regression_tests/lattice_rotated/inputs_true.dat b/tests/regression_tests/lattice_rotated/inputs_true.dat index 8ee34b4f39..e53b93f932 100644 --- a/tests/regression_tests/lattice_rotated/inputs_true.dat +++ b/tests/regression_tests/lattice_rotated/inputs_true.dat @@ -1,18 +1,35 @@ - - - - - - - - - - - 1.25 - 30 -
0.0 0.0
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.25 + 30 +
0.0 0.0
+ 2 1 1 1 2 1 @@ -22,50 +39,33 @@ 1 1 1 1 1 1 -
- - 1.25 1.25 - 30 - 4 4 - -2.5 -2.5 - +
+ + 1.25 1.25 + 30 + 4 4 + -2.5 -2.5 + 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - - - - - - -
- - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - 0.0 0.0 0.0 - - - +
+ + + + + + + + eigenvalue + 1000 + 5 + 0 + + + 0.0 0.0 0.0 + + + + diff --git a/tests/regression_tests/lattice_rotated/results_true.dat b/tests/regression_tests/lattice_rotated/results_true.dat index d02656a3f9..9a96a92594 100644 --- a/tests/regression_tests/lattice_rotated/results_true.dat +++ b/tests/regression_tests/lattice_rotated/results_true.dat @@ -1,2 +1,2 @@ k-combined: -4.674233E-01 1.599236E-02 +4.515246E-01 2.358354E-02 diff --git a/tests/regression_tests/lattice_rotated/test.py b/tests/regression_tests/lattice_rotated/test.py index 9cab92e1e6..63641fb84a 100644 --- a/tests/regression_tests/lattice_rotated/test.py +++ b/tests/regression_tests/lattice_rotated/test.py @@ -73,7 +73,7 @@ def rotated_lattice_model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) model.settings.export_to_xml() return model diff --git a/tests/regression_tests/mg_basic/inputs_true.dat b/tests/regression_tests/mg_basic/inputs_true.dat index 4f2fd3f0b0..aa2c2fa0a4 100644 --- a/tests/regression_tests/mg_basic/inputs_true.dat +++ b/tests/regression_tests/mg_basic/inputs_true.dat @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - 2g.h5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 - - - - false - - multi-group - - false - - + + + 2g.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 + + + + false + + multi-group + + false + + + diff --git a/tests/regression_tests/mg_basic/results_true.dat b/tests/regression_tests/mg_basic/results_true.dat index 15a9f21868..16980732db 100644 --- a/tests/regression_tests/mg_basic/results_true.dat +++ b/tests/regression_tests/mg_basic/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.009864E+00 1.107115E-02 +1.004679E+00 1.329350E-02 diff --git a/tests/regression_tests/mg_basic_delayed/inputs_true.dat b/tests/regression_tests/mg_basic_delayed/inputs_true.dat index 9fb7afe7e2..cc3f2cfb2c 100644 --- a/tests/regression_tests/mg_basic_delayed/inputs_true.dat +++ b/tests/regression_tests/mg_basic_delayed/inputs_true.dat @@ -1,63 +1,63 @@ - - - - - - - - - - - - - - - - - - 2g.h5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 - - - - false - - multi-group - - false - - + + + 2g.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 154.90833333333333 1000.0 1000.0 + + + + false + + multi-group + + false + + + diff --git a/tests/regression_tests/mg_basic_delayed/results_true.dat b/tests/regression_tests/mg_basic_delayed/results_true.dat index 6f7c79c1b2..f150030b9b 100644 --- a/tests/regression_tests/mg_basic_delayed/results_true.dat +++ b/tests/regression_tests/mg_basic_delayed/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.024610E+00 9.643746E-03 +1.017078E+00 1.181139E-02 diff --git a/tests/regression_tests/mg_convert/inputs_true.dat b/tests/regression_tests/mg_convert/inputs_true.dat index 753c0d304c..3b1f511e63 100644 --- a/tests/regression_tests/mg_convert/inputs_true.dat +++ b/tests/regression_tests/mg_convert/inputs_true.dat @@ -1,29 +1,29 @@ - - - - - - - - - - mgxs.h5 - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -5 -5 -5 5 5 5 - - - multi-group - + + + mgxs.h5 + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -5 -5 -5 5 5 5 + + + multi-group + + diff --git a/tests/regression_tests/mg_convert/results_true.dat b/tests/regression_tests/mg_convert/results_true.dat index ff3d7bb913..f8f748cc7c 100644 --- a/tests/regression_tests/mg_convert/results_true.dat +++ b/tests/regression_tests/mg_convert/results_true.dat @@ -1,24 +1,24 @@ k-combined: -9.984888E-01 1.558301E-03 +9.926427E-01 3.067527E-03 k-combined: -1.001035E+00 7.622447E-04 +9.932868E-01 2.780271E-03 k-combined: -9.984888E-01 1.558301E-03 +9.926427E-01 3.067527E-03 k-combined: -9.991101E-01 2.776191E-03 +1.000000E+00 0.000000E+00 k-combined: -9.965954E-01 5.185046E-03 +9.902969E-01 1.654717E-02 k-combined: -9.987613E-01 4.806845E-04 +9.882796E-01 1.929843E-03 k-combined: -9.991101E-01 2.776191E-03 +1.000000E+00 0.000000E+00 k-combined: -9.965954E-01 5.185315E-03 +9.902953E-01 1.654291E-02 k-combined: -9.987610E-01 4.791528E-04 +9.882814E-01 1.927488E-03 k-combined: -9.944808E-01 4.458524E-03 +9.893153E-01 7.576652E-03 k-combined: -9.984888E-01 1.558301E-03 +9.926427E-01 3.067527E-03 k-combined: -9.984888E-01 1.558301E-03 +9.926427E-01 3.067527E-03 diff --git a/tests/regression_tests/mg_convert/test.py b/tests/regression_tests/mg_convert/test.py index f22c15dc07..0e50f3a744 100755 --- a/tests/regression_tests/mg_convert/test.py +++ b/tests/regression_tests/mg_convert/test.py @@ -1,3 +1,4 @@ +from math import isnan import os import hashlib @@ -61,7 +62,8 @@ def build_mgxs_library(convert): class MGXSTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Instantiate some Macroscopic Data uo2_data = openmc.Macroscopic('UO2') @@ -73,7 +75,7 @@ class MGXSTestHarness(PyAPITestHarness): # Instantiate a Materials collection and export to XML materials_file = openmc.Materials([mat]) materials_file.cross_sections = "./mgxs.h5" - materials_file.export_to_xml() + self._model.materials = materials_file # Instantiate ZCylinder surfaces left = openmc.XPlane(surface_id=4, x0=-5., name='left') @@ -102,8 +104,7 @@ class MGXSTestHarness(PyAPITestHarness): root.add_cells([fuel]) # Instantiate a Geometry, register the root Universe, and export to XML - geometry = openmc.Geometry(root) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root) settings_file = openmc.Settings() settings_file.energy_mode = "multi-group" @@ -114,9 +115,9 @@ class MGXSTestHarness(PyAPITestHarness): # Create an initial uniform spatial source distribution bounds = [-5, -5, -5, 5, 5, 5] uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) - settings_file.source = openmc.source.Source(space=uniform_dist) + settings_file.source = openmc.IndependentSource(space=uniform_dist) - settings_file.export_to_xml() + self._model.settings = settings_file def _run_openmc(self): # Run multiple conversions to compare results @@ -142,10 +143,13 @@ class MGXSTestHarness(PyAPITestHarness): openmc.run(openmc_exec=config['exe']) with openmc.StatePoint('statepoint.{}.h5'.format(batches)) as sp: + # Sometimes NaN results are produced; convert these to 0.0 + std_dev = 0.0 if isnan(sp.keff.s) else sp.keff.s + # Write out k-combined. outstr += 'k-combined:\n' form = '{:12.6E} {:12.6E}\n' - outstr += form.format(sp.k_combined.n, sp.k_combined.s) + outstr += form.format(sp.keff.n, std_dev) return outstr @@ -194,5 +198,5 @@ class MGXSTestHarness(PyAPITestHarness): def test_mg_convert(): - harness = MGXSTestHarness('statepoint.10.h5') + harness = MGXSTestHarness('statepoint.10.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/mg_legendre/inputs_true.dat b/tests/regression_tests/mg_legendre/inputs_true.dat index ad3b434e61..81362f20a0 100644 --- a/tests/regression_tests/mg_legendre/inputs_true.dat +++ b/tests/regression_tests/mg_legendre/inputs_true.dat @@ -1,33 +1,33 @@ - - - - - - - - 2g.h5 - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 - - - - false - - multi-group - - false - - + + + 2g.h5 + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + + + false + + multi-group + + false + + + diff --git a/tests/regression_tests/mg_legendre/results_true.dat b/tests/regression_tests/mg_legendre/results_true.dat index e2989469c8..04d9c9874b 100644 --- a/tests/regression_tests/mg_legendre/results_true.dat +++ b/tests/regression_tests/mg_legendre/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.003646E+00 9.134747E-03 +1.009220E+00 9.571832E-03 diff --git a/tests/regression_tests/mg_max_order/inputs_true.dat b/tests/regression_tests/mg_max_order/inputs_true.dat index 2ac83852c9..c8f42d1a3c 100644 --- a/tests/regression_tests/mg_max_order/inputs_true.dat +++ b/tests/regression_tests/mg_max_order/inputs_true.dat @@ -1,34 +1,34 @@ - - - - - - - - 2g.h5 - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 - - - - false - - multi-group - 1 - - false - - + + + 2g.h5 + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + + + false + + multi-group + 1 + + false + + + diff --git a/tests/regression_tests/mg_max_order/results_true.dat b/tests/regression_tests/mg_max_order/results_true.dat index e2989469c8..04d9c9874b 100644 --- a/tests/regression_tests/mg_max_order/results_true.dat +++ b/tests/regression_tests/mg_max_order/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.003646E+00 9.134747E-03 +1.009220E+00 9.571832E-03 diff --git a/tests/regression_tests/mg_survival_biasing/inputs_true.dat b/tests/regression_tests/mg_survival_biasing/inputs_true.dat index 5ece3ce9fd..bee69729de 100644 --- a/tests/regression_tests/mg_survival_biasing/inputs_true.dat +++ b/tests/regression_tests/mg_survival_biasing/inputs_true.dat @@ -1,34 +1,34 @@ - - - - - - - - 2g.h5 - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 - - - - false - - multi-group - true - - false - - + + + 2g.h5 + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + + + false + + multi-group + true + + false + + + diff --git a/tests/regression_tests/mg_survival_biasing/results_true.dat b/tests/regression_tests/mg_survival_biasing/results_true.dat index cddbdaceb3..4b26978ada 100644 --- a/tests/regression_tests/mg_survival_biasing/results_true.dat +++ b/tests/regression_tests/mg_survival_biasing/results_true.dat @@ -1,2 +1,2 @@ k-combined: -9.878738E-01 8.326224E-03 +9.889968E-01 9.144186E-03 diff --git a/tests/regression_tests/mg_tallies/inputs_true.dat b/tests/regression_tests/mg_tallies/inputs_true.dat index 5629587222..f4f154244a 100644 --- a/tests/regression_tests/mg_tallies/inputs_true.dat +++ b/tests/regression_tests/mg_tallies/inputs_true.dat @@ -1,165 +1,164 @@ - - - - - - - - 2g.h5 - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 - - - - false - - multi-group - - false - - - - - - 10 1 1 - 0.0 0.0 0.0 - 929.45 1000 1000 - - - 1 - - - 1 - - - 0.0 20000000.0 - - - 0.0 20000000.0 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 5 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - analog - - - 5 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - tracklength - - - 6 1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux scatter nu-scatter - analog - - - 6 1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - collision - - - 6 1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - tracklength - - - 6 1 2 - scatter nu-scatter nu-fission - - - 6 3 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux scatter nu-scatter - analog - - - 6 3 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - collision - - - 6 3 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux - tracklength - - - 6 3 4 - scatter nu-scatter nu-fission - - - 5 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - analog - - - 5 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - tracklength - - - 6 1 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate scatter nu-scatter - analog - - - 6 1 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - collision - - - 6 1 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - tracklength - - - 6 1 2 - mat_1 - scatter nu-scatter nu-fission - - - 6 3 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate scatter nu-scatter - analog - - - 6 3 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - collision - - - 6 3 - mat_1 - total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate - tracklength - - - 6 3 4 - mat_1 - scatter nu-scatter nu-fission - - + + + 2g.h5 + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + 0.0 -1000.0 -1000.0 929.45 1000.0 1000.0 + + + + false + + multi-group + + false + + + + + 10 1 1 + 0.0 0.0 0.0 + 929.45 1000 1000 + + + 1 + + + 1 + + + 0.0 20000000.0 + + + 0.0 20000000.0 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 5 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + analog + + + 5 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + tracklength + + + 6 1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux scatter nu-scatter + analog + + + 6 1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + collision + + + 6 1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + tracklength + + + 6 1 2 + scatter nu-scatter nu-fission + + + 6 3 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux scatter nu-scatter + analog + + + 6 3 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + collision + + + 6 3 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate flux + tracklength + + + 6 3 4 + scatter nu-scatter nu-fission + + + 5 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + analog + + + 5 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + tracklength + + + 6 1 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate scatter nu-scatter + analog + + + 6 1 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + collision + + + 6 1 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + tracklength + + + 6 1 2 + mat_1 + scatter nu-scatter nu-fission + + + 6 3 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate scatter nu-scatter + analog + + + 6 3 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + collision + + + 6 3 + mat_1 + total absorption fission nu-fission inverse-velocity prompt-nu-fission delayed-nu-fission kappa-fission events decay-rate + tracklength + + + 6 3 4 + mat_1 + scatter nu-scatter nu-fission + + + diff --git a/tests/regression_tests/mg_tallies/results_true.dat b/tests/regression_tests/mg_tallies/results_true.dat index 7484dd2485..07fe22ce50 100644 --- a/tests/regression_tests/mg_tallies/results_true.dat +++ b/tests/regression_tests/mg_tallies/results_true.dat @@ -1,1324 +1,1324 @@ k-combined: -1.003646E+00 9.134747E-03 +1.012390E+00 9.679132E-03 tally 1: -5.220000E-01 -5.995400E-02 -1.400000E-02 -4.000000E-05 -6.718865E-03 -9.371695E-06 -1.106435E-02 -3.119729E-05 -1.216992E-07 -3.699412E-15 -1.106435E-02 -3.119729E-05 +1.342000E+00 +4.291640E-01 +7.000000E-02 +1.158000E-03 +2.919844E-02 +1.953371E-04 +5.372246E-02 +6.734832E-04 +2.939310E-07 +2.375686E-14 +5.372246E-02 +6.734832E-04 0.000000E+00 0.000000E+00 -1.343773E+06 -3.748678E+11 -5.220000E-01 -5.995400E-02 +5.839688E+06 +7.813485E+12 +1.342000E+00 +4.291640E-01 0.000000E+00 0.000000E+00 -1.520872E+00 -5.121226E-01 -2.564000E+00 -1.338762E+00 -1.160000E-01 -2.738000E-03 -4.593685E-02 -4.275719E-04 -1.360763E-01 -3.951569E-03 -8.079937E-07 -1.577355E-13 -1.350645E-01 -3.872739E-03 -1.011813E-03 -1.023765E-06 -9.187371E+06 -1.710288E+13 -2.564000E+00 -1.338762E+00 -8.595248E-04 -7.387829E-07 -7.412367E+00 -1.121746E+01 -2.184000E+00 -9.895940E-01 -1.080000E-01 -2.394000E-03 -4.782785E-02 -4.787428E-04 -1.140806E-01 -2.886440E-03 -6.242229E-07 -9.307161E-14 -1.130687E-01 -2.811706E-03 -1.011813E-03 -1.023765E-06 -9.565570E+06 -1.914971E+13 -2.184000E+00 -9.895940E-01 -3.312574E-05 -1.097314E-09 -6.331465E+00 -8.328826E+00 -5.970000E-01 -8.325100E-02 -3.500000E-02 -3.150000E-04 -1.514871E-02 -5.131352E-05 -3.795718E-02 -3.631435E-04 -2.299730E-07 -1.295603E-14 -3.795718E-02 -3.631435E-04 +3.915206E+00 +3.646810E+00 +2.701000E+00 +1.486587E+00 +1.340000E-01 +3.598000E-03 +5.583136E-02 +6.266530E-04 +1.267275E-01 +3.349463E-03 +7.209619E-07 +1.153253E-13 +1.267275E-01 +3.349463E-03 0.000000E+00 0.000000E+00 -3.029741E+06 -2.052541E+12 -5.970000E-01 -8.325100E-02 +1.116627E+07 +2.506612E+13 +2.701000E+00 +1.486587E+00 0.000000E+00 0.000000E+00 -1.714353E+00 -6.970480E-01 -1.852000E+00 -7.973360E-01 -7.900000E-02 -1.689000E-03 -2.755187E-02 -2.091740E-04 -5.453423E-02 -7.484037E-04 -2.368001E-07 -1.753710E-14 -5.453423E-02 -7.484037E-04 +7.844332E+00 +1.252301E+01 +2.791000E+00 +1.658877E+00 +1.240000E-01 +3.282000E-03 +5.173865E-02 +5.560435E-04 +1.292971E-01 +3.436301E-03 +6.920456E-07 +9.917496E-14 +1.263311E-01 +3.290664E-03 +2.965960E-03 +4.837204E-06 +1.034773E+07 +2.224174E+13 +2.791000E+00 +1.658877E+00 +7.133747E-04 +3.640977E-07 +8.120311E+00 +1.408288E+01 +3.169000E+00 +2.031747E+00 +1.620000E-01 +5.364000E-03 +6.707115E-02 +9.171975E-04 +1.648345E-01 +5.547084E-03 +1.008876E-06 +2.201766E-13 +1.638199E-01 +5.463700E-03 +1.014608E-03 +1.029429E-06 +1.341423E+07 +3.668790E+13 +3.169000E+00 +2.031747E+00 +8.618993E-04 +7.428703E-07 +9.158565E+00 +1.699756E+01 +3.660000E+00 +2.796494E+00 +1.580000E-01 +5.158000E-03 +6.719242E-02 +9.422093E-04 +1.591500E-01 +5.615224E-03 +8.020080E-07 +1.480166E-13 +1.581743E-01 +5.540021E-03 +9.756761E-04 +9.519438E-07 +1.343848E+07 +3.768837E+13 +3.660000E+00 +2.796494E+00 +2.954152E-04 +8.727014E-08 +1.067773E+01 +2.376726E+01 +2.527000E+00 +1.452369E+00 +1.140000E-01 +2.834000E-03 +4.984389E-02 +5.325247E-04 +1.111356E-01 +2.793586E-03 +7.289084E-07 +1.302125E-13 +1.111356E-01 +2.793586E-03 0.000000E+00 0.000000E+00 -5.510374E+06 -8.366961E+12 -1.852000E+00 -7.973360E-01 +9.968777E+06 +2.130099E+13 +2.527000E+00 +1.452369E+00 0.000000E+00 0.000000E+00 -5.449659E+00 -6.908544E+00 -4.177000E+00 -3.780409E+00 -1.760000E-01 -6.584000E-03 -7.049310E-02 -1.047700E-03 -2.082153E-01 -9.154274E-03 -1.090026E-06 -2.590031E-13 -2.082153E-01 -9.154274E-03 +7.323996E+00 +1.221424E+01 +3.204000E+00 +2.092834E+00 +1.500000E-01 +4.600000E-03 +6.029165E-02 +7.387235E-04 +1.410668E-01 +4.414885E-03 +8.384625E-07 +1.602532E-13 +1.400911E-01 +4.332066E-03 +9.756761E-04 +9.519438E-07 +1.205833E+07 +2.954894E+13 +3.204000E+00 +2.092834E+00 +3.194266E-05 +1.020333E-09 +9.309783E+00 +1.769870E+01 +3.463000E+00 +2.477679E+00 +1.670000E-01 +5.837000E-03 +6.829328E-02 +9.882225E-04 +1.758063E-01 +6.470636E-03 +9.887100E-07 +2.199856E-13 +1.737856E-01 +6.307226E-03 +2.020620E-03 +2.041459E-06 +1.365866E+07 +3.952890E+13 +3.463000E+00 +2.477679E+00 +4.276023E-04 +1.081885E-07 +1.003961E+01 +2.083120E+01 +2.970000E-01 +4.491300E-02 +1.500000E-02 +9.100000E-05 +4.215863E-03 +6.275840E-06 +6.040786E-03 +1.401662E-05 +2.142282E-08 +4.361920E-16 +6.040786E-03 +1.401662E-05 0.000000E+00 0.000000E+00 -1.409862E+07 -4.190799E+13 -4.177000E+00 -3.780409E+00 +8.431726E+05 +2.510336E+11 +2.970000E-01 +4.491300E-02 0.000000E+00 0.000000E+00 -1.213785E+01 -3.197979E+01 -2.287000E+00 -1.059809E+00 -1.040000E-01 -2.254000E-03 -4.025632E-02 -3.309459E-04 -9.479325E-02 -1.956154E-03 -4.705869E-07 -4.509695E-14 -9.479325E-02 -1.956154E-03 +8.785109E-01 +3.967096E-01 +1.250000E-01 +5.357000E-03 +2.000000E-03 +2.000000E-06 +4.888503E-04 +1.194873E-07 +2.028292E-03 +2.059943E-06 +8.582314E-09 +7.099216E-17 +2.028292E-03 +2.059943E-06 0.000000E+00 0.000000E+00 -8.051265E+06 -1.323783E+13 -2.287000E+00 -1.059809E+00 +9.777007E+04 +4.779493E+09 +1.250000E-01 +5.357000E-03 0.000000E+00 0.000000E+00 -6.680550E+00 -9.051488E+00 -6.398000E+00 -8.323220E+00 -2.580000E-01 -1.356600E-02 -1.070205E-01 -2.345401E-03 -2.825180E-01 -1.658268E-02 -1.510574E-06 -5.135513E-13 -2.805357E-01 -1.633928E-02 -1.982273E-03 -1.965559E-06 -2.140411E+07 -9.381603E+13 -6.398000E+00 -8.323220E+00 -6.001927E-04 -1.801940E-07 -1.863566E+01 -7.066273E+01 -1.167000E+00 -4.778410E-01 -5.100000E-02 -9.290000E-04 -1.851002E-02 -1.273765E-04 -3.405873E-02 -6.277248E-04 -1.688115E-07 -1.480555E-14 -3.195431E-02 -5.302962E-04 -2.104418E-03 -4.428574E-06 -3.702005E+06 -5.095061E+12 -1.167000E+00 -4.778410E-01 -3.530361E-04 -1.246345E-07 -3.428588E+00 -4.115657E+00 -3.630000E+00 -2.765806E+00 -1.490000E-01 -4.587000E-03 -5.894825E-02 -7.367371E-04 -1.434581E-01 -4.305327E-03 -7.603618E-07 -1.250071E-13 -1.414758E-01 -4.212618E-03 -1.982273E-03 -1.965559E-06 -1.178965E+07 -2.946948E+13 -3.630000E+00 -2.765806E+00 -4.160428E-04 -1.012741E-07 -1.059988E+01 -2.359174E+01 +3.698633E-01 +4.679347E-02 tally 2: -5.033991E-01 -5.701830E-02 -2.304219E-02 -1.162005E-04 -9.376617E-03 -1.933443E-05 -2.344154E-02 -1.208402E-04 -1.277674E-07 -3.888071E-15 -2.329292E-02 -1.193128E-04 -1.486195E-04 -4.857250E-09 -1.875323E+06 -7.733771E+11 -5.220000E-01 -5.995400E-02 -6.963583E-05 -1.066362E-09 -1.463809E+00 -4.840945E-01 -2.621546E+00 -1.396717E+00 -1.276380E-01 -3.311549E-03 -5.565805E-02 -6.598224E-04 -1.391451E-01 -4.123890E-03 -8.331879E-07 -1.660597E-13 -1.382629E-01 -4.071765E-03 -8.821806E-04 -1.657624E-07 -1.113161E+07 -2.639289E+13 -2.581000E+00 -1.357455E+00 -4.133468E-04 -3.639153E-08 -7.576783E+00 -1.169599E+01 -2.230626E+00 -1.033217E+00 -1.065165E-01 -2.416670E-03 -4.549248E-02 -4.740035E-04 -1.137312E-01 -2.962522E-03 -6.630810E-07 -1.170893E-13 -1.130101E-01 -2.925076E-03 -7.210562E-04 -1.190805E-07 -9.098495E+06 -1.896014E+13 -2.202000E+00 -1.007194E+00 -3.378518E-04 -2.614296E-08 -6.459596E+00 -8.662758E+00 -5.565395E-01 -7.242839E-02 -2.955239E-02 -1.840142E-04 -1.400990E-02 -4.103227E-05 -3.502476E-02 -2.564517E-04 -2.308081E-07 -1.205491E-14 -3.480270E-02 -2.532102E-04 -2.220571E-04 -1.030824E-08 -2.801981E+06 -1.641291E+12 -6.090000E-01 -8.636300E-02 -1.040451E-04 -2.263074E-09 -1.593637E+00 -6.040566E-01 -1.817267E+00 -7.710225E-01 -7.367627E-02 -1.267666E-03 -2.535616E-02 -1.551036E-04 -6.339039E-02 -9.693976E-04 -2.524798E-07 -1.896478E-14 -6.298850E-02 -9.571446E-04 -4.018954E-04 -3.896557E-08 -5.071232E+06 -6.204145E+12 -1.881000E+00 -8.206930E-01 -1.883086E-04 -8.554511E-09 -5.341905E+00 -6.666854E+00 -4.106914E+00 -3.704316E+00 -1.888617E-01 -7.777635E-03 -7.727973E-02 -1.309080E-03 -1.931993E-01 -8.181751E-03 -1.061590E-06 -2.554724E-13 -1.919744E-01 -8.078335E-03 -1.224884E-03 -3.288708E-07 -1.545595E+07 -5.236321E+13 -4.223000E+00 -3.850821E+00 -5.739211E-04 -7.220038E-08 -1.193698E+01 -3.133321E+01 -2.346339E+00 -1.119793E+00 -1.017476E-01 -2.080323E-03 -3.865443E-02 -3.024337E-04 -9.663608E-02 -1.890211E-03 -4.714003E-07 -4.853805E-14 -9.602340E-02 -1.866319E-03 -6.126731E-04 -7.597824E-08 -7.730886E+06 -1.209735E+13 -2.338000E+00 -1.106368E+00 -2.870687E-04 -1.668028E-08 -6.857029E+00 -9.581045E+00 -6.446883E+00 -8.447751E+00 -2.867793E-01 -1.670093E-02 -1.126541E-01 -2.611201E-03 -2.816352E-01 -1.632001E-02 -1.453669E-06 -4.585416E-13 -2.798496E-01 -1.611372E-02 -1.785568E-03 -6.559933E-07 -2.253081E+07 -1.044480E+14 -6.454000E+00 -8.471478E+00 -8.366302E-04 -1.440169E-07 -1.879692E+01 -7.185693E+01 -1.219419E+00 -5.127750E-01 -4.857020E-02 -8.389250E-04 -1.623897E-02 -1.008738E-04 -4.059741E-02 -6.304611E-04 -1.503579E-07 -1.146797E-14 -4.034003E-02 -6.224921E-04 -2.573878E-04 -2.534179E-08 -3.247793E+06 -4.034951E+12 -1.199000E+00 -4.975810E-01 -1.205994E-04 -5.563543E-09 -3.589772E+00 -4.434695E+00 -3.623361E+00 -2.780040E+00 -1.560708E-01 -5.162316E-03 -5.875078E-02 -7.419979E-04 -1.468770E-01 -4.637487E-03 -7.048177E-07 -1.136014E-13 -1.459458E-01 -4.578870E-03 -9.312005E-04 -1.864068E-07 -1.175016E+07 -2.967992E+13 -3.668000E+00 -2.824270E+00 -4.363151E-04 -4.092380E-08 -1.059543E+01 -2.377944E+01 +1.353006E+00 +4.298474E-01 +6.067126E-02 +9.182923E-04 +2.407597E-02 +1.589874E-04 +6.018994E-02 +9.936715E-04 +3.157310E-07 +3.268202E-14 +5.980833E-02 +9.811116E-04 +3.816044E-04 +3.994127E-08 +4.815195E+06 +6.359497E+12 +1.373000E+00 +4.448330E-01 +1.788012E-04 +8.768717E-09 +3.941968E+00 +3.631126E+00 +2.806910E+00 +1.600166E+00 +1.295488E-01 +3.449533E-03 +5.323702E-02 +5.945274E-04 +1.330925E-01 +3.715796E-03 +7.358638E-07 +1.182977E-13 +1.322487E-01 +3.668829E-03 +8.438073E-04 +1.493588E-07 +1.064740E+07 +2.378109E+13 +2.753000E+00 +1.542179E+00 +3.953669E-04 +3.279028E-08 +8.155605E+00 +1.349706E+01 +2.869647E+00 +1.739405E+00 +1.278179E-01 +3.407189E-03 +5.029332E-02 +5.252401E-04 +1.257333E-01 +3.282751E-03 +6.507109E-07 +8.979533E-14 +1.249362E-01 +3.241257E-03 +7.971497E-04 +1.319523E-07 +1.005866E+07 +2.100960E+13 +2.829000E+00 +1.702143E+00 +3.735055E-04 +2.896884E-08 +8.365907E+00 +1.480669E+01 +3.141043E+00 +1.981851E+00 +1.526263E-01 +4.699686E-03 +6.641498E-02 +9.211853E-04 +1.660374E-01 +5.757408E-03 +9.915981E-07 +2.229928E-13 +1.649848E-01 +5.684636E-03 +1.052678E-03 +2.314228E-07 +1.328300E+07 +3.684741E+13 +3.246000E+00 +2.132054E+00 +4.932336E-04 +5.080661E-08 +9.080077E+00 +1.658064E+01 +3.682383E+00 +2.808072E+00 +1.593265E-01 +5.346789E-03 +6.034523E-02 +7.901305E-04 +1.508631E-01 +4.938316E-03 +7.319663E-07 +1.246559E-13 +1.499066E-01 +4.875896E-03 +9.564725E-04 +1.984988E-07 +1.206905E+07 +3.160522E+13 +3.759000E+00 +2.944479E+00 +4.481564E-04 +4.357848E-08 +1.076370E+01 +2.396100E+01 +2.634850E+00 +1.584334E+00 +1.218122E-01 +3.391010E-03 +5.015643E-02 +5.871038E-04 +1.253911E-01 +3.669399E-03 +6.952485E-07 +1.202338E-13 +1.245961E-01 +3.623018E-03 +7.949798E-04 +1.474939E-07 +1.003129E+07 +2.348415E+13 +2.577000E+00 +1.509201E+00 +3.724888E-04 +3.238084E-08 +7.654439E+00 +1.338000E+01 +3.192584E+00 +2.071165E+00 +1.491327E-01 +4.485370E-03 +6.214556E-02 +7.933462E-04 +1.553639E-01 +4.958414E-03 +8.761442E-07 +1.691919E-13 +1.543789E-01 +4.895740E-03 +9.850078E-04 +1.993067E-07 +1.242911E+07 +3.173385E+13 +3.266000E+00 +2.172442E+00 +4.615266E-04 +4.375584E-08 +9.265396E+00 +1.747782E+01 +3.402213E+00 +2.415762E+00 +1.592235E-01 +5.219460E-03 +6.649290E-02 +9.066793E-04 +1.662323E-01 +5.666746E-03 +9.402303E-07 +1.840541E-13 +1.651783E-01 +5.595119E-03 +1.053913E-03 +2.277785E-07 +1.329858E+07 +3.626717E+13 +3.485000E+00 +2.510947E+00 +4.938123E-04 +5.000655E-08 +9.871966E+00 +2.037665E+01 +3.110378E-01 +5.227746E-02 +1.171121E-02 +6.542918E-05 +3.536640E-03 +5.149982E-06 +8.841599E-03 +3.218739E-05 +2.347065E-08 +5.243075E-16 +8.785544E-03 +3.178055E-05 +5.605578E-05 +1.293793E-09 +7.073280E+05 +2.059993E+11 +3.000000E-01 +4.514200E-02 +2.626500E-05 +2.840396E-10 +9.197485E-01 +4.619575E-01 +1.267544E-01 +5.491246E-03 +4.806410E-03 +8.161899E-06 +1.471497E-03 +8.779657E-07 +3.678743E-03 +5.487286E-06 +1.030812E-08 +1.028225E-16 +3.655420E-03 +5.417928E-06 +2.332325E-05 +2.205650E-10 +2.942995E+05 +3.511863E+10 +1.300000E-01 +5.822000E-03 +1.092814E-05 +4.842290E-11 +3.746117E-01 +4.790053E-02 tally 3: -1.092010E+02 -2.386227E+03 -4.975000E+00 -4.950147E+00 -2.009472E+00 -8.076733E-01 -5.000649E+00 -5.012321E+00 -2.728992E-05 -1.491241E-10 -4.964474E+00 -4.939215E+00 -3.617555E-02 -2.875519E-04 -4.018944E+08 -3.230693E+16 -1.092010E+02 -2.386227E+03 -1.333979E-02 -5.019969E-05 -3.176576E+02 -2.019260E+04 -1.042260E+02 -2.173864E+03 -1.042260E+02 -2.173864E+03 +1.098420E+02 +2.414277E+03 +4.993000E+00 +4.986021E+00 +2.023763E+00 +8.192234E-01 +5.037812E+00 +5.086734E+00 +2.745346E-05 +1.509092E-10 +5.002505E+00 +5.014944E+00 +3.530702E-02 +2.695026E-04 +4.047525E+08 +3.276894E+16 +1.098420E+02 +2.414277E+03 +1.059695E-02 +2.782849E-05 +3.195213E+02 +2.042961E+04 +1.048490E+02 +2.199887E+03 +1.048490E+02 +2.199887E+03 tally 4: -1.092010E+02 -2.386227E+03 -4.979068E+00 -4.959690E+00 -2.016700E+00 -8.137138E-01 -5.041749E+00 -5.085712E+00 -5.020635E-05 -5.047241E-10 -5.009785E+00 -5.021429E+00 -3.196471E-02 -2.044235E-04 -4.033399E+08 -3.254855E+16 -1.092010E+02 -2.386227E+03 -1.497710E-02 -4.487918E-05 -3.176576E+02 -2.019260E+04 +1.098420E+02 +2.414277E+03 +5.008447E+00 +5.018748E+00 +2.028674E+00 +8.234818E-01 +5.071684E+00 +5.146761E+00 +5.050718E-05 +5.107669E-10 +5.039530E+00 +5.081707E+00 +3.215450E-02 +2.068774E-04 +4.057347E+08 +3.293927E+16 +1.098420E+02 +2.414277E+03 +1.506603E-02 +4.541792E-05 +3.195213E+02 +2.042961E+04 tally 5: -1.097121E+02 -2.408636E+03 -5.009145E+00 -5.019563E+00 -2.032191E+00 -8.264420E-01 -5.080477E+00 -5.165263E+00 -2.756641E-05 -1.523527E-10 -5.048267E+00 -5.099975E+00 -3.221025E-02 -2.076211E-04 -4.064382E+08 -3.305768E+16 -1.093810E+02 -2.394075E+03 -1.509215E-02 -4.558119E-05 -3.191033E+02 -2.037736E+04 +1.103689E+02 +2.437562E+03 +5.035611E+00 +5.073005E+00 +2.041209E+00 +8.337913E-01 +5.103022E+00 +5.211196E+00 +2.765405E-05 +1.532759E-10 +5.070669E+00 +5.145327E+00 +3.235318E-02 +2.094674E-04 +4.082417E+08 +3.335165E+16 +1.098580E+02 +2.414983E+03 +1.515912E-02 +4.598653E-05 +3.210351E+02 +2.062463E+04 tally 6: -1.042260E+02 -2.173864E+03 -1.042260E+02 -2.173864E+03 -5.000649E+00 -5.012321E+00 +1.048490E+02 +2.199887E+03 +1.048490E+02 +2.199887E+03 +5.037812E+00 +5.086734E+00 tally 7: -6.507000E+00 -8.478423E+00 -1.444000E+00 -4.172780E-01 -1.146407E+00 -2.630079E-01 -2.867025E+00 -1.647980E+00 -2.707153E-05 -1.467503E-10 -2.848880E+00 -1.627146E+00 -1.814469E-02 -6.685238E-05 -2.292814E+08 -1.052031E+16 -6.507000E+00 -8.478423E+00 -9.467712E-03 -2.396878E-05 -1.191147E+01 -2.841087E+01 -5.063000E+00 -5.136983E+00 -5.063000E+00 -5.136983E+00 -1.026940E+02 -2.110535E+03 +6.546000E+00 +8.579894E+00 +1.462000E+00 +4.278220E-01 +1.160697E+00 +2.696537E-01 +2.879845E+00 +1.663297E+00 +2.723379E-05 +1.485067E-10 +2.862628E+00 +1.643479E+00 +1.721726E-02 +6.055382E-05 +2.321395E+08 +1.078615E+16 +6.546000E+00 +8.579894E+00 +6.709978E-03 +1.387848E-05 +1.198287E+01 +2.875089E+01 +5.084000E+00 +5.178800E+00 +5.084000E+00 +5.178800E+00 +1.032960E+02 +2.135240E+03 3.531000E+00 -2.493861E+00 +2.493943E+00 8.630652E-01 -1.489924E-01 -2.133624E+00 -9.150435E-01 -2.183901E-07 -9.544829E-15 -2.115593E+00 -8.991467E-01 -1.803087E-02 -8.677353E-05 +1.489973E-01 +2.157967E+00 +9.345420E-01 +2.196703E-07 +9.656558E-15 +2.139877E+00 +9.185178E-01 +1.808977E-02 +8.744581E-05 1.726130E+08 -5.959695E+15 -1.026940E+02 -2.110535E+03 -3.872081E-03 -5.919692E-06 -3.057461E+02 -1.870786E+04 -9.916300E+01 -1.968002E+03 -9.916300E+01 -1.968002E+03 +5.959891E+15 +1.032960E+02 +2.135240E+03 +3.886968E-03 +5.976458E-06 +3.075384E+02 +1.892685E+04 +9.976500E+01 +1.991860E+03 +9.976500E+01 +1.991860E+03 tally 8: -6.507000E+00 -8.478423E+00 -1.455344E+00 -4.241161E-01 -1.155413E+00 -2.673178E-01 -2.888532E+00 -1.670737E+00 -4.955615E-05 -4.917547E-10 -2.870219E+00 -1.649619E+00 -1.831331E-02 -6.715634E-05 -2.310826E+08 -1.069271E+16 -6.507000E+00 -8.478423E+00 -8.580723E-03 -1.474352E-05 -1.191147E+01 -2.841087E+01 -1.026940E+02 -2.110535E+03 -3.523724E+00 -2.484884E+00 -8.612868E-01 -1.484560E-01 -2.153217E+00 -9.278503E-01 -6.502027E-07 -8.460574E-14 -2.139566E+00 -9.161225E-01 -1.365140E-02 -3.729555E-05 -1.722574E+08 -5.938242E+15 -1.026940E+02 -2.110535E+03 -6.396382E-03 -8.187874E-06 -3.057461E+02 -1.870786E+04 +6.546000E+00 +8.579894E+00 +1.464067E+00 +4.291919E-01 +1.162338E+00 +2.705171E-01 +2.905845E+00 +1.690732E+00 +4.985316E-05 +4.976401E-10 +2.887422E+00 +1.669362E+00 +1.842307E-02 +6.796008E-05 +2.324676E+08 +1.082069E+16 +6.546000E+00 +8.579894E+00 +8.632151E-03 +1.491997E-05 +1.198287E+01 +2.875089E+01 +1.032960E+02 +2.135240E+03 +3.544380E+00 +2.513971E+00 +8.663357E-01 +1.501938E-01 +2.165839E+00 +9.387115E-01 +6.540142E-07 +8.559612E-14 +2.152108E+00 +9.268463E-01 +1.373143E-02 +3.773212E-05 +1.732671E+08 +6.007753E+15 +1.032960E+02 +2.135240E+03 +6.433878E-03 +8.283719E-06 +3.075384E+02 +1.892685E+04 tally 9: -6.573230E+00 -8.663027E+00 -1.470157E+00 -4.333505E-01 -1.167173E+00 -2.731383E-01 -2.917933E+00 -1.707114E+00 -2.734707E-05 -1.499456E-10 -2.899433E+00 -1.685536E+00 -1.849971E-02 -6.861856E-05 -2.334346E+08 -1.092553E+16 -6.518000E+00 -8.507644E+00 -8.668060E-03 -1.506454E-05 -1.203271E+01 -2.902947E+01 -1.031389E+02 -2.128993E+03 -3.538988E+00 -2.506616E+00 -8.650177E-01 -1.497544E-01 -2.162544E+00 -9.359650E-01 -2.193361E-07 -9.628305E-15 -2.148834E+00 -9.241346E-01 -1.371054E-02 -3.762172E-05 -1.730035E+08 -5.990176E+15 -1.028630E+02 -2.117467E+03 -6.424090E-03 -8.259483E-06 -3.070706E+02 -1.887148E+04 +6.593971E+00 +8.715030E+00 +1.474796E+00 +4.359519E-01 +1.170856E+00 +2.747779E-01 +2.927140E+00 +1.717362E+00 +2.743336E-05 +1.508457E-10 +2.908582E+00 +1.695655E+00 +1.855808E-02 +6.903047E-05 +2.341712E+08 +1.099112E+16 +6.547000E+00 +8.582573E+00 +8.695410E-03 +1.515497E-05 +1.207068E+01 +2.920373E+01 +1.037750E+02 +2.155274E+03 +3.560815E+00 +2.537559E+00 +8.703528E-01 +1.516031E-01 +2.175882E+00 +9.475192E-01 +2.206889E-07 +9.747163E-15 +2.162087E+00 +9.355427E-01 +1.379510E-02 +3.808615E-05 +1.740706E+08 +6.064123E+15 +1.033110E+02 +2.135863E+03 +6.463712E-03 +8.361443E-06 +3.089644E+02 +1.910444E+04 tally 10: -5.063000E+00 -5.136983E+00 -5.063000E+00 -5.136983E+00 +5.084000E+00 +5.178800E+00 +5.084000E+00 +5.178800E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.867025E+00 -1.647980E+00 -1.445000E+00 -4.178610E-01 -1.445000E+00 -4.178610E-01 +2.879845E+00 +1.663297E+00 +1.463000E+00 +4.284330E-01 +1.463000E+00 +4.284330E-01 0.000000E+00 0.000000E+00 -9.771800E+01 -1.911083E+03 -9.771800E+01 -1.911083E+03 -2.133624E+00 -9.150435E-01 +9.830200E+01 +1.933884E+03 +9.830200E+01 +1.933884E+03 +2.157967E+00 +9.345420E-01 tally 11: -5.220000E-01 -5.995400E-02 -1.400000E-02 -4.000000E-05 -6.718865E-03 -9.371695E-06 -1.106435E-02 -3.119729E-05 -1.216992E-07 -3.699412E-15 -1.106435E-02 -3.119729E-05 +1.342000E+00 +4.291640E-01 +7.000000E-02 +1.158000E-03 +2.919844E-02 +1.953371E-04 +5.372246E-02 +6.734832E-04 +2.939310E-07 +2.375686E-14 +5.372246E-02 +6.734832E-04 0.000000E+00 0.000000E+00 -1.343773E+06 -3.748678E+11 -5.220000E-01 -5.995400E-02 +5.839688E+06 +7.813485E+12 +1.342000E+00 +4.291640E-01 0.000000E+00 0.000000E+00 -2.564000E+00 -1.338762E+00 -1.160000E-01 -2.738000E-03 -4.593685E-02 -4.275719E-04 -1.360763E-01 -3.951569E-03 -8.079937E-07 -1.577355E-13 -1.350645E-01 -3.872739E-03 -1.011813E-03 -1.023765E-06 -9.187371E+06 -1.710288E+13 -2.564000E+00 -1.338762E+00 -8.595248E-04 -7.387829E-07 -2.184000E+00 -9.895940E-01 -1.080000E-01 -2.394000E-03 -4.782785E-02 -4.787428E-04 -1.140806E-01 -2.886440E-03 -6.242229E-07 -9.307161E-14 -1.130687E-01 -2.811706E-03 -1.011813E-03 -1.023765E-06 -9.565570E+06 -1.914971E+13 -2.184000E+00 -9.895940E-01 -3.312574E-05 -1.097314E-09 -5.970000E-01 -8.325100E-02 -3.500000E-02 -3.150000E-04 -1.514871E-02 -5.131352E-05 -3.795718E-02 -3.631435E-04 -2.299730E-07 -1.295603E-14 -3.795718E-02 -3.631435E-04 +2.701000E+00 +1.486587E+00 +1.340000E-01 +3.598000E-03 +5.583136E-02 +6.266530E-04 +1.267275E-01 +3.349463E-03 +7.209619E-07 +1.153253E-13 +1.267275E-01 +3.349463E-03 0.000000E+00 0.000000E+00 -3.029741E+06 -2.052541E+12 -5.970000E-01 -8.325100E-02 +1.116627E+07 +2.506612E+13 +2.701000E+00 +1.486587E+00 0.000000E+00 0.000000E+00 -1.852000E+00 -7.973360E-01 -7.900000E-02 -1.689000E-03 -2.755187E-02 -2.091740E-04 -5.453423E-02 -7.484037E-04 -2.368001E-07 -1.753710E-14 -5.453423E-02 -7.484037E-04 +2.791000E+00 +1.658877E+00 +1.240000E-01 +3.282000E-03 +5.173865E-02 +5.560435E-04 +1.292971E-01 +3.436301E-03 +6.920456E-07 +9.917496E-14 +1.263311E-01 +3.290664E-03 +2.965960E-03 +4.837204E-06 +1.034773E+07 +2.224174E+13 +2.791000E+00 +1.658877E+00 +7.133747E-04 +3.640977E-07 +3.169000E+00 +2.031747E+00 +1.620000E-01 +5.364000E-03 +6.707115E-02 +9.171975E-04 +1.648345E-01 +5.547084E-03 +1.008876E-06 +2.201766E-13 +1.638199E-01 +5.463700E-03 +1.014608E-03 +1.029429E-06 +1.341423E+07 +3.668790E+13 +3.169000E+00 +2.031747E+00 +8.618993E-04 +7.428703E-07 +3.660000E+00 +2.796494E+00 +1.580000E-01 +5.158000E-03 +6.719242E-02 +9.422093E-04 +1.591500E-01 +5.615224E-03 +8.020080E-07 +1.480166E-13 +1.581743E-01 +5.540021E-03 +9.756761E-04 +9.519438E-07 +1.343848E+07 +3.768837E+13 +3.660000E+00 +2.796494E+00 +2.954152E-04 +8.727014E-08 +2.527000E+00 +1.452369E+00 +1.140000E-01 +2.834000E-03 +4.984389E-02 +5.325247E-04 +1.111356E-01 +2.793586E-03 +7.289084E-07 +1.302125E-13 +1.111356E-01 +2.793586E-03 0.000000E+00 0.000000E+00 -5.510374E+06 -8.366961E+12 -1.852000E+00 -7.973360E-01 +9.968777E+06 +2.130099E+13 +2.527000E+00 +1.452369E+00 0.000000E+00 0.000000E+00 -4.177000E+00 -3.780409E+00 -1.760000E-01 -6.584000E-03 -7.049310E-02 -1.047700E-03 -2.082153E-01 -9.154274E-03 -1.090026E-06 -2.590031E-13 -2.082153E-01 -9.154274E-03 +3.204000E+00 +2.092834E+00 +1.500000E-01 +4.600000E-03 +6.029165E-02 +7.387235E-04 +1.410668E-01 +4.414885E-03 +8.384625E-07 +1.602532E-13 +1.400911E-01 +4.332066E-03 +9.756761E-04 +9.519438E-07 +1.205833E+07 +2.954894E+13 +3.204000E+00 +2.092834E+00 +3.194266E-05 +1.020333E-09 +3.463000E+00 +2.477679E+00 +1.670000E-01 +5.837000E-03 +6.829328E-02 +9.882225E-04 +1.758063E-01 +6.470636E-03 +9.887100E-07 +2.199856E-13 +1.737856E-01 +6.307226E-03 +2.020620E-03 +2.041459E-06 +1.365866E+07 +3.952890E+13 +3.463000E+00 +2.477679E+00 +4.276023E-04 +1.081885E-07 +2.970000E-01 +4.491300E-02 +1.500000E-02 +9.100000E-05 +4.215863E-03 +6.275840E-06 +6.040786E-03 +1.401662E-05 +2.142282E-08 +4.361920E-16 +6.040786E-03 +1.401662E-05 0.000000E+00 0.000000E+00 -1.409862E+07 -4.190799E+13 -4.177000E+00 -3.780409E+00 +8.431726E+05 +2.510336E+11 +2.970000E-01 +4.491300E-02 0.000000E+00 0.000000E+00 -2.287000E+00 -1.059809E+00 -1.040000E-01 -2.254000E-03 -4.025632E-02 -3.309459E-04 -9.479325E-02 -1.956154E-03 -4.705869E-07 -4.509695E-14 -9.479325E-02 -1.956154E-03 +1.250000E-01 +5.357000E-03 +2.000000E-03 +2.000000E-06 +4.888503E-04 +1.194873E-07 +2.028292E-03 +2.059943E-06 +8.582314E-09 +7.099216E-17 +2.028292E-03 +2.059943E-06 0.000000E+00 0.000000E+00 -8.051265E+06 -1.323783E+13 -2.287000E+00 -1.059809E+00 +9.777007E+04 +4.779493E+09 +1.250000E-01 +5.357000E-03 0.000000E+00 0.000000E+00 -6.398000E+00 -8.323220E+00 -2.580000E-01 -1.356600E-02 -1.070205E-01 -2.345401E-03 -2.825180E-01 -1.658268E-02 -1.510574E-06 -5.135513E-13 -2.805357E-01 -1.633928E-02 -1.982273E-03 -1.965559E-06 -2.140411E+07 -9.381603E+13 -6.398000E+00 -8.323220E+00 -6.001927E-04 -1.801940E-07 -1.167000E+00 -4.778410E-01 -5.100000E-02 -9.290000E-04 -1.851002E-02 -1.273765E-04 -3.405873E-02 -6.277248E-04 -1.688115E-07 -1.480555E-14 -3.195431E-02 -5.302962E-04 -2.104418E-03 -4.428574E-06 -3.702005E+06 -5.095061E+12 -1.167000E+00 -4.778410E-01 -3.530361E-04 -1.246345E-07 -3.630000E+00 -2.765806E+00 -1.490000E-01 -4.587000E-03 -5.894825E-02 -7.367371E-04 -1.434581E-01 -4.305327E-03 -7.603618E-07 -1.250071E-13 -1.414758E-01 -4.212618E-03 -1.982273E-03 -1.965559E-06 -1.178965E+07 -2.946948E+13 -3.630000E+00 -2.765806E+00 -4.160428E-04 -1.012741E-07 tally 12: -5.033991E-01 -5.701830E-02 -2.304219E-02 -1.162005E-04 -9.376617E-03 -1.933443E-05 -2.344154E-02 -1.208402E-04 -1.277674E-07 -3.888071E-15 -2.329292E-02 -1.193128E-04 -1.486195E-04 -4.857250E-09 -1.875323E+06 -7.733771E+11 -5.220000E-01 -5.995400E-02 -6.963583E-05 -1.066362E-09 -2.621546E+00 -1.396717E+00 -1.276380E-01 -3.311549E-03 -5.565805E-02 -6.598224E-04 -1.391451E-01 -4.123890E-03 -8.331879E-07 -1.660597E-13 -1.382629E-01 -4.071765E-03 -8.821806E-04 -1.657624E-07 -1.113161E+07 -2.639289E+13 -2.581000E+00 -1.357455E+00 -4.133468E-04 -3.639153E-08 -2.230626E+00 -1.033217E+00 -1.065165E-01 -2.416670E-03 -4.549248E-02 -4.740035E-04 -1.137312E-01 -2.962522E-03 -6.630810E-07 -1.170893E-13 -1.130101E-01 -2.925076E-03 -7.210562E-04 -1.190805E-07 -9.098495E+06 -1.896014E+13 -2.202000E+00 -1.007194E+00 -3.378518E-04 -2.614296E-08 -5.565395E-01 -7.242839E-02 -2.955239E-02 -1.840142E-04 -1.400990E-02 -4.103227E-05 -3.502476E-02 -2.564517E-04 -2.308081E-07 -1.205491E-14 -3.480270E-02 -2.532102E-04 -2.220571E-04 -1.030824E-08 -2.801981E+06 -1.641291E+12 -6.090000E-01 -8.636300E-02 -1.040451E-04 -2.263074E-09 -1.817267E+00 -7.710225E-01 -7.367627E-02 -1.267666E-03 -2.535616E-02 -1.551036E-04 -6.339039E-02 -9.693976E-04 -2.524798E-07 -1.896478E-14 -6.298850E-02 -9.571446E-04 -4.018954E-04 -3.896557E-08 -5.071232E+06 -6.204145E+12 -1.881000E+00 -8.206930E-01 -1.883086E-04 -8.554511E-09 -4.106914E+00 -3.704316E+00 -1.888617E-01 -7.777635E-03 -7.727973E-02 -1.309080E-03 -1.931993E-01 -8.181751E-03 -1.061590E-06 -2.554724E-13 -1.919744E-01 -8.078335E-03 -1.224884E-03 -3.288708E-07 -1.545595E+07 -5.236321E+13 -4.223000E+00 -3.850821E+00 -5.739211E-04 -7.220038E-08 -2.346339E+00 -1.119793E+00 -1.017476E-01 -2.080323E-03 -3.865443E-02 -3.024337E-04 -9.663608E-02 -1.890211E-03 -4.714003E-07 -4.853805E-14 -9.602340E-02 -1.866319E-03 -6.126731E-04 -7.597824E-08 -7.730886E+06 -1.209735E+13 -2.338000E+00 -1.106368E+00 -2.870687E-04 -1.668028E-08 -6.446883E+00 -8.447751E+00 -2.867793E-01 -1.670093E-02 -1.126541E-01 -2.611201E-03 -2.816352E-01 -1.632001E-02 -1.453669E-06 -4.585416E-13 -2.798496E-01 -1.611372E-02 -1.785568E-03 -6.559933E-07 -2.253081E+07 -1.044480E+14 -6.454000E+00 -8.471478E+00 -8.366302E-04 -1.440169E-07 -1.219419E+00 -5.127750E-01 -4.857020E-02 -8.389250E-04 -1.623897E-02 -1.008738E-04 -4.059741E-02 -6.304611E-04 -1.503579E-07 -1.146797E-14 -4.034003E-02 -6.224921E-04 -2.573878E-04 -2.534179E-08 -3.247793E+06 -4.034951E+12 -1.199000E+00 -4.975810E-01 -1.205994E-04 -5.563543E-09 -3.623361E+00 -2.780040E+00 -1.560708E-01 -5.162316E-03 -5.875078E-02 -7.419979E-04 -1.468770E-01 -4.637487E-03 -7.048177E-07 -1.136014E-13 -1.459458E-01 -4.578870E-03 -9.312005E-04 -1.864068E-07 -1.175016E+07 -2.967992E+13 -3.668000E+00 -2.824270E+00 -4.363151E-04 -4.092380E-08 +1.353006E+00 +4.298474E-01 +6.067126E-02 +9.182923E-04 +2.407597E-02 +1.589874E-04 +6.018994E-02 +9.936715E-04 +3.157310E-07 +3.268202E-14 +5.980833E-02 +9.811116E-04 +3.816044E-04 +3.994127E-08 +4.815195E+06 +6.359497E+12 +1.373000E+00 +4.448330E-01 +1.788012E-04 +8.768717E-09 +2.806910E+00 +1.600166E+00 +1.295488E-01 +3.449533E-03 +5.323702E-02 +5.945274E-04 +1.330925E-01 +3.715796E-03 +7.358638E-07 +1.182977E-13 +1.322487E-01 +3.668829E-03 +8.438073E-04 +1.493588E-07 +1.064740E+07 +2.378109E+13 +2.753000E+00 +1.542179E+00 +3.953669E-04 +3.279028E-08 +2.869647E+00 +1.739405E+00 +1.278179E-01 +3.407189E-03 +5.029332E-02 +5.252401E-04 +1.257333E-01 +3.282751E-03 +6.507109E-07 +8.979533E-14 +1.249362E-01 +3.241257E-03 +7.971497E-04 +1.319523E-07 +1.005866E+07 +2.100960E+13 +2.829000E+00 +1.702143E+00 +3.735055E-04 +2.896884E-08 +3.141043E+00 +1.981851E+00 +1.526263E-01 +4.699686E-03 +6.641498E-02 +9.211853E-04 +1.660374E-01 +5.757408E-03 +9.915981E-07 +2.229928E-13 +1.649848E-01 +5.684636E-03 +1.052678E-03 +2.314228E-07 +1.328300E+07 +3.684741E+13 +3.246000E+00 +2.132054E+00 +4.932336E-04 +5.080661E-08 +3.682383E+00 +2.808072E+00 +1.593265E-01 +5.346789E-03 +6.034523E-02 +7.901305E-04 +1.508631E-01 +4.938316E-03 +7.319663E-07 +1.246559E-13 +1.499066E-01 +4.875896E-03 +9.564725E-04 +1.984988E-07 +1.206905E+07 +3.160522E+13 +3.759000E+00 +2.944479E+00 +4.481564E-04 +4.357848E-08 +2.634850E+00 +1.584334E+00 +1.218122E-01 +3.391010E-03 +5.015643E-02 +5.871038E-04 +1.253911E-01 +3.669399E-03 +6.952485E-07 +1.202338E-13 +1.245961E-01 +3.623018E-03 +7.949798E-04 +1.474939E-07 +1.003129E+07 +2.348415E+13 +2.577000E+00 +1.509201E+00 +3.724888E-04 +3.238084E-08 +3.192584E+00 +2.071165E+00 +1.491327E-01 +4.485370E-03 +6.214556E-02 +7.933462E-04 +1.553639E-01 +4.958414E-03 +8.761442E-07 +1.691919E-13 +1.543789E-01 +4.895740E-03 +9.850078E-04 +1.993067E-07 +1.242911E+07 +3.173385E+13 +3.266000E+00 +2.172442E+00 +4.615266E-04 +4.375584E-08 +3.402213E+00 +2.415762E+00 +1.592235E-01 +5.219460E-03 +6.649290E-02 +9.066793E-04 +1.662323E-01 +5.666746E-03 +9.402303E-07 +1.840541E-13 +1.651783E-01 +5.595119E-03 +1.053913E-03 +2.277785E-07 +1.329858E+07 +3.626717E+13 +3.485000E+00 +2.510947E+00 +4.938123E-04 +5.000655E-08 +3.110378E-01 +5.227746E-02 +1.171121E-02 +6.542918E-05 +3.536640E-03 +5.149982E-06 +8.841599E-03 +3.218739E-05 +2.347065E-08 +5.243075E-16 +8.785544E-03 +3.178055E-05 +5.605578E-05 +1.293793E-09 +7.073280E+05 +2.059993E+11 +3.000000E-01 +4.514200E-02 +2.626500E-05 +2.840396E-10 +1.267544E-01 +5.491246E-03 +4.806410E-03 +8.161899E-06 +1.471497E-03 +8.779657E-07 +3.678743E-03 +5.487286E-06 +1.030812E-08 +1.028225E-16 +3.655420E-03 +5.417928E-06 +2.332325E-05 +2.205650E-10 +2.942995E+05 +3.511863E+10 +1.300000E-01 +5.822000E-03 +1.092814E-05 +4.842290E-11 tally 13: -1.092010E+02 -2.386227E+03 -4.975000E+00 -4.950147E+00 -2.009472E+00 -8.076733E-01 -5.000649E+00 -5.012321E+00 -2.728992E-05 -1.491241E-10 -4.964474E+00 -4.939215E+00 -3.617555E-02 -2.875519E-04 -4.018944E+08 -3.230693E+16 -1.092010E+02 -2.386227E+03 -1.333979E-02 -5.019969E-05 -1.042260E+02 -2.173864E+03 -1.042260E+02 -2.173864E+03 +1.098420E+02 +2.414277E+03 +4.993000E+00 +4.986021E+00 +2.023763E+00 +8.192234E-01 +5.037812E+00 +5.086734E+00 +2.745346E-05 +1.509092E-10 +5.002505E+00 +5.014944E+00 +3.530702E-02 +2.695026E-04 +4.047525E+08 +3.276894E+16 +1.098420E+02 +2.414277E+03 +1.059695E-02 +2.782849E-05 +1.048490E+02 +2.199887E+03 +1.048490E+02 +2.199887E+03 tally 14: -1.092010E+02 -2.386227E+03 -4.979068E+00 -4.959690E+00 -2.016700E+00 -8.137138E-01 -5.041749E+00 -5.085712E+00 -5.020635E-05 -5.047241E-10 -5.009785E+00 -5.021429E+00 -3.196471E-02 -2.044235E-04 -4.033399E+08 -3.254855E+16 -1.092010E+02 -2.386227E+03 -1.497710E-02 -4.487918E-05 +1.098420E+02 +2.414277E+03 +5.008447E+00 +5.018748E+00 +2.028674E+00 +8.234818E-01 +5.071684E+00 +5.146761E+00 +5.050718E-05 +5.107669E-10 +5.039530E+00 +5.081707E+00 +3.215450E-02 +2.068774E-04 +4.057347E+08 +3.293927E+16 +1.098420E+02 +2.414277E+03 +1.506603E-02 +4.541792E-05 tally 15: -1.097121E+02 -2.408636E+03 -5.009145E+00 -5.019563E+00 -2.032191E+00 -8.264420E-01 -5.080477E+00 -5.165263E+00 -2.756641E-05 -1.523527E-10 -5.048267E+00 -5.099975E+00 -3.221025E-02 -2.076211E-04 -4.064382E+08 -3.305768E+16 -1.093810E+02 -2.394075E+03 -1.509215E-02 -4.558119E-05 +1.103689E+02 +2.437562E+03 +5.035611E+00 +5.073005E+00 +2.041209E+00 +8.337913E-01 +5.103022E+00 +5.211196E+00 +2.765405E-05 +1.532759E-10 +5.070669E+00 +5.145327E+00 +3.235318E-02 +2.094674E-04 +4.082417E+08 +3.335165E+16 +1.098580E+02 +2.414983E+03 +1.515912E-02 +4.598653E-05 tally 16: -1.042260E+02 -2.173864E+03 -1.042260E+02 -2.173864E+03 -5.000649E+00 -5.012321E+00 +1.048490E+02 +2.199887E+03 +1.048490E+02 +2.199887E+03 +5.037812E+00 +5.086734E+00 tally 17: -6.507000E+00 -8.478423E+00 -1.444000E+00 -4.172780E-01 -1.146407E+00 -2.630079E-01 -2.867025E+00 -1.647980E+00 -2.707153E-05 -1.467503E-10 -2.848880E+00 -1.627146E+00 -1.814469E-02 -6.685238E-05 -2.292814E+08 -1.052031E+16 -6.507000E+00 -8.478423E+00 -9.467712E-03 -2.396878E-05 -5.063000E+00 -5.136983E+00 -5.063000E+00 -5.136983E+00 -1.026940E+02 -2.110535E+03 +6.546000E+00 +8.579894E+00 +1.462000E+00 +4.278220E-01 +1.160697E+00 +2.696537E-01 +2.879845E+00 +1.663297E+00 +2.723379E-05 +1.485067E-10 +2.862628E+00 +1.643479E+00 +1.721726E-02 +6.055382E-05 +2.321395E+08 +1.078615E+16 +6.546000E+00 +8.579894E+00 +6.709978E-03 +1.387848E-05 +5.084000E+00 +5.178800E+00 +5.084000E+00 +5.178800E+00 +1.032960E+02 +2.135240E+03 3.531000E+00 -2.493861E+00 +2.493943E+00 8.630652E-01 -1.489924E-01 -2.133624E+00 -9.150435E-01 -2.183901E-07 -9.544829E-15 -2.115593E+00 -8.991467E-01 -1.803087E-02 -8.677353E-05 +1.489973E-01 +2.157967E+00 +9.345420E-01 +2.196703E-07 +9.656558E-15 +2.139877E+00 +9.185178E-01 +1.808977E-02 +8.744581E-05 1.726130E+08 -5.959695E+15 -1.026940E+02 -2.110535E+03 -3.872081E-03 -5.919692E-06 -9.916300E+01 -1.968002E+03 -9.916300E+01 -1.968002E+03 +5.959891E+15 +1.032960E+02 +2.135240E+03 +3.886968E-03 +5.976458E-06 +9.976500E+01 +1.991860E+03 +9.976500E+01 +1.991860E+03 tally 18: -6.507000E+00 -8.478423E+00 -1.455344E+00 -4.241161E-01 -1.155413E+00 -2.673178E-01 -2.888532E+00 -1.670737E+00 -4.955615E-05 -4.917547E-10 -2.870219E+00 -1.649619E+00 -1.831331E-02 -6.715634E-05 -2.310826E+08 -1.069271E+16 -6.507000E+00 -8.478423E+00 -8.580723E-03 -1.474352E-05 -1.026940E+02 -2.110535E+03 -3.523724E+00 -2.484884E+00 -8.612868E-01 -1.484560E-01 -2.153217E+00 -9.278503E-01 -6.502027E-07 -8.460574E-14 -2.139566E+00 -9.161225E-01 -1.365140E-02 -3.729555E-05 -1.722574E+08 -5.938242E+15 -1.026940E+02 -2.110535E+03 -6.396382E-03 -8.187874E-06 +6.546000E+00 +8.579894E+00 +1.464067E+00 +4.291919E-01 +1.162338E+00 +2.705171E-01 +2.905845E+00 +1.690732E+00 +4.985316E-05 +4.976401E-10 +2.887422E+00 +1.669362E+00 +1.842307E-02 +6.796008E-05 +2.324676E+08 +1.082069E+16 +6.546000E+00 +8.579894E+00 +8.632151E-03 +1.491997E-05 +1.032960E+02 +2.135240E+03 +3.544380E+00 +2.513971E+00 +8.663357E-01 +1.501938E-01 +2.165839E+00 +9.387115E-01 +6.540142E-07 +8.559612E-14 +2.152108E+00 +9.268463E-01 +1.373143E-02 +3.773212E-05 +1.732671E+08 +6.007753E+15 +1.032960E+02 +2.135240E+03 +6.433878E-03 +8.283719E-06 tally 19: -6.573230E+00 -8.663027E+00 -1.470157E+00 -4.333505E-01 -1.167173E+00 -2.731383E-01 -2.917933E+00 -1.707114E+00 -2.734707E-05 -1.499456E-10 -2.899433E+00 -1.685536E+00 -1.849971E-02 -6.861856E-05 -2.334346E+08 -1.092553E+16 -6.518000E+00 -8.507644E+00 -8.668060E-03 -1.506454E-05 -1.031389E+02 -2.128993E+03 -3.538988E+00 -2.506616E+00 -8.650177E-01 -1.497544E-01 -2.162544E+00 -9.359650E-01 -2.193361E-07 -9.628305E-15 -2.148834E+00 -9.241346E-01 -1.371054E-02 -3.762172E-05 -1.730035E+08 -5.990176E+15 -1.028630E+02 -2.117467E+03 -6.424090E-03 -8.259483E-06 +6.593971E+00 +8.715030E+00 +1.474796E+00 +4.359519E-01 +1.170856E+00 +2.747779E-01 +2.927140E+00 +1.717362E+00 +2.743336E-05 +1.508457E-10 +2.908582E+00 +1.695655E+00 +1.855808E-02 +6.903047E-05 +2.341712E+08 +1.099112E+16 +6.547000E+00 +8.582573E+00 +8.695410E-03 +1.515497E-05 +1.037750E+02 +2.155274E+03 +3.560815E+00 +2.537559E+00 +8.703528E-01 +1.516031E-01 +2.175882E+00 +9.475192E-01 +2.206889E-07 +9.747163E-15 +2.162087E+00 +9.355427E-01 +1.379510E-02 +3.808615E-05 +1.740706E+08 +6.064123E+15 +1.033110E+02 +2.135863E+03 +6.463712E-03 +8.361443E-06 tally 20: -5.063000E+00 -5.136983E+00 -5.063000E+00 -5.136983E+00 +5.084000E+00 +5.178800E+00 +5.084000E+00 +5.178800E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.867025E+00 -1.647980E+00 -1.445000E+00 -4.178610E-01 -1.445000E+00 -4.178610E-01 +2.879845E+00 +1.663297E+00 +1.463000E+00 +4.284330E-01 +1.463000E+00 +4.284330E-01 0.000000E+00 0.000000E+00 -9.771800E+01 -1.911083E+03 -9.771800E+01 -1.911083E+03 -2.133624E+00 -9.150435E-01 +9.830200E+01 +1.933884E+03 +9.830200E+01 +1.933884E+03 +2.157967E+00 +9.345420E-01 diff --git a/tests/regression_tests/mg_temperature/build_2g.py b/tests/regression_tests/mg_temperature/build_2g.py index 1256ca0f7f..1fb7234499 100644 --- a/tests/regression_tests/mg_temperature/build_2g.py +++ b/tests/regression_tests/mg_temperature/build_2g.py @@ -1,297 +1,297 @@ -import openmc -import numpy as np - -names = ['H', 'O', 'Zr', 'U235', 'U238'] - - -def build_openmc_xs_lib(name, groups, temperatures, xsdict, micro=True): - """Build an Openm XSdata based on dictionary values""" - xsdata = openmc.XSdata(name, groups, temperatures=temperatures) - xsdata.order = 0 - for tt in temperatures: - xsdata.set_absorption(xsdict[tt]['absorption'][name], temperature=tt) - xsdata.set_scatter_matrix(xsdict[tt]['scatter'][name], temperature=tt) - xsdata.set_total(xsdict[tt]['total'][name], temperature=tt) - if (name in xsdict[tt]['nu-fission'].keys()): - xsdata.set_nu_fission(xsdict[tt]['nu-fission'][name], - temperature=tt) - xsdata.set_chi(np.array([1., 0.]), temperature=tt) - return xsdata - - -def create_micro_xs_dict(): - """Returns micro xs library""" - xs_micro = {} - reactions = ['absorption', 'total', 'scatter', 'nu-fission'] - # chi is unnecessary when energy bound is in thermal region - # Temperature 300K - # absorption - xs_micro[300] = {r: {} for r in reactions} - xs_micro[300]['absorption']['H'] = np.array([1.0285E-4, 0.0057]) - xs_micro[300]['absorption']['O'] = np.array([7.1654E-5, 3.0283E-6]) - xs_micro[300]['absorption']['Zr'] = np.array([4.5918E-5, 3.6303E-5]) - xs_micro[300]['absorption']['U235'] = np.array([0.0035, 0.1040]) - xs_micro[300]['absorption']['U238'] = np.array([0.0056, 0.0094]) - # nu-scatter matrix - xs_micro[300]['scatter']['H'] = np.array([[[0.0910, 0.01469], - [0.0, 0.3316]]]) - xs_micro[300]['scatter']['O'] = np.array([[[0.0814, 3.3235E-4], - [0.0, 0.0960]]]) - xs_micro[300]['scatter']['Zr'] = np.array([[[0.0311, 2.6373E-5], - [0.0, 0.0315]]]) - xs_micro[300]['scatter']['U235'] = np.array([[[0.0311, 2.6373E-5], - [0.0, 0.0315]]]) - xs_micro[300]['scatter']['U238'] = np.array([[[0.0551, 2.2341E-5], - [0.0, 0.0526]]]) - # nu-fission - xs_micro[300]['nu-fission']['U235'] = np.array([0.0059, 0.2160]) - xs_micro[300]['nu-fission']['U238'] = np.array([0.0019, 1.4627E-7]) - # total - xs_micro[300]['total']['H'] = xs_micro[300]['absorption']['H'] + \ - np.sum(xs_micro[300]['scatter']['H'][0], 1) - xs_micro[300]['total']['O'] = xs_micro[300]['absorption']['O'] + \ - np.sum(xs_micro[300]['scatter']['O'][0], 1) - - xs_micro[300]['total']['Zr'] = xs_micro[300]['absorption']['Zr'] + \ - np.sum(xs_micro[300]['scatter']['Zr'][0], 1) - - xs_micro[300]['total']['U235'] = xs_micro[300]['absorption']['U235'] + \ - np.sum(xs_micro[300]['scatter']['U235'][0], 1) - - xs_micro[300]['total']['U238'] = xs_micro[300]['absorption']['U238'] + \ - np.sum(xs_micro[300]['scatter']['U238'][0], 1) - - # Temperature 600K - xs_micro[600] = {r: {} for r in reactions} - # absorption - xs_micro[600]['absorption']['H'] = np.array([1.0356E-4, 0.0046]) - xs_micro[600]['absorption']['O'] = np.array([7.2678E-5, 2.4963E-6]) - xs_micro[600]['absorption']['Zr'] = np.array([4.7256E-5, 2.9757E-5]) - xs_micro[600]['absorption']['U235'] = np.array([0.0035, 0.0853]) - xs_micro[600]['absorption']['U238'] = np.array([0.0058, 0.0079]) - # nu-scatter matrix - xs_micro[600]['scatter']['H'] = np.array([[[0.0910, 0.0138], - [0.0, 0.3316]]]) - xs_micro[600]['scatter']['O'] = np.array([[[0.0814, 3.5367E-4], - [0.0, 0.0959]]]) - xs_micro[600]['scatter']['Zr'] = np.array([[[0.0311, 3.2293E-5], - [0.0, 0.0314]]]) - xs_micro[600]['scatter']['U235'] = np.array([[[0.0022, 1.9763E-6], - [9.1634E-8, 0.0039]]]) - xs_micro[600]['scatter']['U238'] = np.array([[[0.0556, 2.8803E-5], - [0.0, 0.0536]]]) - # nu-fission - xs_micro[600]['nu-fission']['U235'] = np.array([0.0059, 0.1767]) - xs_micro[600]['nu-fission']['U238'] = np.array([0.0019, 1.2405E-7]) - # total - xs_micro[600]['total']['H'] = xs_micro[600]['absorption']['H'] + \ - np.sum(xs_micro[600]['scatter']['H'][0], 1) - xs_micro[600]['total']['O'] = xs_micro[600]['absorption']['O'] + \ - np.sum(xs_micro[600]['scatter']['O'][0], 1) - - xs_micro[600]['total']['Zr'] = xs_micro[600]['absorption']['Zr'] + \ - np.sum(xs_micro[600]['scatter']['Zr'][0], 1) - - xs_micro[600]['total']['U235'] = xs_micro[600]['absorption']['U235'] + \ - np.sum(xs_micro[600]['scatter']['U235'][0], 1) - - xs_micro[600]['total']['U238'] = xs_micro[600]['absorption']['U238'] + \ - np.sum(xs_micro[600]['scatter']['U238'][0], 1) - - # Temperature 900K - xs_micro[900] = {r: {} for r in reactions} - # absorption - xs_micro[900]['absorption']['H'] = np.array([1.0529E-4, 0.0040]) - xs_micro[900]['absorption']['O'] = np.array([7.3055E-5, 2.1850E-6]) - xs_micro[900]['absorption']['Zr'] = np.array([4.7141E-5, 2.5941E-5]) - xs_micro[900]['absorption']['U235'] = np.array([0.0035, 0.0749]) - xs_micro[900]['absorption']['U238'] = np.array([0.0060, 0.0071]) - # total - xs_micro[900]['total']['H'] = np.array([0.2982, 0.7332]) - xs_micro[900]['total']['O'] = np.array([0.0885, 0.1004]) - xs_micro[900]['total']['Zr'] = np.array([0.0370, 0.0317]) - xs_micro[900]['total']['U235'] = np.array([0.0061, 0.0789]) - xs_micro[900]['total']['U238'] = np.array([0.0707, 0.0613]) - # nu-scatter matrix - xs_micro[900]['scatter']['H'] = np.array([[[0.0913, 0.0147], - [0.0, 0.4020]]]) - xs_micro[900]['scatter']['O'] = np.array([[[0.0812, 4.0413E-4], - [0.0, 0.0965]]]) - xs_micro[900]['scatter']['Zr'] = np.array([[[0.0311, 3.6735E-5], - [0.0, 0.0314]]]) - xs_micro[900]['scatter']['U235'] = np.array([[[0.0022, 2.9034E-6], - [1.3117E-8, 0.0039]]]) - xs_micro[900]['scatter']['U238'] = np.array([[[0.0560, 3.7619E-5], - [0.0, 0.0538]]]) - # nu-fission - xs_micro[900]['nu-fission']['U235'] = np.array([0.0059, 0.1545]) - xs_micro[900]['nu-fission']['U238'] = np.array([0.0019, 1.1017E-7]) - # total - xs_micro[900]['total']['H'] = xs_micro[900]['absorption']['H'] + \ - np.sum(xs_micro[900]['scatter']['H'][0], 1) - xs_micro[900]['total']['O'] = xs_micro[900]['absorption']['O'] + \ - np.sum(xs_micro[900]['scatter']['O'][0], 1) - - xs_micro[900]['total']['Zr'] = xs_micro[900]['absorption']['Zr'] + \ - np.sum(xs_micro[900]['scatter']['Zr'][0], 1) - - xs_micro[900]['total']['U235'] = xs_micro[900]['absorption']['U235'] + \ - np.sum(xs_micro[900]['scatter']['U235'][0], 1) - - xs_micro[900]['total']['U238'] = xs_micro[900]['absorption']['U238'] + \ - np.sum(xs_micro[900]['scatter']['U238'][0], 1) - - # roll axis for scatter matrix - for t in xs_micro: - for n in xs_micro[t]['scatter']: - xs_micro[t]['scatter'][n] = np.rollaxis(xs_micro[t]['scatter'][n], - 0, 3) - return xs_micro - - -def create_macro_dict(xs_micro): - """Create a dictionary with two group cross-section""" - xs_macro = {} - for t, d1 in xs_micro.items(): - xs_macro[t] = {} - for r, d2 in d1.items(): - temp = [] - xs_macro[t][r] = {} - for n, v in d2.items(): - temp.append(d2[n]) - # The name 'macro' is needed to store data at the same level - # of a xs_macro dictionary as for xs_micro and use it in - # function build_openmc_xs_lib - xs_macro[t][r]['macro'] = sum(temp) - return xs_macro - - -def create_openmc_2mg_libs(names): - """Built a micro/macro two group openmc MGXS libraries""" - # Initialized library params - group_edges = [0.0, 0.625, 20.0e6] - groups = openmc.mgxs.EnergyGroups(group_edges=group_edges) - mg_cross_sections_file_micro = openmc.MGXSLibrary(groups) - mg_cross_sections_file_macro = openmc.MGXSLibrary(groups) - # Building a micro mg library - micro_cs = create_micro_xs_dict() - for name in names: - mg_cross_sections_file_micro.add_xsdata(build_openmc_xs_lib(name, - groups, - [t for t in - micro_cs], - micro_cs)) - # Building a macro mg library - macro_xs = create_macro_dict(micro_cs) - mg_cross_sections_file_macro.add_xsdata(build_openmc_xs_lib('macro', - groups, - [t for t in - macro_xs], - macro_xs)) - # Exporting library to hdf5 files - mg_cross_sections_file_micro.export_to_hdf5('micro_2g.h5') - mg_cross_sections_file_macro.export_to_hdf5('macro_2g.h5') - # Returning the macro_xs dict is needed for analytical solution - return macro_xs - - -def analytical_solution_2g_therm(xsmin, xsmax=None, wgt=1.0): - """ Calculate eigenvalue based on analytical solution for eq Lf = (1/k)Qf - in two group for infinity dilution media in assumption of group - boundary in thermal spectra < 1.e+3 Ev - Parameters: - ---------- - xsmin : dict - macro cross-sections dictionary with minimum range temperature - xsmax : dict - macro cross-sections dictionary with maximum range temperature - by default: None not used for standalone temperature - wgt : float - weight for interpolation by default 1.0 - Returns: - ------- - keff : np.float64 - analytical eigenvalue of critical eq matrix - """ - if xsmax is None: - sa = xsmin['absorption']['macro'] - ss12 = xsmin['scatter']['macro'][0][1][0] - nsf = xsmin['nu-fission']['macro'] - else: - sa = xsmin['absorption']['macro'] * wgt + \ - xsmax['absorption']['macro'] * (1 - wgt) - ss12 = xsmin['scatter']['macro'][0][1][0] * wgt + \ - xsmax['scatter']['macro'][0][1][0] * (1 - wgt) - nsf = xsmin['nu-fission']['macro'] * wgt + \ - xsmax['nu-fission']['macro'] * (1 - wgt) - L = np.array([sa[0] + ss12, 0.0, -ss12, sa[1]]).reshape(2, 2) - Q = np.array([nsf[0], nsf[1], 0.0, 0.0]).reshape(2, 2) - arr = np.linalg.inv(L).dot(Q) - return np.amax(np.linalg.eigvals(arr)) - - -def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): - """ Building an infinite medium for openmc multi-group testing - Parameters: - ---------- - xsnames : list of str() - list with xs names - xslibname: - name of hdf5 file with cross-section library - temperature : float - value of a current temperature in K - tempmethod : {'nearest', 'interpolation'} - by default 'nearest' - """ - inf_medium = openmc.Material(name='test material', material_id=1) - inf_medium.set_density("sum") - for xs in xsnames: - inf_medium.add_nuclide(xs, 1) - INF = 11.1 - # Instantiate a Materials collection and export to XML - materials_file = openmc.Materials([inf_medium]) - materials_file.cross_sections = xslibname - materials_file.export_to_xml() - - # Instantiate boundary Planes - min_x = openmc.XPlane(boundary_type='reflective', x0=-INF) - max_x = openmc.XPlane(boundary_type='reflective', x0=INF) - min_y = openmc.YPlane(boundary_type='reflective', y0=-INF) - max_y = openmc.YPlane(boundary_type='reflective', y0=INF) - - # Instantiate a Cell - cell = openmc.Cell(cell_id=1, name='cell') - cell.temperature = temperature - # Register bounding Surfaces with the Cell - cell.region = +min_x & -max_x & +min_y & -max_y - - # Fill the Cell with the Material - cell.fill = inf_medium - - # Create root universe - root_universe = openmc.Universe(name='root universe', cells=[cell]) - - # Create Geometry and set root Universe - openmc_geometry = openmc.Geometry(root_universe) - - # Export to "geometry.xml" - openmc_geometry.export_to_xml() - - # OpenMC simulation parameters - batches = 200 - inactive = 5 - particles = 5000 - - # Instantiate a Settings object - settings_file = openmc.Settings() - settings_file.batches = batches - settings_file.inactive = inactive - settings_file.particles = particles - settings_file.energy_mode = 'multi-group' - settings_file.output = {'summary': False} - # Create an initial uniform spatial source distribution over fissionable zones - bounds = [-INF, -INF, -INF, INF, INF, INF] - uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) - settings_file.temperature = {'method': tempmethod} - settings_file.source = openmc.Source(space=uniform_dist) - settings_file.export_to_xml() +import openmc +import numpy as np + +names = ['H', 'O', 'Zr', 'U235', 'U238'] + + +def build_openmc_xs_lib(name, groups, temperatures, xsdict, micro=True): + """Build an Openm XSdata based on dictionary values""" + xsdata = openmc.XSdata(name, groups, temperatures=temperatures) + xsdata.order = 0 + for tt in temperatures: + xsdata.set_absorption(xsdict[tt]['absorption'][name], temperature=tt) + xsdata.set_scatter_matrix(xsdict[tt]['scatter'][name], temperature=tt) + xsdata.set_total(xsdict[tt]['total'][name], temperature=tt) + if (name in xsdict[tt]['nu-fission'].keys()): + xsdata.set_nu_fission(xsdict[tt]['nu-fission'][name], + temperature=tt) + xsdata.set_chi(np.array([1., 0.]), temperature=tt) + return xsdata + + +def create_micro_xs_dict(): + """Returns micro xs library""" + xs_micro = {} + reactions = ['absorption', 'total', 'scatter', 'nu-fission'] + # chi is unnecessary when energy bound is in thermal region + # Temperature 300K + # absorption + xs_micro[300] = {r: {} for r in reactions} + xs_micro[300]['absorption']['H'] = np.array([1.0285E-4, 0.0057]) + xs_micro[300]['absorption']['O'] = np.array([7.1654E-5, 3.0283E-6]) + xs_micro[300]['absorption']['Zr'] = np.array([4.5918E-5, 3.6303E-5]) + xs_micro[300]['absorption']['U235'] = np.array([0.0035, 0.1040]) + xs_micro[300]['absorption']['U238'] = np.array([0.0056, 0.0094]) + # nu-scatter matrix + xs_micro[300]['scatter']['H'] = np.array([[[0.0910, 0.01469], + [0.0, 0.3316]]]) + xs_micro[300]['scatter']['O'] = np.array([[[0.0814, 3.3235E-4], + [0.0, 0.0960]]]) + xs_micro[300]['scatter']['Zr'] = np.array([[[0.0311, 2.6373E-5], + [0.0, 0.0315]]]) + xs_micro[300]['scatter']['U235'] = np.array([[[0.0311, 2.6373E-5], + [0.0, 0.0315]]]) + xs_micro[300]['scatter']['U238'] = np.array([[[0.0551, 2.2341E-5], + [0.0, 0.0526]]]) + # nu-fission + xs_micro[300]['nu-fission']['U235'] = np.array([0.0059, 0.2160]) + xs_micro[300]['nu-fission']['U238'] = np.array([0.0019, 1.4627E-7]) + # total + xs_micro[300]['total']['H'] = xs_micro[300]['absorption']['H'] + \ + np.sum(xs_micro[300]['scatter']['H'][0], 1) + xs_micro[300]['total']['O'] = xs_micro[300]['absorption']['O'] + \ + np.sum(xs_micro[300]['scatter']['O'][0], 1) + + xs_micro[300]['total']['Zr'] = xs_micro[300]['absorption']['Zr'] + \ + np.sum(xs_micro[300]['scatter']['Zr'][0], 1) + + xs_micro[300]['total']['U235'] = xs_micro[300]['absorption']['U235'] + \ + np.sum(xs_micro[300]['scatter']['U235'][0], 1) + + xs_micro[300]['total']['U238'] = xs_micro[300]['absorption']['U238'] + \ + np.sum(xs_micro[300]['scatter']['U238'][0], 1) + + # Temperature 600K + xs_micro[600] = {r: {} for r in reactions} + # absorption + xs_micro[600]['absorption']['H'] = np.array([1.0356E-4, 0.0046]) + xs_micro[600]['absorption']['O'] = np.array([7.2678E-5, 2.4963E-6]) + xs_micro[600]['absorption']['Zr'] = np.array([4.7256E-5, 2.9757E-5]) + xs_micro[600]['absorption']['U235'] = np.array([0.0035, 0.0853]) + xs_micro[600]['absorption']['U238'] = np.array([0.0058, 0.0079]) + # nu-scatter matrix + xs_micro[600]['scatter']['H'] = np.array([[[0.0910, 0.0138], + [0.0, 0.3316]]]) + xs_micro[600]['scatter']['O'] = np.array([[[0.0814, 3.5367E-4], + [0.0, 0.0959]]]) + xs_micro[600]['scatter']['Zr'] = np.array([[[0.0311, 3.2293E-5], + [0.0, 0.0314]]]) + xs_micro[600]['scatter']['U235'] = np.array([[[0.0022, 1.9763E-6], + [9.1634E-8, 0.0039]]]) + xs_micro[600]['scatter']['U238'] = np.array([[[0.0556, 2.8803E-5], + [0.0, 0.0536]]]) + # nu-fission + xs_micro[600]['nu-fission']['U235'] = np.array([0.0059, 0.1767]) + xs_micro[600]['nu-fission']['U238'] = np.array([0.0019, 1.2405E-7]) + # total + xs_micro[600]['total']['H'] = xs_micro[600]['absorption']['H'] + \ + np.sum(xs_micro[600]['scatter']['H'][0], 1) + xs_micro[600]['total']['O'] = xs_micro[600]['absorption']['O'] + \ + np.sum(xs_micro[600]['scatter']['O'][0], 1) + + xs_micro[600]['total']['Zr'] = xs_micro[600]['absorption']['Zr'] + \ + np.sum(xs_micro[600]['scatter']['Zr'][0], 1) + + xs_micro[600]['total']['U235'] = xs_micro[600]['absorption']['U235'] + \ + np.sum(xs_micro[600]['scatter']['U235'][0], 1) + + xs_micro[600]['total']['U238'] = xs_micro[600]['absorption']['U238'] + \ + np.sum(xs_micro[600]['scatter']['U238'][0], 1) + + # Temperature 900K + xs_micro[900] = {r: {} for r in reactions} + # absorption + xs_micro[900]['absorption']['H'] = np.array([1.0529E-4, 0.0040]) + xs_micro[900]['absorption']['O'] = np.array([7.3055E-5, 2.1850E-6]) + xs_micro[900]['absorption']['Zr'] = np.array([4.7141E-5, 2.5941E-5]) + xs_micro[900]['absorption']['U235'] = np.array([0.0035, 0.0749]) + xs_micro[900]['absorption']['U238'] = np.array([0.0060, 0.0071]) + # total + xs_micro[900]['total']['H'] = np.array([0.2982, 0.7332]) + xs_micro[900]['total']['O'] = np.array([0.0885, 0.1004]) + xs_micro[900]['total']['Zr'] = np.array([0.0370, 0.0317]) + xs_micro[900]['total']['U235'] = np.array([0.0061, 0.0789]) + xs_micro[900]['total']['U238'] = np.array([0.0707, 0.0613]) + # nu-scatter matrix + xs_micro[900]['scatter']['H'] = np.array([[[0.0913, 0.0147], + [0.0, 0.4020]]]) + xs_micro[900]['scatter']['O'] = np.array([[[0.0812, 4.0413E-4], + [0.0, 0.0965]]]) + xs_micro[900]['scatter']['Zr'] = np.array([[[0.0311, 3.6735E-5], + [0.0, 0.0314]]]) + xs_micro[900]['scatter']['U235'] = np.array([[[0.0022, 2.9034E-6], + [1.3117E-8, 0.0039]]]) + xs_micro[900]['scatter']['U238'] = np.array([[[0.0560, 3.7619E-5], + [0.0, 0.0538]]]) + # nu-fission + xs_micro[900]['nu-fission']['U235'] = np.array([0.0059, 0.1545]) + xs_micro[900]['nu-fission']['U238'] = np.array([0.0019, 1.1017E-7]) + # total + xs_micro[900]['total']['H'] = xs_micro[900]['absorption']['H'] + \ + np.sum(xs_micro[900]['scatter']['H'][0], 1) + xs_micro[900]['total']['O'] = xs_micro[900]['absorption']['O'] + \ + np.sum(xs_micro[900]['scatter']['O'][0], 1) + + xs_micro[900]['total']['Zr'] = xs_micro[900]['absorption']['Zr'] + \ + np.sum(xs_micro[900]['scatter']['Zr'][0], 1) + + xs_micro[900]['total']['U235'] = xs_micro[900]['absorption']['U235'] + \ + np.sum(xs_micro[900]['scatter']['U235'][0], 1) + + xs_micro[900]['total']['U238'] = xs_micro[900]['absorption']['U238'] + \ + np.sum(xs_micro[900]['scatter']['U238'][0], 1) + + # roll axis for scatter matrix + for t in xs_micro: + for n in xs_micro[t]['scatter']: + xs_micro[t]['scatter'][n] = np.rollaxis(xs_micro[t]['scatter'][n], + 0, 3) + return xs_micro + + +def create_macro_dict(xs_micro): + """Create a dictionary with two group cross-section""" + xs_macro = {} + for t, d1 in xs_micro.items(): + xs_macro[t] = {} + for r, d2 in d1.items(): + temp = [] + xs_macro[t][r] = {} + for n, v in d2.items(): + temp.append(d2[n]) + # The name 'macro' is needed to store data at the same level + # of a xs_macro dictionary as for xs_micro and use it in + # function build_openmc_xs_lib + xs_macro[t][r]['macro'] = sum(temp) + return xs_macro + + +def create_openmc_2mg_libs(names): + """Built a micro/macro two group openmc MGXS libraries""" + # Initialized library params + group_edges = [0.0, 0.625, 20.0e6] + groups = openmc.mgxs.EnergyGroups(group_edges=group_edges) + mg_cross_sections_file_micro = openmc.MGXSLibrary(groups) + mg_cross_sections_file_macro = openmc.MGXSLibrary(groups) + # Building a micro mg library + micro_cs = create_micro_xs_dict() + for name in names: + mg_cross_sections_file_micro.add_xsdata(build_openmc_xs_lib(name, + groups, + [t for t in + micro_cs], + micro_cs)) + # Building a macro mg library + macro_xs = create_macro_dict(micro_cs) + mg_cross_sections_file_macro.add_xsdata(build_openmc_xs_lib('macro', + groups, + [t for t in + macro_xs], + macro_xs)) + # Exporting library to hdf5 files + mg_cross_sections_file_micro.export_to_hdf5('micro_2g.h5') + mg_cross_sections_file_macro.export_to_hdf5('macro_2g.h5') + # Returning the macro_xs dict is needed for analytical solution + return macro_xs + + +def analytical_solution_2g_therm(xsmin, xsmax=None, wgt=1.0): + """ Calculate eigenvalue based on analytical solution for eq Lf = (1/k)Qf + in two group for infinity dilution media in assumption of group + boundary in thermal spectra < 1.e+3 Ev + Parameters: + ---------- + xsmin : dict + macro cross-sections dictionary with minimum range temperature + xsmax : dict + macro cross-sections dictionary with maximum range temperature + by default: None not used for standalone temperature + wgt : float + weight for interpolation by default 1.0 + Returns: + ------- + keff : np.float64 + analytical eigenvalue of critical eq matrix + """ + if xsmax is None: + sa = xsmin['absorption']['macro'] + ss12 = xsmin['scatter']['macro'][0][1][0] + nsf = xsmin['nu-fission']['macro'] + else: + sa = xsmin['absorption']['macro'] * wgt + \ + xsmax['absorption']['macro'] * (1 - wgt) + ss12 = xsmin['scatter']['macro'][0][1][0] * wgt + \ + xsmax['scatter']['macro'][0][1][0] * (1 - wgt) + nsf = xsmin['nu-fission']['macro'] * wgt + \ + xsmax['nu-fission']['macro'] * (1 - wgt) + L = np.array([sa[0] + ss12, 0.0, -ss12, sa[1]]).reshape(2, 2) + Q = np.array([nsf[0], nsf[1], 0.0, 0.0]).reshape(2, 2) + arr = np.linalg.inv(L).dot(Q) + return np.amax(np.linalg.eigvals(arr)) + + +def build_inf_model(xsnames, xslibname, temperature, tempmethod='nearest'): + """ Building an infinite medium for openmc multi-group testing + Parameters: + ---------- + xsnames : list of str() + list with xs names + xslibname: + name of hdf5 file with cross-section library + temperature : float + value of a current temperature in K + tempmethod : {'nearest', 'interpolation'} + by default 'nearest' + """ + model = openmc.Model() + inf_medium = openmc.Material(name='test material', material_id=1) + inf_medium.set_density("sum") + for xs in xsnames: + inf_medium.add_nuclide(xs, 1) + INF = 11.1 + # Instantiate a Materials collection and export to XML + materials_file = openmc.Materials([inf_medium]) + materials_file.cross_sections = xslibname + model.materials = materials_file + + # Instantiate boundary Planes + min_x = openmc.XPlane(boundary_type='reflective', x0=-INF) + max_x = openmc.XPlane(boundary_type='reflective', x0=INF) + min_y = openmc.YPlane(boundary_type='reflective', y0=-INF) + max_y = openmc.YPlane(boundary_type='reflective', y0=INF) + + # Instantiate a Cell + cell = openmc.Cell(cell_id=1, name='cell') + cell.temperature = temperature + # Register bounding Surfaces with the Cell + cell.region = +min_x & -max_x & +min_y & -max_y + + # Fill the Cell with the Material + cell.fill = inf_medium + + # Create root universe + root_universe = openmc.Universe(name='root universe', cells=[cell]) + + # Create Geometry and set root Universe + model.geometry = openmc.Geometry(root_universe) + + # OpenMC simulation parameters + batches = 200 + inactive = 5 + particles = 5000 + + # Instantiate a Settings object + settings_file = openmc.Settings() + settings_file.batches = batches + settings_file.inactive = inactive + settings_file.particles = particles + settings_file.energy_mode = 'multi-group' + settings_file.output = {'summary': False} + # Create an initial uniform spatial source distribution over fissionable zones + bounds = [-INF, -INF, -INF, INF, INF, INF] + uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) + settings_file.temperature = {'method': tempmethod} + settings_file.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) + model.settings = settings_file + model.export_to_model_xml() diff --git a/tests/regression_tests/mg_temperature/results_true.dat b/tests/regression_tests/mg_temperature/results_true.dat index 19364e1d96..6de4c6bb13 100644 --- a/tests/regression_tests/mg_temperature/results_true.dat +++ b/tests/regression_tests/mg_temperature/results_true.dat @@ -1,40 +1,40 @@ micro, method: nearest, t: 300.0, k-combined: -1.439563E+00 4.526076E-04 +1.439913E+00 4.285638E-04 kanalyt 1.440410E+00 micro, method: nearest, t: 600.0, k-combined: -1.409389E+00 4.684481E-04 +1.410750E+00 4.829834E-04 kanalyt 1.410164E+00 micro, method: nearest, t: 900.0, k-combined: -1.407593E+00 4.410387E-04 +1.408232E+00 4.946310E-04 kanalyt 1.407830E+00 micro, method: interpolation, t: 520.0, k-combined: -1.418259E+00 4.242856E-04 +1.418877E+00 4.651822E-04 kanalyt 1.418514E+00 micro, method: interpolation, t: 600.0, k-combined: -1.409389E+00 4.684481E-04 +1.410750E+00 4.829834E-04 kanalyt 1.410164E+00 macro, method: nearest, t: 300.0, k-combined: -1.439563E+00 4.526076E-04 +1.439913E+00 4.285638E-04 kanalyt 1.440410E+00 macro, method: nearest, t: 600.0, k-combined: -1.409389E+00 4.684481E-04 +1.410750E+00 4.829834E-04 kanalyt 1.410164E+00 macro, method: nearest, t: 900.0, k-combined: -1.407593E+00 4.410387E-04 +1.408232E+00 4.946310E-04 kanalyt 1.407830E+00 macro, method: interpolation, t: 520.0, k-combined: -1.418259E+00 4.242856E-04 +1.418877E+00 4.651822E-04 kanalyt 1.418514E+00 macro, method: interpolation, t: 600, k-combined: -1.409389E+00 4.684481E-04 +1.410750E+00 4.829834E-04 kanalyt 1.410164E+00 diff --git a/tests/regression_tests/mg_temperature_multi/__init__.py b/tests/regression_tests/mg_temperature_multi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/mg_temperature_multi/inputs_true.dat b/tests/regression_tests/mg_temperature_multi/inputs_true.dat new file mode 100644 index 0000000000..b84a6782b9 --- /dev/null +++ b/tests/regression_tests/mg_temperature_multi/inputs_true.dat @@ -0,0 +1,56 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + 1 + + + 2 + + + 1 + flux + + + 2 + flux + + + diff --git a/tests/regression_tests/mg_temperature_multi/results_true.dat b/tests/regression_tests/mg_temperature_multi/results_true.dat new file mode 100644 index 0000000000..3e2f990c8e --- /dev/null +++ b/tests/regression_tests/mg_temperature_multi/results_true.dat @@ -0,0 +1,8 @@ +k-combined: +1.309371E+00 6.765039E-03 +tally 1: +2.532303E+01 +1.282689E+02 +tally 2: +9.336894E+01 +1.743765E+03 diff --git a/tests/regression_tests/mg_temperature_multi/test.py b/tests/regression_tests/mg_temperature_multi/test.py new file mode 100755 index 0000000000..3117e29ba0 --- /dev/null +++ b/tests/regression_tests/mg_temperature_multi/test.py @@ -0,0 +1,167 @@ +import os + +import numpy as np +import openmc +import openmc.mgxs + +from tests.testing_harness import PyAPITestHarness + + +def create_library(): + # Instantiate the energy group data + egroups = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + groups = openmc.mgxs.EnergyGroups(egroups) + + # Instantiate the 7-group (C5G7) cross section data + uo2_xsdata = openmc.XSdata('UO2', groups, temperatures=[294.0, 600.0]) + uo2_xsdata.order = 0 + scatter_matrix = np.array([[ + [0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800] + ]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + + # Original C5G7 data + uo2_xsdata.set_total([0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], temperature=294.0) + uo2_xsdata.set_absorption([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02, 3.0020E-02, 1.1126E-01, 2.8278E-01], temperature=294.0) + uo2_xsdata.set_scatter_matrix(scatter_matrix, temperature=294.0) + uo2_xsdata.set_fission([7.21206E-03, 8.19301E-04, 6.45320E-03, 1.85648E-02, 1.78084E-02, 8.30348E-02, 2.16004E-01], temperature=294.0) + uo2_xsdata.set_nu_fission([2.005998E-02, 2.027303E-03, 1.570599E-02, 4.518301E-02, 4.334208E-02, 2.020901E-01, 5.257105E-01], temperature=294.0) + uo2_xsdata.set_chi([5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07, 0.0000E+00, 0.0000E+00, 0.0000E+00], temperature=294.0) + + # Altered C5G7 data (permuted Chi) + uo2_xsdata.set_total([0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678, 0.5644058], temperature=600.0) + uo2_xsdata.set_absorption([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02, 3.0020E-02, 1.1126E-01, 2.8278E-01], temperature=600.0) + uo2_xsdata.set_scatter_matrix(scatter_matrix, temperature=600.0) + uo2_xsdata.set_fission([7.21206E-03, 8.19301E-04, 6.45320E-03, 1.85648E-02, 1.78084E-02, 8.30348E-02, 2.16004E-01], temperature=600.0) + uo2_xsdata.set_nu_fission([2.005998E-02, 2.027303E-03, 1.570599E-02, 4.518301E-02, 4.334208E-02, 2.020901E-01, 5.257105E-01], temperature=600.0) + uo2_xsdata.set_chi([4.1176E-01, 5.8791E-01, 3.3906E-04, 1.1761E-07, 0.0000E+00, 0.0000E+00, 0.0000E+00], temperature=600.0) + + h2o_xsdata = openmc.XSdata('LWTR', groups) + h2o_xsdata.order = 0 + h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435, + 0.718, 1.2544497, 2.650379]) + h2o_xsdata.set_absorption([6.0105E-04, 1.5793E-05, 3.3716E-04, + 1.9406E-03, 5.7416E-03, 1.5001E-02, + 3.7239E-02]) + scatter_matrix = np.array([[ + [0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000], + [0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010], + [0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034], + [0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390], + [0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200], + [0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000] + ]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + h2o_xsdata.set_scatter_matrix(scatter_matrix) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.add_xsdatas([uo2_xsdata, h2o_xsdata]) + mg_cross_sections_file.export_to_hdf5() + + +class MGXSTestHarness(PyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_mg_temperature_multi(): + ############################################################################### + # Create multigroup data + create_library() + + ############################################################################### + # Create materials for the problem + + # Instantiate some Macroscopic Data + uo2_data = openmc.Macroscopic('UO2') + h2o_data = openmc.Macroscopic('LWTR') + + # Instantiate some Materials and register the appropriate Macroscopic objects + uo2 = openmc.Material(name='UO2 fuel') + uo2.set_density('macro', 1.0) + uo2.add_macroscopic(uo2_data) + + water = openmc.Material(name='Water') + water.set_density('macro', 1.0) + water.add_macroscopic(h2o_data) + + # Instantiate a Materials collection and export to XML + materials = openmc.Materials([uo2, water]) + materials.cross_sections = "mgxs.h5" + + ############################################################################### + # Define problem geometry + + # Create a surface for the fuel outer radius + fuel_ir = openmc.ZCylinder(r=0.25, name='Fuel IR') + fuel_or = openmc.ZCylinder(r=0.54, name='Fuel OR') + + # Create a region represented as the inside of a rectangular prism + pitch = 1.26 + box = openmc.model.RectangularPrism(pitch, pitch, boundary_type='reflective') + + # Instantiate Cells + fuel_inner = openmc.Cell(fill=uo2, region=-fuel_ir, name='fuel inner') + fuel_inner.temperature = 600.0 + fuel_outer = openmc.Cell(fill=uo2, region=+fuel_ir & -fuel_or, name='fuel outer') + fuel_outer.temperature = 294.0 + moderator = openmc.Cell(fill=water, region=+fuel_or & -box, name='moderator') + + # Create a geometry with the two cells and export to XML + geometry = openmc.Geometry([fuel_inner, fuel_outer, moderator]) + + ############################################################################### + # Define problem settings + + # Instantiate a Settings object, set all runtime parameters, and export to XML + settings = openmc.Settings() + settings.energy_mode = "multi-group" + settings.batches = 10 + settings.inactive = 5 + settings.particles = 1000 + + # Create an initial uniform spatial source distribution over fissionable zones + lower_left = (-pitch/2, -pitch/2, -1) + upper_right = (pitch/2, pitch/2, 1) + uniform_dist = openmc.stats.Box(lower_left, upper_right) + settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) + + ############################################################################### + # Define tallies + + # Instantiate the energy group data + egroups = [1e-5, 0.0635, 10.0, 1.0e2, 1.0e3, 0.5e6, 1.0e6, 20.0e6] + + inner_filter = openmc.CellFilter(fuel_inner) + outer_filter = openmc.CellFilter(fuel_outer) + energy_filter = openmc.EnergyFilter(egroups) + + inner_tally = openmc.Tally(name="inner tally") + inner_tally.filters = [energy_filter] + inner_tally.filters = [inner_filter] + inner_tally.scores = ['flux'] + + outer_tally = openmc.Tally(name="outer tally") + outer_tally.filters = [energy_filter] + outer_tally.filters = [outer_filter] + outer_tally.scores = ['flux'] + + # Instantiate a Tallies collection and export to XML + tallies = openmc.Tallies([inner_tally, outer_tally]) + + # Generate model and run test + model = openmc.Model(geometry, materials, settings, tallies) + + harness = MGXSTestHarness('statepoint.10.h5', model=model) + harness.main() diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat index bc5c4b2d46..2f6dde1ab0 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg/inputs_true.dat @@ -1,251 +1,253 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 3 - - - 2 - - - 3 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - analog - - - 1 2 7 - total - nu-fission - analog - - - 1 2 - total - flux - analog - - - 1 2 7 11 - total - nu-scatter - analog - - - 1 2 7 - total - nu-scatter - analog - - - 1 2 7 - total - scatter - analog - - - 15 2 - total - flux - tracklength - - - 15 2 - total - total - tracklength - - - 15 2 - total - flux - tracklength - - - 15 2 - total - absorption - tracklength - - - 15 2 - total - flux - analog - - - 15 2 7 - total - nu-fission - analog - - - 15 2 - total - flux - analog - - - 15 2 7 11 - total - nu-scatter - analog - - - 15 2 7 - total - nu-scatter - analog - - - 15 2 7 - total - scatter - analog - - - 29 2 - total - flux - tracklength - - - 29 2 - total - total - tracklength - - - 29 2 - total - flux - tracklength - - - 29 2 - total - absorption - tracklength - - - 29 2 - total - flux - analog - - - 29 2 7 - total - nu-fission - analog - - - 29 2 - total - flux - analog - - - 29 2 7 11 - total - nu-scatter - analog - - - 29 2 7 - total - nu-scatter - analog - - - 29 2 7 - total - scatter - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 3 + + + 2 + + + 3 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + analog + + + 1 2 7 + total + nu-fission + analog + + + 1 2 + total + flux + analog + + + 1 2 7 11 + total + nu-scatter + analog + + + 1 2 7 + total + nu-scatter + analog + + + 1 2 7 + total + scatter + analog + + + 15 2 + total + flux + tracklength + + + 15 2 + total + total + tracklength + + + 15 2 + total + flux + tracklength + + + 15 2 + total + absorption + tracklength + + + 15 2 + total + flux + analog + + + 15 2 7 + total + nu-fission + analog + + + 15 2 + total + flux + analog + + + 15 2 7 11 + total + nu-scatter + analog + + + 15 2 7 + total + nu-scatter + analog + + + 15 2 7 + total + scatter + analog + + + 29 2 + total + flux + tracklength + + + 29 2 + total + total + tracklength + + + 29 2 + total + flux + tracklength + + + 29 2 + total + absorption + tracklength + + + 29 2 + total + flux + analog + + + 29 2 7 + total + nu-fission + analog + + + 29 2 + total + flux + analog + + + 29 2 7 11 + total + nu-scatter + analog + + + 29 2 7 + total + nu-scatter + analog + + + 29 2 7 + total + scatter + analog + + + diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat index b4de4c7756..7ce1063146 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.263928E+00 3.768356E-02 +1.152065E+00 2.768158E-02 diff --git a/tests/regression_tests/mgxs_library_ce_to_mg/test.py b/tests/regression_tests/mgxs_library_ce_to_mg/test.py index 72f052e68e..48a715997a 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg/test.py +++ b/tests/regression_tests/mgxs_library_ce_to_mg/test.py @@ -40,28 +40,20 @@ class MGXSTestHarness(PyAPITestHarness): # Build MG Inputs # Get data needed to execute Library calculations. - sp = openmc.StatePoint(self._sp_name) - self.mgxs_lib.load_from_statepoint(sp) + with openmc.StatePoint(self._sp_name) as sp: + self.mgxs_lib.load_from_statepoint(sp) self._model.mgxs_file, self._model.materials, \ self._model.geometry = self.mgxs_lib.create_mg_mode() # Modify materials and settings so we can run in MG mode self._model.materials.cross_sections = './mgxs.h5' self._model.settings.energy_mode = 'multi-group' + # Dont need tallies so clear them from the model + self._model.tallies = openmc.Tallies() # Write modified input files - self._model.settings.export_to_xml() - self._model.geometry.export_to_xml() - self._model.materials.export_to_xml() + self._model.export_to_model_xml() self._model.mgxs_file.export_to_hdf5() - # Dont need tallies.xml, so remove the file - if os.path.exists('tallies.xml'): - os.remove('tallies.xml') - - # Enforce closing statepoint and summary files so HDF5 - # does not throw an error during the next OpenMC execution - sp._f.close() - sp._summary._f.close() # Re-run MG mode. if config['mpi']: diff --git a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/inputs_true.dat index 576f279661..e1f330cf92 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/inputs_true.dat @@ -1,251 +1,253 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 3 - - - 2 - - - 3 - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - absorption - tracklength - - - 1 2 - total - flux - analog - - - 1 2 7 - U234 U235 U238 O16 - nu-fission - analog - - - 1 2 - total - flux - analog - - - 1 2 7 11 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 7 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 7 - U234 U235 U238 O16 - scatter - analog - - - 15 2 - total - flux - tracklength - - - 15 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 15 2 - total - flux - tracklength - - - 15 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - absorption - tracklength - - - 15 2 - total - flux - analog - - - 15 2 7 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission - analog - - - 15 2 - total - flux - analog - - - 15 2 7 11 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 15 2 7 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 15 2 7 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 29 2 - total - flux - tracklength - - - 29 2 - H1 O16 B10 B11 - total - tracklength - - - 29 2 - total - flux - tracklength - - - 29 2 - H1 O16 B10 B11 - absorption - tracklength - - - 29 2 - total - flux - analog - - - 29 2 7 - H1 O16 B10 B11 - nu-fission - analog - - - 29 2 - total - flux - analog - - - 29 2 7 11 - H1 O16 B10 B11 - nu-scatter - analog - - - 29 2 7 - H1 O16 B10 B11 - nu-scatter - analog - - - 29 2 7 - H1 O16 B10 B11 - scatter - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 3 + + + 2 + + + 3 + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + absorption + tracklength + + + 1 2 + total + flux + analog + + + 1 2 7 + U234 U235 U238 O16 + nu-fission + analog + + + 1 2 + total + flux + analog + + + 1 2 7 11 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 7 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 7 + U234 U235 U238 O16 + scatter + analog + + + 15 2 + total + flux + tracklength + + + 15 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 15 2 + total + flux + tracklength + + + 15 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + absorption + tracklength + + + 15 2 + total + flux + analog + + + 15 2 7 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission + analog + + + 15 2 + total + flux + analog + + + 15 2 7 11 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 15 2 7 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 15 2 7 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 29 2 + total + flux + tracklength + + + 29 2 + H1 O16 B10 B11 + total + tracklength + + + 29 2 + total + flux + tracklength + + + 29 2 + H1 O16 B10 B11 + absorption + tracklength + + + 29 2 + total + flux + analog + + + 29 2 7 + H1 O16 B10 B11 + nu-fission + analog + + + 29 2 + total + flux + analog + + + 29 2 7 11 + H1 O16 B10 B11 + nu-scatter + analog + + + 29 2 7 + H1 O16 B10 B11 + nu-scatter + analog + + + 29 2 7 + H1 O16 B10 B11 + scatter + analog + + + diff --git a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/results_true.dat index f2844ed293..50c8328c88 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.817369E-01 1.166296E-02 +4.139942E-01 1.181308E-02 diff --git a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py index b624140d4e..a77ad24296 100644 --- a/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py +++ b/tests/regression_tests/mgxs_library_ce_to_mg_nuclides/test.py @@ -40,28 +40,20 @@ class MGXSTestHarness(PyAPITestHarness): # Build MG Inputs # Get data needed to execute Library calculations. - sp = openmc.StatePoint(self._sp_name) - self.mgxs_lib.load_from_statepoint(sp) + with openmc.StatePoint(self._sp_name) as sp: + self.mgxs_lib.load_from_statepoint(sp) self._model.mgxs_file, self._model.materials, \ self._model.geometry = self.mgxs_lib.create_mg_mode() # Modify materials and settings so we can run in MG mode self._model.materials.cross_sections = './mgxs.h5' self._model.settings.energy_mode = 'multi-group' + # Dont need tallies so clear them from the model + self._model.tallies = openmc.Tallies() # Write modified input files - self._model.settings.export_to_xml() - self._model.geometry.export_to_xml() - self._model.materials.export_to_xml() + self._model.export_to_model_xml() self._model.mgxs_file.export_to_hdf5() - # Dont need tallies.xml, so remove the file - if os.path.exists('tallies.xml'): - os.remove('tallies.xml') - - # Enforce closing statepoint and summary files so HDF5 - # does not throw an error during the next OpenMC execution - sp._f.close() - sp._summary._f.close() # Re-run MG mode. if config['mpi']: diff --git a/tests/regression_tests/mgxs_library_condense/inputs_true.dat b/tests/regression_tests/mgxs_library_condense/inputs_true.dat index 9661cd46be..4451d214c4 100644 --- a/tests/regression_tests/mgxs_library_condense/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_condense/inputs_true.dat @@ -1,502 +1,534 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 2 2 - -100.0 -100.0 - 100.0 100.0 - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 1 - - - 3 - - - 0.0 20000000.0 - - - 1 - - - 1 2 3 4 5 6 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - nu-scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - nu-fission - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 52 - total - nu-fission - analog - - - 1 5 - total - nu-fission - analog - - - 1 52 - total - prompt-nu-fission - analog - - - 1 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - prompt-nu-fission - analog - - - 66 2 - total - current - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 52 - total - delayed-nu-fission - analog - - - 1 77 5 - total - delayed-nu-fission - analog - - - 1 2 - total - nu-fission - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 - total - delayed-nu-fission - tracklength - - - 1 77 - total - decay-rate - tracklength - - - 1 2 - total - flux - analog - - - 1 77 2 5 - total - delayed-nu-fission - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 2 2 + -100.0 -100.0 + 100.0 100.0 + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 1 + + + 3 + + + 0.0 20000000.0 + + + 1 + + + 1 2 3 4 5 6 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + (n,3n) + tracklength + + + 1 2 + total + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + nu-scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + nu-fission + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 54 + total + nu-fission + analog + + + 1 5 + total + nu-fission + analog + + + 1 54 + total + prompt-nu-fission + analog + + + 1 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + prompt-nu-fission + analog + + + 68 2 + total + current + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 54 + total + delayed-nu-fission + analog + + + 1 79 5 + total + delayed-nu-fission + analog + + + 1 2 + total + nu-fission + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 + total + delayed-nu-fission + tracklength + + + 1 79 + total + decay-rate + tracklength + + + 1 2 + total + flux + analog + + + 1 79 2 5 + total + delayed-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_condense/results_true.dat b/tests/regression_tests/mgxs_library_condense/results_true.dat index 508984e54f..98b30932c3 100644 --- a/tests/regression_tests/mgxs_library_condense/results_true.dat +++ b/tests/regression_tests/mgxs_library_condense/results_true.dat @@ -1,356 +1,362 @@ mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.693468 0.019161 -2 1 2 1 1 total 0.694532 0.021411 -1 2 1 1 1 total 0.693746 0.023378 -3 2 2 1 1 total 0.697424 0.031315 +0 1 1 1 1 total 0.702881 0.026175 +2 1 2 1 1 total 0.706921 0.029169 +1 2 1 1 1 total 0.707809 0.024766 +3 2 2 1 1 total 0.717967 0.024008 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.439784 0.020575 -2 1 2 1 1 total 0.427837 0.025828 -1 2 1 1 1 total 0.423294 0.027075 -3 2 2 1 1 total 0.433094 0.032674 +0 1 1 1 1 total 0.431023 0.028803 +2 1 2 1 1 total 0.451864 0.030748 +1 2 1 1 1 total 0.456990 0.026359 +3 2 2 1 1 total 0.450621 0.026744 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.439784 0.020575 -2 1 2 1 1 total 0.427935 0.025836 -1 2 1 1 1 total 0.423294 0.027075 -3 2 2 1 1 total 0.433094 0.032674 +0 1 1 1 1 total 0.431023 0.028803 +2 1 2 1 1 total 0.451864 0.030748 +1 2 1 1 1 total 0.456990 0.026359 +3 2 2 1 1 total 0.450621 0.026744 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.021509 0.001396 -2 1 2 1 1 total 0.020854 0.001436 -1 2 1 1 1 total 0.021469 0.001983 -3 2 2 1 1 total 0.022046 0.001594 +0 1 1 1 1 total 0.022398 0.001401 +2 1 2 1 1 total 0.022325 0.001371 +1 2 1 1 1 total 0.022942 0.000990 +3 2 2 1 1 total 0.022705 0.001322 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.011598 0.001508 -2 1 2 1 1 total 0.010856 0.001613 -1 2 1 1 1 total 0.011622 0.002259 -3 2 2 1 1 total 0.012002 0.001685 +0 1 1 1 1 total 0.022394 0.001401 +2 1 2 1 1 total 0.022321 0.001371 +1 2 1 1 1 total 0.022935 0.000990 +3 2 2 1 1 total 0.022699 0.001322 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.009911 0.000667 -2 1 2 1 1 total 0.009998 0.000859 -1 2 1 1 1 total 0.009847 0.001153 -3 2 2 1 1 total 0.010044 0.000939 +0 1 1 1 1 total 0.011562 0.001544 +2 1 2 1 1 total 0.011852 0.001418 +1 2 1 1 1 total 0.012168 0.000958 +3 2 2 1 1 total 0.011986 0.001418 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.024369 0.001625 -2 1 2 1 1 total 0.024572 0.002095 -1 2 1 1 1 total 0.024189 0.002811 -3 2 2 1 1 total 0.024677 0.002291 +0 1 1 1 1 total 0.010836 0.000803 +2 1 2 1 1 total 0.010473 0.000591 +1 2 1 1 1 total 0.010774 0.000415 +3 2 2 1 1 total 0.010719 0.000688 + mesh 1 group in nuclide mean std. dev. + x y z +0 1 1 1 1 total 0.026602 0.001957 +2 1 2 1 1 total 0.025695 0.001442 +1 2 1 1 1 total 0.026454 0.001015 +3 2 2 1 1 total 0.026310 0.001678 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 1.919781e+06 128933.976741 -2 1 2 1 1 total 1.936343e+06 166141.202022 -1 2 1 1 1 total 1.906964e+06 223076.300071 -3 2 2 1 1 total 1.945248e+06 181591.269295 +0 1 1 1 1 total 2.098256e+06 155243.612264 +2 1 2 1 1 total 2.027699e+06 114334.400924 +1 2 1 1 1 total 2.086255e+06 80325.567787 +3 2 2 1 1 total 2.075596e+06 133128.805680 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.671959 0.018153 -2 1 2 1 1 total 0.673678 0.020448 -1 2 1 1 1 total 0.672277 0.021882 -3 2 2 1 1 total 0.675378 0.030166 +0 1 1 1 1 total 0.680483 0.025407 +2 1 2 1 1 total 0.684597 0.028126 +1 2 1 1 1 total 0.684867 0.024133 +3 2 2 1 1 total 0.695262 0.023087 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.672619 0.022961 -2 1 2 1 1 total 0.687448 0.033696 -1 2 1 1 1 total 0.676738 0.023823 -3 2 2 1 1 total 0.673350 0.021595 +0 1 1 1 1 total 0.678017 0.026288 +2 1 2 1 1 total 0.674888 0.033989 +1 2 1 1 1 total 0.681736 0.025618 +3 2 2 1 1 total 0.683701 0.023788 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.672619 0.023173 -1 1 1 1 1 1 P1 total 0.253684 0.007398 -2 1 1 1 1 1 P2 total 0.083914 0.004899 -3 1 1 1 1 1 P3 total 0.006851 0.005511 -8 1 2 1 1 1 P0 total 0.687265 0.033719 -9 1 2 1 1 1 P1 total 0.266694 0.014161 -10 1 2 1 1 1 P2 total 0.110092 0.005824 -11 1 2 1 1 1 P3 total 0.014970 0.004933 -4 2 1 1 1 1 P0 total 0.676738 0.023406 -5 2 1 1 1 1 P1 total 0.270452 0.013885 -6 2 1 1 1 1 P2 total 0.107918 0.005840 -7 2 1 1 1 1 P3 total 0.016160 0.004108 -12 2 2 1 1 1 P0 total 0.673350 0.021192 -13 2 2 1 1 1 P1 total 0.264330 0.009415 -14 2 2 1 1 1 P2 total 0.093703 0.004850 -15 2 2 1 1 1 P3 total 0.019800 0.005236 +0 1 1 1 1 1 P0 total 0.678017 0.026290 +1 1 1 1 1 1 P1 total 0.271858 0.011693 +2 1 1 1 1 1 P2 total 0.095219 0.002950 +3 1 1 1 1 1 P3 total 0.012808 0.004686 +8 1 2 1 1 1 P0 total 0.674888 0.033578 +9 1 2 1 1 1 P1 total 0.255058 0.009912 +10 1 2 1 1 1 P2 total 0.098001 0.005459 +11 1 2 1 1 1 P3 total 0.012058 0.005439 +4 2 1 1 1 1 P0 total 0.681736 0.025439 +5 2 1 1 1 1 P1 total 0.250820 0.009035 +6 2 1 1 1 1 P2 total 0.092563 0.006549 +7 2 1 1 1 1 P3 total 0.008511 0.003905 +12 2 2 1 1 1 P0 total 0.683701 0.024254 +13 2 2 1 1 1 P1 total 0.267345 0.011695 +14 2 2 1 1 1 P2 total 0.096222 0.005551 +15 2 2 1 1 1 P3 total 0.011515 0.003236 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.672619 0.023173 -1 1 1 1 1 1 P1 total 0.253684 0.007398 -2 1 1 1 1 1 P2 total 0.083914 0.004899 -3 1 1 1 1 1 P3 total 0.006851 0.005511 -8 1 2 1 1 1 P0 total 0.687448 0.033656 -9 1 2 1 1 1 P1 total 0.266596 0.014177 -10 1 2 1 1 1 P2 total 0.110079 0.005826 -11 1 2 1 1 1 P3 total 0.015046 0.004973 -4 2 1 1 1 1 P0 total 0.676738 0.023406 -5 2 1 1 1 1 P1 total 0.270452 0.013885 -6 2 1 1 1 1 P2 total 0.107918 0.005840 -7 2 1 1 1 1 P3 total 0.016160 0.004108 -12 2 2 1 1 1 P0 total 0.673350 0.021192 -13 2 2 1 1 1 P1 total 0.264330 0.009415 -14 2 2 1 1 1 P2 total 0.093703 0.004850 -15 2 2 1 1 1 P3 total 0.019800 0.005236 - mesh 1 group in group out nuclide mean std. dev. - x y z -0 1 1 1 1 1 total 1.000000 0.036767 -2 1 2 1 1 1 total 1.000266 0.042769 -1 2 1 1 1 1 total 1.000000 0.034289 -3 2 2 1 1 1 total 1.000000 0.022887 - mesh 1 group in group out nuclide mean std. dev. - x y z -0 1 1 1 1 1 total 0.025263 0.001825 -2 1 2 1 1 1 total 0.021868 0.001931 -1 2 1 1 1 1 total 0.025960 0.002413 -3 2 2 1 1 1 total 0.028694 0.002815 +0 1 1 1 1 1 P0 total 0.678017 0.026290 +1 1 1 1 1 1 P1 total 0.271858 0.011693 +2 1 1 1 1 1 P2 total 0.095219 0.002950 +3 1 1 1 1 1 P3 total 0.012808 0.004686 +8 1 2 1 1 1 P0 total 0.674888 0.033578 +9 1 2 1 1 1 P1 total 0.255058 0.009912 +10 1 2 1 1 1 P2 total 0.098001 0.005459 +11 1 2 1 1 1 P3 total 0.012058 0.005439 +4 2 1 1 1 1 P0 total 0.681736 0.025439 +5 2 1 1 1 1 P1 total 0.250820 0.009035 +6 2 1 1 1 1 P2 total 0.092563 0.006549 +7 2 1 1 1 1 P3 total 0.008511 0.003905 +12 2 2 1 1 1 P0 total 0.683701 0.024254 +13 2 2 1 1 1 P1 total 0.267345 0.011695 +14 2 2 1 1 1 P2 total 0.096222 0.005551 +15 2 2 1 1 1 P3 total 0.011515 0.003236 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 1.0 0.036767 -2 1 2 1 1 1 total 1.0 0.042878 -1 2 1 1 1 1 total 1.0 0.034289 -3 2 2 1 1 1 total 1.0 0.022887 - mesh 1 group in group out legendre nuclide mean std. dev. - x y z -0 1 1 1 1 1 P0 total 0.671959 0.030658 -1 1 1 1 1 1 P1 total 0.253435 0.010587 -2 1 1 1 1 1 P2 total 0.083831 0.005499 -3 1 1 1 1 1 P3 total 0.006845 0.005509 -8 1 2 1 1 1 P0 total 0.673678 0.035391 -9 1 2 1 1 1 P1 total 0.261422 0.014724 -10 1 2 1 1 1 P2 total 0.107915 0.006058 -11 1 2 1 1 1 P3 total 0.014674 0.004843 -4 2 1 1 1 1 P0 total 0.672277 0.031784 -5 2 1 1 1 1 P1 total 0.268669 0.016287 -6 2 1 1 1 1 P2 total 0.107206 0.006753 -7 2 1 1 1 1 P3 total 0.016054 0.004114 -12 2 2 1 1 1 P0 total 0.675378 0.033896 -13 2 2 1 1 1 P1 total 0.265126 0.014021 -14 2 2 1 1 1 P2 total 0.093985 0.006096 -15 2 2 1 1 1 P3 total 0.019860 0.005309 - mesh 1 group in group out legendre nuclide mean std. dev. - x y z -0 1 1 1 1 1 P0 total 0.671959 0.039374 -1 1 1 1 1 1 P1 total 0.253435 0.014104 -2 1 1 1 1 1 P2 total 0.083831 0.006304 -3 1 1 1 1 1 P3 total 0.006845 0.005515 -8 1 2 1 1 1 P0 total 0.673857 0.045644 -9 1 2 1 1 1 P1 total 0.261492 0.018491 -10 1 2 1 1 1 P2 total 0.107944 0.007617 -11 1 2 1 1 1 P3 total 0.014678 0.004885 -4 2 1 1 1 1 P0 total 0.672277 0.039263 -5 2 1 1 1 1 P1 total 0.268669 0.018712 -6 2 1 1 1 1 P2 total 0.107206 0.007689 -7 2 1 1 1 1 P3 total 0.016054 0.004150 -12 2 2 1 1 1 P0 total 0.675378 0.037254 -13 2 2 1 1 1 P1 total 0.265126 0.015278 -14 2 2 1 1 1 P2 total 0.093985 0.006465 -15 2 2 1 1 1 P3 total 0.019860 0.005328 - mesh 1 group out nuclide mean std. dev. - x y z -0 1 1 1 1 total 1.0 0.080776 -2 1 2 1 1 total 1.0 0.117674 -1 2 1 1 1 total 1.0 0.160576 -3 2 2 1 1 total 1.0 0.151384 - mesh 1 group out nuclide mean std. dev. - x y z -0 1 1 1 1 total 1.0 0.080742 -2 1 2 1 1 total 1.0 0.117674 -1 2 1 1 1 total 1.0 0.160576 -3 2 2 1 1 total 1.0 0.150417 - mesh 1 group in nuclide mean std. dev. - x y z -0 1 1 1 1 total 4.800874e-07 2.910227e-08 -2 1 2 1 1 total 5.008000e-07 3.613802e-08 -1 2 1 1 1 total 4.872638e-07 4.324238e-08 -3 2 2 1 1 total 4.890453e-07 2.833017e-08 - mesh 1 group in nuclide mean std. dev. - x y z -0 1 1 1 1 total 0.024194 0.001614 -2 1 2 1 1 total 0.024397 0.002081 -1 2 1 1 1 total 0.024017 0.002793 -3 2 2 1 1 total 0.024501 0.002276 +0 1 1 1 1 1 total 1.0 0.041785 +2 1 2 1 1 1 total 1.0 0.057717 +1 2 1 1 1 1 total 1.0 0.040074 +3 2 2 1 1 1 total 1.0 0.042758 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.024899 0.001762 -2 1 2 1 1 1 total 0.021868 0.001931 -1 2 1 1 1 1 total 0.025960 0.002413 -3 2 2 1 1 1 total 0.028508 0.002818 +0 1 1 1 1 1 total 0.028438 0.003513 +2 1 2 1 1 1 total 0.022222 0.001560 +1 2 1 1 1 1 total 0.025698 0.002756 +3 2 2 1 1 1 total 0.026501 0.002315 + mesh 1 group in group out nuclide mean std. dev. + x y z +0 1 1 1 1 1 total 1.0 0.041785 +2 1 2 1 1 1 total 1.0 0.057717 +1 2 1 1 1 1 total 1.0 0.040074 +3 2 2 1 1 1 total 1.0 0.042758 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.680483 0.038131 +1 1 1 1 1 1 P1 total 0.272847 0.016111 +2 1 1 1 1 1 P2 total 0.095565 0.004869 +3 1 1 1 1 1 P3 total 0.012854 0.004731 +8 1 2 1 1 1 P0 total 0.684597 0.048501 +9 1 2 1 1 1 P1 total 0.258727 0.016473 +10 1 2 1 1 1 P2 total 0.099411 0.007470 +11 1 2 1 1 1 P3 total 0.012231 0.005552 +4 2 1 1 1 1 P0 total 0.684867 0.036546 +5 2 1 1 1 1 P1 total 0.251972 0.013220 +6 2 1 1 1 1 P2 total 0.092988 0.007474 +7 2 1 1 1 1 P3 total 0.008550 0.003936 +12 2 2 1 1 1 P0 total 0.695262 0.037640 +13 2 2 1 1 1 P1 total 0.271866 0.016280 +14 2 2 1 1 1 P2 total 0.097849 0.006919 +15 2 2 1 1 1 P3 total 0.011710 0.003325 + mesh 1 group in group out legendre nuclide mean std. dev. + x y z +0 1 1 1 1 1 P0 total 0.680483 0.047566 +1 1 1 1 1 1 P1 total 0.272847 0.019737 +2 1 1 1 1 1 P2 total 0.095565 0.006297 +3 1 1 1 1 1 P3 total 0.012854 0.004762 +8 1 2 1 1 1 P0 total 0.684597 0.062559 +9 1 2 1 1 1 P1 total 0.258727 0.022234 +10 1 2 1 1 1 P2 total 0.099411 0.009420 +11 1 2 1 1 1 P3 total 0.012231 0.005597 +4 2 1 1 1 1 P0 total 0.684867 0.045704 +5 2 1 1 1 1 P1 total 0.251972 0.016635 +6 2 1 1 1 1 P2 total 0.092988 0.008352 +7 2 1 1 1 1 P3 total 0.008550 0.003951 +12 2 2 1 1 1 P0 total 0.695262 0.047964 +13 2 2 1 1 1 P1 total 0.271866 0.020004 +14 2 2 1 1 1 P2 total 0.097849 0.008086 +15 2 2 1 1 1 P3 total 0.011710 0.003363 + mesh 1 group out nuclide mean std. dev. + x y z +0 1 1 1 1 total 1.0 0.142430 +2 1 2 1 1 total 1.0 0.112715 +1 2 1 1 1 total 1.0 0.192385 +3 2 2 1 1 total 1.0 0.109836 + mesh 1 group out nuclide mean std. dev. + x y z +0 1 1 1 1 total 1.0 0.143959 +2 1 2 1 1 total 1.0 0.102504 +1 2 1 1 1 total 1.0 0.188381 +3 2 2 1 1 total 1.0 0.116230 + mesh 1 group in nuclide mean std. dev. + x y z +0 1 1 1 1 total 5.060544e-07 3.651604e-08 +2 1 2 1 1 total 5.101988e-07 4.947639e-08 +1 2 1 1 1 total 5.206450e-07 2.814648e-08 +3 2 2 1 1 total 5.278759e-07 3.171554e-08 + mesh 1 group in nuclide mean std. dev. + x y z +0 1 1 1 1 total 0.026414 0.001944 +2 1 2 1 1 total 0.025515 0.001432 +1 2 1 1 1 total 0.026267 0.001008 +3 2 2 1 1 total 0.026125 0.001667 + mesh 1 group in group out nuclide mean std. dev. + x y z +0 1 1 1 1 1 total 0.028220 0.003510 +2 1 2 1 1 1 total 0.021754 0.001519 +1 2 1 1 1 1 total 0.025487 0.002676 +3 2 2 1 1 1 total 0.026281 0.002263 mesh 1 group in nuclide mean std. dev. x y surf -3 1 1 x-max in 1 total 4.520 0.122262 -2 1 1 x-max out 1 total 4.486 0.128841 +3 1 1 x-max in 1 total 4.244 0.096333 +2 1 1 x-max out 1 total 4.378 0.107210 1 1 1 x-min in 1 total 0.000 0.000000 0 1 1 x-min out 1 total 0.000 0.000000 -7 1 1 y-max in 1 total 4.406 0.116576 -6 1 1 y-max out 1 total 4.436 0.064637 +7 1 1 y-max in 1 total 4.388 0.116508 +6 1 1 y-max out 1 total 4.284 0.129066 5 1 1 y-min in 1 total 0.000 0.000000 4 1 1 y-min out 1 total 0.000 0.000000 -19 1 2 x-max in 1 total 4.460 0.122752 -18 1 2 x-max out 1 total 4.480 0.162721 +19 1 2 x-max in 1 total 4.280 0.139971 +18 1 2 x-max out 1 total 4.152 0.117141 17 1 2 x-min in 1 total 0.000 0.000000 16 1 2 x-min out 1 total 0.000 0.000000 23 1 2 y-max in 1 total 0.000 0.000000 22 1 2 y-max out 1 total 0.000 0.000000 -21 1 2 y-min in 1 total 4.436 0.064637 -20 1 2 y-min out 1 total 4.406 0.116576 +21 1 2 y-min in 1 total 4.284 0.129066 +20 1 2 y-min out 1 total 4.388 0.116508 11 2 1 x-max in 1 total 0.000 0.000000 10 2 1 x-max out 1 total 0.000 0.000000 -9 2 1 x-min in 1 total 4.486 0.128841 -8 2 1 x-min out 1 total 4.520 0.122262 -15 2 1 y-max in 1 total 4.548 0.156691 -14 2 1 y-max out 1 total 4.526 0.166571 +9 2 1 x-min in 1 total 4.378 0.107210 +8 2 1 x-min out 1 total 4.244 0.096333 +15 2 1 y-max in 1 total 4.280 0.079070 +14 2 1 y-max out 1 total 4.416 0.093648 13 2 1 y-min in 1 total 0.000 0.000000 12 2 1 y-min out 1 total 0.000 0.000000 27 2 2 x-max in 1 total 0.000 0.000000 26 2 2 x-max out 1 total 0.000 0.000000 -25 2 2 x-min in 1 total 4.480 0.162721 -24 2 2 x-min out 1 total 4.460 0.122752 +25 2 2 x-min in 1 total 4.152 0.117141 +24 2 2 x-min out 1 total 4.280 0.139971 31 2 2 y-max in 1 total 0.000 0.000000 30 2 2 y-max out 1 total 0.000 0.000000 -29 2 2 y-min in 1 total 4.526 0.166571 -28 2 2 y-min out 1 total 4.548 0.156691 +29 2 2 y-min in 1 total 4.416 0.093648 +28 2 2 y-min out 1 total 4.280 0.079070 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.757948 0.035459 -2 1 2 1 1 total 0.779112 0.047034 -1 2 1 1 1 total 0.787475 0.050368 -3 2 2 1 1 total 0.769656 0.058065 +0 1 1 1 1 total 0.943198 0.067915 +2 1 2 1 1 total 0.895741 0.058714 +1 2 1 1 1 total 0.911011 0.068461 +3 2 2 1 1 total 0.927611 0.066426 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.757948 0.035459 -2 1 2 1 1 total 0.778934 0.047027 -1 2 1 1 1 total 0.787475 0.050368 -3 2 2 1 1 total 0.769656 0.058065 +0 1 1 1 1 total 0.943198 0.067915 +2 1 2 1 1 total 0.895741 0.058714 +1 2 1 1 1 total 0.911011 0.068461 +3 2 2 1 1 total 0.927611 0.066426 mesh 1 delayedgroup group in nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.000006 3.699363e-07 -1 1 1 1 2 1 total 0.000030 1.914853e-06 -2 1 1 1 3 1 total 0.000029 1.834339e-06 -3 1 1 1 4 1 total 0.000068 4.149880e-06 -4 1 1 1 5 1 total 0.000030 1.769320e-06 -5 1 1 1 6 1 total 0.000013 7.382097e-07 -12 1 2 1 1 1 total 0.000006 4.767315e-07 -13 1 2 1 2 1 total 0.000030 2.471571e-06 -14 1 2 1 3 1 total 0.000029 2.368041e-06 -15 1 2 1 4 1 total 0.000068 5.351482e-06 -16 1 2 1 5 1 total 0.000030 2.259042e-06 -17 1 2 1 6 1 total 0.000013 9.436237e-07 -6 2 1 1 1 1 total 0.000005 6.400406e-07 -7 2 1 1 2 1 total 0.000029 3.309040e-06 -8 2 1 1 3 1 total 0.000029 3.163385e-06 -9 2 1 1 4 1 total 0.000067 7.114281e-06 -10 2 1 1 5 1 total 0.000030 2.951164e-06 -11 2 1 1 6 1 total 0.000012 1.234794e-06 -18 2 2 1 1 1 total 0.000006 5.212824e-07 -19 2 2 1 2 1 total 0.000030 2.712206e-06 -20 2 2 1 3 1 total 0.000029 2.604912e-06 -21 2 2 1 4 1 total 0.000068 5.914482e-06 -22 2 2 1 5 1 total 0.000030 2.531950e-06 -23 2 2 1 6 1 total 0.000013 1.056326e-06 +0 1 1 1 1 1 total 0.000006 4.453421e-07 +1 1 1 1 2 1 total 0.000032 2.303082e-06 +2 1 1 1 3 1 total 0.000031 2.202789e-06 +3 1 1 1 4 1 total 0.000072 4.961025e-06 +4 1 1 1 5 1 total 0.000032 2.071919e-06 +5 1 1 1 6 1 total 0.000013 8.662934e-07 +12 1 2 1 1 1 total 0.000006 3.282329e-07 +13 1 2 1 2 1 total 0.000031 1.701766e-06 +14 1 2 1 3 1 total 0.000030 1.629615e-06 +15 1 2 1 4 1 total 0.000070 3.675759e-06 +16 1 2 1 5 1 total 0.000031 1.536226e-06 +17 1 2 1 6 1 total 0.000013 6.423838e-07 +6 2 1 1 1 1 total 0.000006 2.308186e-07 +7 2 1 1 2 1 total 0.000032 1.211112e-06 +8 2 1 1 3 1 total 0.000031 1.169911e-06 +9 2 1 1 4 1 total 0.000072 2.685832e-06 +10 2 1 1 5 1 total 0.000032 1.187072e-06 +11 2 1 1 6 1 total 0.000013 4.939053e-07 +18 2 2 1 1 1 total 0.000006 3.819684e-07 +19 2 2 1 2 1 total 0.000032 1.976073e-06 +20 2 2 1 3 1 total 0.000031 1.889748e-06 +21 2 2 1 4 1 total 0.000072 4.252207e-06 +22 2 2 1 5 1 total 0.000032 1.765565e-06 +23 2 2 1 6 1 total 0.000013 7.386890e-07 mesh 1 delayedgroup group out nuclide mean std. dev. x y z 0 1 1 1 1 1 total 0.0 0.000000 1 1 1 1 2 1 total 0.0 0.000000 2 1 1 1 3 1 total 0.0 0.000000 3 1 1 1 4 1 total 1.0 1.414214 -4 1 1 1 5 1 total 1.0 1.414214 +4 1 1 1 5 1 total 0.0 0.000000 5 1 1 1 6 1 total 0.0 0.000000 12 1 2 1 1 1 total 0.0 0.000000 13 1 2 1 2 1 total 0.0 0.000000 14 1 2 1 3 1 total 0.0 0.000000 -15 1 2 1 4 1 total 0.0 0.000000 +15 1 2 1 4 1 total 1.0 0.869026 16 1 2 1 5 1 total 0.0 0.000000 17 1 2 1 6 1 total 0.0 0.000000 6 2 1 1 1 1 total 0.0 0.000000 7 2 1 1 2 1 total 0.0 0.000000 8 2 1 1 3 1 total 0.0 0.000000 -9 2 1 1 4 1 total 0.0 0.000000 +9 2 1 1 4 1 total 1.0 1.414214 10 2 1 1 5 1 total 0.0 0.000000 11 2 1 1 6 1 total 0.0 0.000000 18 2 2 1 1 1 total 0.0 0.000000 -19 2 2 1 2 1 total 1.0 1.414214 +19 2 2 1 2 1 total 0.0 0.000000 20 2 2 1 3 1 total 0.0 0.000000 -21 2 2 1 4 1 total 0.0 0.000000 +21 2 2 1 4 1 total 1.0 1.414214 22 2 2 1 5 1 total 0.0 0.000000 23 2 2 1 6 1 total 0.0 0.000000 mesh 1 delayedgroup group in nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.000227 0.000021 -1 1 1 1 2 1 total 0.001219 0.000109 -2 1 1 1 3 1 total 0.001191 0.000106 -3 1 1 1 4 1 total 0.002775 0.000243 -4 1 1 1 5 1 total 0.001249 0.000106 -5 1 1 1 6 1 total 0.000519 0.000044 -12 1 2 1 1 1 total 0.000227 0.000026 -13 1 2 1 2 1 total 0.001216 0.000140 -14 1 2 1 3 1 total 0.001187 0.000135 -15 1 2 1 4 1 total 0.002760 0.000309 -16 1 2 1 5 1 total 0.001236 0.000135 -17 1 2 1 6 1 total 0.000514 0.000056 -6 2 1 1 1 1 total 0.000227 0.000037 -7 2 1 1 2 1 total 0.001215 0.000194 -8 2 1 1 3 1 total 0.001184 0.000187 -9 2 1 1 4 1 total 0.002749 0.000428 -10 2 1 1 5 1 total 0.001227 0.000185 -11 2 1 1 6 1 total 0.000511 0.000077 -18 2 2 1 1 1 total 0.000227 0.000027 -19 2 2 1 2 1 total 0.001215 0.000145 -20 2 2 1 3 1 total 0.001185 0.000140 -21 2 2 1 4 1 total 0.002752 0.000321 -22 2 2 1 5 1 total 0.001229 0.000140 -23 2 2 1 6 1 total 0.000511 0.000058 +0 1 1 1 1 1 total 0.000227 0.000023 +1 1 1 1 2 1 total 0.001210 0.000119 +2 1 1 1 3 1 total 0.001176 0.000114 +3 1 1 1 4 1 total 0.002722 0.000261 +4 1 1 1 5 1 total 0.001204 0.000112 +5 1 1 1 6 1 total 0.000501 0.000047 +12 1 2 1 1 1 total 0.000227 0.000017 +13 1 2 1 2 1 total 0.001208 0.000087 +14 1 2 1 3 1 total 0.001174 0.000084 +15 1 2 1 4 1 total 0.002710 0.000192 +16 1 2 1 5 1 total 0.001194 0.000082 +17 1 2 1 6 1 total 0.000497 0.000034 +6 2 1 1 1 1 total 0.000227 0.000010 +7 2 1 1 2 1 total 0.001210 0.000053 +8 2 1 1 3 1 total 0.001177 0.000052 +9 2 1 1 4 1 total 0.002726 0.000119 +10 2 1 1 5 1 total 0.001208 0.000053 +11 2 1 1 6 1 total 0.000503 0.000022 +18 2 2 1 1 1 total 0.000227 0.000019 +19 2 2 1 2 1 total 0.001211 0.000102 +20 2 2 1 3 1 total 0.001178 0.000098 +21 2 2 1 4 1 total 0.002726 0.000223 +22 2 2 1 5 1 total 0.001207 0.000096 +23 2 2 1 6 1 total 0.000503 0.000040 mesh 1 delayedgroup nuclide mean std. dev. x y z -0 1 1 1 1 total 0.013357 0.001250 -1 1 1 1 2 total 0.032589 0.002884 -2 1 1 1 3 total 0.121105 0.010300 -3 1 1 1 4 total 0.306138 0.024490 -4 1 1 1 5 total 0.862757 0.061312 -5 1 1 1 6 total 2.897868 0.207582 -12 1 2 1 1 total 0.013356 0.001625 -13 1 2 1 2 total 0.032597 0.003869 -14 1 2 1 3 total 0.121088 0.014125 -15 1 2 1 4 total 0.305963 0.034783 -16 1 2 1 5 total 0.862125 0.093434 -17 1 2 1 6 total 2.895716 0.314825 -6 2 1 1 1 total 0.013355 0.002181 -7 2 1 1 2 total 0.032604 0.005141 -8 2 1 1 3 total 0.121074 0.018615 -9 2 1 1 4 total 0.305828 0.045169 -10 2 1 1 5 total 0.861633 0.116817 -11 2 1 1 6 total 2.894042 0.394847 -18 2 2 1 1 total 0.013355 0.001620 -19 2 2 1 2 total 0.032602 0.003798 -20 2 2 1 3 total 0.121077 0.013715 -21 2 2 1 4 total 0.305857 0.033214 -22 2 2 1 5 total 0.861740 0.086567 -23 2 2 1 6 total 2.894407 0.292238 +0 1 1 1 1 total 0.013353 0.001345 +1 1 1 1 2 total 0.032619 0.003120 +2 1 1 1 3 total 0.121042 0.011142 +3 1 1 1 4 total 0.305503 0.026418 +4 1 1 1 5 total 0.860433 0.064665 +5 1 1 1 6 total 2.889963 0.219527 +12 1 2 1 1 total 0.013352 0.001049 +13 1 2 1 2 total 0.032626 0.002465 +14 1 2 1 3 total 0.121027 0.008891 +15 1 2 1 4 total 0.305351 0.021426 +16 1 2 1 5 total 0.859866 0.054490 +17 1 2 1 6 total 2.888034 0.184436 +6 2 1 1 1 total 0.013353 0.000612 +7 2 1 1 2 total 0.032616 0.001412 +8 2 1 1 3 total 0.121047 0.005039 +9 2 1 1 4 total 0.305559 0.012002 +10 2 1 1 5 total 0.860640 0.030707 +11 2 1 1 6 total 2.890664 0.103692 +18 2 2 1 1 total 0.013353 0.001132 +19 2 2 1 2 total 0.032617 0.002633 +20 2 2 1 3 total 0.121046 0.009426 +21 2 2 1 4 total 0.305545 0.022417 +22 2 2 1 5 total 0.860588 0.055030 +23 2 2 1 6 total 2.890489 0.186821 mesh 1 delayedgroup group in group out nuclide mean std. dev. x y z 0 1 1 1 1 1 1 total 0.000000 0.000000 1 1 1 1 2 1 1 total 0.000000 0.000000 2 1 1 1 3 1 1 total 0.000000 0.000000 -3 1 1 1 4 1 1 total 0.000172 0.000172 -4 1 1 1 5 1 1 total 0.000192 0.000192 +3 1 1 1 4 1 1 total 0.000219 0.000219 +4 1 1 1 5 1 1 total 0.000000 0.000000 5 1 1 1 6 1 1 total 0.000000 0.000000 12 1 2 1 1 1 1 total 0.000000 0.000000 13 1 2 1 2 1 1 total 0.000000 0.000000 14 1 2 1 3 1 1 total 0.000000 0.000000 -15 1 2 1 4 1 1 total 0.000000 0.000000 +15 1 2 1 4 1 1 total 0.000467 0.000287 16 1 2 1 5 1 1 total 0.000000 0.000000 17 1 2 1 6 1 1 total 0.000000 0.000000 6 2 1 1 1 1 1 total 0.000000 0.000000 7 2 1 1 2 1 1 total 0.000000 0.000000 8 2 1 1 3 1 1 total 0.000000 0.000000 -9 2 1 1 4 1 1 total 0.000000 0.000000 +9 2 1 1 4 1 1 total 0.000211 0.000211 10 2 1 1 5 1 1 total 0.000000 0.000000 11 2 1 1 6 1 1 total 0.000000 0.000000 18 2 2 1 1 1 1 total 0.000000 0.000000 -19 2 2 1 2 1 1 total 0.000186 0.000186 +19 2 2 1 2 1 1 total 0.000000 0.000000 20 2 2 1 3 1 1 total 0.000000 0.000000 -21 2 2 1 4 1 1 total 0.000000 0.000000 +21 2 2 1 4 1 1 total 0.000219 0.000219 22 2 2 1 5 1 1 total 0.000000 0.000000 23 2 2 1 6 1 1 total 0.000000 0.000000 diff --git a/tests/regression_tests/mgxs_library_correction/inputs_true.dat b/tests/regression_tests/mgxs_library_correction/inputs_true.dat index 8c1cd4d165..bb463778fd 100644 --- a/tests/regression_tests/mgxs_library_correction/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_correction/inputs_true.dat @@ -1,344 +1,346 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 1 - - - 0 - - - 2 - - - 3 - - - 1 2 - total - flux - analog - - - 1 2 3 4 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 3 4 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 3 12 - total - scatter - analog - - - 1 3 4 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 3 12 - total - scatter - analog - - - 1 2 3 - total - nu-scatter - analog - - - 1 3 4 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 19 2 - total - flux - analog - - - 19 2 3 4 - total - scatter - analog - - - 19 2 - total - flux - analog - - - 19 2 3 4 - total - nu-scatter - analog - - - 19 2 - total - flux - tracklength - - - 19 2 - total - scatter - tracklength - - - 19 2 3 12 - total - scatter - analog - - - 19 3 4 - total - scatter - analog - - - 19 2 - total - flux - analog - - - 19 2 - total - flux - tracklength - - - 19 2 - total - scatter - tracklength - - - 19 2 3 12 - total - scatter - analog - - - 19 2 3 - total - nu-scatter - analog - - - 19 3 4 - total - nu-scatter - analog - - - 19 2 - total - flux - analog - - - 37 2 - total - flux - analog - - - 37 2 3 4 - total - scatter - analog - - - 37 2 - total - flux - analog - - - 37 2 3 4 - total - nu-scatter - analog - - - 37 2 - total - flux - tracklength - - - 37 2 - total - scatter - tracklength - - - 37 2 3 12 - total - scatter - analog - - - 37 3 4 - total - scatter - analog - - - 37 2 - total - flux - analog - - - 37 2 - total - flux - tracklength - - - 37 2 - total - scatter - tracklength - - - 37 2 3 12 - total - scatter - analog - - - 37 2 3 - total - nu-scatter - analog - - - 37 3 4 - total - nu-scatter - analog - - - 37 2 - total - flux - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 1 + + + 0 + + + 2 + + + 3 + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 12 + total + scatter + analog + + + 1 3 4 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 12 + total + scatter + analog + + + 1 2 3 + total + nu-scatter + analog + + + 1 3 4 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 19 2 + total + flux + analog + + + 19 2 3 4 + total + scatter + analog + + + 19 2 + total + flux + analog + + + 19 2 3 4 + total + nu-scatter + analog + + + 19 2 + total + flux + tracklength + + + 19 2 + total + scatter + tracklength + + + 19 2 3 12 + total + scatter + analog + + + 19 3 4 + total + scatter + analog + + + 19 2 + total + flux + analog + + + 19 2 + total + flux + tracklength + + + 19 2 + total + scatter + tracklength + + + 19 2 3 12 + total + scatter + analog + + + 19 2 3 + total + nu-scatter + analog + + + 19 3 4 + total + nu-scatter + analog + + + 19 2 + total + flux + analog + + + 37 2 + total + flux + analog + + + 37 2 3 4 + total + scatter + analog + + + 37 2 + total + flux + analog + + + 37 2 3 4 + total + nu-scatter + analog + + + 37 2 + total + flux + tracklength + + + 37 2 + total + scatter + tracklength + + + 37 2 3 12 + total + scatter + analog + + + 37 3 4 + total + scatter + analog + + + 37 2 + total + flux + analog + + + 37 2 + total + flux + tracklength + + + 37 2 + total + scatter + tracklength + + + 37 2 3 12 + total + scatter + analog + + + 37 2 3 + total + nu-scatter + analog + + + 37 3 4 + total + nu-scatter + analog + + + 37 2 + total + flux + analog + + + diff --git a/tests/regression_tests/mgxs_library_correction/results_true.dat b/tests/regression_tests/mgxs_library_correction/results_true.dat index 690c9e4dca..b6aaad0441 100644 --- a/tests/regression_tests/mgxs_library_correction/results_true.dat +++ b/tests/regression_tests/mgxs_library_correction/results_true.dat @@ -1,60 +1,60 @@ material group in group out nuclide mean std. dev. -3 1 1 1 total 0.353219 0.011858 -2 1 1 2 total 0.000876 0.000554 +3 1 1 1 total 0.353477 0.019952 +2 1 1 2 total 0.000522 0.000349 1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 0.367572 0.024736 +0 1 2 2 total 0.414134 0.029955 material group in group out nuclide mean std. dev. -3 1 1 1 total 0.353488 0.011854 -2 1 1 2 total 0.000876 0.000554 +3 1 1 1 total 0.353477 0.019952 +2 1 1 2 total 0.000522 0.000349 1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 0.367572 0.024736 +0 1 2 2 total 0.414134 0.029955 material group in group out nuclide mean std. dev. -3 1 1 1 total 0.350138 0.017141 -2 1 1 2 total 0.000869 0.000551 +3 1 1 1 total 0.356124 0.026110 +2 1 1 2 total 0.000526 0.000352 1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 0.378130 0.046663 +0 1 2 2 total 0.413622 0.044755 material group in group out nuclide mean std. dev. -3 1 1 1 total 0.350406 0.018910 -2 1 1 2 total 0.000869 0.000953 +3 1 1 1 total 0.356124 0.033873 +2 1 1 2 total 0.000526 0.000608 1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 0.378130 0.057580 +0 1 2 2 total 0.413622 0.054899 material group in group out nuclide mean std. dev. -3 2 1 1 total 0.272853 0.018051 -2 2 1 2 total 0.000486 0.000486 +3 2 1 1 total 0.261901 0.016456 +2 2 1 2 total 0.000000 0.000000 1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 0.287308 0.063393 +0 2 2 2 total 0.322376 0.044195 material group in group out nuclide mean std. dev. -3 2 1 1 total 0.272853 0.018051 -2 2 1 2 total 0.000486 0.000486 +3 2 1 1 total 0.261901 0.016456 +2 2 1 2 total 0.000000 0.000000 1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 0.287308 0.063393 +0 2 2 2 total 0.322376 0.044195 material group in group out nuclide mean std. dev. -3 2 1 1 total 0.273465 0.021487 -2 2 1 2 total 0.000487 0.000487 +3 2 1 1 total 0.266612 0.021169 +2 2 1 2 total 0.000000 0.000000 1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 0.294966 0.065882 +0 2 2 2 total 0.321966 0.054140 material group in group out nuclide mean std. dev. -3 2 1 1 total 0.273465 0.025834 -2 2 1 2 total 0.000487 0.000843 +3 2 1 1 total 0.266612 0.025384 +2 2 1 2 total 0.000000 0.000000 1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 0.294966 0.089321 +0 2 2 2 total 0.321966 0.068014 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.262301 0.012948 -2 3 1 2 total 0.029783 0.000974 -1 3 2 1 total 0.000000 0.000000 -0 3 2 2 total 1.418218 0.082199 +3 3 1 1 total 0.259426 0.012116 +2 3 1 2 total 0.031650 0.000613 +1 3 2 1 total 0.000474 0.000475 +0 3 2 2 total 1.416407 0.162435 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.262301 0.012948 -2 3 1 2 total 0.029783 0.000974 -1 3 2 1 total 0.000000 0.000000 -0 3 2 2 total 1.418218 0.082199 +3 3 1 1 total 0.259426 0.012116 +2 3 1 2 total 0.031650 0.000613 +1 3 2 1 total 0.000474 0.000475 +0 3 2 2 total 1.416407 0.162435 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.261486 0.022535 -2 3 1 2 total 0.029746 0.001063 -1 3 2 1 total 0.000000 0.000000 -0 3 2 2 total 1.432958 0.149985 +3 3 1 1 total 0.266083 0.024503 +2 3 1 2 total 0.031973 0.001157 +1 3 2 1 total 0.000477 0.000480 +0 3 2 2 total 1.427932 0.269812 material group in group out nuclide mean std. dev. -3 3 1 1 total 0.261486 0.026061 -2 3 1 2 total 0.029746 0.001468 -1 3 2 1 total 0.000000 0.000000 -0 3 2 2 total 1.432958 0.178757 +3 3 1 1 total 0.266083 0.027910 +2 3 1 2 total 0.031973 0.001391 +1 3 2 1 total 0.000477 0.000827 +0 3 2 2 total 1.427932 0.328026 diff --git a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat index 602e04abe0..e3826cc18b 100644 --- a/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/inputs_true.dat @@ -1,17 +1,43 @@ - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 @@ -29,484 +55,490 @@ 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -10.71 -10.71 -1 10.71 10.71 1 - - - - - - - 1 - - - 0.0 20000000.0 - - - 0.0 20000000.0 - - - 1 - - - 3 - - - 1 2 3 4 5 6 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - nu-scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - nu-fission - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 - total - nu-fission - analog - - - 1 5 - total - nu-fission - analog - - - 1 2 - total - prompt-nu-fission - analog - - - 1 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 73 2 - total - delayed-nu-fission - tracklength - - - 1 73 2 - total - delayed-nu-fission - analog - - - 1 73 5 - total - delayed-nu-fission - analog - - - 1 2 - total - nu-fission - tracklength - - - 1 73 2 - total - delayed-nu-fission - tracklength - - - 1 73 - total - delayed-nu-fission - tracklength - - - 1 73 - total - decay-rate - tracklength - - - 1 2 - total - flux - analog - - - 1 73 2 5 - total - delayed-nu-fission - analog - - + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -10.71 -10.71 -1 10.71 10.71 1 + + + true + + + + + + 1 + + + 0.0 20000000.0 + + + 0.0 20000000.0 + + + 1 + + + 3 + + + 1 2 3 4 5 6 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + (n,3n) + tracklength + + + 1 2 + total + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + nu-scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + nu-fission + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 + total + nu-fission + analog + + + 1 5 + total + nu-fission + analog + + + 1 2 + total + prompt-nu-fission + analog + + + 1 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 75 2 + total + delayed-nu-fission + tracklength + + + 1 75 2 + total + delayed-nu-fission + analog + + + 1 75 5 + total + delayed-nu-fission + analog + + + 1 2 + total + nu-fission + tracklength + + + 1 75 2 + total + delayed-nu-fission + tracklength + + + 1 75 + total + delayed-nu-fission + tracklength + + + 1 75 + total + decay-rate + tracklength + + + 1 2 + total + flux + analog + + + 1 75 2 5 + total + delayed-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_distribcell/results_true.dat b/tests/regression_tests/mgxs_library_distribcell/results_true.dat index e6e4b99e42..127df75c1e 100644 --- a/tests/regression_tests/mgxs_library_distribcell/results_true.dat +++ b/tests/regression_tests/mgxs_library_distribcell/results_true.dat @@ -1,95 +1,97 @@ sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.450382 0.010238 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.459656 0.010039 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.413436 0.011349 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.416327 0.01121 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.413276 0.011357 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.06484 0.002514 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.416327 0.01121 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.028638 0.002713 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.070545 0.002486 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.036203 0.001449 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.070348 0.002485 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.089088 0.003536 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.029374 0.002719 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.041172 0.001562 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.101218 0.003812 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 7.011996e+06 280281.488034 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 7.972654e+06 302079.851251 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.38911 0.00831 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.385542 0.008566 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.386602 0.013718 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.394876 0.014019 sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.386439 0.013702 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.036946 0.004896 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.016245 0.003818 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005544 0.003119 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.394876 0.014019 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.043329 0.004988 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.027490 0.003974 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.016004 0.003232 sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.386602 0.013718 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.037106 0.004917 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.016398 0.003839 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005688 0.003138 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.000421 0.036026 - sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.083975 0.005759 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.394876 0.014019 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.043329 0.004988 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.027490 0.003974 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.016004 0.003232 sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.0 0.035985 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.385542 0.016305 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.036860 0.004958 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.016207 0.003827 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005531 0.003114 - sum(distribcell) group in group out legendre nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.385704 0.021424 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.036876 0.005135 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.016214 0.003873 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.005533 0.003122 - sum(distribcell) group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.090473 - sum(distribcell) group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.090571 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 4.896406e-07 2.047455e-08 - sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.088451 0.003512 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.0 0.036306 sum(distribcell) group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.082789 0.005683 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.097856 0.006191 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.0 0.036306 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.389110 0.016390 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.042696 0.005009 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.027088 0.003964 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.015770 0.003204 + sum(distribcell) group in group out legendre nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P0 total 0.389110 0.021638 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P1 total 0.042696 0.005244 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P2 total 0.027088 0.004084 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 P3 total 0.015770 0.003255 + sum(distribcell) group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.082469 + sum(distribcell) group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 1.0 0.082587 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 5.626624e-07 2.235532e-08 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.806252 0.022131 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.100506 0.003787 + sum(distribcell) group in group out nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.097658 0.006185 sum(distribcell) group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.806564 0.022166 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.800653 0.021558 + sum(distribcell) group in nuclide mean std. dev. +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.800653 0.021558 sum(distribcell) delayedgroup group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000020 8.047454e-07 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.000108 4.184372e-06 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.000106 4.014315e-06 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.000246 9.085237e-06 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.000111 3.832957e-06 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000046 1.601490e-06 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000023 8.667436e-07 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.000122 4.499059e-06 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.000119 4.311220e-06 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.000275 9.735290e-06 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.000122 4.078954e-06 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000051 1.705327e-06 sum(distribcell) delayedgroup group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 1.0 1.414214 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.0 0.000000 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.0 0.000000 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 1.0 1.414214 2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.0 0.000000 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 1.0 0.708218 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 1.0 1.414214 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.0 0.000000 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.0 0.000000 5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.0 0.000000 sum(distribcell) delayedgroup group in nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000227 0.000012 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.001216 0.000062 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.001187 0.000060 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.002764 0.000138 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.001241 0.000060 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000516 0.000025 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 total 0.000227 0.000011 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 total 0.001208 0.000059 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 total 0.001175 0.000056 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 total 0.002721 0.000129 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 total 0.001206 0.000055 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 total 0.000502 0.000023 sum(distribcell) delayedgroup nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.013356 0.000697 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 total 0.032593 0.001644 -2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 total 0.121097 0.005963 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 total 0.306056 0.014519 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 total 0.862463 0.037889 -5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 total 2.896867 0.127967 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 total 0.013353 0.000658 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 total 0.032616 0.001562 +2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 total 0.121048 0.005678 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 total 0.305568 0.013868 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 total 0.860675 0.036434 +5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 total 2.890786 0.122997 sum(distribcell) delayedgroup group in group out nuclide mean std. dev. -0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 1 total 0.000189 0.000189 -1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 1 total 0.000000 0.000000 +0 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 1 1 1 total 0.000000 0.000000 +1 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 2 1 1 total 0.000198 0.000198 2 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 3 1 1 total 0.000000 0.000000 -3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 1 total 0.000807 0.000405 -4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 1 total 0.000191 0.000191 +3 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 4 1 1 total 0.000000 0.000000 +4 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 5 1 1 total 0.000000 0.000000 5 ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...),) 6 1 1 total 0.000000 0.000000 diff --git a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat index 9661cd46be..4451d214c4 100644 --- a/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_hdf5/inputs_true.dat @@ -1,502 +1,534 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 2 2 - -100.0 -100.0 - 100.0 100.0 - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 1 - - - 3 - - - 0.0 20000000.0 - - - 1 - - - 1 2 3 4 5 6 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - nu-scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - nu-fission - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 52 - total - nu-fission - analog - - - 1 5 - total - nu-fission - analog - - - 1 52 - total - prompt-nu-fission - analog - - - 1 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - prompt-nu-fission - analog - - - 66 2 - total - current - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 52 - total - delayed-nu-fission - analog - - - 1 77 5 - total - delayed-nu-fission - analog - - - 1 2 - total - nu-fission - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 - total - delayed-nu-fission - tracklength - - - 1 77 - total - decay-rate - tracklength - - - 1 2 - total - flux - analog - - - 1 77 2 5 - total - delayed-nu-fission - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 2 2 + -100.0 -100.0 + 100.0 100.0 + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 1 + + + 3 + + + 0.0 20000000.0 + + + 1 + + + 1 2 3 4 5 6 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + (n,3n) + tracklength + + + 1 2 + total + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + nu-scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + nu-fission + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 54 + total + nu-fission + analog + + + 1 5 + total + nu-fission + analog + + + 1 54 + total + prompt-nu-fission + analog + + + 1 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + prompt-nu-fission + analog + + + 68 2 + total + current + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 54 + total + delayed-nu-fission + analog + + + 1 79 5 + total + delayed-nu-fission + analog + + + 1 2 + total + nu-fission + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 + total + delayed-nu-fission + tracklength + + + 1 79 + total + decay-rate + tracklength + + + 1 2 + total + flux + analog + + + 1 79 2 5 + total + delayed-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_hdf5/results_true.dat b/tests/regression_tests/mgxs_library_hdf5/results_true.dat index 6da73ec9bd..14d7371fe6 100644 --- a/tests/regression_tests/mgxs_library_hdf5/results_true.dat +++ b/tests/regression_tests/mgxs_library_hdf5/results_true.dat @@ -1,198 +1,201 @@ domain=1 type=total -[5.62093429e-01 1.47762208e+00] -[1.25813379e-02 1.33969236e-01] +[5.66580451e-01 1.44262943e+00] +[1.96770003e-02 1.46112369e-01] domain=1 type=transport -[3.20646457e-01 1.15112225e+00] -[1.43813685e-02 1.38168210e-01] +[3.14856281e-01 1.05346368e+00] +[2.15203447e-02 1.60562179e-01] domain=1 type=nu-transport -[3.20646457e-01 1.15112225e+00] -[1.43813685e-02 1.38168210e-01] +[3.14856281e-01 1.05346368e+00] +[2.15203447e-02 1.60562179e-01] domain=1 type=absorption -[9.18204614e-03 9.50890834e-02] -[1.02291781e-03 9.51211716e-03] +[8.63921263e-03 9.70718967e-02] +[7.09849180e-04 9.96697703e-03] +domain=1 type=reduced absorption +[8.63459183e-03 9.70718967e-02] +[7.09826160e-04 9.96697703e-03] domain=1 type=capture -[6.76780024e-03 4.04282690e-02] -[1.01848835e-03 8.89313901e-03] +[6.29732743e-03 4.01344183e-02] +[7.03181817e-04 9.43458504e-03] domain=1 type=fission -[2.41424590e-03 5.46608144e-02] -[4.85861462e-05 5.74352403e-03] +[2.34188519e-03 5.69374784e-02] +[1.04006997e-04 6.19904834e-03] domain=1 type=nu-fission -[6.13719950e-03 1.33192007e-01] -[1.12501202e-04 1.39952450e-02] +[5.93985124e-03 1.38739554e-01] +[2.57215008e-04 1.51052211e-02] domain=1 type=kappa-fission -[4.70267044e+05 1.05716969e+07] -[9.26434576e+03 1.11082859e+06] +[4.55876276e+05 1.10120160e+07] +[2.00450394e+04 1.19892945e+06] domain=1 type=scatter -[5.52911383e-01 1.38253299e+00] -[1.21249866e-02 1.25146938e-01] +[5.57941239e-01 1.34555753e+00] +[1.96103602e-02 1.38008873e-01] domain=1 type=nu-scatter -[5.53262132e-01 1.38285128e+00] -[1.67013167e-02 1.47879104e-01] +[5.53883536e-01 1.40126963e+00] +[1.89917740e-02 1.62647765e-01] domain=1 type=scatter matrix -[[[5.38262098e-01 2.41446972e-01 9.56713637e-02 1.27736375e-02] - [1.50000342e-02 3.45918070e-03 -2.20959984e-03 -2.07635400e-03]] +[[[5.35878034e-01 2.51724170e-01 1.01011269e-01 1.03439439e-02] + [1.80055019e-02 5.80562809e-03 -1.57470166e-03 -2.27320020e-03]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.38285128e+00 3.05915919e-01 2.70982432e-02 -1.60336325e-02]]] -[[[1.67044118e-02 6.96661310e-03 4.42653888e-03 5.36446548e-03] - [2.18467018e-03 6.35479378e-04 8.96740238e-04 4.72822150e-04]] + [1.40126963e+00 3.55339640e-01 7.06453615e-02 4.04065595e-02]]] +[[[1.86010787e-02 8.71440749e-03 3.01351835e-03 5.22968320e-03] + [1.36795874e-03 6.67372701e-04 3.26122899e-04 8.68935369e-04]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.47879104e-01 3.12865012e-02 2.09135204e-02 2.10640949e-02]]] + [1.62647765e-01 6.23420543e-02 9.61788834e-03 8.80966610e-03]]] domain=1 type=nu-scatter matrix -[[[5.38262098e-01 2.41446972e-01 9.56713637e-02 1.27736375e-02] - [1.50000342e-02 3.45918070e-03 -2.20959984e-03 -2.07635400e-03]] +[[[5.35878034e-01 2.51724170e-01 1.01011269e-01 1.03439439e-02] + [1.80055019e-02 5.80562809e-03 -1.57470166e-03 -2.27320020e-03]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.38285128e+00 3.05915919e-01 2.70982432e-02 -1.60336325e-02]]] -[[[1.67044118e-02 6.96661310e-03 4.42653888e-03 5.36446548e-03] - [2.18467018e-03 6.35479378e-04 8.96740238e-04 4.72822150e-04]] + [1.40126963e+00 3.55339640e-01 7.06453615e-02 4.04065595e-02]]] +[[[1.86010787e-02 8.71440749e-03 3.01351835e-03 5.22968320e-03] + [1.36795874e-03 6.67372701e-04 3.26122899e-04 8.68935369e-04]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.47879104e-01 3.12865012e-02 2.09135204e-02 2.10640949e-02]]] + [1.62647765e-01 6.23420543e-02 9.61788834e-03 8.80966610e-03]]] domain=1 type=multiplicity matrix [[1.00000000e+00 1.00000000e+00] [0.00000000e+00 1.00000000e+00]] -[[2.96568009e-02 2.03415491e-01] - [0.00000000e+00 1.02759493e-01]] +[[3.27047397e-02 1.01015254e-01] + [0.00000000e+00 1.16966513e-01]] domain=1 type=nu-fission matrix -[[4.80904641e-03 0.00000000e+00] - [1.46971102e-01 0.00000000e+00]] -[[7.56765200e-04 0.00000000e+00] - [1.60726822e-02 0.00000000e+00]] +[[7.11392182e-03 0.00000000e+00] + [1.52683850e-01 0.00000000e+00]] +[[1.05605314e-03 0.00000000e+00] + [2.58713491e-02 0.00000000e+00]] domain=1 type=scatter probability matrix -[[9.72888016e-01 2.71119843e-02] +[[9.67492260e-01 3.25077399e-02] [0.00000000e+00 1.00000000e+00]] -[[2.87160386e-02 3.94014457e-03] - [0.00000000e+00 1.02759493e-01]] +[[3.12124830e-02 2.43439942e-03] + [0.00000000e+00 1.16966513e-01]] domain=1 type=consistent scatter matrix -[[[5.37920858e-01 2.41293903e-01 9.56107113e-02 1.27655394e-02] - [1.49905247e-02 3.45698770e-03 -2.20819902e-03 -2.07503766e-03]] +[[[5.39803830e-01 2.53568280e-01 1.01751269e-01 1.04197228e-02] + [1.81374087e-02 5.84815962e-03 -1.58623779e-03 -2.28985347e-03]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.38253299e+00 3.05845506e-01 2.70920060e-02 -1.60299421e-02]]] -[[[1.97798945e-02 8.43331598e-03 4.80888930e-03 5.36697350e-03] - [2.20321323e-03 6.38726086e-04 8.97229401e-04 4.74291433e-04]] + [1.34555753e+00 3.41211940e-01 6.78366221e-02 3.88000634e-02]]] +[[[2.57534994e-02 1.20804286e-02 4.50621873e-03 5.27902298e-03] + [1.50041314e-03 6.98980912e-04 3.32589287e-04 8.78503928e-04]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.89328242e-01 4.07787000e-02 2.10367544e-02 2.11038436e-02]]] + [2.09324012e-01 6.95175402e-02 1.16044952e-02 9.36549883e-03]]] domain=1 type=consistent nu-scatter matrix -[[[5.37920858e-01 2.41293903e-01 9.56107113e-02 1.27655394e-02] - [1.49905247e-02 3.45698770e-03 -2.20819902e-03 -2.07503766e-03]] +[[[5.39803830e-01 2.53568280e-01 1.01751269e-01 1.04197228e-02] + [1.81374087e-02 5.84815962e-03 -1.58623779e-03 -2.28985347e-03]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [1.38253299e+00 3.05845506e-01 2.70920060e-02 -1.60299421e-02]]] -[[[2.54114701e-02 1.10602545e-02 5.58260879e-03 5.38030958e-03] - [3.76196879e-03 9.49983195e-04 1.00338675e-03 6.34914473e-04]] + [1.34555753e+00 3.41211940e-01 6.78366221e-02 3.88000634e-02]]] +[[[3.12235732e-02 1.46529414e-02 5.60177820e-03 5.29001047e-03] + [2.36812824e-03 9.15185125e-04 3.69175618e-04 9.08445666e-04]] [[0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [2.36703634e-01 5.14845104e-02 2.12201666e-02 2.11680319e-02]]] + [2.61890501e-01 8.01593794e-02 1.40578233e-02 1.04071518e-02]]] domain=1 type=chi [1.00000000e+00 0.00000000e+00] -[8.07756455e-02 0.00000000e+00] +[1.42429813e-01 0.00000000e+00] domain=1 type=chi-prompt [1.00000000e+00 0.00000000e+00] -[8.07424611e-02 0.00000000e+00] +[1.43958515e-01 0.00000000e+00] domain=1 type=inverse-velocity -[5.93309775e-08 2.99151502e-06] -[3.31163250e-09 2.75469881e-07] +[6.05275939e-08 2.92408191e-06] +[4.98534008e-09 2.95326306e-07] domain=1 type=prompt-nu-fission -[6.07803967e-03 1.32325631e-01] -[1.12229103e-04 1.39042100e-02] +[5.88433433e-03 1.37837093e-01] +[2.56012352e-04 1.50069660e-02] domain=1 type=prompt-nu-fission matrix -[[4.80904641e-03 0.00000000e+00] - [1.44441596e-01 0.00000000e+00]] -[[7.56765200e-04 0.00000000e+00] - [1.55945206e-02 0.00000000e+00]] +[[7.11392182e-03 0.00000000e+00] + [1.51190909e-01 0.00000000e+00]] +[[1.05605314e-03 0.00000000e+00] + [2.57973847e-02 0.00000000e+00]] domain=1 type=current -[[[0.00000000e+00 0.00000000e+00 3.87200000e+00 3.85800000e+00 - 0.00000000e+00 0.00000000e+00 3.79800000e+00 3.79400000e+00] - [0.00000000e+00 0.00000000e+00 6.14000000e-01 6.62000000e-01 - 0.00000000e+00 0.00000000e+00 6.38000000e-01 6.12000000e-01]] +[[[0.00000000e+00 0.00000000e+00 3.71800000e+00 3.58600000e+00 + 0.00000000e+00 0.00000000e+00 3.62200000e+00 3.71800000e+00] + [0.00000000e+00 0.00000000e+00 6.60000000e-01 6.58000000e-01 + 0.00000000e+00 0.00000000e+00 6.62000000e-01 6.70000000e-01]] - [[3.85800000e+00 3.87200000e+00 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 3.87000000e+00 3.85400000e+00] - [6.62000000e-01 6.14000000e-01 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 6.56000000e-01 6.94000000e-01]] + [[3.58600000e+00 3.71800000e+00 0.00000000e+00 0.00000000e+00 + 0.00000000e+00 0.00000000e+00 3.71200000e+00 3.60600000e+00] + [6.58000000e-01 6.60000000e-01 0.00000000e+00 0.00000000e+00 + 0.00000000e+00 0.00000000e+00 7.04000000e-01 6.74000000e-01]] - [[0.00000000e+00 0.00000000e+00 3.77200000e+00 3.82200000e+00 - 3.79400000e+00 3.79800000e+00 0.00000000e+00 0.00000000e+00] - [0.00000000e+00 0.00000000e+00 7.08000000e-01 6.38000000e-01 - 6.12000000e-01 6.38000000e-01 0.00000000e+00 0.00000000e+00]] + [[0.00000000e+00 0.00000000e+00 3.48600000e+00 3.60600000e+00 + 3.71800000e+00 3.62200000e+00 0.00000000e+00 0.00000000e+00] + [0.00000000e+00 0.00000000e+00 6.66000000e-01 6.74000000e-01 + 6.70000000e-01 6.62000000e-01 0.00000000e+00 0.00000000e+00]] - [[3.82200000e+00 3.77200000e+00 0.00000000e+00 0.00000000e+00 - 3.85400000e+00 3.87000000e+00 0.00000000e+00 0.00000000e+00] - [6.38000000e-01 7.08000000e-01 0.00000000e+00 0.00000000e+00 - 6.94000000e-01 6.56000000e-01 0.00000000e+00 0.00000000e+00]]] -[[[0.00000000e+00 0.00000000e+00 1.14952164e-01 1.04661359e-01 - 0.00000000e+00 0.00000000e+00 5.36097006e-02 1.14873844e-01] - [0.00000000e+00 0.00000000e+00 5.81893461e-02 6.31981012e-02 - 0.00000000e+00 0.00000000e+00 3.61109402e-02 1.98494332e-02]] + [[3.60600000e+00 3.48600000e+00 0.00000000e+00 0.00000000e+00 + 3.60600000e+00 3.71200000e+00 0.00000000e+00 0.00000000e+00] + [6.74000000e-01 6.66000000e-01 0.00000000e+00 0.00000000e+00 + 6.74000000e-01 7.04000000e-01 0.00000000e+00 0.00000000e+00]]] +[[[0.00000000e+00 0.00000000e+00 9.96192752e-02 8.73269718e-02 + 0.00000000e+00 0.00000000e+00 1.22531629e-01 1.08369737e-01] + [0.00000000e+00 0.00000000e+00 3.96232255e-02 4.06693988e-02 + 0.00000000e+00 0.00000000e+00 4.05462699e-02 4.27784993e-02]] - [[1.04661359e-01 1.14952164e-01 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 1.52643375e-01 1.35003704e-01] - [6.31981012e-02 5.81893461e-02 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 6.66783323e-02 7.95361553e-02]] + [[8.73269718e-02 9.96192752e-02 0.00000000e+00 0.00000000e+00 + 0.00000000e+00 0.00000000e+00 7.09506871e-02 5.27825729e-02] + [4.06693988e-02 3.96232255e-02 0.00000000e+00 0.00000000e+00 + 0.00000000e+00 0.00000000e+00 6.11228272e-02 5.88727441e-02]] - [[0.00000000e+00 0.00000000e+00 1.58946532e-01 1.20971071e-01 - 1.14873844e-01 5.36097006e-02 0.00000000e+00 0.00000000e+00] - [0.00000000e+00 0.00000000e+00 3.48425028e-02 2.08326667e-02 - 1.98494332e-02 3.61109402e-02 0.00000000e+00 0.00000000e+00]] + [[0.00000000e+00 0.00000000e+00 1.00279609e-01 1.31209756e-01 + 1.08369737e-01 1.22531629e-01 0.00000000e+00 0.00000000e+00] + [0.00000000e+00 0.00000000e+00 6.05475020e-02 4.87442304e-02 + 4.27784993e-02 4.05462699e-02 0.00000000e+00 0.00000000e+00]] - [[1.20971071e-01 1.58946532e-01 0.00000000e+00 0.00000000e+00 - 1.35003704e-01 1.52643375e-01 0.00000000e+00 0.00000000e+00] - [2.08326667e-02 3.48425028e-02 0.00000000e+00 0.00000000e+00 - 7.95361553e-02 6.66783323e-02 0.00000000e+00 0.00000000e+00]]] + [[1.31209756e-01 1.00279609e-01 0.00000000e+00 0.00000000e+00 + 5.27825729e-02 7.09506871e-02 0.00000000e+00 0.00000000e+00] + [4.87442304e-02 6.05475020e-02 0.00000000e+00 0.00000000e+00 + 5.88727441e-02 6.11228272e-02 0.00000000e+00 0.00000000e+00]]] domain=1 type=diffusion-coefficient -[1.03956656e+00 2.89572488e-01] -[4.66257756e-02 3.47571359e-02] +[1.05868408e+00 3.16416542e-01] +[7.23607812e-02 4.82261806e-02] domain=1 type=nu-diffusion-coefficient -[1.03956656e+00 2.89572488e-01] -[4.66257756e-02 3.47571359e-02] +[1.05868408e+00 3.16416542e-01] +[7.23607812e-02 4.82261806e-02] domain=1 type=delayed-nu-fission -[[1.37840363e-06 3.03296462e-05] - [8.45663047e-06 1.56552364e-04] - [8.84043266e-06 1.49458552e-04] - [2.28234463e-05 3.35098665e-04] - [1.25147617e-05 1.37385990e-04] - [5.13410858e-06 5.75504990e-05]] -[[2.55826273e-08 3.18690909e-06] - [1.59619691e-07 1.64498503e-05] - [2.08725713e-07 1.57044628e-05] - [7.35913253e-07 3.52107280e-05] - [6.08970534e-07 1.44359288e-05] - [2.44376821e-07 6.04715887e-06]] +[[1.33370452e-06 3.15928985e-05] + [8.06563789e-06 1.63072885e-04] + [8.37555675e-06 1.55683610e-04] + [2.14225848e-05 3.49055766e-04] + [1.15633419e-05 1.43108212e-04] + [4.74849054e-06 5.99475175e-05]] +[[5.58190638e-08 3.43966581e-06] + [2.80924725e-07 1.77545031e-05] + [2.81900859e-07 1.69499982e-05] + [7.44207592e-07 3.80033227e-05] + [4.95821820e-07 1.55808550e-05] + [2.00315983e-07 6.52676436e-06]] domain=1 type=chi-delayed [[0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00] [1.00000000e+00 0.00000000e+00] - [1.00000000e+00 0.00000000e+00] + [0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00] [1.41421356e+00 0.00000000e+00] - [1.41421356e+00 0.00000000e+00] + [0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00]] domain=1 type=beta -[[2.24598146e-04 2.27713712e-04] - [1.37792986e-03 1.17538858e-03] - [1.44046689e-03 1.12212854e-03] - [3.71886987e-03 2.51590672e-03] - [2.03916489e-03 1.03148825e-03] - [8.36555595e-04 4.32086733e-04]] -[[2.89454288e-06 2.65002712e-05] - [1.83788053e-05 1.36786298e-04] - [2.80459046e-05 1.30588139e-04] - [1.09140523e-04 2.92789605e-04] - [9.54156078e-05 1.20039834e-04] - [3.82194001e-05 5.02842563e-05]] +[[2.24535003e-04 2.27713710e-04] + [1.35788550e-03 1.17538857e-03] + [1.41006170e-03 1.12212852e-03] + [3.60658609e-03 2.51590665e-03] + [1.94673931e-03 1.03148820e-03] + [7.99429201e-04 4.32086711e-04]] +[[1.15072629e-05 2.77827007e-05] + [6.20475481e-05 1.43405806e-04] + [6.31808205e-05 1.36907698e-04] + [1.64551758e-04 3.06958585e-04] + [1.01406915e-04 1.25848924e-04] + [4.11875799e-05 5.27176635e-05]] domain=1 type=decay-rate -[1.33568264e-02 3.25888950e-02 1.21105369e-01 3.06137651e-01 - 8.62756682e-01 2.89786766e+00] -[1.25011470e-03 2.88396059e-03 1.03002481e-02 2.44903689e-02 - 6.13119717e-02 2.07581680e-01] +[1.33525569e-02 3.26187089e-02 1.21041926e-01 3.05503129e-01 + 8.60433350e-01 2.88996258e+00] +[1.34484408e-03 3.11955840e-03 1.11418058e-02 2.64184722e-02 + 6.46651447e-02 2.19526509e-01] domain=1 type=delayed-nu-fission matrix [[[0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00]] @@ -204,10 +207,10 @@ domain=1 type=delayed-nu-fission matrix [0.00000000e+00 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] - [1.19201367e-03 0.00000000e+00]] + [1.49294023e-03 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] - [1.33749216e-03 0.00000000e+00]] + [0.00000000e+00 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00]]] @@ -221,10 +224,10 @@ domain=1 type=delayed-nu-fission matrix [0.00000000e+00 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] - [1.19567703e-03 0.00000000e+00]] + [1.49788268e-03 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] - [1.34160260e-03 0.00000000e+00]] + [0.00000000e+00 0.00000000e+00]] [[0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00]]] diff --git a/tests/regression_tests/mgxs_library_hdf5/test.py b/tests/regression_tests/mgxs_library_hdf5/test.py index 2f3c9d149d..06625c25f9 100644 --- a/tests/regression_tests/mgxs_library_hdf5/test.py +++ b/tests/regression_tests/mgxs_library_hdf5/test.py @@ -54,6 +54,11 @@ class MGXSTestHarness(PyAPITestHarness): # Export the MGXS Library to an HDF5 file self.mgxs_lib.build_hdf5_store(directory='.') + # Test export of the MGXS Library to an Excel spreadsheet + for mgxs in self.mgxs_lib.all_mgxs.values(): + for xs in mgxs.values(): + xs.export_xs_data('mgxs', xs_type='macro', format='excel') + # Open the MGXS HDF5 file with h5py.File('mgxs.h5', 'r') as f: @@ -76,9 +81,8 @@ class MGXSTestHarness(PyAPITestHarness): def _cleanup(self): super()._cleanup() - f = 'mgxs.h5' - if os.path.exists(f): - os.remove(f) + files = ['mgxs.h5', 'mgxs.xlsx'] + (os.remove(f) for f in files if os.path.exists(f)) def test_mgxs_library_hdf5(): diff --git a/tests/regression_tests/mgxs_library_histogram/inputs_true.dat b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat index d1bd197a7c..94c6564180 100644 --- a/tests/regression_tests/mgxs_library_histogram/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_histogram/inputs_true.dat @@ -1,269 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - -1.0 -0.8181818181818181 -0.6363636363636364 -0.4545454545454546 -0.2727272727272727 -0.09090909090909083 0.09090909090909083 0.2727272727272727 0.4545454545454546 0.6363636363636365 0.8181818181818183 1.0 - - - 2 - - - 3 - - - 1 2 - total - flux - analog - - - 1 2 3 4 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 3 4 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 3 4 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 3 4 - total - scatter - analog - - - 1 2 3 - total - nu-scatter - analog - - - 17 2 - total - flux - analog - - - 17 2 3 4 - total - scatter - analog - - - 17 2 - total - flux - analog - - - 17 2 3 4 - total - nu-scatter - analog - - - 17 2 - total - flux - tracklength - - - 17 2 - total - scatter - tracklength - - - 17 2 3 4 - total - scatter - analog - - - 17 2 - total - flux - tracklength - - - 17 2 - total - scatter - tracklength - - - 17 2 3 4 - total - scatter - analog - - - 17 2 3 - total - nu-scatter - analog - - - 33 2 - total - flux - analog - - - 33 2 3 4 - total - scatter - analog - - - 33 2 - total - flux - analog - - - 33 2 3 4 - total - nu-scatter - analog - - - 33 2 - total - flux - tracklength - - - 33 2 - total - scatter - tracklength - - - 33 2 3 4 - total - scatter - analog - - - 33 2 - total - flux - tracklength - - - 33 2 - total - scatter - tracklength - - - 33 2 3 4 - total - scatter - analog - - - 33 2 3 - total - nu-scatter - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + -1.0 -0.8181818181818181 -0.6363636363636364 -0.4545454545454546 -0.2727272727272727 -0.09090909090909083 0.09090909090909083 0.2727272727272727 0.4545454545454546 0.6363636363636365 0.8181818181818183 1.0 + + + 2 + + + 3 + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 3 4 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 4 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 3 4 + total + scatter + analog + + + 1 2 3 + total + nu-scatter + analog + + + 17 2 + total + flux + analog + + + 17 2 3 4 + total + scatter + analog + + + 17 2 + total + flux + analog + + + 17 2 3 4 + total + nu-scatter + analog + + + 17 2 + total + flux + tracklength + + + 17 2 + total + scatter + tracklength + + + 17 2 3 4 + total + scatter + analog + + + 17 2 + total + flux + tracklength + + + 17 2 + total + scatter + tracklength + + + 17 2 3 4 + total + scatter + analog + + + 17 2 3 + total + nu-scatter + analog + + + 33 2 + total + flux + analog + + + 33 2 3 4 + total + scatter + analog + + + 33 2 + total + flux + analog + + + 33 2 3 4 + total + nu-scatter + analog + + + 33 2 + total + flux + tracklength + + + 33 2 + total + scatter + tracklength + + + 33 2 3 4 + total + scatter + analog + + + 33 2 + total + flux + tracklength + + + 33 2 + total + scatter + tracklength + + + 33 2 3 4 + total + scatter + analog + + + 33 2 3 + total + nu-scatter + analog + + + diff --git a/tests/regression_tests/mgxs_library_histogram/results_true.dat b/tests/regression_tests/mgxs_library_histogram/results_true.dat index 74ae456e5c..f927d057ee 100644 --- a/tests/regression_tests/mgxs_library_histogram/results_true.dat +++ b/tests/regression_tests/mgxs_library_histogram/results_true.dat @@ -1,115 +1,25 @@ material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.028385 0.002919 -34 1 1 1 2 total 0.031890 0.001548 -35 1 1 1 3 total 0.027684 0.002596 -36 1 1 1 4 total 0.035920 0.003337 -37 1 1 1 5 total 0.034343 0.001897 -38 1 1 1 6 total 0.032591 0.002289 -39 1 1 1 7 total 0.033467 0.002209 -40 1 1 1 8 total 0.031714 0.002398 -41 1 1 1 9 total 0.033467 0.003343 -42 1 1 1 10 total 0.041001 0.002362 -43 1 1 1 11 total 0.060801 0.003580 -22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000350 0.000351 -24 1 1 2 3 total 0.000175 0.000175 -25 1 1 2 4 total 0.000000 0.000000 -26 1 1 2 5 total 0.000175 0.000175 -27 1 1 2 6 total 0.000000 0.000000 -28 1 1 2 7 total 0.000000 0.000000 -29 1 1 2 8 total 0.000000 0.000000 -30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000175 0.000175 -32 1 1 2 11 total 0.000000 0.000000 -11 1 2 1 1 total 0.000000 0.000000 -12 1 2 1 2 total 0.000000 0.000000 -13 1 2 1 3 total 0.000000 0.000000 -14 1 2 1 4 total 0.000000 0.000000 -15 1 2 1 5 total 0.000000 0.000000 -16 1 2 1 6 total 0.000000 0.000000 -17 1 2 1 7 total 0.000000 0.000000 -18 1 2 1 8 total 0.000000 0.000000 -19 1 2 1 9 total 0.000000 0.000000 -20 1 2 1 10 total 0.000000 0.000000 -21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.023599 0.005270 -1 1 2 2 2 total 0.036471 0.008401 -2 1 2 2 3 total 0.034325 0.003271 -3 1 2 2 4 total 0.030035 0.003674 -4 1 2 2 5 total 0.039688 0.001478 -5 1 2 2 6 total 0.033253 0.005499 -6 1 2 2 7 total 0.039688 0.004725 -7 1 2 2 8 total 0.031107 0.006458 -8 1 2 2 9 total 0.031107 0.005996 -9 1 2 2 10 total 0.052560 0.005543 -10 1 2 2 11 total 0.033253 0.002688 - material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.028385 0.002919 -34 1 1 1 2 total 0.031890 0.001548 -35 1 1 1 3 total 0.027860 0.002738 -36 1 1 1 4 total 0.035920 0.003337 -37 1 1 1 5 total 0.034343 0.001897 -38 1 1 1 6 total 0.032591 0.002289 -39 1 1 1 7 total 0.033467 0.002209 -40 1 1 1 8 total 0.031714 0.002398 -41 1 1 1 9 total 0.033467 0.003343 -42 1 1 1 10 total 0.041001 0.002362 -43 1 1 1 11 total 0.060801 0.003580 -22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000350 0.000351 -24 1 1 2 3 total 0.000175 0.000175 -25 1 1 2 4 total 0.000000 0.000000 -26 1 1 2 5 total 0.000175 0.000175 -27 1 1 2 6 total 0.000000 0.000000 -28 1 1 2 7 total 0.000000 0.000000 -29 1 1 2 8 total 0.000000 0.000000 -30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000175 0.000175 -32 1 1 2 11 total 0.000000 0.000000 -11 1 2 1 1 total 0.000000 0.000000 -12 1 2 1 2 total 0.000000 0.000000 -13 1 2 1 3 total 0.000000 0.000000 -14 1 2 1 4 total 0.000000 0.000000 -15 1 2 1 5 total 0.000000 0.000000 -16 1 2 1 6 total 0.000000 0.000000 -17 1 2 1 7 total 0.000000 0.000000 -18 1 2 1 8 total 0.000000 0.000000 -19 1 2 1 9 total 0.000000 0.000000 -20 1 2 1 10 total 0.000000 0.000000 -21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.023599 0.005270 -1 1 2 2 2 total 0.036471 0.008401 -2 1 2 2 3 total 0.034325 0.003271 -3 1 2 2 4 total 0.030035 0.003674 -4 1 2 2 5 total 0.039688 0.001478 -5 1 2 2 6 total 0.033253 0.005499 -6 1 2 2 7 total 0.039688 0.004725 -7 1 2 2 8 total 0.031107 0.006458 -8 1 2 2 9 total 0.031107 0.005996 -9 1 2 2 10 total 0.052560 0.005543 -10 1 2 2 11 total 0.033253 0.002688 - material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.028162 0.003056 -34 1 1 1 2 total 0.031639 0.001886 -35 1 1 1 3 total 0.027466 0.002746 -36 1 1 1 4 total 0.035637 0.003533 -37 1 1 1 5 total 0.034072 0.002221 -38 1 1 1 6 total 0.032334 0.002532 -39 1 1 1 7 total 0.033203 0.002475 -40 1 1 1 8 total 0.031465 0.002617 -41 1 1 1 9 total 0.033203 0.003510 -42 1 1 1 10 total 0.040678 0.002734 -43 1 1 1 11 total 0.060322 0.004120 -22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000348 0.000348 +33 1 1 1 1 total 0.029945 0.003043 +34 1 1 1 2 total 0.028378 0.003793 +35 1 1 1 3 total 0.033079 0.002866 +36 1 1 1 4 total 0.030119 0.002259 +37 1 1 1 5 total 0.033601 0.003739 +38 1 1 1 6 total 0.035516 0.001929 +39 1 1 1 7 total 0.032382 0.001744 +40 1 1 1 8 total 0.031860 0.002565 +41 1 1 1 9 total 0.038302 0.004757 +42 1 1 1 10 total 0.041784 0.003047 +43 1 1 1 11 total 0.057453 0.003686 +22 1 1 2 1 total 0.000174 0.000174 +23 1 1 2 2 total 0.000000 0.000000 24 1 1 2 3 total 0.000174 0.000174 -25 1 1 2 4 total 0.000000 0.000000 -26 1 1 2 5 total 0.000174 0.000174 +25 1 1 2 4 total 0.000174 0.000174 +26 1 1 2 5 total 0.000000 0.000000 27 1 1 2 6 total 0.000000 0.000000 28 1 1 2 7 total 0.000000 0.000000 29 1 1 2 8 total 0.000000 0.000000 30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000174 0.000174 +31 1 1 2 10 total 0.000000 0.000000 32 1 1 2 11 total 0.000000 0.000000 11 1 2 1 1 total 0.000000 0.000000 12 1 2 1 2 total 0.000000 0.000000 @@ -122,39 +32,39 @@ 19 1 2 1 9 total 0.000000 0.000000 20 1 2 1 10 total 0.000000 0.000000 21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.024246 0.005837 -1 1 2 2 2 total 0.037470 0.009265 -2 1 2 2 3 total 0.035266 0.004620 -3 1 2 2 4 total 0.030858 0.004684 -4 1 2 2 5 total 0.040777 0.003968 -5 1 2 2 6 total 0.034164 0.006431 -6 1 2 2 7 total 0.040777 0.006083 -7 1 2 2 8 total 0.031960 0.007230 -8 1 2 2 9 total 0.031960 0.006797 -9 1 2 2 10 total 0.054002 0.007483 -10 1 2 2 11 total 0.034164 0.004130 +0 1 2 2 1 total 0.037212 0.004892 +1 1 2 2 2 total 0.039224 0.003682 +2 1 2 2 3 total 0.044253 0.005869 +3 1 2 2 4 total 0.043247 0.004970 +4 1 2 2 5 total 0.025144 0.006632 +5 1 2 2 6 total 0.037212 0.009199 +6 1 2 2 7 total 0.035201 0.008964 +7 1 2 2 8 total 0.041235 0.009733 +8 1 2 2 9 total 0.032184 0.002384 +9 1 2 2 10 total 0.026149 0.006714 +10 1 2 2 11 total 0.035201 0.008073 material group in group out mu bin nuclide mean std. dev. -33 1 1 1 1 total 0.028174 0.003144 -34 1 1 1 2 total 0.031653 0.002058 -35 1 1 1 3 total 0.027479 0.002838 -36 1 1 1 4 total 0.035653 0.003654 -37 1 1 1 5 total 0.034088 0.002392 -38 1 1 1 6 total 0.032348 0.002668 -39 1 1 1 7 total 0.033218 0.002621 -40 1 1 1 8 total 0.031479 0.002742 -41 1 1 1 9 total 0.033218 0.003616 -42 1 1 1 10 total 0.040696 0.002931 -43 1 1 1 11 total 0.060349 0.004409 -22 1 1 2 1 total 0.000000 0.000000 -23 1 1 2 2 total 0.000348 0.000451 -24 1 1 2 3 total 0.000174 0.000225 -25 1 1 2 4 total 0.000000 0.000000 -26 1 1 2 5 total 0.000174 0.000225 +33 1 1 1 1 total 0.029945 0.003043 +34 1 1 1 2 total 0.028378 0.003793 +35 1 1 1 3 total 0.033079 0.002866 +36 1 1 1 4 total 0.030119 0.002259 +37 1 1 1 5 total 0.033601 0.003739 +38 1 1 1 6 total 0.035516 0.001929 +39 1 1 1 7 total 0.032382 0.001744 +40 1 1 1 8 total 0.031860 0.002565 +41 1 1 1 9 total 0.038302 0.004757 +42 1 1 1 10 total 0.041784 0.003047 +43 1 1 1 11 total 0.057453 0.003686 +22 1 1 2 1 total 0.000174 0.000174 +23 1 1 2 2 total 0.000000 0.000000 +24 1 1 2 3 total 0.000174 0.000174 +25 1 1 2 4 total 0.000174 0.000174 +26 1 1 2 5 total 0.000000 0.000000 27 1 1 2 6 total 0.000000 0.000000 28 1 1 2 7 total 0.000000 0.000000 29 1 1 2 8 total 0.000000 0.000000 30 1 1 2 9 total 0.000000 0.000000 -31 1 1 2 10 total 0.000174 0.000225 +31 1 1 2 10 total 0.000000 0.000000 32 1 1 2 11 total 0.000000 0.000000 11 1 2 1 1 total 0.000000 0.000000 12 1 2 1 2 total 0.000000 0.000000 @@ -167,29 +77,119 @@ 19 1 2 1 9 total 0.000000 0.000000 20 1 2 1 10 total 0.000000 0.000000 21 1 2 1 11 total 0.000000 0.000000 -0 1 2 2 1 total 0.024246 0.006112 -1 1 2 2 2 total 0.037470 0.009679 -2 1 2 2 3 total 0.035266 0.005319 -3 1 2 2 4 total 0.030858 0.005221 -4 1 2 2 5 total 0.040777 0.005003 -5 1 2 2 6 total 0.034164 0.006919 -6 1 2 2 7 total 0.040777 0.006803 -7 1 2 2 8 total 0.031960 0.007614 -8 1 2 2 9 total 0.031960 0.007205 -9 1 2 2 10 total 0.054002 0.008502 -10 1 2 2 11 total 0.034164 0.004856 +0 1 2 2 1 total 0.037212 0.004892 +1 1 2 2 2 total 0.039224 0.003682 +2 1 2 2 3 total 0.044253 0.005869 +3 1 2 2 4 total 0.043247 0.004970 +4 1 2 2 5 total 0.025144 0.006632 +5 1 2 2 6 total 0.037212 0.009199 +6 1 2 2 7 total 0.035201 0.008964 +7 1 2 2 8 total 0.041235 0.009733 +8 1 2 2 9 total 0.032184 0.002384 +9 1 2 2 10 total 0.026149 0.006714 +10 1 2 2 11 total 0.035201 0.008073 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.025262 0.003309 -34 2 1 1 2 total 0.023805 0.004083 -35 2 1 1 3 total 0.027205 0.001631 -36 2 1 1 4 total 0.021376 0.003175 -37 2 1 1 5 total 0.017489 0.002412 -38 2 1 1 6 total 0.024291 0.002862 -39 2 1 1 7 total 0.029634 0.005250 -40 2 1 1 8 total 0.025262 0.001228 -41 2 1 1 9 total 0.028663 0.003403 -42 2 1 1 10 total 0.034493 0.004425 -43 2 1 1 11 total 0.052467 0.006227 +33 1 1 1 1 total 0.030147 0.003165 +34 1 1 1 2 total 0.028570 0.003893 +35 1 1 1 3 total 0.033302 0.003016 +36 1 1 1 4 total 0.030322 0.002411 +37 1 1 1 5 total 0.033828 0.003868 +38 1 1 1 6 total 0.035756 0.002159 +39 1 1 1 7 total 0.032601 0.001955 +40 1 1 1 8 total 0.032075 0.002718 +41 1 1 1 9 total 0.038560 0.004896 +42 1 1 1 10 total 0.042066 0.003262 +43 1 1 1 11 total 0.057840 0.004013 +22 1 1 2 1 total 0.000175 0.000175 +23 1 1 2 2 total 0.000000 0.000000 +24 1 1 2 3 total 0.000175 0.000175 +25 1 1 2 4 total 0.000175 0.000175 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000000 0.000000 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000000 0.000000 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000000 0.000000 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.037164 0.005797 +1 1 2 2 2 total 0.039173 0.004933 +2 1 2 2 3 total 0.044195 0.006937 +3 1 2 2 4 total 0.043191 0.006147 +4 1 2 2 5 total 0.025111 0.006951 +5 1 2 2 6 total 0.037164 0.009702 +6 1 2 2 7 total 0.035155 0.009426 +7 1 2 2 8 total 0.041182 0.010317 +8 1 2 2 9 total 0.032142 0.003598 +9 1 2 2 10 total 0.026115 0.007055 +10 1 2 2 11 total 0.035155 0.008586 + material group in group out mu bin nuclide mean std. dev. +33 1 1 1 1 total 0.030147 0.003454 +34 1 1 1 2 total 0.028570 0.004107 +35 1 1 1 3 total 0.033302 0.003381 +36 1 1 1 4 total 0.030322 0.002783 +37 1 1 1 5 total 0.033828 0.004168 +38 1 1 1 6 total 0.035756 0.002711 +39 1 1 1 7 total 0.032601 0.002461 +40 1 1 1 8 total 0.032075 0.003090 +41 1 1 1 9 total 0.038560 0.005205 +42 1 1 1 10 total 0.042066 0.003790 +43 1 1 1 11 total 0.057840 0.004811 +22 1 1 2 1 total 0.000175 0.000234 +23 1 1 2 2 total 0.000000 0.000000 +24 1 1 2 3 total 0.000175 0.000234 +25 1 1 2 4 total 0.000175 0.000234 +26 1 1 2 5 total 0.000000 0.000000 +27 1 1 2 6 total 0.000000 0.000000 +28 1 1 2 7 total 0.000000 0.000000 +29 1 1 2 8 total 0.000000 0.000000 +30 1 1 2 9 total 0.000000 0.000000 +31 1 1 2 10 total 0.000000 0.000000 +32 1 1 2 11 total 0.000000 0.000000 +11 1 2 1 1 total 0.000000 0.000000 +12 1 2 1 2 total 0.000000 0.000000 +13 1 2 1 3 total 0.000000 0.000000 +14 1 2 1 4 total 0.000000 0.000000 +15 1 2 1 5 total 0.000000 0.000000 +16 1 2 1 6 total 0.000000 0.000000 +17 1 2 1 7 total 0.000000 0.000000 +18 1 2 1 8 total 0.000000 0.000000 +19 1 2 1 9 total 0.000000 0.000000 +20 1 2 1 10 total 0.000000 0.000000 +21 1 2 1 11 total 0.000000 0.000000 +0 1 2 2 1 total 0.037164 0.006511 +1 1 2 2 2 total 0.039173 0.005840 +2 1 2 2 3 total 0.044195 0.007782 +3 1 2 2 4 total 0.043191 0.007047 +4 1 2 2 5 total 0.025111 0.007234 +5 1 2 2 6 total 0.037164 0.010146 +6 1 2 2 7 total 0.035155 0.009835 +7 1 2 2 8 total 0.041182 0.010828 +8 1 2 2 9 total 0.032142 0.004419 +9 1 2 2 10 total 0.026115 0.007356 +10 1 2 2 11 total 0.035155 0.009032 + material group in group out mu bin nuclide mean std. dev. +33 2 1 1 1 total 0.025373 0.004281 +34 2 1 1 2 total 0.023909 0.004395 +35 2 1 1 3 total 0.019518 0.003911 +36 2 1 1 4 total 0.019518 0.003424 +37 2 1 1 5 total 0.020006 0.002901 +38 2 1 1 6 total 0.024885 0.005801 +39 2 1 1 7 total 0.019030 0.002566 +40 2 1 1 8 total 0.030741 0.001410 +41 2 1 1 9 total 0.034156 0.002902 +42 2 1 1 10 total 0.042939 0.004498 +43 2 1 1 11 total 0.054650 0.003865 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -199,7 +199,7 @@ 28 2 1 2 7 total 0.000000 0.000000 29 2 1 2 8 total 0.000000 0.000000 30 2 1 2 9 total 0.000000 0.000000 -31 2 1 2 10 total 0.000486 0.000486 +31 2 1 2 10 total 0.000000 0.000000 32 2 1 2 11 total 0.000000 0.000000 11 2 2 1 1 total 0.000000 0.000000 12 2 2 1 2 total 0.000000 0.000000 @@ -212,29 +212,29 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.032689 0.010313 -1 2 2 2 2 total 0.035958 0.011071 -2 2 2 2 3 total 0.019614 0.012398 -3 2 2 2 4 total 0.022883 0.009072 -4 2 2 2 5 total 0.009807 0.009926 -5 2 2 2 6 total 0.029420 0.011781 -6 2 2 2 7 total 0.032689 0.014602 -7 2 2 2 8 total 0.019614 0.006844 -8 2 2 2 9 total 0.032689 0.010313 -9 2 2 2 10 total 0.022883 0.009072 -10 2 2 2 11 total 0.029420 0.005650 +0 2 2 2 1 total 0.026892 0.012233 +1 2 2 2 2 total 0.034959 0.007449 +2 2 2 2 3 total 0.040337 0.009144 +3 2 2 2 4 total 0.021513 0.003750 +4 2 2 2 5 total 0.018824 0.005602 +5 2 2 2 6 total 0.026892 0.004806 +6 2 2 2 7 total 0.037648 0.012716 +7 2 2 2 8 total 0.026892 0.009768 +8 2 2 2 9 total 0.024202 0.005420 +9 2 2 2 10 total 0.018824 0.010183 +10 2 2 2 11 total 0.018824 0.007033 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.025262 0.003309 -34 2 1 1 2 total 0.023805 0.004083 -35 2 1 1 3 total 0.027205 0.001631 -36 2 1 1 4 total 0.021376 0.003175 -37 2 1 1 5 total 0.017489 0.002412 -38 2 1 1 6 total 0.024291 0.002862 -39 2 1 1 7 total 0.029634 0.005250 -40 2 1 1 8 total 0.025262 0.001228 -41 2 1 1 9 total 0.028663 0.003403 -42 2 1 1 10 total 0.034493 0.004425 -43 2 1 1 11 total 0.052467 0.006227 +33 2 1 1 1 total 0.025373 0.004281 +34 2 1 1 2 total 0.023909 0.004395 +35 2 1 1 3 total 0.019518 0.003911 +36 2 1 1 4 total 0.019518 0.003424 +37 2 1 1 5 total 0.020006 0.002901 +38 2 1 1 6 total 0.024885 0.005801 +39 2 1 1 7 total 0.019030 0.002566 +40 2 1 1 8 total 0.030741 0.001410 +41 2 1 1 9 total 0.034156 0.002902 +42 2 1 1 10 total 0.042939 0.004498 +43 2 1 1 11 total 0.054650 0.003865 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -244,7 +244,7 @@ 28 2 1 2 7 total 0.000000 0.000000 29 2 1 2 8 total 0.000000 0.000000 30 2 1 2 9 total 0.000000 0.000000 -31 2 1 2 10 total 0.000486 0.000486 +31 2 1 2 10 total 0.000000 0.000000 32 2 1 2 11 total 0.000000 0.000000 11 2 2 1 1 total 0.000000 0.000000 12 2 2 1 2 total 0.000000 0.000000 @@ -257,29 +257,29 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.032689 0.010313 -1 2 2 2 2 total 0.035958 0.011071 -2 2 2 2 3 total 0.019614 0.012398 -3 2 2 2 4 total 0.022883 0.009072 -4 2 2 2 5 total 0.009807 0.009926 -5 2 2 2 6 total 0.029420 0.011781 -6 2 2 2 7 total 0.032689 0.014602 -7 2 2 2 8 total 0.019614 0.006844 -8 2 2 2 9 total 0.032689 0.010313 -9 2 2 2 10 total 0.022883 0.009072 -10 2 2 2 11 total 0.029420 0.005650 +0 2 2 2 1 total 0.026892 0.012233 +1 2 2 2 2 total 0.034959 0.007449 +2 2 2 2 3 total 0.040337 0.009144 +3 2 2 2 4 total 0.021513 0.003750 +4 2 2 2 5 total 0.018824 0.005602 +5 2 2 2 6 total 0.026892 0.004806 +6 2 2 2 7 total 0.037648 0.012716 +7 2 2 2 8 total 0.026892 0.009768 +8 2 2 2 9 total 0.024202 0.005420 +9 2 2 2 10 total 0.018824 0.010183 +10 2 2 2 11 total 0.018824 0.007033 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.025312 0.003469 -34 2 1 1 2 total 0.023852 0.004203 -35 2 1 1 3 total 0.027259 0.001971 -36 2 1 1 4 total 0.021418 0.003297 -37 2 1 1 5 total 0.017524 0.002518 -38 2 1 1 6 total 0.024339 0.003032 -39 2 1 1 7 total 0.029693 0.005395 -40 2 1 1 8 total 0.025312 0.001600 -41 2 1 1 9 total 0.028719 0.003602 -42 2 1 1 10 total 0.034561 0.004648 -43 2 1 1 11 total 0.052571 0.006591 +33 2 1 1 1 total 0.025753 0.004484 +34 2 1 1 2 total 0.024267 0.004581 +35 2 1 1 3 total 0.019810 0.004060 +36 2 1 1 4 total 0.019810 0.003579 +37 2 1 1 5 total 0.020305 0.003071 +38 2 1 1 6 total 0.025258 0.005987 +39 2 1 1 7 total 0.019315 0.002734 +40 2 1 1 8 total 0.031201 0.001961 +41 2 1 1 9 total 0.034668 0.003301 +42 2 1 1 10 total 0.043582 0.004935 +43 2 1 1 11 total 0.055468 0.004591 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -289,7 +289,7 @@ 28 2 1 2 7 total 0.000000 0.000000 29 2 1 2 8 total 0.000000 0.000000 30 2 1 2 9 total 0.000000 0.000000 -31 2 1 2 10 total 0.000487 0.000487 +31 2 1 2 10 total 0.000000 0.000000 32 2 1 2 11 total 0.000000 0.000000 11 2 2 1 1 total 0.000000 0.000000 12 2 2 1 2 total 0.000000 0.000000 @@ -302,29 +302,29 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.033560 0.010222 -1 2 2 2 2 total 0.036916 0.010953 -2 2 2 2 3 total 0.020136 0.012619 -3 2 2 2 4 total 0.023492 0.009111 -4 2 2 2 5 total 0.010068 0.010157 -5 2 2 2 6 total 0.030204 0.011837 -6 2 2 2 7 total 0.033560 0.014735 -7 2 2 2 8 total 0.020136 0.006828 -8 2 2 2 9 total 0.033560 0.010222 -9 2 2 2 10 total 0.023492 0.009111 -10 2 2 2 11 total 0.030204 0.005242 +0 2 2 2 1 total 0.026854 0.012543 +1 2 2 2 2 total 0.034911 0.008307 +2 2 2 2 3 total 0.040281 0.010079 +3 2 2 2 4 total 0.021483 0.004382 +4 2 2 2 5 total 0.018798 0.005938 +5 2 2 2 6 total 0.026854 0.005579 +6 2 2 2 7 total 0.037596 0.013308 +7 2 2 2 8 total 0.026854 0.010161 +8 2 2 2 9 total 0.024169 0.005987 +9 2 2 2 10 total 0.018798 0.010362 +10 2 2 2 11 total 0.018798 0.007300 material group in group out mu bin nuclide mean std. dev. -33 2 1 1 1 total 0.025312 0.003700 -34 2 1 1 2 total 0.023852 0.004373 -35 2 1 1 3 total 0.027259 0.002407 -36 2 1 1 4 total 0.021418 0.003471 -37 2 1 1 5 total 0.017524 0.002670 -38 2 1 1 6 total 0.024339 0.003273 -39 2 1 1 7 total 0.029693 0.005602 -40 2 1 1 8 total 0.025312 0.002052 -41 2 1 1 9 total 0.028719 0.003886 -42 2 1 1 10 total 0.034561 0.004968 -43 2 1 1 11 total 0.052571 0.007110 +33 2 1 1 1 total 0.025753 0.004661 +34 2 1 1 2 total 0.024267 0.004736 +35 2 1 1 3 total 0.019810 0.004177 +36 2 1 1 4 total 0.019810 0.003711 +37 2 1 1 5 total 0.020305 0.003231 +38 2 1 1 6 total 0.025258 0.006117 +39 2 1 1 7 total 0.019315 0.002897 +40 2 1 1 8 total 0.031201 0.002497 +41 2 1 1 9 total 0.034668 0.003721 +42 2 1 1 10 total 0.043582 0.005386 +43 2 1 1 11 total 0.055468 0.005350 22 2 1 2 1 total 0.000000 0.000000 23 2 1 2 2 total 0.000000 0.000000 24 2 1 2 3 total 0.000000 0.000000 @@ -334,7 +334,7 @@ 28 2 1 2 7 total 0.000000 0.000000 29 2 1 2 8 total 0.000000 0.000000 30 2 1 2 9 total 0.000000 0.000000 -31 2 1 2 10 total 0.000487 0.000843 +31 2 1 2 10 total 0.000000 0.000000 32 2 1 2 11 total 0.000000 0.000000 11 2 2 1 1 total 0.000000 0.000000 12 2 2 1 2 total 0.000000 0.000000 @@ -347,40 +347,40 @@ 19 2 2 1 9 total 0.000000 0.000000 20 2 2 1 10 total 0.000000 0.000000 21 2 2 1 11 total 0.000000 0.000000 -0 2 2 2 1 total 0.033560 0.011889 -1 2 2 2 2 total 0.036916 0.012828 -2 2 2 2 3 total 0.020136 0.013135 -3 2 2 2 4 total 0.023492 0.010054 -4 2 2 2 5 total 0.010068 0.010319 -5 2 2 2 6 total 0.030204 0.013037 -6 2 2 2 7 total 0.033560 0.015937 -7 2 2 2 8 total 0.020136 0.007739 -8 2 2 2 9 total 0.033560 0.011889 -9 2 2 2 10 total 0.023492 0.010054 -10 2 2 2 11 total 0.030204 0.007573 +0 2 2 2 1 total 0.026854 0.013054 +1 2 2 2 2 total 0.034911 0.009546 +2 2 2 2 3 total 0.040281 0.011446 +3 2 2 2 4 total 0.021483 0.005251 +4 2 2 2 5 total 0.018798 0.006456 +5 2 2 2 6 total 0.026854 0.006649 +6 2 2 2 7 total 0.037596 0.014239 +7 2 2 2 8 total 0.026854 0.010785 +8 2 2 2 9 total 0.024169 0.006815 +9 2 2 2 10 total 0.018798 0.010667 +10 2 2 2 11 total 0.018798 0.007727 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.008818 0.001587 -34 3 1 1 2 total 0.006389 0.000610 -35 3 1 1 3 total 0.007288 0.000775 -36 3 1 1 4 total 0.008008 0.001111 -37 3 1 1 5 total 0.007828 0.000452 -38 3 1 1 6 total 0.011247 0.000473 -39 3 1 1 7 total 0.039411 0.002111 -40 3 1 1 8 total 0.071443 0.002082 -41 3 1 1 9 total 0.115713 0.004165 -42 3 1 1 10 total 0.164662 0.005479 -43 3 1 1 11 total 0.214060 0.006189 -22 3 1 2 1 total 0.000360 0.000090 -23 3 1 2 2 total 0.000810 0.000169 -24 3 1 2 3 total 0.000810 0.000331 -25 3 1 2 4 total 0.001440 0.000332 -26 3 1 2 5 total 0.002339 0.000267 -27 3 1 2 6 total 0.003149 0.000256 -28 3 1 2 7 total 0.004409 0.000601 -29 3 1 2 8 total 0.004139 0.000398 -30 3 1 2 9 total 0.004769 0.000639 -31 3 1 2 10 total 0.004769 0.000453 -32 3 1 2 11 total 0.002789 0.000630 +33 3 1 1 1 total 0.007681 0.001043 +34 3 1 1 2 total 0.005645 0.000710 +35 3 1 1 3 total 0.007403 0.000552 +36 3 1 1 4 total 0.007866 0.000512 +37 3 1 1 5 total 0.007589 0.000524 +38 3 1 1 6 total 0.011198 0.001548 +39 3 1 1 7 total 0.039701 0.002533 +40 3 1 1 8 total 0.075978 0.001897 +41 3 1 1 9 total 0.112532 0.003677 +42 3 1 1 10 total 0.166670 0.005003 +43 3 1 1 11 total 0.210443 0.005656 +22 3 1 2 1 total 0.000463 0.000207 +23 3 1 2 2 total 0.000555 0.000227 +24 3 1 2 3 total 0.000740 0.000429 +25 3 1 2 4 total 0.001111 0.000236 +26 3 1 2 5 total 0.002128 0.000631 +27 3 1 2 6 total 0.003146 0.000645 +28 3 1 2 7 total 0.004905 0.000561 +29 3 1 2 8 total 0.005738 0.000742 +30 3 1 2 9 total 0.005090 0.000672 +31 3 1 2 10 total 0.005367 0.000561 +32 3 1 2 11 total 0.002406 0.000593 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -391,41 +391,41 @@ 18 3 2 1 8 total 0.000000 0.000000 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 -21 3 2 1 11 total 0.000000 0.000000 -0 3 2 2 1 total 0.086327 0.004499 -1 3 2 2 2 total 0.088278 0.008634 -2 3 2 2 3 total 0.108275 0.008710 -3 3 2 2 4 total 0.115103 0.005859 -4 3 2 2 5 total 0.139489 0.012654 -5 3 2 2 6 total 0.152658 0.005193 -6 3 2 2 7 total 0.182897 0.007164 -7 3 2 2 8 total 0.202894 0.014663 -8 3 2 2 9 total 0.268249 0.019575 -9 3 2 2 10 total 0.294098 0.015467 -10 3 2 2 11 total 0.354088 0.012355 +21 3 2 1 11 total 0.000474 0.000475 +0 3 2 2 1 total 0.074402 0.007811 +1 3 2 2 2 total 0.103783 0.009042 +2 3 2 2 3 total 0.106153 0.012691 +3 3 2 2 4 total 0.115631 0.011475 +4 3 2 2 5 total 0.128900 0.014096 +5 3 2 2 6 total 0.169655 0.021010 +6 3 2 2 7 total 0.175816 0.016086 +7 3 2 2 8 total 0.217519 0.029631 +8 3 2 2 9 total 0.247374 0.021476 +9 3 2 2 10 total 0.299977 0.031756 +10 3 2 2 11 total 0.351157 0.027654 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.008818 0.001587 -34 3 1 1 2 total 0.006389 0.000610 -35 3 1 1 3 total 0.007288 0.000775 -36 3 1 1 4 total 0.008008 0.001111 -37 3 1 1 5 total 0.007828 0.000452 -38 3 1 1 6 total 0.011247 0.000473 -39 3 1 1 7 total 0.039411 0.002111 -40 3 1 1 8 total 0.071443 0.002082 -41 3 1 1 9 total 0.115713 0.004165 -42 3 1 1 10 total 0.164662 0.005479 -43 3 1 1 11 total 0.214060 0.006189 -22 3 1 2 1 total 0.000360 0.000090 -23 3 1 2 2 total 0.000810 0.000169 -24 3 1 2 3 total 0.000810 0.000331 -25 3 1 2 4 total 0.001440 0.000332 -26 3 1 2 5 total 0.002339 0.000267 -27 3 1 2 6 total 0.003149 0.000256 -28 3 1 2 7 total 0.004409 0.000601 -29 3 1 2 8 total 0.004139 0.000398 -30 3 1 2 9 total 0.004769 0.000639 -31 3 1 2 10 total 0.004769 0.000453 -32 3 1 2 11 total 0.002789 0.000630 +33 3 1 1 1 total 0.007681 0.001043 +34 3 1 1 2 total 0.005645 0.000710 +35 3 1 1 3 total 0.007403 0.000552 +36 3 1 1 4 total 0.007866 0.000512 +37 3 1 1 5 total 0.007589 0.000524 +38 3 1 1 6 total 0.011198 0.001548 +39 3 1 1 7 total 0.039701 0.002533 +40 3 1 1 8 total 0.075978 0.001897 +41 3 1 1 9 total 0.112532 0.003677 +42 3 1 1 10 total 0.166670 0.005003 +43 3 1 1 11 total 0.210443 0.005656 +22 3 1 2 1 total 0.000463 0.000207 +23 3 1 2 2 total 0.000555 0.000227 +24 3 1 2 3 total 0.000740 0.000429 +25 3 1 2 4 total 0.001111 0.000236 +26 3 1 2 5 total 0.002128 0.000631 +27 3 1 2 6 total 0.003146 0.000645 +28 3 1 2 7 total 0.004905 0.000561 +29 3 1 2 8 total 0.005738 0.000742 +30 3 1 2 9 total 0.005090 0.000672 +31 3 1 2 10 total 0.005367 0.000561 +32 3 1 2 11 total 0.002406 0.000593 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -436,41 +436,41 @@ 18 3 2 1 8 total 0.000000 0.000000 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 -21 3 2 1 11 total 0.000000 0.000000 -0 3 2 2 1 total 0.086327 0.004499 -1 3 2 2 2 total 0.088278 0.008634 -2 3 2 2 3 total 0.108275 0.008710 -3 3 2 2 4 total 0.115103 0.005859 -4 3 2 2 5 total 0.139489 0.012654 -5 3 2 2 6 total 0.152658 0.005193 -6 3 2 2 7 total 0.182897 0.007164 -7 3 2 2 8 total 0.202894 0.014663 -8 3 2 2 9 total 0.268249 0.019575 -9 3 2 2 10 total 0.294098 0.015467 -10 3 2 2 11 total 0.354088 0.012355 +21 3 2 1 11 total 0.000474 0.000475 +0 3 2 2 1 total 0.074402 0.007811 +1 3 2 2 2 total 0.103783 0.009042 +2 3 2 2 3 total 0.106153 0.012691 +3 3 2 2 4 total 0.115631 0.011475 +4 3 2 2 5 total 0.128900 0.014096 +5 3 2 2 6 total 0.169655 0.021010 +6 3 2 2 7 total 0.175816 0.016086 +7 3 2 2 8 total 0.217519 0.029631 +8 3 2 2 9 total 0.247374 0.021476 +9 3 2 2 10 total 0.299977 0.031756 +10 3 2 2 11 total 0.351157 0.027654 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.008807 0.001589 -34 3 1 1 2 total 0.006381 0.000614 -35 3 1 1 3 total 0.007279 0.000779 -36 3 1 1 4 total 0.007998 0.001113 -37 3 1 1 5 total 0.007818 0.000461 -38 3 1 1 6 total 0.011233 0.000491 -39 3 1 1 7 total 0.039362 0.002160 -40 3 1 1 8 total 0.071354 0.002246 -41 3 1 1 9 total 0.115569 0.004381 -42 3 1 1 10 total 0.164456 0.005812 -43 3 1 1 11 total 0.213793 0.006684 -22 3 1 2 1 total 0.000359 0.000090 -23 3 1 2 2 total 0.000809 0.000169 -24 3 1 2 3 total 0.000809 0.000331 -25 3 1 2 4 total 0.001438 0.000332 -26 3 1 2 5 total 0.002337 0.000269 -27 3 1 2 6 total 0.003145 0.000259 -28 3 1 2 7 total 0.004403 0.000603 -29 3 1 2 8 total 0.004134 0.000400 -30 3 1 2 9 total 0.004763 0.000640 -31 3 1 2 10 total 0.004763 0.000456 -32 3 1 2 11 total 0.002786 0.000630 +33 3 1 1 1 total 0.007759 0.001080 +34 3 1 1 2 total 0.005703 0.000738 +35 3 1 1 3 total 0.007479 0.000602 +36 3 1 1 4 total 0.007946 0.000571 +37 3 1 1 5 total 0.007666 0.000578 +38 3 1 1 6 total 0.011312 0.001601 +39 3 1 1 7 total 0.040106 0.002833 +40 3 1 1 8 total 0.076753 0.003016 +41 3 1 1 9 total 0.113680 0.005068 +42 3 1 1 10 total 0.168370 0.007185 +43 3 1 1 11 total 0.212589 0.008616 +22 3 1 2 1 total 0.000467 0.000210 +23 3 1 2 2 total 0.000561 0.000230 +24 3 1 2 3 total 0.000748 0.000434 +25 3 1 2 4 total 0.001122 0.000241 +26 3 1 2 5 total 0.002150 0.000641 +27 3 1 2 6 total 0.003179 0.000659 +28 3 1 2 7 total 0.004955 0.000586 +29 3 1 2 8 total 0.005796 0.000770 +30 3 1 2 9 total 0.005142 0.000697 +31 3 1 2 10 total 0.005422 0.000590 +32 3 1 2 11 total 0.002431 0.000604 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -481,41 +481,41 @@ 18 3 2 1 8 total 0.000000 0.000000 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 -21 3 2 1 11 total 0.000000 0.000000 -0 3 2 2 1 total 0.086966 0.006257 -1 3 2 2 2 total 0.088931 0.009752 -2 3 2 2 3 total 0.109076 0.010309 -3 3 2 2 4 total 0.115955 0.008241 -4 3 2 2 5 total 0.140521 0.014528 -5 3 2 2 6 total 0.153787 0.009249 -6 3 2 2 7 total 0.184250 0.011645 -7 3 2 2 8 total 0.204395 0.017916 -8 3 2 2 9 total 0.270233 0.023844 -9 3 2 2 10 total 0.296274 0.021418 -10 3 2 2 11 total 0.356708 0.021633 +21 3 2 1 11 total 0.000477 0.000479 +0 3 2 2 1 total 0.074833 0.009685 +1 3 2 2 2 total 0.104384 0.012046 +2 3 2 2 3 total 0.106768 0.015107 +3 3 2 2 4 total 0.116300 0.014515 +4 3 2 2 5 total 0.129646 0.017242 +5 3 2 2 6 total 0.170637 0.024765 +6 3 2 2 7 total 0.176834 0.020997 +7 3 2 2 8 total 0.218778 0.034093 +8 3 2 2 9 total 0.248807 0.028656 +9 3 2 2 10 total 0.301714 0.039263 +10 3 2 2 11 total 0.353191 0.038577 material group in group out mu bin nuclide mean std. dev. -33 3 1 1 1 total 0.008807 0.001597 -34 3 1 1 2 total 0.006381 0.000625 -35 3 1 1 3 total 0.007279 0.000790 -36 3 1 1 4 total 0.007998 0.001123 -37 3 1 1 5 total 0.007818 0.000482 -38 3 1 1 6 total 0.011233 0.000531 -39 3 1 1 7 total 0.039362 0.002274 -40 3 1 1 8 total 0.071354 0.002589 -41 3 1 1 9 total 0.115569 0.004852 -42 3 1 1 10 total 0.164456 0.006526 -43 3 1 1 11 total 0.213793 0.007718 -22 3 1 2 1 total 0.000359 0.000092 -23 3 1 2 2 total 0.000809 0.000174 -24 3 1 2 3 total 0.000809 0.000333 -25 3 1 2 4 total 0.001438 0.000340 -26 3 1 2 5 total 0.002337 0.000295 -27 3 1 2 6 total 0.003145 0.000306 -28 3 1 2 7 total 0.004403 0.000645 -29 3 1 2 8 total 0.004134 0.000454 -30 3 1 2 9 total 0.004763 0.000686 -31 3 1 2 10 total 0.004763 0.000519 -32 3 1 2 11 total 0.002786 0.000646 +33 3 1 1 1 total 0.007759 0.001091 +34 3 1 1 2 total 0.005703 0.000746 +35 3 1 1 3 total 0.007479 0.000619 +36 3 1 1 4 total 0.007946 0.000592 +37 3 1 1 5 total 0.007666 0.000598 +38 3 1 1 6 total 0.011312 0.001616 +39 3 1 1 7 total 0.040106 0.002941 +40 3 1 1 8 total 0.076753 0.003373 +41 3 1 1 9 total 0.113680 0.005540 +42 3 1 1 10 total 0.168370 0.007913 +43 3 1 1 11 total 0.212589 0.009578 +22 3 1 2 1 total 0.000467 0.000211 +23 3 1 2 2 total 0.000561 0.000232 +24 3 1 2 3 total 0.000748 0.000436 +25 3 1 2 4 total 0.001122 0.000250 +26 3 1 2 5 total 0.002150 0.000653 +27 3 1 2 6 total 0.003179 0.000684 +28 3 1 2 7 total 0.004955 0.000654 +29 3 1 2 8 total 0.005796 0.000841 +30 3 1 2 9 total 0.005142 0.000759 +31 3 1 2 10 total 0.005422 0.000670 +32 3 1 2 11 total 0.002431 0.000620 11 3 2 1 1 total 0.000000 0.000000 12 3 2 1 2 total 0.000000 0.000000 13 3 2 1 3 total 0.000000 0.000000 @@ -526,15 +526,15 @@ 18 3 2 1 8 total 0.000000 0.000000 19 3 2 1 9 total 0.000000 0.000000 20 3 2 1 10 total 0.000000 0.000000 -21 3 2 1 11 total 0.000000 0.000000 -0 3 2 2 1 total 0.086966 0.007070 -1 3 2 2 2 total 0.088931 0.010317 -2 3 2 2 3 total 0.109076 0.011105 -3 3 2 2 4 total 0.115955 0.009338 -4 3 2 2 5 total 0.140521 0.015472 -5 3 2 2 6 total 0.153787 0.010930 -6 3 2 2 7 total 0.184250 0.013575 -7 3 2 2 8 total 0.204395 0.019516 -8 3 2 2 9 total 0.270233 0.025947 -9 3 2 2 10 total 0.296274 0.024178 -10 3 2 2 11 total 0.356708 0.025503 +21 3 2 1 11 total 0.000477 0.000827 +0 3 2 2 1 total 0.074833 0.011051 +1 3 2 2 2 total 0.104384 0.014150 +2 3 2 2 3 total 0.106768 0.016908 +3 3 2 2 4 total 0.116300 0.016707 +4 3 2 2 5 total 0.129646 0.019553 +5 3 2 2 6 total 0.170637 0.027579 +6 3 2 2 7 total 0.176834 0.024476 +7 3 2 2 8 total 0.218778 0.037476 +8 3 2 2 9 total 0.248807 0.033680 +9 3 2 2 10 total 0.301714 0.044745 +10 3 2 2 11 total 0.353191 0.046035 diff --git a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat index 5cac8ecf42..5a6e8a20a9 100644 --- a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat @@ -1,482 +1,511 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 2 2 - -100.0 -100.0 - 100.0 100.0 - - - 1 - - - 0.0 20000000.0 - - - 0.0 20000000.0 - - - 1 - - - 3 - - - 1 - - - 1 2 3 4 5 6 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - nu-scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - nu-fission - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 - total - nu-fission - analog - - - 1 5 - total - nu-fission - analog - - - 1 2 - total - prompt-nu-fission - analog - - - 1 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - prompt-nu-fission - analog - - - 66 2 - total - current - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 2 - total - delayed-nu-fission - analog - - - 1 77 5 - total - delayed-nu-fission - analog - - - 1 2 - total - nu-fission - tracklength - - - 1 77 2 - total - delayed-nu-fission - tracklength - - - 1 77 - total - delayed-nu-fission - tracklength - - - 1 77 - total - decay-rate - tracklength - - - 1 2 - total - flux - analog - - - 1 77 2 5 - total - delayed-nu-fission - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 2 2 + -100.0 -100.0 + 100.0 100.0 + + + 1 + + + 0.0 20000000.0 + + + 0.0 20000000.0 + + + 1 + + + 3 + + + 1 + + + 1 2 3 4 5 6 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + (n,3n) + tracklength + + + 1 2 + total + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + nu-scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + nu-fission + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 + total + nu-fission + analog + + + 1 5 + total + nu-fission + analog + + + 1 2 + total + prompt-nu-fission + analog + + + 1 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + prompt-nu-fission + analog + + + 68 2 + total + current + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 2 + total + delayed-nu-fission + analog + + + 1 79 5 + total + delayed-nu-fission + analog + + + 1 2 + total + nu-fission + tracklength + + + 1 79 2 + total + delayed-nu-fission + tracklength + + + 1 79 + total + delayed-nu-fission + tracklength + + + 1 79 + total + decay-rate + tracklength + + + 1 2 + total + flux + analog + + + 1 79 2 5 + total + delayed-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_mesh/results_true.dat b/tests/regression_tests/mgxs_library_mesh/results_true.dat index 7a6b4abe7c..16b86870d7 100644 --- a/tests/regression_tests/mgxs_library_mesh/results_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/results_true.dat @@ -1,356 +1,362 @@ mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.102319 0.005483 -2 1 2 1 1 total 0.104659 0.002878 -1 2 1 1 1 total 0.107122 0.005105 -3 2 2 1 1 total 0.103856 0.003459 +0 1 1 1 1 total 0.103374 0.004981 +2 1 2 1 1 total 0.103852 0.004752 +1 2 1 1 1 total 0.103322 0.003613 +3 2 2 1 1 total 0.102889 0.003387 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.072455 0.005799 -2 1 2 1 1 total 0.072899 0.003254 -1 2 1 1 1 total 0.074187 0.005441 -3 2 2 1 1 total 0.074241 0.003779 +0 1 1 1 1 total 0.072760 0.005235 +2 1 2 1 1 total 0.074856 0.004972 +1 2 1 1 1 total 0.074583 0.004000 +3 2 2 1 1 total 0.072925 0.003485 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.072455 0.005799 -2 1 2 1 1 total 0.072912 0.003251 -1 2 1 1 1 total 0.074140 0.005442 -3 2 2 1 1 total 0.074192 0.003783 +0 1 1 1 1 total 0.072768 0.005236 +2 1 2 1 1 total 0.074864 0.004972 +1 2 1 1 1 total 0.074603 0.004002 +3 2 2 1 1 total 0.072881 0.003491 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.012943 0.000836 -2 1 2 1 1 total 0.013363 0.000553 -1 2 1 1 1 total 0.013980 0.000715 -3 2 2 1 1 total 0.013286 0.000621 +0 1 1 1 1 total 0.013309 0.000729 +2 1 2 1 1 total 0.013223 0.000707 +1 2 1 1 1 total 0.013222 0.000596 +3 2 2 1 1 total 0.013282 0.000564 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.001231 0.000979 -2 1 2 1 1 total 0.001332 0.000691 -1 2 1 1 1 total 0.001346 0.000770 -3 2 2 1 1 total 0.001258 0.000815 +0 1 1 1 1 total 0.013241 0.000728 +2 1 2 1 1 total 0.013142 0.000705 +1 2 1 1 1 total 0.013137 0.000596 +3 2 2 1 1 total 0.013221 0.000564 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.011712 0.000762 -2 1 2 1 1 total 0.012031 0.000468 -1 2 1 1 1 total 0.012635 0.000646 -3 2 2 1 1 total 0.012028 0.000556 +0 1 1 1 1 total 0.001281 0.000874 +2 1 2 1 1 total 0.001297 0.000731 +1 2 1 1 1 total 0.001281 0.000744 +3 2 2 1 1 total 0.001288 0.000719 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.030549 0.001995 -2 1 2 1 1 total 0.031338 0.001160 -1 2 1 1 1 total 0.032944 0.001703 -3 2 2 1 1 total 0.031480 0.001423 +0 1 1 1 1 total 0.012029 0.000664 +2 1 2 1 1 total 0.011926 0.000639 +1 2 1 1 1 total 0.011941 0.000545 +3 2 2 1 1 total 0.011994 0.000515 + mesh 1 group in nuclide mean std. dev. + x y z +0 1 1 1 1 total 0.031361 0.001735 +2 1 2 1 1 total 0.031091 0.001675 +1 2 1 1 1 total 0.031169 0.001418 +3 2 2 1 1 total 0.031255 0.001366 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 2.265259e+06 147469.851100 -2 1 2 1 1 total 2.326873e+06 90605.035909 -1 2 1 1 1 total 2.443628e+06 124917.160682 -3 2 2 1 1 total 2.326322e+06 107513.428248 +0 1 1 1 1 total 2.326447e+06 128504.886535 +2 1 2 1 1 total 2.306518e+06 123638.096130 +1 2 1 1 1 total 2.309435e+06 105395.059055 +3 2 2 1 1 total 2.319667e+06 99692.620001 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.089376 0.004670 -2 1 2 1 1 total 0.091296 0.002370 -1 2 1 1 1 total 0.093142 0.004401 -3 2 2 1 1 total 0.090570 0.002842 +0 1 1 1 1 total 0.090065 0.004262 +2 1 2 1 1 total 0.090629 0.004086 +1 2 1 1 1 total 0.090100 0.003045 +3 2 2 1 1 total 0.089607 0.002828 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.089670 0.005421 -2 1 2 1 1 total 0.094557 0.003843 -1 2 1 1 1 total 0.094972 0.005963 -3 2 2 1 1 total 0.088538 0.002530 +0 1 1 1 1 total 0.090188 0.005151 +2 1 2 1 1 total 0.094339 0.004349 +1 2 1 1 1 total 0.088294 0.003953 +3 2 2 1 1 total 0.089633 0.002592 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.089670 0.005421 -1 1 1 1 1 1 P1 total 0.029864 0.001887 -2 1 1 1 1 1 P2 total 0.015945 0.001296 -3 1 1 1 1 1 P3 total 0.009511 0.000946 -8 1 2 1 1 1 P0 total 0.094524 0.003835 -9 1 2 1 1 1 P1 total 0.031760 0.001520 -10 1 2 1 1 1 P2 total 0.017210 0.000821 -11 1 2 1 1 1 P3 total 0.009315 0.000631 -4 2 1 1 1 1 P0 total 0.094835 0.005973 -5 2 1 1 1 1 P1 total 0.032936 0.001884 -6 2 1 1 1 1 P2 total 0.017196 0.001626 -7 2 1 1 1 1 P3 total 0.010278 0.001345 -12 2 2 1 1 1 P0 total 0.088412 0.002491 -13 2 2 1 1 1 P1 total 0.029615 0.001524 -14 2 2 1 1 1 P2 total 0.016925 0.000613 -15 2 2 1 1 1 P3 total 0.009759 0.000466 +0 1 1 1 1 1 P0 total 0.090122 0.005149 +1 1 1 1 1 1 P1 total 0.030614 0.001612 +2 1 1 1 1 1 P2 total 0.016490 0.000897 +3 1 1 1 1 1 P3 total 0.010163 0.000829 +8 1 2 1 1 1 P0 total 0.094307 0.004337 +9 1 2 1 1 1 P1 total 0.028996 0.001462 +10 1 2 1 1 1 P2 total 0.017486 0.000808 +11 1 2 1 1 1 P3 total 0.009753 0.000742 +4 2 1 1 1 1 P0 total 0.088198 0.003959 +5 2 1 1 1 1 P1 total 0.028739 0.001715 +6 2 1 1 1 1 P2 total 0.015608 0.000680 +7 2 1 1 1 1 P3 total 0.008232 0.000363 +12 2 2 1 1 1 P0 total 0.089536 0.002551 +13 2 2 1 1 1 P1 total 0.029964 0.000821 +14 2 2 1 1 1 P2 total 0.015742 0.001260 +15 2 2 1 1 1 P3 total 0.008944 0.000385 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.089670 0.005421 -1 1 1 1 1 1 P1 total 0.029864 0.001887 -2 1 1 1 1 1 P2 total 0.015945 0.001296 -3 1 1 1 1 1 P3 total 0.009511 0.000946 -8 1 2 1 1 1 P0 total 0.094557 0.003843 -9 1 2 1 1 1 P1 total 0.031747 0.001512 -10 1 2 1 1 1 P2 total 0.017201 0.000817 -11 1 2 1 1 1 P3 total 0.009329 0.000635 -4 2 1 1 1 1 P0 total 0.094972 0.005963 -5 2 1 1 1 1 P1 total 0.032983 0.001886 -6 2 1 1 1 1 P2 total 0.017162 0.001631 -7 2 1 1 1 1 P3 total 0.010240 0.001336 -12 2 2 1 1 1 P0 total 0.088538 0.002530 -13 2 2 1 1 1 P1 total 0.029663 0.001532 -14 2 2 1 1 1 P2 total 0.016931 0.000627 -15 2 2 1 1 1 P3 total 0.009762 0.000466 +0 1 1 1 1 1 P0 total 0.090188 0.005151 +1 1 1 1 1 1 P1 total 0.030606 0.001614 +2 1 1 1 1 1 P2 total 0.016462 0.000898 +3 1 1 1 1 1 P3 total 0.010173 0.000824 +8 1 2 1 1 1 P0 total 0.094339 0.004349 +9 1 2 1 1 1 P1 total 0.028988 0.001462 +10 1 2 1 1 1 P2 total 0.017473 0.000811 +11 1 2 1 1 1 P3 total 0.009763 0.000749 +4 2 1 1 1 1 P0 total 0.088294 0.003953 +5 2 1 1 1 1 P1 total 0.028719 0.001720 +6 2 1 1 1 1 P2 total 0.015571 0.000691 +7 2 1 1 1 1 P3 total 0.008255 0.000361 +12 2 2 1 1 1 P0 total 0.089633 0.002592 +13 2 2 1 1 1 P1 total 0.030008 0.000847 +14 2 2 1 1 1 P2 total 0.015748 0.001284 +15 2 2 1 1 1 P3 total 0.008951 0.000387 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 1.000000 0.056046 -2 1 2 1 1 1 total 1.000346 0.042442 -1 2 1 1 1 1 total 1.001447 0.056345 -3 2 2 1 1 1 total 1.001422 0.026106 +0 1 1 1 1 1 total 1.000736 0.060260 +2 1 2 1 1 1 total 1.000346 0.042656 +1 2 1 1 1 1 total 1.001091 0.052736 +3 2 2 1 1 1 total 1.001078 0.038212 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.031401 0.002755 -2 1 2 1 1 1 total 0.033268 0.001692 -1 2 1 1 1 1 total 0.033756 0.002609 -3 2 2 1 1 1 total 0.030234 0.001308 +0 1 1 1 1 1 total 0.031890 0.002523 +2 1 2 1 1 1 total 0.032714 0.001502 +1 2 1 1 1 1 total 0.030414 0.002369 +3 2 2 1 1 1 total 0.031051 0.001521 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 1.0 0.056046 -2 1 2 1 1 1 total 1.0 0.042360 -1 2 1 1 1 1 total 1.0 0.056488 -3 2 2 1 1 1 total 1.0 0.025630 +0 1 1 1 1 1 total 1.0 0.060234 +2 1 2 1 1 1 total 1.0 0.042523 +1 2 1 1 1 1 total 1.0 0.052777 +3 2 2 1 1 1 total 1.0 0.037842 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.089376 0.006848 -1 1 1 1 1 1 P1 total 0.029766 0.002346 -2 1 1 1 1 1 P2 total 0.015893 0.001493 -3 1 1 1 1 1 P3 total 0.009480 0.001043 -8 1 2 1 1 1 P0 total 0.091296 0.004536 -9 1 2 1 1 1 P1 total 0.030675 0.001712 -10 1 2 1 1 1 P2 total 0.016622 0.000925 -11 1 2 1 1 1 P3 total 0.008997 0.000662 -4 2 1 1 1 1 P0 total 0.093142 0.006860 -5 2 1 1 1 1 P1 total 0.032348 0.002224 -6 2 1 1 1 1 P2 total 0.016889 0.001722 -7 2 1 1 1 1 P3 total 0.010094 0.001376 -12 2 2 1 1 1 P0 total 0.090570 0.003669 -13 2 2 1 1 1 P1 total 0.030338 0.001794 -14 2 2 1 1 1 P2 total 0.017338 0.000806 -15 2 2 1 1 1 P3 total 0.009997 0.000559 +0 1 1 1 1 1 P0 total 0.090065 0.006899 +1 1 1 1 1 1 P1 total 0.030595 0.002243 +2 1 1 1 1 1 P2 total 0.016480 0.001229 +3 1 1 1 1 1 P3 total 0.010157 0.000977 +8 1 2 1 1 1 P0 total 0.090629 0.005616 +9 1 2 1 1 1 P1 total 0.027865 0.001820 +10 1 2 1 1 1 P2 total 0.016804 0.001044 +11 1 2 1 1 1 P3 total 0.009373 0.000813 +4 2 1 1 1 1 P0 total 0.090100 0.005647 +5 2 1 1 1 1 P1 total 0.029359 0.002172 +6 2 1 1 1 1 P2 total 0.015944 0.000984 +7 2 1 1 1 1 P3 total 0.008410 0.000523 +12 2 2 1 1 1 P0 total 0.089607 0.004415 +13 2 2 1 1 1 P1 total 0.029988 0.001459 +14 2 2 1 1 1 P2 total 0.015755 0.001411 +15 2 2 1 1 1 P3 total 0.008951 0.000528 mesh 1 group in group out legendre nuclide mean std. dev. x y z -0 1 1 1 1 1 P0 total 0.089376 0.008485 -1 1 1 1 1 1 P1 total 0.029766 0.002878 -2 1 1 1 1 1 P2 total 0.015893 0.001738 -3 1 1 1 1 1 P3 total 0.009480 0.001171 -8 1 2 1 1 1 P0 total 0.091328 0.005967 -9 1 2 1 1 1 P1 total 0.030686 0.002151 -10 1 2 1 1 1 P2 total 0.016628 0.001164 -11 1 2 1 1 1 P3 total 0.009000 0.000764 -4 2 1 1 1 1 P0 total 0.093277 0.008645 -5 2 1 1 1 1 P1 total 0.032394 0.002878 -6 2 1 1 1 1 P2 total 0.016913 0.001969 -7 2 1 1 1 1 P3 total 0.010109 0.001491 -12 2 2 1 1 1 P0 total 0.090699 0.004369 -13 2 2 1 1 1 P1 total 0.030381 0.001963 -14 2 2 1 1 1 P2 total 0.017362 0.000925 -15 2 2 1 1 1 P3 total 0.010011 0.000618 +0 1 1 1 1 1 P0 total 0.090131 0.008782 +1 1 1 1 1 1 P1 total 0.030617 0.002905 +2 1 1 1 1 1 P2 total 0.016492 0.001581 +3 1 1 1 1 1 P3 total 0.010164 0.001154 +8 1 2 1 1 1 P0 total 0.090661 0.006820 +9 1 2 1 1 1 P1 total 0.027875 0.002174 +10 1 2 1 1 1 P2 total 0.016810 0.001267 +11 1 2 1 1 1 P3 total 0.009376 0.000906 +4 2 1 1 1 1 P0 total 0.090199 0.007385 +5 2 1 1 1 1 P1 total 0.029391 0.002669 +6 2 1 1 1 1 P2 total 0.015962 0.001295 +7 2 1 1 1 1 P3 total 0.008419 0.000686 +12 2 2 1 1 1 P0 total 0.089704 0.005591 +13 2 2 1 1 1 P1 total 0.030020 0.001856 +14 2 2 1 1 1 P2 total 0.015772 0.001536 +15 2 2 1 1 1 P3 total 0.008961 0.000629 mesh 1 group out nuclide mean std. dev. x y z -0 1 1 1 1 total 1.0 0.105972 -2 1 2 1 1 total 1.0 0.060624 -1 2 1 1 1 total 1.0 0.084855 -3 2 2 1 1 total 1.0 0.053024 +0 1 1 1 1 total 1.0 0.098093 +2 1 2 1 1 total 1.0 0.042346 +1 2 1 1 1 total 1.0 0.104352 +3 2 2 1 1 total 1.0 0.067889 mesh 1 group out nuclide mean std. dev. x y z -0 1 1 1 1 total 1.0 0.108202 -2 1 2 1 1 total 1.0 0.058908 -1 2 1 1 1 total 1.0 0.085583 -3 2 2 1 1 total 1.0 0.052287 +0 1 1 1 1 total 1.0 0.099401 +2 1 2 1 1 total 1.0 0.042085 +1 2 1 1 1 total 1.0 0.104135 +3 2 2 1 1 total 1.0 0.067307 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 8.529639e-10 3.484230e-11 -2 1 2 1 1 total 9.050800e-10 2.850752e-11 -1 2 1 1 1 total 8.650057e-10 4.049297e-11 -3 2 2 1 1 total 8.673543e-10 1.753754e-11 +0 1 1 1 1 total 8.547713e-10 3.477664e-11 +2 1 2 1 1 total 9.057083e-10 4.171858e-11 +1 2 1 1 1 total 8.666731e-10 2.371072e-11 +3 2 2 1 1 total 8.532016e-10 1.638929e-11 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 0.030356 0.001982 -2 1 2 1 1 total 0.031140 0.001152 -1 2 1 1 1 total 0.032736 0.001693 -3 2 2 1 1 total 0.031283 0.001414 +0 1 1 1 1 total 0.031163 0.001724 +2 1 2 1 1 total 0.030895 0.001664 +1 2 1 1 1 total 0.030973 0.001409 +3 2 2 1 1 total 0.031058 0.001358 mesh 1 group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.031233 0.002783 -2 1 2 1 1 1 total 0.033040 0.001647 -1 2 1 1 1 1 total 0.033581 0.002609 -3 2 2 1 1 1 total 0.030070 0.001287 +0 1 1 1 1 1 total 0.031781 0.002541 +2 1 2 1 1 1 total 0.032490 0.001487 +1 2 1 1 1 1 total 0.030274 0.002354 +3 2 2 1 1 1 total 0.030882 0.001500 mesh 1 group in nuclide mean std. dev. x y surf -3 1 1 x-max in 1 total 0.1816 0.006129 -2 1 1 x-max out 1 total 0.1730 0.004382 +3 1 1 x-max in 1 total 0.1888 0.008218 +2 1 1 x-max out 1 total 0.1828 0.008212 1 1 1 x-min in 1 total 0.0000 0.000000 0 1 1 x-min out 1 total 0.0000 0.000000 -7 1 1 y-max in 1 total 0.1790 0.011921 -6 1 1 y-max out 1 total 0.1802 0.016599 +7 1 1 y-max in 1 total 0.1820 0.012534 +6 1 1 y-max out 1 total 0.1794 0.015302 5 1 1 y-min in 1 total 0.0000 0.000000 4 1 1 y-min out 1 total 0.0000 0.000000 -19 1 2 x-max in 1 total 0.1872 0.012447 -18 1 2 x-max out 1 total 0.1952 0.015948 +19 1 2 x-max in 1 total 0.1870 0.011696 +18 1 2 x-max out 1 total 0.1850 0.012919 17 1 2 x-min in 1 total 0.0000 0.000000 16 1 2 x-min out 1 total 0.0000 0.000000 23 1 2 y-max in 1 total 0.0000 0.000000 22 1 2 y-max out 1 total 0.0000 0.000000 -21 1 2 y-min in 1 total 0.1802 0.016599 -20 1 2 y-min out 1 total 0.1790 0.011921 +21 1 2 y-min in 1 total 0.1794 0.015302 +20 1 2 y-min out 1 total 0.1820 0.012534 11 2 1 x-max in 1 total 0.0000 0.000000 10 2 1 x-max out 1 total 0.0000 0.000000 -9 2 1 x-min in 1 total 0.1730 0.004382 -8 2 1 x-min out 1 total 0.1816 0.006129 -15 2 1 y-max in 1 total 0.1778 0.009484 -14 2 1 y-max out 1 total 0.1822 0.010077 +9 2 1 x-min in 1 total 0.1828 0.008212 +8 2 1 x-min out 1 total 0.1888 0.008218 +15 2 1 y-max in 1 total 0.1850 0.011256 +14 2 1 y-max out 1 total 0.1870 0.009597 13 2 1 y-min in 1 total 0.0000 0.000000 12 2 1 y-min out 1 total 0.0000 0.000000 27 2 2 x-max in 1 total 0.0000 0.000000 26 2 2 x-max out 1 total 0.0000 0.000000 -25 2 2 x-min in 1 total 0.1952 0.015948 -24 2 2 x-min out 1 total 0.1872 0.012447 +25 2 2 x-min in 1 total 0.1850 0.012919 +24 2 2 x-min out 1 total 0.1870 0.011696 31 2 2 y-max in 1 total 0.0000 0.000000 30 2 2 y-max out 1 total 0.0000 0.000000 -29 2 2 y-min in 1 total 0.1822 0.010077 -28 2 2 y-min out 1 total 0.1778 0.009484 +29 2 2 y-min in 1 total 0.1870 0.009597 +28 2 2 y-min out 1 total 0.1850 0.011256 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 4.600555 0.368212 -2 1 2 1 1 total 4.572518 0.204129 -1 2 1 1 1 total 4.493168 0.329560 -3 2 2 1 1 total 4.489908 0.228574 +0 1 1 1 1 total 4.581283 0.329623 +2 1 2 1 1 total 4.452968 0.295762 +1 2 1 1 1 total 4.469295 0.239674 +3 2 2 1 1 total 4.570894 0.218416 mesh 1 group in nuclide mean std. dev. x y z -0 1 1 1 1 total 4.600555 0.368212 -2 1 2 1 1 total 4.571696 0.203824 -1 2 1 1 1 total 4.496020 0.330019 -3 2 2 1 1 total 4.492825 0.229077 +0 1 1 1 1 total 4.580746 0.329583 +2 1 2 1 1 total 4.452519 0.295716 +1 2 1 1 1 total 4.468066 0.239680 +3 2 2 1 1 total 4.573657 0.219069 mesh 1 delayedgroup group in nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.000007 4.371033e-07 -1 1 1 1 2 1 total 0.000035 2.256193e-06 -2 1 1 1 3 1 total 0.000033 2.153958e-06 -3 1 1 1 4 1 total 0.000075 4.829351e-06 -4 1 1 1 5 1 total 0.000031 1.979966e-06 -5 1 1 1 6 1 total 0.000013 8.294008e-07 -12 1 2 1 1 1 total 0.000007 2.763697e-07 -13 1 2 1 2 1 total 0.000036 1.426535e-06 -14 1 2 1 3 1 total 0.000034 1.361895e-06 -15 1 2 1 4 1 total 0.000077 3.053480e-06 -16 1 2 1 5 1 total 0.000031 1.251884e-06 -17 1 2 1 6 1 total 0.000013 5.244097e-07 -6 2 1 1 1 1 total 0.000007 3.644103e-07 -7 2 1 1 2 1 total 0.000038 1.880974e-06 -8 2 1 1 3 1 total 0.000036 1.795741e-06 -9 2 1 1 4 1 total 0.000080 4.026199e-06 -10 2 1 1 5 1 total 0.000033 1.650685e-06 -11 2 1 1 6 1 total 0.000014 6.914661e-07 -18 2 2 1 1 1 total 0.000007 3.263844e-07 -19 2 2 1 2 1 total 0.000036 1.684696e-06 -20 2 2 1 3 1 total 0.000034 1.608357e-06 -21 2 2 1 4 1 total 0.000076 3.606069e-06 -22 2 2 1 5 1 total 0.000031 1.478437e-06 -23 2 2 1 6 1 total 0.000013 6.193123e-07 +0 1 1 1 1 1 total 0.000007 3.812309e-07 +1 1 1 1 2 1 total 0.000036 1.967797e-06 +2 1 1 1 3 1 total 0.000034 1.878630e-06 +3 1 1 1 4 1 total 0.000077 4.212043e-06 +4 1 1 1 5 1 total 0.000031 1.726878e-06 +5 1 1 1 6 1 total 0.000013 7.233832e-07 +12 1 2 1 1 1 total 0.000007 3.663985e-07 +13 1 2 1 2 1 total 0.000035 1.891236e-06 +14 1 2 1 3 1 total 0.000034 1.805539e-06 +15 1 2 1 4 1 total 0.000076 4.048166e-06 +16 1 2 1 5 1 total 0.000031 1.659691e-06 +17 1 2 1 6 1 total 0.000013 6.952388e-07 +6 2 1 1 1 1 total 0.000007 3.123173e-07 +7 2 1 1 2 1 total 0.000035 1.612086e-06 +8 2 1 1 3 1 total 0.000034 1.539037e-06 +9 2 1 1 4 1 total 0.000076 3.450649e-06 +10 2 1 1 5 1 total 0.000031 1.414717e-06 +11 2 1 1 6 1 total 0.000013 5.926201e-07 +18 2 2 1 1 1 total 0.000007 2.946716e-07 +19 2 2 1 2 1 total 0.000036 1.521004e-06 +20 2 2 1 3 1 total 0.000034 1.452083e-06 +21 2 2 1 4 1 total 0.000076 3.255689e-06 +22 2 2 1 5 1 total 0.000031 1.334787e-06 +23 2 2 1 6 1 total 0.000013 5.591374e-07 mesh 1 delayedgroup group out nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.0 0.000000 -1 1 1 1 2 1 total 1.0 1.414214 -2 1 1 1 3 1 total 1.0 1.414214 -3 1 1 1 4 1 total 1.0 0.579241 -4 1 1 1 5 1 total 1.0 1.414214 +0 1 1 1 1 1 total 1.0 1.414214 +1 1 1 1 2 1 total 0.0 0.000000 +2 1 1 1 3 1 total 0.0 0.000000 +3 1 1 1 4 1 total 1.0 0.578922 +4 1 1 1 5 1 total 0.0 0.000000 5 1 1 1 6 1 total 0.0 0.000000 -12 1 2 1 1 1 total 1.0 1.414214 -13 1 2 1 2 1 total 0.0 0.000000 -14 1 2 1 3 1 total 1.0 0.866166 -15 1 2 1 4 1 total 1.0 0.868547 -16 1 2 1 5 1 total 1.0 0.873899 +12 1 2 1 1 1 total 0.0 0.000000 +13 1 2 1 2 1 total 1.0 0.578922 +14 1 2 1 3 1 total 1.0 1.414214 +15 1 2 1 4 1 total 1.0 1.414214 +16 1 2 1 5 1 total 1.0 0.875472 17 1 2 1 6 1 total 1.0 1.414214 6 2 1 1 1 1 total 0.0 0.000000 -7 2 1 1 2 1 total 1.0 0.654642 +7 2 1 1 2 1 total 0.0 0.000000 8 2 1 1 3 1 total 1.0 1.414214 -9 2 1 1 4 1 total 1.0 1.414214 -10 2 1 1 5 1 total 0.0 0.000000 +9 2 1 1 4 1 total 1.0 0.579392 +10 2 1 1 5 1 total 1.0 1.414214 11 2 1 1 6 1 total 0.0 0.000000 -18 2 2 1 1 1 total 1.0 0.867501 -19 2 2 1 2 1 total 0.0 0.000000 +18 2 2 1 1 1 total 1.0 0.868163 +19 2 2 1 2 1 total 1.0 1.414214 20 2 2 1 3 1 total 0.0 0.000000 -21 2 2 1 4 1 total 1.0 0.867501 +21 2 2 1 4 1 total 1.0 0.868969 22 2 2 1 5 1 total 1.0 1.414214 -23 2 2 1 6 1 total 1.0 1.414214 +23 2 2 1 6 1 total 0.0 0.000000 mesh 1 delayedgroup group in nuclide mean std. dev. x y z -0 1 1 1 1 1 total 0.000221 0.000018 -1 1 1 1 2 1 total 0.001141 0.000091 -2 1 1 1 3 1 total 0.001090 0.000087 -3 1 1 1 4 1 total 0.002443 0.000194 -4 1 1 1 5 1 total 0.001002 0.000080 -5 1 1 1 6 1 total 0.000420 0.000033 -12 1 2 1 1 1 total 0.000221 0.000011 -13 1 2 1 2 1 total 0.001142 0.000059 -14 1 2 1 3 1 total 0.001090 0.000056 -15 1 2 1 4 1 total 0.002444 0.000126 -16 1 2 1 5 1 total 0.001002 0.000052 -17 1 2 1 6 1 total 0.000420 0.000022 -6 2 1 1 1 1 total 0.000221 0.000013 -7 2 1 1 2 1 total 0.001140 0.000065 -8 2 1 1 3 1 total 0.001088 0.000062 -9 2 1 1 4 1 total 0.002440 0.000140 -10 2 1 1 5 1 total 0.001000 0.000057 -11 2 1 1 6 1 total 0.000419 0.000024 -18 2 2 1 1 1 total 0.000219 0.000014 -19 2 2 1 2 1 total 0.001132 0.000072 -20 2 2 1 3 1 total 0.001081 0.000069 -21 2 2 1 4 1 total 0.002424 0.000155 -22 2 2 1 5 1 total 0.000994 0.000064 -23 2 2 1 6 1 total 0.000416 0.000027 +0 1 1 1 1 1 total 0.000221 0.000015 +1 1 1 1 2 1 total 0.001140 0.000079 +2 1 1 1 3 1 total 0.001088 0.000075 +3 1 1 1 4 1 total 0.002440 0.000169 +4 1 1 1 5 1 total 0.001001 0.000069 +5 1 1 1 6 1 total 0.000419 0.000029 +12 1 2 1 1 1 total 0.000221 0.000013 +13 1 2 1 2 1 total 0.001139 0.000066 +14 1 2 1 3 1 total 0.001088 0.000063 +15 1 2 1 4 1 total 0.002439 0.000142 +16 1 2 1 5 1 total 0.001000 0.000058 +17 1 2 1 6 1 total 0.000419 0.000024 +6 2 1 1 1 1 total 0.000220 0.000013 +7 2 1 1 2 1 total 0.001138 0.000067 +8 2 1 1 3 1 total 0.001086 0.000064 +9 2 1 1 4 1 total 0.002435 0.000144 +10 2 1 1 5 1 total 0.000998 0.000059 +11 2 1 1 6 1 total 0.000418 0.000025 +18 2 2 1 1 1 total 0.000221 0.000013 +19 2 2 1 2 1 total 0.001141 0.000066 +20 2 2 1 3 1 total 0.001090 0.000063 +21 2 2 1 4 1 total 0.002443 0.000141 +22 2 2 1 5 1 total 0.001002 0.000058 +23 2 2 1 6 1 total 0.000420 0.000024 mesh 1 delayedgroup nuclide mean std. dev. x y z -0 1 1 1 1 total 0.013336 0.001054 -1 1 1 1 2 total 0.032739 0.002588 -2 1 1 1 3 total 0.120780 0.009548 -3 1 1 1 4 total 0.302780 0.023936 -4 1 1 1 5 total 0.849490 0.067157 -5 1 1 1 6 total 2.853000 0.225544 -12 1 2 1 1 total 0.013336 0.000716 -13 1 2 1 2 total 0.032739 0.001758 -14 1 2 1 3 total 0.120780 0.006485 -15 1 2 1 4 total 0.302780 0.016257 -16 1 2 1 5 total 0.849490 0.045611 -17 1 2 1 6 total 2.853000 0.153186 -6 2 1 1 1 total 0.013336 0.000744 -7 2 1 1 2 total 0.032739 0.001827 -8 2 1 1 3 total 0.120780 0.006740 -9 2 1 1 4 total 0.302780 0.016897 -10 2 1 1 5 total 0.849490 0.047407 -11 2 1 1 6 total 2.853000 0.159216 -18 2 2 1 1 total 0.013336 0.000872 -19 2 2 1 2 total 0.032739 0.002141 -20 2 2 1 3 total 0.120780 0.007899 -21 2 2 1 4 total 0.302780 0.019803 -22 2 2 1 5 total 0.849490 0.055560 -23 2 2 1 6 total 2.853000 0.186598 +0 1 1 1 1 total 0.013336 0.000919 +1 1 1 1 2 total 0.032739 0.002257 +2 1 1 1 3 total 0.120780 0.008325 +3 1 1 1 4 total 0.302780 0.020871 +4 1 1 1 5 total 0.849490 0.058555 +5 1 1 1 6 total 2.853000 0.196656 +12 1 2 1 1 total 0.013336 0.000770 +13 1 2 1 2 total 0.032739 0.001891 +14 1 2 1 3 total 0.120780 0.006977 +15 1 2 1 4 total 0.302780 0.017490 +16 1 2 1 5 total 0.849490 0.049071 +17 1 2 1 6 total 2.853000 0.164804 +6 2 1 1 1 total 0.013336 0.000790 +7 2 1 1 2 total 0.032739 0.001940 +8 2 1 1 3 total 0.120780 0.007157 +9 2 1 1 4 total 0.302780 0.017942 +10 2 1 1 5 total 0.849490 0.050338 +11 2 1 1 6 total 2.853000 0.169061 +18 2 2 1 1 total 0.013336 0.000757 +19 2 2 1 2 total 0.032739 0.001858 +20 2 2 1 3 total 0.120780 0.006855 +21 2 2 1 4 total 0.302780 0.017186 +22 2 2 1 5 total 0.849490 0.048217 +23 2 2 1 6 total 2.853000 0.161935 mesh 1 delayedgroup group in group out nuclide mean std. dev. x y z -0 1 1 1 1 1 1 total 0.000000 0.000000 -1 1 1 1 2 1 1 total 0.000029 0.000030 -2 1 1 1 3 1 1 total 0.000028 0.000028 +0 1 1 1 1 1 1 total 0.000026 0.000026 +1 1 1 1 2 1 1 total 0.000000 0.000000 +2 1 1 1 3 1 1 total 0.000000 0.000000 3 1 1 1 4 1 1 total 0.000083 0.000034 -4 1 1 1 5 1 1 total 0.000028 0.000028 +4 1 1 1 5 1 1 total 0.000000 0.000000 5 1 1 1 6 1 1 total 0.000000 0.000000 -12 1 2 1 1 1 1 total 0.000029 0.000029 -13 1 2 1 2 1 1 total 0.000000 0.000000 -14 1 2 1 3 1 1 total 0.000052 0.000032 -15 1 2 1 4 1 1 total 0.000055 0.000034 -16 1 2 1 5 1 1 total 0.000059 0.000037 +12 1 2 1 1 1 1 total 0.000000 0.000000 +13 1 2 1 2 1 1 total 0.000081 0.000033 +14 1 2 1 3 1 1 total 0.000026 0.000026 +15 1 2 1 4 1 1 total 0.000026 0.000026 +16 1 2 1 5 1 1 total 0.000059 0.000036 17 1 2 1 6 1 1 total 0.000033 0.000033 6 2 1 1 1 1 1 total 0.000000 0.000000 -7 2 1 1 2 1 1 total 0.000113 0.000053 -8 2 1 1 3 1 1 total 0.000034 0.000034 -9 2 1 1 4 1 1 total 0.000029 0.000029 -10 2 1 1 5 1 1 total 0.000000 0.000000 +7 2 1 1 2 1 1 total 0.000000 0.000000 +8 2 1 1 3 1 1 total 0.000032 0.000032 +9 2 1 1 4 1 1 total 0.000080 0.000033 +10 2 1 1 5 1 1 total 0.000028 0.000028 11 2 1 1 6 1 1 total 0.000000 0.000000 -18 2 2 1 1 1 1 total 0.000053 0.000033 -19 2 2 1 2 1 1 total 0.000000 0.000000 +18 2 2 1 1 1 1 total 0.000054 0.000033 +19 2 2 1 2 1 1 total 0.000029 0.000029 20 2 2 1 3 1 1 total 0.000000 0.000000 -21 2 2 1 4 1 1 total 0.000053 0.000033 -22 2 2 1 5 1 1 total 0.000031 0.000031 -23 2 2 1 6 1 1 total 0.000025 0.000025 +21 2 2 1 4 1 1 total 0.000054 0.000033 +22 2 2 1 5 1 1 total 0.000032 0.000032 +23 2 2 1 6 1 1 total 0.000000 0.000000 diff --git a/tests/regression_tests/mgxs_library_mesh/test.py b/tests/regression_tests/mgxs_library_mesh/test.py index 3660d3eb74..89c68a75a3 100644 --- a/tests/regression_tests/mgxs_library_mesh/test.py +++ b/tests/regression_tests/mgxs_library_mesh/test.py @@ -19,12 +19,12 @@ def model(): zr.add_nuclide('Zr90', 1.0) model.materials.extend([fuel, zr]) - box1 = openmc.model.rectangular_prism(10.0, 10.0) - box2 = openmc.model.rectangular_prism(20.0, 20.0, boundary_type='reflective') + box1 = openmc.model.RectangularPrism(10.0, 10.0) + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') - cell1 = openmc.Cell(fill=fuel, region=box1 & +bottom & -top) - cell2 = openmc.Cell(fill=zr, region=~box1 & box2 & +bottom & -top) + cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) + cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) model.settings.batches = 5 diff --git a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat index 0d9dbf8215..af09268cde 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/inputs_true.dat @@ -1,1862 +1,1954 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 1 - - - 3 - - - 0.0 20000000.0 - - - 1 2 3 4 5 6 - - - 2 - - - 3 - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - absorption - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - total - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - total - nu-scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - nu-fission - analog - - - 1 2 5 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - scatter - tracklength - - - 1 2 5 28 - total - scatter - analog - - - 1 2 5 - total - nu-scatter - analog - - - 1 52 - total - nu-fission - analog - - - 1 5 - total - nu-fission - analog - - - 1 52 - total - prompt-nu-fission - analog - - - 1 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - total - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,elastic) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,level) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,2n) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,na) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,nc) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,gamma) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,a) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,Xa) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - heating - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - damage-energy - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,n1) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - total - (n,a0) - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - total - (n,nc) - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - (n,n1) - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - total - (n,2n) - analog - - - 1 2 - total - flux - tracklength - - - 1 106 2 - total - delayed-nu-fission - tracklength - - - 1 106 52 - total - delayed-nu-fission - analog - - - 1 106 5 - total - delayed-nu-fission - analog - - - 1 2 - total - nu-fission - tracklength - - - 1 106 2 - total - delayed-nu-fission - tracklength - - - 1 106 - total - delayed-nu-fission - tracklength - - - 1 106 - total - decay-rate - tracklength - - - 1 2 - total - flux - analog - - - 1 106 2 5 - total - delayed-nu-fission - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - total - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - total - tracklength - - - 120 2 - total - flux - analog - - - 120 5 6 - total - scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - total - tracklength - - - 120 2 - total - flux - analog - - - 120 5 6 - total - nu-scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - absorption - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - absorption - tracklength - - - 120 2 - total - fission - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - fission - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - nu-fission - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - kappa-fission - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - scatter - tracklength - - - 120 2 - total - flux - analog - - - 120 2 - total - nu-scatter - analog - - - 120 2 - total - flux - analog - - - 120 2 5 28 - total - scatter - analog - - - 120 2 - total - flux - analog - - - 120 2 5 28 - total - nu-scatter - analog - - - 120 2 5 - total - nu-scatter - analog - - - 120 2 5 - total - scatter - analog - - - 120 2 - total - flux - analog - - - 120 2 5 - total - nu-fission - analog - - - 120 2 5 - total - scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - scatter - tracklength - - - 120 2 5 28 - total - scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - scatter - tracklength - - - 120 2 5 28 - total - scatter - analog - - - 120 2 5 - total - nu-scatter - analog - - - 120 52 - total - nu-fission - analog - - - 120 5 - total - nu-fission - analog - - - 120 52 - total - prompt-nu-fission - analog - - - 120 5 - total - prompt-nu-fission - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - inverse-velocity - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - prompt-nu-fission - tracklength - - - 120 2 - total - flux - analog - - - 120 2 5 - total - prompt-nu-fission - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - total - tracklength - - - 120 2 - total - flux - analog - - - 120 5 6 - total - scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - total - tracklength - - - 120 2 - total - flux - analog - - - 120 5 6 - total - nu-scatter - analog - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,elastic) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,level) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,2n) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,na) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,nc) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,gamma) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,a) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,Xa) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - heating - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - damage-energy - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,n1) - tracklength - - - 120 2 - total - flux - tracklength - - - 120 2 - total - (n,a0) - tracklength - - - 120 2 - total - flux - analog - - - 120 2 5 - total - (n,nc) - analog - - - 120 2 - total - flux - analog - - - 120 2 5 - total - (n,n1) - analog - - - 120 2 - total - flux - analog - - - 120 2 5 - total - (n,2n) - analog - - - 120 2 - total - flux - tracklength - - - 120 106 2 - total - delayed-nu-fission - tracklength - - - 120 106 52 - total - delayed-nu-fission - analog - - - 120 106 5 - total - delayed-nu-fission - analog - - - 120 2 - total - nu-fission - tracklength - - - 120 106 2 - total - delayed-nu-fission - tracklength - - - 120 106 - total - delayed-nu-fission - tracklength - - - 120 106 - total - decay-rate - tracklength - - - 120 2 - total - flux - analog - - - 120 106 2 5 - total - delayed-nu-fission - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - total - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - total - tracklength - - - 239 2 - total - flux - analog - - - 239 5 6 - total - scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - total - tracklength - - - 239 2 - total - flux - analog - - - 239 5 6 - total - nu-scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - absorption - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - absorption - tracklength - - - 239 2 - total - fission - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - fission - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - nu-fission - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - kappa-fission - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - scatter - tracklength - - - 239 2 - total - flux - analog - - - 239 2 - total - nu-scatter - analog - - - 239 2 - total - flux - analog - - - 239 2 5 28 - total - scatter - analog - - - 239 2 - total - flux - analog - - - 239 2 5 28 - total - nu-scatter - analog - - - 239 2 5 - total - nu-scatter - analog - - - 239 2 5 - total - scatter - analog - - - 239 2 - total - flux - analog - - - 239 2 5 - total - nu-fission - analog - - - 239 2 5 - total - scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - scatter - tracklength - - - 239 2 5 28 - total - scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - scatter - tracklength - - - 239 2 5 28 - total - scatter - analog - - - 239 2 5 - total - nu-scatter - analog - - - 239 52 - total - nu-fission - analog - - - 239 5 - total - nu-fission - analog - - - 239 52 - total - prompt-nu-fission - analog - - - 239 5 - total - prompt-nu-fission - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - inverse-velocity - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - prompt-nu-fission - tracklength - - - 239 2 - total - flux - analog - - - 239 2 5 - total - prompt-nu-fission - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - total - tracklength - - - 239 2 - total - flux - analog - - - 239 5 6 - total - scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - total - tracklength - - - 239 2 - total - flux - analog - - - 239 5 6 - total - nu-scatter - analog - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,elastic) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,level) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,2n) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,na) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,nc) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,gamma) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,a) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,Xa) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - heating - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - damage-energy - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,n1) - tracklength - - - 239 2 - total - flux - tracklength - - - 239 2 - total - (n,a0) - tracklength - - - 239 2 - total - flux - analog - - - 239 2 5 - total - (n,nc) - analog - - - 239 2 - total - flux - analog - - - 239 2 5 - total - (n,n1) - analog - - - 239 2 - total - flux - analog - - - 239 2 5 - total - (n,2n) - analog - - - 239 2 - total - flux - tracklength - - - 239 106 2 - total - delayed-nu-fission - tracklength - - - 239 106 52 - total - delayed-nu-fission - analog - - - 239 106 5 - total - delayed-nu-fission - analog - - - 239 2 - total - nu-fission - tracklength - - - 239 106 2 - total - delayed-nu-fission - tracklength - - - 239 106 - total - delayed-nu-fission - tracklength - - - 239 106 - total - decay-rate - tracklength - - - 239 2 - total - flux - analog - - - 239 106 2 5 - total - delayed-nu-fission - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 1 + + + 3 + + + 0.0 20000000.0 + + + 1 2 3 4 5 6 + + + 2 + + + 3 + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + (n,3n) + tracklength + + + 1 2 + total + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + absorption + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + total + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + total + nu-scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + nu-fission + analog + + + 1 2 5 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + scatter + tracklength + + + 1 2 5 30 + total + scatter + analog + + + 1 2 5 + total + nu-scatter + analog + + + 1 54 + total + nu-fission + analog + + + 1 5 + total + nu-fission + analog + + + 1 54 + total + prompt-nu-fission + analog + + + 1 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + total + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,elastic) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,level) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,2n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,na) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,nc) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,gamma) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,a) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,Xa) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + heating + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + damage-energy + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,n1) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + total + (n,a0) + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + total + (n,nc) + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + (n,n1) + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + total + (n,2n) + analog + + + 1 2 + total + flux + tracklength + + + 1 108 2 + total + delayed-nu-fission + tracklength + + + 1 108 54 + total + delayed-nu-fission + analog + + + 1 108 5 + total + delayed-nu-fission + analog + + + 1 2 + total + nu-fission + tracklength + + + 1 108 2 + total + delayed-nu-fission + tracklength + + + 1 108 + total + delayed-nu-fission + tracklength + + + 1 108 + total + decay-rate + tracklength + + + 1 2 + total + flux + analog + + + 1 108 2 5 + total + delayed-nu-fission + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + total + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + total + tracklength + + + 122 2 + total + flux + analog + + + 122 5 6 + total + scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + total + tracklength + + + 122 2 + total + flux + analog + + + 122 5 6 + total + nu-scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + absorption + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + absorption + tracklength + + + 122 2 + total + (n,2n) + tracklength + + + 122 2 + total + (n,3n) + tracklength + + + 122 2 + total + (n,4n) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + absorption + tracklength + + + 122 2 + total + fission + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + fission + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + nu-fission + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + kappa-fission + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + scatter + tracklength + + + 122 2 + total + flux + analog + + + 122 2 + total + nu-scatter + analog + + + 122 2 + total + flux + analog + + + 122 2 5 30 + total + scatter + analog + + + 122 2 + total + flux + analog + + + 122 2 5 30 + total + nu-scatter + analog + + + 122 2 5 + total + nu-scatter + analog + + + 122 2 5 + total + scatter + analog + + + 122 2 + total + flux + analog + + + 122 2 5 + total + nu-fission + analog + + + 122 2 5 + total + scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + scatter + tracklength + + + 122 2 5 30 + total + scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + scatter + tracklength + + + 122 2 5 30 + total + scatter + analog + + + 122 2 5 + total + nu-scatter + analog + + + 122 54 + total + nu-fission + analog + + + 122 5 + total + nu-fission + analog + + + 122 54 + total + prompt-nu-fission + analog + + + 122 5 + total + prompt-nu-fission + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + inverse-velocity + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + prompt-nu-fission + tracklength + + + 122 2 + total + flux + analog + + + 122 2 5 + total + prompt-nu-fission + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + total + tracklength + + + 122 2 + total + flux + analog + + + 122 5 6 + total + scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + total + tracklength + + + 122 2 + total + flux + analog + + + 122 5 6 + total + nu-scatter + analog + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,elastic) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,level) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,2n) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,na) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,nc) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,gamma) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,a) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,Xa) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + heating + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + damage-energy + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,n1) + tracklength + + + 122 2 + total + flux + tracklength + + + 122 2 + total + (n,a0) + tracklength + + + 122 2 + total + flux + analog + + + 122 2 5 + total + (n,nc) + analog + + + 122 2 + total + flux + analog + + + 122 2 5 + total + (n,n1) + analog + + + 122 2 + total + flux + analog + + + 122 2 5 + total + (n,2n) + analog + + + 122 2 + total + flux + tracklength + + + 122 108 2 + total + delayed-nu-fission + tracklength + + + 122 108 54 + total + delayed-nu-fission + analog + + + 122 108 5 + total + delayed-nu-fission + analog + + + 122 2 + total + nu-fission + tracklength + + + 122 108 2 + total + delayed-nu-fission + tracklength + + + 122 108 + total + delayed-nu-fission + tracklength + + + 122 108 + total + decay-rate + tracklength + + + 122 2 + total + flux + analog + + + 122 108 2 5 + total + delayed-nu-fission + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + total + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + total + tracklength + + + 243 2 + total + flux + analog + + + 243 5 6 + total + scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + total + tracklength + + + 243 2 + total + flux + analog + + + 243 5 6 + total + nu-scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + absorption + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + absorption + tracklength + + + 243 2 + total + (n,2n) + tracklength + + + 243 2 + total + (n,3n) + tracklength + + + 243 2 + total + (n,4n) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + absorption + tracklength + + + 243 2 + total + fission + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + fission + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + nu-fission + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + kappa-fission + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + scatter + tracklength + + + 243 2 + total + flux + analog + + + 243 2 + total + nu-scatter + analog + + + 243 2 + total + flux + analog + + + 243 2 5 30 + total + scatter + analog + + + 243 2 + total + flux + analog + + + 243 2 5 30 + total + nu-scatter + analog + + + 243 2 5 + total + nu-scatter + analog + + + 243 2 5 + total + scatter + analog + + + 243 2 + total + flux + analog + + + 243 2 5 + total + nu-fission + analog + + + 243 2 5 + total + scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + scatter + tracklength + + + 243 2 5 30 + total + scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + scatter + tracklength + + + 243 2 5 30 + total + scatter + analog + + + 243 2 5 + total + nu-scatter + analog + + + 243 54 + total + nu-fission + analog + + + 243 5 + total + nu-fission + analog + + + 243 54 + total + prompt-nu-fission + analog + + + 243 5 + total + prompt-nu-fission + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + inverse-velocity + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + prompt-nu-fission + tracklength + + + 243 2 + total + flux + analog + + + 243 2 5 + total + prompt-nu-fission + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + total + tracklength + + + 243 2 + total + flux + analog + + + 243 5 6 + total + scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + total + tracklength + + + 243 2 + total + flux + analog + + + 243 5 6 + total + nu-scatter + analog + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,elastic) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,level) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,2n) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,na) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,nc) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,gamma) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,a) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,Xa) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + heating + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + damage-energy + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,n1) + tracklength + + + 243 2 + total + flux + tracklength + + + 243 2 + total + (n,a0) + tracklength + + + 243 2 + total + flux + analog + + + 243 2 5 + total + (n,nc) + analog + + + 243 2 + total + flux + analog + + + 243 2 5 + total + (n,n1) + analog + + + 243 2 + total + flux + analog + + + 243 2 5 + total + (n,2n) + analog + + + 243 2 + total + flux + tracklength + + + 243 108 2 + total + delayed-nu-fission + tracklength + + + 243 108 54 + total + delayed-nu-fission + analog + + + 243 108 5 + total + delayed-nu-fission + analog + + + 243 2 + total + nu-fission + tracklength + + + 243 108 2 + total + delayed-nu-fission + tracklength + + + 243 108 + total + delayed-nu-fission + tracklength + + + 243 108 + total + decay-rate + tracklength + + + 243 2 + total + flux + analog + + + 243 108 2 5 + total + delayed-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat index 32ae7f4cf2..aec8e8bbc1 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat @@ -1,250 +1,254 @@ total material group in nuclide mean std. dev. -1 1 1 total 0.416386 0.014741 -0 1 2 total 0.658651 0.060521 +1 1 1 total 0.422772 0.014069 +0 1 2 total 0.661309 0.049458 transport material group in nuclide mean std. dev. -1 1 1 total 0.378343 0.016533 -0 1 2 total 0.641137 0.060797 +1 1 1 total 0.383831 0.015331 +0 1 2 total 0.679181 0.050786 nu-transport material group in nuclide mean std. dev. -1 1 1 total 0.378437 0.016556 -0 1 2 total 0.641137 0.060797 +1 1 1 total 0.383831 0.015331 +0 1 2 total 0.679181 0.050786 absorption material group in nuclide mean std. dev. -1 1 1 total 0.027335 0.002038 -0 1 2 total 0.263007 0.029616 +1 1 1 total 0.027181 0.001539 +0 1 2 total 0.265559 0.020699 +reduced absorption + material group in nuclide mean std. dev. +1 1 1 total 0.027162 0.001538 +0 1 2 total 0.265559 0.020699 capture material group in nuclide mean std. dev. -1 1 1 total 0.019722 0.001980 -0 1 2 total 0.071284 0.032059 +1 1 1 total 0.019717 0.001492 +0 1 2 total 0.072029 0.019378 fission material group in nuclide mean std. dev. -1 1 1 total 0.007613 0.000245 -0 1 2 total 0.191723 0.021698 +1 1 1 total 0.007464 0.000222 +0 1 2 total 0.193530 0.015119 nu-fission material group in nuclide mean std. dev. -1 1 1 total 0.019339 0.000628 -0 1 2 total 0.467173 0.052872 +1 1 1 total 0.018925 0.000543 +0 1 2 total 0.471574 0.036840 kappa-fission material group in nuclide mean std. dev. -1 1 1 total 1.482697e+06 4.755501e+04 -0 1 2 total 3.708035e+07 4.196524e+06 +1 1 1 total 1.453014e+06 4.287190e+04 +0 1 2 total 3.742974e+07 2.924022e+06 scatter material group in nuclide mean std. dev. -1 1 1 total 0.389051 0.013216 -0 1 2 total 0.395644 0.031715 +1 1 1 total 0.395591 0.013417 +0 1 2 total 0.395750 0.029309 nu-scatter material group in nuclide mean std. dev. -1 1 1 total 0.392313 0.009662 -0 1 2 total 0.385086 0.024132 +1 1 1 total 0.392941 0.019689 +0 1 2 total 0.396262 0.027471 scatter matrix material group in group out legendre nuclide mean std. dev. -12 1 1 1 P0 total 0.391262 0.009858 -13 1 1 1 P1 total 0.038043 0.007486 -14 1 1 1 P2 total 0.019717 0.003619 -15 1 1 1 P3 total 0.018917 0.001007 -8 1 1 2 P0 total 0.000876 0.000554 -9 1 1 2 P1 total -0.000275 0.000386 -10 1 1 2 P2 total 0.000074 0.000114 -11 1 1 2 P3 total 0.000141 0.000113 +12 1 1 1 P0 total 0.392419 0.019837 +13 1 1 1 P1 total 0.038941 0.006089 +14 1 1 1 P2 total 0.019512 0.003548 +15 1 1 1 P3 total 0.012951 0.002399 +8 1 1 2 P0 total 0.000522 0.000349 +9 1 1 2 P1 total -0.000301 0.000185 +10 1 1 2 P2 total 0.000045 0.000147 +11 1 1 2 P3 total 0.000069 0.000162 4 1 2 1 P0 total 0.000000 0.000000 5 1 2 1 P1 total 0.000000 0.000000 6 1 2 1 P2 total 0.000000 0.000000 7 1 2 1 P3 total 0.000000 0.000000 -0 1 2 2 P0 total 0.385086 0.024132 -1 1 2 2 P1 total 0.019196 0.005279 -2 1 2 2 P2 total -0.005654 0.006088 -3 1 2 2 P3 total 0.002768 0.007045 +0 1 2 2 P0 total 0.396262 0.027471 +1 1 2 2 P1 total -0.016133 0.010911 +2 1 2 2 P2 total -0.001147 0.010536 +3 1 2 2 P3 total 0.005359 0.008143 nu-scatter matrix material group in group out legendre nuclide mean std. dev. -12 1 1 1 P0 total 0.391437 0.009813 -13 1 1 1 P1 total 0.037949 0.007537 -14 1 1 1 P2 total 0.019705 0.003623 -15 1 1 1 P3 total 0.018991 0.001065 -8 1 1 2 P0 total 0.000876 0.000554 -9 1 1 2 P1 total -0.000275 0.000386 -10 1 1 2 P2 total 0.000074 0.000114 -11 1 1 2 P3 total 0.000141 0.000113 +12 1 1 1 P0 total 0.392419 0.019837 +13 1 1 1 P1 total 0.038941 0.006089 +14 1 1 1 P2 total 0.019512 0.003548 +15 1 1 1 P3 total 0.012951 0.002399 +8 1 1 2 P0 total 0.000522 0.000349 +9 1 1 2 P1 total -0.000301 0.000185 +10 1 1 2 P2 total 0.000045 0.000147 +11 1 1 2 P3 total 0.000069 0.000162 4 1 2 1 P0 total 0.000000 0.000000 5 1 2 1 P1 total 0.000000 0.000000 6 1 2 1 P2 total 0.000000 0.000000 7 1 2 1 P3 total 0.000000 0.000000 -0 1 2 2 P0 total 0.385086 0.024132 -1 1 2 2 P1 total 0.019196 0.005279 -2 1 2 2 P2 total -0.005654 0.006088 -3 1 2 2 P3 total 0.002768 0.007045 +0 1 2 2 P0 total 0.396262 0.027471 +1 1 2 2 P1 total -0.016133 0.010911 +2 1 2 2 P2 total -0.001147 0.010536 +3 1 2 2 P3 total 0.005359 0.008143 multiplicity matrix - material group in group out nuclide mean std. dev. -3 1 1 1 total 1.000448 0.020416 -2 1 1 2 total 1.000000 0.894427 -1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 1.000000 0.085266 + material group in group out nuclide mean std. dev. +3 1 1 1 total 1.0 0.054620 +2 1 1 2 total 1.0 0.942809 +1 1 2 1 total 0.0 0.000000 +0 1 2 2 total 1.0 0.080341 nu-fission matrix material group in group out nuclide mean std. dev. -3 1 1 1 total 0.017516 0.001935 +3 1 1 1 total 0.019257 0.001831 2 1 1 2 total 0.000000 0.000000 -1 1 2 1 total 0.498681 0.013398 +1 1 2 1 total 0.459401 0.022054 0 1 2 2 total 0.000000 0.000000 scatter probability matrix material group in group out nuclide mean std. dev. -3 1 1 1 total 0.997766 0.020541 -2 1 1 2 total 0.002234 0.001413 +3 1 1 1 total 0.998671 0.054518 +2 1 1 2 total 0.001329 0.000888 1 1 2 1 total 0.000000 0.000000 -0 1 2 2 total 1.000000 0.085266 +0 1 2 2 total 1.000000 0.080341 consistent scatter matrix material group in group out legendre nuclide mean std. dev. -12 1 1 1 P0 total 0.388182 0.015419 -13 1 1 1 P1 total 0.037744 0.007517 -14 1 1 1 P2 total 0.019562 0.003641 -15 1 1 1 P3 total 0.018769 0.001154 -8 1 1 2 P0 total 0.000869 0.000551 -9 1 1 2 P1 total -0.000273 0.000383 -10 1 1 2 P2 total 0.000073 0.000113 -11 1 1 2 P3 total 0.000140 0.000112 +12 1 1 1 P0 total 0.395065 0.025390 +13 1 1 1 P1 total 0.039204 0.006325 +14 1 1 1 P2 total 0.019644 0.003656 +15 1 1 1 P3 total 0.013039 0.002470 +8 1 1 2 P0 total 0.000526 0.000352 +9 1 1 2 P1 total -0.000303 0.000186 +10 1 1 2 P2 total 0.000045 0.000148 +11 1 1 2 P3 total 0.000069 0.000163 4 1 2 1 P0 total 0.000000 0.000000 5 1 2 1 P1 total 0.000000 0.000000 6 1 2 1 P2 total 0.000000 0.000000 7 1 2 1 P3 total 0.000000 0.000000 -0 1 2 2 P0 total 0.395644 0.046302 -1 1 2 2 P1 total 0.019723 0.005763 -2 1 2 2 P2 total -0.005809 0.006281 -3 1 2 2 P3 total 0.002843 0.007244 +0 1 2 2 P0 total 0.395750 0.043243 +1 1 2 2 P1 total -0.016112 0.010982 +2 1 2 2 P2 total -0.001146 0.010523 +3 1 2 2 P3 total 0.005353 0.008145 consistent nu-scatter matrix material group in group out legendre nuclide mean std. dev. -12 1 1 1 P0 total 0.388355 0.017343 -13 1 1 1 P1 total 0.037760 0.007560 -14 1 1 1 P2 total 0.019571 0.003664 -15 1 1 1 P3 total 0.018777 0.001216 -8 1 1 2 P0 total 0.000869 0.000953 -9 1 1 2 P1 total -0.000273 0.000454 -10 1 1 2 P2 total 0.000073 0.000131 -11 1 1 2 P3 total 0.000140 0.000168 +12 1 1 1 P0 total 0.395065 0.033321 +13 1 1 1 P1 total 0.039204 0.006677 +14 1 1 1 P2 total 0.019644 0.003810 +15 1 1 1 P3 total 0.013039 0.002571 +8 1 1 2 P0 total 0.000526 0.000608 +9 1 1 2 P1 total -0.000303 0.000341 +10 1 1 2 P2 total 0.000045 0.000154 +11 1 1 2 P3 total 0.000069 0.000175 4 1 2 1 P0 total 0.000000 0.000000 5 1 2 1 P1 total 0.000000 0.000000 6 1 2 1 P2 total 0.000000 0.000000 7 1 2 1 P3 total 0.000000 0.000000 -0 1 2 2 P0 total 0.395644 0.057288 -1 1 2 2 P1 total 0.019723 0.006004 -2 1 2 2 P2 total -0.005809 0.006300 -3 1 2 2 P3 total 0.002843 0.007248 +0 1 2 2 P0 total 0.395750 0.053673 +1 1 2 2 P1 total -0.016112 0.011058 +2 1 2 2 P2 total -0.001146 0.010523 +3 1 2 2 P3 total 0.005353 0.008157 chi material group out nuclide mean std. dev. -1 1 1 total 1.0 0.029903 +1 1 1 total 1.0 0.015644 0 1 2 total 0.0 0.000000 chi-prompt material group out nuclide mean std. dev. -1 1 1 total 1.0 0.028494 +1 1 1 total 1.0 0.017529 0 1 2 total 0.0 0.000000 inverse-velocity material group in nuclide mean std. dev. -1 1 1 total 6.148661e-08 3.647428e-09 -0 1 2 total 2.844242e-06 3.098230e-07 +1 1 1 total 6.047675e-08 4.367288e-09 +0 1 2 total 2.861927e-06 2.241423e-07 prompt-nu-fission material group in nuclide mean std. dev. -1 1 1 total 0.019154 0.000623 -0 1 2 total 0.464134 0.052528 +1 1 1 total 0.018748 0.00054 +0 1 2 total 0.468507 0.03660 prompt-nu-fission matrix material group in group out nuclide mean std. dev. -3 1 1 1 total 0.017516 0.001935 +3 1 1 1 total 0.019049 0.001675 2 1 1 2 total 0.000000 0.000000 -1 1 2 1 total 0.495450 0.012592 +1 1 2 1 total 0.454399 0.022578 0 1 2 2 total 0.000000 0.000000 diffusion-coefficient material group in nuclide mean std. dev. -1 1 1 total 0.881035 0.038500 -0 1 2 total 0.519910 0.049301 +1 1 1 total 0.868438 0.034686 +0 1 2 total 0.490787 0.036698 nu-diffusion-coefficient material group in nuclide mean std. dev. -1 1 1 total 0.880816 0.038534 -0 1 2 total 0.519910 0.049301 +1 1 1 total 0.868438 0.034686 +0 1 2 total 0.490787 0.036698 (n,elastic) material group in nuclide mean std. dev. -1 1 1 total 0.361427 0.011879 -0 1 2 total 0.395644 0.031715 +1 1 1 total 0.368341 0.013033 +0 1 2 total 0.395750 0.029309 (n,level) material group in nuclide mean std. dev. -1 1 1 total 0.000549 0.000035 +1 1 1 total 0.000518 0.000016 0 1 2 total 0.000000 0.000000 (n,2n) material group in nuclide mean std. dev. -1 1 1 total 0.000058 0.000012 +1 1 1 total 0.000019 0.000012 0 1 2 total 0.000000 0.000000 (n,na) - material group in nuclide mean std. dev. -1 1 1 total 4.608391e-11 2.324841e-11 -0 1 2 total 0.000000e+00 0.000000e+00 + material group in nuclide mean std. dev. +1 1 1 total 0.0 0.0 +0 1 2 total 0.0 0.0 (n,nc) material group in nuclide mean std. dev. -1 1 1 total 0.008349 0.000869 +1 1 1 total 0.007599 0.000559 0 1 2 total 0.000000 0.000000 (n,gamma) material group in nuclide mean std. dev. -1 1 1 total 0.019640 0.001935 -0 1 2 total 0.071284 0.007918 +1 1 1 total 0.019608 0.001470 +0 1 2 total 0.072029 0.005584 (n,a) material group in nuclide mean std. dev. -1 1 1 total 0.000082 0.000016 -0 1 2 total 0.000000 0.000000 +1 1 1 total 0.000109 0.00002 +0 1 2 total 0.000000 0.00000 (n,Xa) material group in nuclide mean std. dev. -1 1 1 total 0.000082 0.000016 -0 1 2 total 0.000000 0.000000 +1 1 1 total 0.000109 0.00002 +0 1 2 total 0.000000 0.00000 heating material group in nuclide mean std. dev. -1 1 1 total 1.302118e+06 4.240633e+04 -0 1 2 total 3.235834e+07 3.683247e+06 +1 1 1 total 1.270644e+06 3.732629e+04 +0 1 2 total 3.241569e+07 2.530346e+06 damage-energy material group in nuclide mean std. dev. -1 1 1 total 2323.848080 106.914413 -0 1 2 total 1349.749682 152.756195 +1 1 1 total 2405.735342 79.042000 +0 1 2 total 1362.470597 106.435803 (n,n1) material group in nuclide mean std. dev. -1 1 1 total 0.011709 0.000412 +1 1 1 total 0.011931 0.000455 0 1 2 total 0.000000 0.000000 (n,a0) material group in nuclide mean std. dev. -1 1 1 total 0.000076 0.000018 -0 1 2 total 0.000000 0.000000 +1 1 1 total 0.000108 0.00002 +0 1 2 total 0.000000 0.00000 (n,nc) matrix material group in group out nuclide mean std. dev. -3 1 1 1 total 0.007009 0.000797 +3 1 1 1 total 0.005745 0.001068 2 1 1 2 total 0.000000 0.000000 1 1 2 1 total 0.000000 0.000000 0 1 2 2 total 0.000000 0.000000 (n,n1) matrix - material group in group out nuclide mean std. dev. -3 1 1 1 total 0.01209 0.00108 -2 1 1 2 total 0.00000 0.00000 -1 1 2 1 total 0.00000 0.00000 -0 1 2 2 total 0.00000 0.00000 -(n,2n) matrix material group in group out nuclide mean std. dev. -3 1 1 1 total 0.000175 0.000175 +3 1 1 1 total 0.013232 0.001025 2 1 1 2 total 0.000000 0.000000 1 1 2 1 total 0.000000 0.000000 0 1 2 2 total 0.000000 0.000000 +(n,2n) matrix + material group in group out nuclide mean std. dev. +3 1 1 1 total 0.0 0.0 +2 1 1 2 total 0.0 0.0 +1 1 2 1 total 0.0 0.0 +0 1 2 2 total 0.0 0.0 delayed-nu-fission material delayedgroup group in nuclide mean std. dev. -1 1 1 1 total 0.000004 1.358228e-07 -3 1 2 1 total 0.000027 8.873679e-07 -5 1 3 1 total 0.000028 1.017223e-06 -7 1 4 1 total 0.000071 3.064066e-06 -9 1 5 1 total 0.000039 2.180731e-06 -11 1 6 1 total 0.000016 8.808101e-07 -0 1 1 2 total 0.000106 1.203961e-05 -2 1 2 2 total 0.000549 6.214477e-05 -4 1 3 2 total 0.000524 5.932882e-05 -6 1 4 2 total 0.001175 1.330202e-04 -8 1 5 2 total 0.000482 5.453651e-05 -10 1 6 2 total 0.000202 2.284515e-05 +1 1 1 1 total 0.000004 1.226367e-07 +3 1 2 1 total 0.000026 6.725093e-07 +5 1 3 1 total 0.000027 7.028316e-07 +7 1 4 1 total 0.000068 1.919534e-06 +9 1 5 1 total 0.000037 1.268939e-06 +11 1 6 1 total 0.000015 5.136452e-07 +0 1 1 2 total 0.000107 8.388866e-06 +2 1 2 2 total 0.000554 4.330076e-05 +4 1 3 2 total 0.000529 4.133869e-05 +6 1 4 2 total 0.001186 9.268481e-05 +8 1 5 2 total 0.000486 3.799954e-05 +10 1 6 2 total 0.000204 1.591787e-05 chi-delayed material delayedgroup group out nuclide mean std. dev. 1 1 1 1 total 0.0 0.000000 -3 1 2 1 total 1.0 1.414214 +3 1 2 1 total 0.0 0.000000 5 1 3 1 total 0.0 0.000000 -7 1 4 1 total 1.0 1.414214 -9 1 5 1 total 1.0 1.414214 +7 1 4 1 total 1.0 0.433956 +9 1 5 1 total 0.0 0.000000 11 1 6 1 total 0.0 0.000000 0 1 1 2 total 0.0 0.000000 2 1 2 2 total 0.0 0.000000 @@ -255,31 +259,31 @@ chi-delayed beta material delayedgroup group in nuclide mean std. dev. 1 1 1 1 total 0.000225 0.000006 -3 1 2 1 total 0.001373 0.000038 -5 1 3 1 total 0.001433 0.000045 -7 1 4 1 total 0.003692 0.000142 -9 1 5 1 total 0.002016 0.000106 -11 1 6 1 total 0.000827 0.000043 -0 1 1 2 total 0.000228 0.000032 -2 1 2 2 total 0.001175 0.000163 -4 1 3 2 total 0.001122 0.000156 -6 1 4 2 total 0.002516 0.000349 -8 1 5 2 total 0.001031 0.000143 -10 1 6 2 total 0.000432 0.000060 +3 1 2 1 total 0.001359 0.000033 +5 1 3 1 total 0.001412 0.000034 +7 1 4 1 total 0.003611 0.000094 +9 1 5 1 total 0.001950 0.000064 +11 1 6 1 total 0.000801 0.000026 +0 1 1 2 total 0.000228 0.000019 +2 1 2 2 total 0.001175 0.000096 +4 1 3 2 total 0.001122 0.000092 +6 1 4 2 total 0.002516 0.000206 +8 1 5 2 total 0.001031 0.000085 +10 1 6 2 total 0.000432 0.000035 decay-rate material delayedgroup nuclide mean std. dev. -0 1 1 total 0.013356 0.001466 -1 1 2 total 0.032598 0.003432 -2 1 3 total 0.121086 0.012382 -3 1 4 total 0.305945 0.029907 -4 1 5 total 0.862061 0.077144 -5 1 6 total 2.895499 0.260691 +0 1 1 total 0.013352 0.000905 +1 1 2 total 0.032619 0.002094 +2 1 3 total 0.121041 0.007464 +3 1 4 total 0.305491 0.017639 +4 1 5 total 0.860388 0.042833 +5 1 6 total 2.889807 0.145503 delayed-nu-fission matrix material delayedgroup group in group out nuclide mean std. dev. 3 1 1 1 1 total 0.000000 0.000000 7 1 2 1 1 total 0.000000 0.000000 11 1 3 1 1 total 0.000000 0.000000 -15 1 4 1 1 total 0.000000 0.000000 +15 1 4 1 1 total 0.000207 0.000207 19 1 5 1 1 total 0.000000 0.000000 23 1 6 1 1 total 0.000000 0.000000 2 1 1 1 2 total 0.000000 0.000000 @@ -289,10 +293,10 @@ delayed-nu-fission matrix 18 1 5 1 2 total 0.000000 0.000000 22 1 6 1 2 total 0.000000 0.000000 1 1 1 2 1 total 0.000000 0.000000 -5 1 2 2 1 total 0.001133 0.001133 +5 1 2 2 1 total 0.000000 0.000000 9 1 3 2 1 total 0.000000 0.000000 -13 1 4 2 1 total 0.000988 0.000989 -17 1 5 2 1 total 0.001109 0.001109 +13 1 4 2 1 total 0.005002 0.001278 +17 1 5 2 1 total 0.000000 0.000000 21 1 6 2 1 total 0.000000 0.000000 0 1 1 2 2 total 0.000000 0.000000 4 1 2 2 2 total 0.000000 0.000000 @@ -302,24 +306,28 @@ delayed-nu-fission matrix 20 1 6 2 2 total 0.000000 0.000000 total material group in nuclide mean std. dev. -1 2 1 total 0.312441 0.009893 -0 2 2 total 0.300527 0.023155 +1 2 1 total 0.320478 0.012201 +0 2 2 total 0.300681 0.029685 transport material group in nuclide mean std. dev. -1 2 1 total 0.275347 0.016021 -0 2 2 total 0.300167 0.026917 +1 2 1 total 0.267653 0.015887 +0 2 2 total 0.327250 0.035614 nu-transport material group in nuclide mean std. dev. -1 2 1 total 0.275347 0.016021 -0 2 2 total 0.300167 0.026917 +1 2 1 total 0.267653 0.015887 +0 2 2 total 0.327250 0.035614 absorption material group in nuclide mean std. dev. -1 2 1 total 0.001395 0.000129 -0 2 2 total 0.005202 0.000498 +1 2 1 total 0.001040 0.000121 +0 2 2 total 0.005284 0.000548 +reduced absorption + material group in nuclide mean std. dev. +1 2 1 total 0.001040 0.000121 +0 2 2 total 0.005284 0.000548 capture material group in nuclide mean std. dev. -1 2 1 total 0.001395 0.000129 -0 2 2 total 0.005202 0.000498 +1 2 1 total 0.001040 0.000121 +0 2 2 total 0.005284 0.000548 fission material group in nuclide mean std. dev. 1 2 1 total 0.0 0.0 @@ -334,54 +342,54 @@ kappa-fission 0 2 2 total 0.0 0.0 scatter material group in nuclide mean std. dev. -1 2 1 total 0.311046 0.009880 -0 2 2 total 0.295325 0.022678 +1 2 1 total 0.319438 0.012181 +0 2 2 total 0.295397 0.029142 nu-scatter material group in nuclide mean std. dev. -1 2 1 total 0.310433 0.013519 -0 2 2 total 0.287667 0.061288 +1 2 1 total 0.314727 0.014272 +0 2 2 total 0.295807 0.038170 scatter matrix - material group in group out legendre nuclide mean std. dev. -12 2 1 1 P0 total 3.099468e-01 1.368691e-02 -13 2 1 1 P1 total 3.709418e-02 1.260219e-02 -14 2 1 1 P2 total 2.620103e-02 2.237097e-03 -15 2 1 1 P3 total 1.039112e-02 2.917283e-03 -8 2 1 2 P0 total 4.858100e-04 4.860246e-04 -9 2 1 2 P1 total 3.762752e-04 3.764414e-04 -10 2 1 2 P2 total 1.942506e-04 1.943364e-04 -11 2 1 2 P3 total -9.482181e-08 9.486370e-08 -4 2 2 1 P0 total 0.000000e+00 0.000000e+00 -5 2 2 1 P1 total 0.000000e+00 0.000000e+00 -6 2 2 1 P2 total 0.000000e+00 0.000000e+00 -7 2 2 1 P3 total 0.000000e+00 0.000000e+00 -0 2 2 2 P0 total 2.876667e-01 6.128794e-02 -1 2 2 2 P1 total -2.172735e-03 1.616275e-02 -2 2 2 2 P2 total 9.596560e-03 1.756046e-02 -3 2 2 2 P3 total -5.006322e-03 1.249952e-02 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.314727 0.014272 +13 2 1 1 P1 total 0.052826 0.010175 +14 2 1 1 P2 total 0.033176 0.002264 +15 2 1 1 P3 total 0.006129 0.004317 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295807 0.038170 +1 2 2 2 P1 total -0.026569 0.019676 +2 2 2 2 P2 total -0.005395 0.007578 +3 2 2 2 P3 total -0.005680 0.012737 nu-scatter matrix - material group in group out legendre nuclide mean std. dev. -12 2 1 1 P0 total 3.099468e-01 1.368691e-02 -13 2 1 1 P1 total 3.709418e-02 1.260219e-02 -14 2 1 1 P2 total 2.620103e-02 2.237097e-03 -15 2 1 1 P3 total 1.039112e-02 2.917283e-03 -8 2 1 2 P0 total 4.858100e-04 4.860246e-04 -9 2 1 2 P1 total 3.762752e-04 3.764414e-04 -10 2 1 2 P2 total 1.942506e-04 1.943364e-04 -11 2 1 2 P3 total -9.482181e-08 9.486370e-08 -4 2 2 1 P0 total 0.000000e+00 0.000000e+00 -5 2 2 1 P1 total 0.000000e+00 0.000000e+00 -6 2 2 1 P2 total 0.000000e+00 0.000000e+00 -7 2 2 1 P3 total 0.000000e+00 0.000000e+00 -0 2 2 2 P0 total 2.876667e-01 6.128794e-02 -1 2 2 2 P1 total -2.172735e-03 1.616275e-02 -2 2 2 2 P2 total 9.596560e-03 1.756046e-02 -3 2 2 2 P3 total -5.006322e-03 1.249952e-02 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.314727 0.014272 +13 2 1 1 P1 total 0.052826 0.010175 +14 2 1 1 P2 total 0.033176 0.002264 +15 2 1 1 P3 total 0.006129 0.004317 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295807 0.038170 +1 2 2 2 P1 total -0.026569 0.019676 +2 2 2 2 P2 total -0.005395 0.007578 +3 2 2 2 P3 total -0.005680 0.012737 multiplicity matrix material group in group out nuclide mean std. dev. -3 2 1 1 total 1.0 0.046178 -2 2 1 2 total 1.0 1.414214 +3 2 1 1 total 1.0 0.043852 +2 2 1 2 total 0.0 0.000000 1 2 2 1 total 0.0 0.000000 -0 2 2 2 total 1.0 0.204230 +0 2 2 2 total 1.0 0.139361 nu-fission matrix material group in group out nuclide mean std. dev. 3 2 1 1 total 0.0 0.0 @@ -389,47 +397,47 @@ nu-fission matrix 1 2 2 1 total 0.0 0.0 0 2 2 2 total 0.0 0.0 scatter probability matrix - material group in group out nuclide mean std. dev. -3 2 1 1 total 0.998435 0.046097 -2 2 1 2 total 0.001565 0.001566 -1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 1.000000 0.204230 + material group in group out nuclide mean std. dev. +3 2 1 1 total 1.0 0.043852 +2 2 1 2 total 0.0 0.000000 +1 2 2 1 total 0.0 0.000000 +0 2 2 2 total 1.0 0.139361 consistent scatter matrix - material group in group out legendre nuclide mean std. dev. -12 2 1 1 P0 total 3.105594e-01 1.740367e-02 -13 2 1 1 P1 total 3.716750e-02 1.269205e-02 -14 2 1 1 P2 total 2.625282e-02 2.417618e-03 -15 2 1 1 P3 total 1.041166e-02 2.945041e-03 -8 2 1 2 P0 total 4.867703e-04 4.872749e-04 -9 2 1 2 P1 total 3.770190e-04 3.774098e-04 -10 2 1 2 P2 total 1.946345e-04 1.948363e-04 -11 2 1 2 P3 total -9.500924e-08 9.510772e-08 -4 2 2 1 P0 total 0.000000e+00 0.000000e+00 -5 2 2 1 P1 total 0.000000e+00 0.000000e+00 -6 2 2 1 P2 total 0.000000e+00 0.000000e+00 -7 2 2 1 P3 total 0.000000e+00 0.000000e+00 -0 2 2 2 P0 total 2.953250e-01 6.443681e-02 -1 2 2 2 P1 total -2.230578e-03 1.659337e-02 -2 2 2 2 P2 total 9.852041e-03 1.803392e-02 -3 2 2 2 P3 total -5.139601e-03 1.283456e-02 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.319438 0.018563 +13 2 1 1 P1 total 0.053616 0.010510 +14 2 1 1 P2 total 0.033673 0.002604 +15 2 1 1 P3 total 0.006221 0.004387 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295397 0.050438 +1 2 2 2 P1 total -0.026532 0.019872 +2 2 2 2 P2 total -0.005388 0.007591 +3 2 2 2 P3 total -0.005672 0.012735 consistent nu-scatter matrix - material group in group out legendre nuclide mean std. dev. -12 2 1 1 P0 total 3.105594e-01 2.255119e-02 -13 2 1 1 P1 total 3.716750e-02 1.280757e-02 -14 2 1 1 P2 total 2.625282e-02 2.704548e-03 -15 2 1 1 P3 total 1.041166e-02 2.984029e-03 -8 2 1 2 P0 total 4.867703e-04 8.434023e-04 -9 2 1 2 P1 total 3.770190e-04 6.532417e-04 -10 2 1 2 P2 total 1.946345e-04 3.372334e-04 -11 2 1 2 P3 total -9.500924e-08 1.646177e-07 -4 2 2 1 P0 total 0.000000e+00 0.000000e+00 -5 2 2 1 P1 total 0.000000e+00 0.000000e+00 -6 2 2 1 P2 total 0.000000e+00 0.000000e+00 -7 2 2 1 P3 total 0.000000e+00 0.000000e+00 -0 2 2 2 P0 total 2.953250e-01 8.826037e-02 -1 2 2 2 P1 total -2.230578e-03 1.659963e-02 -2 2 2 2 P2 total 9.852041e-03 1.814582e-02 -3 2 2 2 P3 total -5.139601e-03 1.287741e-02 + material group in group out legendre nuclide mean std. dev. +12 2 1 1 P0 total 0.319438 0.023255 +13 2 1 1 P1 total 0.053616 0.010769 +14 2 1 1 P2 total 0.033673 0.002993 +15 2 1 1 P3 total 0.006221 0.004396 +8 2 1 2 P0 total 0.000000 0.000000 +9 2 1 2 P1 total 0.000000 0.000000 +10 2 1 2 P2 total 0.000000 0.000000 +11 2 1 2 P3 total 0.000000 0.000000 +4 2 2 1 P0 total 0.000000 0.000000 +5 2 2 1 P1 total 0.000000 0.000000 +6 2 2 1 P2 total 0.000000 0.000000 +7 2 2 1 P3 total 0.000000 0.000000 +0 2 2 2 P0 total 0.295397 0.065105 +1 2 2 2 P1 total -0.026532 0.020213 +2 2 2 2 P2 total -0.005388 0.007628 +3 2 2 2 P3 total -0.005672 0.012759 chi material group out nuclide mean std. dev. 1 2 1 total 0.0 0.0 @@ -440,8 +448,8 @@ chi-prompt 0 2 2 total 0.0 0.0 inverse-velocity material group in nuclide mean std. dev. -1 2 1 total 6.397205e-08 3.117085e-09 -0 2 2 total 2.875630e-06 2.751728e-07 +1 2 1 total 6.068670e-08 4.861582e-09 +0 2 2 total 2.921021e-06 3.028269e-07 prompt-nu-fission material group in nuclide mean std. dev. 1 2 1 total 0.0 0.0 @@ -454,72 +462,72 @@ prompt-nu-fission matrix 0 2 2 2 total 0.0 0.0 diffusion-coefficient material group in nuclide mean std. dev. -1 2 1 total 1.210594 0.070439 -0 2 2 total 1.110491 0.099580 +1 2 1 total 1.245396 0.073923 +0 2 2 total 1.018589 0.110853 nu-diffusion-coefficient material group in nuclide mean std. dev. -1 2 1 total 1.210594 0.070439 -0 2 2 total 1.110491 0.099580 +1 2 1 total 1.245396 0.073923 +0 2 2 total 1.018589 0.110853 (n,elastic) material group in nuclide mean std. dev. -1 2 1 total 0.301494 0.009403 -0 2 2 total 0.295325 0.022678 +1 2 1 total 0.310592 0.012188 +0 2 2 total 0.295397 0.029142 (n,level) material group in nuclide mean std. dev. 1 2 1 total 0.0 0.0 0 2 2 total 0.0 0.0 (n,2n) - material group in nuclide mean std. dev. -1 2 1 total 0.000005 0.000003 -0 2 2 total 0.000000 0.000000 + material group in nuclide mean std. dev. +1 2 1 total 0.0 0.0 +0 2 2 total 0.0 0.0 (n,na) material group in nuclide mean std. dev. -1 2 1 total 1.085156e-09 6.357350e-10 +1 2 1 total 2.157338e-13 1.864239e-13 0 2 2 total 0.000000e+00 0.000000e+00 (n,nc) - material group in nuclide mean std. dev. -1 2 1 total 0.001947 0.000276 -0 2 2 total 0.000000 0.000000 + material group in nuclide mean std. dev. +1 2 1 total 0.00187 0.000152 +0 2 2 total 0.00000 0.000000 (n,gamma) material group in nuclide mean std. dev. -1 2 1 total 0.001392 0.000128 -0 2 2 total 0.005201 0.000498 +1 2 1 total 0.001038 0.000121 +0 2 2 total 0.005283 0.000548 (n,a) material group in nuclide mean std. dev. -1 2 1 total 8.373873e-07 5.295954e-08 -0 2 2 total 2.735007e-07 2.100237e-08 +1 2 1 total 7.402079e-07 3.197538e-08 +0 2 2 total 2.735682e-07 2.698845e-08 (n,Xa) material group in nuclide mean std. dev. -1 2 1 total 8.384725e-07 5.277215e-08 -0 2 2 total 2.735007e-07 2.100237e-08 +1 2 1 total 7.402081e-07 3.197534e-08 +0 2 2 total 2.735682e-07 2.698845e-08 heating material group in nuclide mean std. dev. -1 2 1 total 2151.700349 142.319459 -0 2 2 total 1.947222 0.155279 +1 2 1 total 2479.710231 157.161879 +0 2 2 total 2.314706 0.265682 damage-energy material group in nuclide mean std. dev. -1 2 1 total 1591.760682 90.145807 -0 2 2 total 0.284888 0.026354 +1 2 1 total 1566.139383 61.602407 +0 2 2 total 0.288739 0.029698 (n,n1) material group in nuclide mean std. dev. -1 2 1 total 0.002962 0.00032 -0 2 2 total 0.000000 0.00000 +1 2 1 total 0.002897 0.000181 +0 2 2 total 0.000000 0.000000 (n,a0) material group in nuclide mean std. dev. -1 2 1 total 7.199126e-07 5.011215e-08 -0 2 2 total 2.732253e-07 2.098122e-08 +1 2 1 total 6.646569e-07 2.959064e-08 +0 2 2 total 2.732927e-07 2.696126e-08 (n,nc) matrix material group in group out nuclide mean std. dev. -3 2 1 1 total 0.002915 0.000493 +3 2 1 1 total 0.000488 0.000488 2 2 1 2 total 0.000000 0.000000 1 2 2 1 total 0.000000 0.000000 0 2 2 2 total 0.000000 0.000000 (n,n1) matrix - material group in group out nuclide mean std. dev. -3 2 1 1 total 0.002429 0.001332 -2 2 1 2 total 0.000000 0.000000 -1 2 2 1 total 0.000000 0.000000 -0 2 2 2 total 0.000000 0.000000 + material group in group out nuclide mean std. dev. +3 2 1 1 total 0.00244 0.001094 +2 2 1 2 total 0.00000 0.000000 +1 2 2 1 total 0.00000 0.000000 +0 2 2 2 total 0.00000 0.000000 (n,2n) matrix material group in group out nuclide mean std. dev. 3 2 1 1 total 0.0 0.0 @@ -604,24 +612,28 @@ delayed-nu-fission matrix 20 2 6 2 2 total 0.0 0.0 total material group in nuclide mean std. dev. -1 3 1 total 0.684512 0.015513 -0 3 2 total 2.038387 0.113839 +1 3 1 total 0.692034 0.019973 +0 3 2 total 2.033425 0.189463 transport material group in nuclide mean std. dev. -1 3 1 total 0.291947 0.019086 -0 3 2 total 1.464248 0.116005 +1 3 1 total 0.298754 0.021602 +0 3 2 total 1.459465 0.197868 nu-transport material group in nuclide mean std. dev. -1 3 1 total 0.291947 0.019086 -0 3 2 total 1.464248 0.116005 +1 3 1 total 0.298754 0.021602 +0 3 2 total 1.459465 0.197868 absorption material group in nuclide mean std. dev. -1 3 1 total 0.000715 0.000032 -0 3 2 total 0.031290 0.001882 +1 3 1 total 0.000699 0.000035 +0 3 2 total 0.031056 0.003017 +reduced absorption + material group in nuclide mean std. dev. +1 3 1 total 0.000699 0.000035 +0 3 2 total 0.031056 0.003017 capture material group in nuclide mean std. dev. -1 3 1 total 0.000715 0.000032 -0 3 2 total 0.031290 0.001882 +1 3 1 total 0.000699 0.000035 +0 3 2 total 0.031056 0.003017 fission material group in nuclide mean std. dev. 1 3 1 total 0.0 0.0 @@ -636,54 +648,54 @@ kappa-fission 0 3 2 total 0.0 0.0 scatter material group in nuclide mean std. dev. -1 3 1 total 0.683797 0.015496 -0 3 2 total 2.007097 0.111979 +1 3 1 total 0.691336 0.019945 +0 3 2 total 2.002368 0.186454 nu-scatter material group in nuclide mean std. dev. -1 3 1 total 0.684650 0.017888 -0 3 2 total 1.992357 0.090997 +1 3 1 total 0.684356 0.011639 +0 3 2 total 1.990840 0.178205 scatter matrix material group in group out legendre nuclide mean std. dev. -12 3 1 1 P0 total 0.654867 0.017215 -13 3 1 1 P1 total 0.392566 0.011118 -14 3 1 1 P2 total 0.165345 0.005121 -15 3 1 1 P3 total 0.016963 0.003137 -8 3 1 2 P0 total 0.029783 0.000974 -9 3 1 2 P1 total 0.008810 0.000479 -10 3 1 2 P2 total -0.002652 0.000630 -11 3 1 2 P3 total -0.002872 0.000517 -4 3 2 1 P0 total 0.000000 0.000000 -5 3 2 1 P1 total 0.000000 0.000000 -6 3 2 1 P2 total 0.000000 0.000000 -7 3 2 1 P3 total 0.000000 0.000000 -0 3 2 2 P0 total 1.992357 0.090997 -1 3 2 2 P1 total 0.526383 0.022569 -2 3 2 2 P2 total 0.108016 0.009047 -3 3 2 2 P3 total 0.016856 0.010707 +12 3 1 1 P0 total 0.652706 0.011111 +13 3 1 1 P1 total 0.393203 0.008164 +14 3 1 1 P2 total 0.162146 0.003332 +15 3 1 1 P3 total 0.014967 0.004773 +8 3 1 2 P0 total 0.031650 0.000613 +9 3 1 2 P1 total 0.009845 0.000647 +10 3 1 2 P2 total -0.003284 0.000758 +11 3 1 2 P3 total -0.003971 0.000187 +4 3 2 1 P0 total 0.000474 0.000475 +5 3 2 1 P1 total 0.000396 0.000397 +6 3 2 1 P2 total 0.000260 0.000261 +7 3 2 1 P3 total 0.000099 0.000099 +0 3 2 2 P0 total 1.990366 0.178042 +1 3 2 2 P1 total 0.523546 0.053239 +2 3 2 2 P2 total 0.101180 0.015832 +3 3 2 2 P3 total 0.017873 0.005685 nu-scatter matrix material group in group out legendre nuclide mean std. dev. -12 3 1 1 P0 total 0.654867 0.017215 -13 3 1 1 P1 total 0.392566 0.011118 -14 3 1 1 P2 total 0.165345 0.005121 -15 3 1 1 P3 total 0.016963 0.003137 -8 3 1 2 P0 total 0.029783 0.000974 -9 3 1 2 P1 total 0.008810 0.000479 -10 3 1 2 P2 total -0.002652 0.000630 -11 3 1 2 P3 total -0.002872 0.000517 -4 3 2 1 P0 total 0.000000 0.000000 -5 3 2 1 P1 total 0.000000 0.000000 -6 3 2 1 P2 total 0.000000 0.000000 -7 3 2 1 P3 total 0.000000 0.000000 -0 3 2 2 P0 total 1.992357 0.090997 -1 3 2 2 P1 total 0.526383 0.022569 -2 3 2 2 P2 total 0.108016 0.009047 -3 3 2 2 P3 total 0.016856 0.010707 +12 3 1 1 P0 total 0.652706 0.011111 +13 3 1 1 P1 total 0.393203 0.008164 +14 3 1 1 P2 total 0.162146 0.003332 +15 3 1 1 P3 total 0.014967 0.004773 +8 3 1 2 P0 total 0.031650 0.000613 +9 3 1 2 P1 total 0.009845 0.000647 +10 3 1 2 P2 total -0.003284 0.000758 +11 3 1 2 P3 total -0.003971 0.000187 +4 3 2 1 P0 total 0.000474 0.000475 +5 3 2 1 P1 total 0.000396 0.000397 +6 3 2 1 P2 total 0.000260 0.000261 +7 3 2 1 P3 total 0.000099 0.000099 +0 3 2 2 P0 total 1.990366 0.178042 +1 3 2 2 P1 total 0.523546 0.053239 +2 3 2 2 P2 total 0.101180 0.015832 +3 3 2 2 P3 total 0.017873 0.005685 multiplicity matrix material group in group out nuclide mean std. dev. -3 3 1 1 total 1.0 0.020014 -2 3 1 2 total 1.0 0.034047 -1 3 2 1 total 0.0 0.000000 -0 3 2 2 total 1.0 0.048455 +3 3 1 1 total 1.0 0.020267 +2 3 1 2 total 1.0 0.024112 +1 3 2 1 total 1.0 1.414214 +0 3 2 2 total 1.0 0.093189 nu-fission matrix material group in group out nuclide mean std. dev. 3 3 1 1 total 0.0 0.0 @@ -692,46 +704,46 @@ nu-fission matrix 0 3 2 2 total 0.0 0.0 scatter probability matrix material group in group out nuclide mean std. dev. -3 3 1 1 total 0.956499 0.018759 -2 3 1 2 total 0.043501 0.001202 -1 3 2 1 total 0.000000 0.000000 -0 3 2 2 total 1.000000 0.048455 +3 3 1 1 total 0.953753 0.018903 +2 3 1 2 total 0.046247 0.001011 +1 3 2 1 total 0.000238 0.000239 +0 3 2 2 total 0.999762 0.093156 consistent scatter matrix material group in group out legendre nuclide mean std. dev. -12 3 1 1 P0 total 0.654051 0.019602 -13 3 1 1 P1 total 0.392076 0.012456 -14 3 1 1 P2 total 0.165139 0.005640 -15 3 1 1 P3 total 0.016942 0.003143 -8 3 1 2 P0 total 0.029746 0.001063 -9 3 1 2 P1 total 0.008799 0.000495 -10 3 1 2 P2 total -0.002648 0.000631 -11 3 1 2 P3 total -0.002868 0.000518 -4 3 2 1 P0 total 0.000000 0.000000 -5 3 2 1 P1 total 0.000000 0.000000 -6 3 2 1 P2 total 0.000000 0.000000 -7 3 2 1 P3 total 0.000000 0.000000 -0 3 2 2 P0 total 2.007097 0.148316 -1 3 2 2 P1 total 0.530278 0.038286 -2 3 2 2 P2 total 0.108815 0.011091 -3 3 2 2 P3 total 0.016981 0.010831 +12 3 1 1 P0 total 0.659363 0.023079 +13 3 1 1 P1 total 0.397213 0.014683 +14 3 1 1 P2 total 0.163800 0.006035 +15 3 1 1 P3 total 0.015120 0.004843 +8 3 1 2 P0 total 0.031973 0.001157 +9 3 1 2 P1 total 0.009945 0.000721 +10 3 1 2 P2 total -0.003318 0.000772 +11 3 1 2 P3 total -0.004011 0.000225 +4 3 2 1 P0 total 0.000477 0.000480 +5 3 2 1 P1 total 0.000399 0.000401 +6 3 2 1 P2 total 0.000262 0.000264 +7 3 2 1 P3 total 0.000099 0.000100 +0 3 2 2 P0 total 2.001892 0.263710 +1 3 2 2 P1 total 0.526577 0.073894 +2 3 2 2 P2 total 0.101766 0.018719 +3 3 2 2 P3 total 0.017976 0.005976 consistent nu-scatter matrix material group in group out legendre nuclide mean std. dev. -12 3 1 1 P0 total 0.654051 0.023571 -13 3 1 1 P1 total 0.392076 0.014722 -14 3 1 1 P2 total 0.165139 0.006537 -15 3 1 1 P3 total 0.016942 0.003161 -8 3 1 2 P0 total 0.029746 0.001468 -9 3 1 2 P1 total 0.008799 0.000579 -10 3 1 2 P2 total -0.002648 0.000637 -11 3 1 2 P3 total -0.002868 0.000528 -4 3 2 1 P0 total 0.000000 0.000000 -5 3 2 1 P1 total 0.000000 0.000000 -6 3 2 1 P2 total 0.000000 0.000000 -7 3 2 1 P3 total 0.000000 0.000000 -0 3 2 2 P0 total 2.007097 0.177359 -1 3 2 2 P1 total 0.530278 0.046109 -2 3 2 2 P2 total 0.108815 0.012281 -3 3 2 2 P3 total 0.016981 0.010862 +12 3 1 1 P0 total 0.659363 0.026669 +13 3 1 1 P1 total 0.397213 0.016746 +14 3 1 1 P2 total 0.163800 0.006888 +15 3 1 1 P3 total 0.015120 0.004853 +8 3 1 2 P0 total 0.031973 0.001391 +9 3 1 2 P1 total 0.009945 0.000759 +10 3 1 2 P2 total -0.003318 0.000776 +11 3 1 2 P3 total -0.004011 0.000245 +4 3 2 1 P0 total 0.000477 0.000827 +5 3 2 1 P1 total 0.000399 0.000692 +6 3 2 1 P2 total 0.000262 0.000455 +7 3 2 1 P3 total 0.000099 0.000172 +0 3 2 2 P0 total 2.001892 0.323026 +1 3 2 2 P1 total 0.526577 0.088703 +2 3 2 2 P2 total 0.101766 0.020985 +3 3 2 2 P3 total 0.017976 0.006207 chi material group out nuclide mean std. dev. 1 3 1 total 0.0 0.0 @@ -742,8 +754,8 @@ chi-prompt 0 3 2 total 0.0 0.0 inverse-velocity material group in nuclide mean std. dev. -1 3 1 total 6.447429e-08 2.866173e-09 -0 3 2 total 3.006810e-06 1.808750e-07 +1 3 1 total 6.187551e-08 3.945367e-09 +0 3 2 total 2.984343e-06 2.898715e-07 prompt-nu-fission material group in nuclide mean std. dev. 1 3 1 total 0.0 0.0 @@ -756,60 +768,60 @@ prompt-nu-fission matrix 0 3 2 2 total 0.0 0.0 diffusion-coefficient material group in nuclide mean std. dev. -1 3 1 total 1.141761 0.074641 -0 3 2 total 0.227648 0.018035 +1 3 1 total 1.115746 0.080676 +0 3 2 total 0.228394 0.030965 nu-diffusion-coefficient material group in nuclide mean std. dev. -1 3 1 total 1.141761 0.074641 -0 3 2 total 0.227648 0.018035 +1 3 1 total 1.115746 0.080676 +0 3 2 total 0.228394 0.030965 (n,elastic) material group in nuclide mean std. dev. -1 3 1 total 0.683777 0.015496 -0 3 2 total 2.007097 0.111979 +1 3 1 total 0.691333 0.019945 +0 3 2 total 2.002368 0.186454 (n,level) - material group in nuclide mean std. dev. -1 3 1 total 0.00002 0.000006 -0 3 2 total 0.00000 0.000000 + material group in nuclide mean std. dev. +1 3 1 total 0.000003 0.000002 +0 3 2 total 0.000000 0.000000 (n,2n) material group in nuclide mean std. dev. 1 3 1 total 0.0 0.0 0 3 2 total 0.0 0.0 (n,na) - material group in nuclide mean std. dev. -1 3 1 total 3.714897e-11 2.058007e-11 -0 3 2 total 0.000000e+00 0.000000e+00 + material group in nuclide mean std. dev. +1 3 1 total 0.0 0.0 +0 3 2 total 0.0 0.0 (n,nc) material group in nuclide mean std. dev. 1 3 1 total 0.0 0.0 0 3 2 total 0.0 0.0 (n,gamma) material group in nuclide mean std. dev. -1 3 1 total 0.000234 0.000010 -0 3 2 total 0.010896 0.000655 +1 3 1 total 0.000225 0.000014 +0 3 2 total 0.010815 0.001050 (n,a) material group in nuclide mean std. dev. -1 3 1 total 0.000481 0.000022 -0 3 2 total 0.020394 0.001227 +1 3 1 total 0.000474 0.000021 +0 3 2 total 0.020241 0.001966 (n,Xa) material group in nuclide mean std. dev. -1 3 1 total 0.000481 0.000022 -0 3 2 total 0.020394 0.001227 +1 3 1 total 0.000474 0.000021 +0 3 2 total 0.020242 0.001966 heating material group in nuclide mean std. dev. -1 3 1 total 64982.284489 3654.924951 -0 3 2 total 49786.819570 2967.698779 +1 3 1 total 74978.139095 3970.725596 +0 3 2 total 58539.697122 6443.645451 damage-energy material group in nuclide mean std. dev. -1 3 1 total 1147.965123 36.313921 -0 3 2 total 332.961479 20.030541 +1 3 1 total 1161.596397 41.187452 +0 3 2 total 330.473159 32.100259 (n,n1) material group in nuclide mean std. dev. -1 3 1 total 7.030461e-07 2.133325e-07 +1 3 1 total 2.912377e-07 3.897408e-08 0 3 2 total 0.000000e+00 0.000000e+00 (n,a0) material group in nuclide mean std. dev. -1 3 1 total 0.000068 0.000011 -0 3 2 total 0.001283 0.000077 +1 3 1 total 0.000082 0.000009 +0 3 2 total 0.001273 0.000124 (n,nc) matrix material group in group out nuclide mean std. dev. 3 3 1 1 total 0.0 0.0 diff --git a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat index f29a41f3d7..c35e57f0ef 100644 --- a/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/inputs_true.dat @@ -1,1679 +1,1771 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -0.63 -0.63 -1 0.63 0.63 1 - - - - - - - 1 - - - 0.0 0.625 20000000.0 - - - 0.0 0.625 20000000.0 - - - 1 - - - 3 - - - 0.0 20000000.0 - - - 2 - - - 3 - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - absorption - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - absorption - tracklength - - - 1 2 - U234 U235 U238 O16 - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - nu-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - kappa-fission - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - scatter - tracklength - - - 1 2 - total - flux - analog - - - 1 2 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 28 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 5 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 5 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - U234 U235 U238 O16 - nu-fission - analog - - - 1 2 5 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - scatter - tracklength - - - 1 2 5 28 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - scatter - tracklength - - - 1 2 5 28 - U234 U235 U238 O16 - scatter - analog - - - 1 2 5 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 52 - U234 U235 U238 O16 - nu-fission - analog - - - 1 5 - U234 U235 U238 O16 - nu-fission - analog - - - 1 52 - U234 U235 U238 O16 - prompt-nu-fission - analog - - - 1 5 - U234 U235 U238 O16 - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - inverse-velocity - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - prompt-nu-fission - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - U234 U235 U238 O16 - prompt-nu-fission - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - U234 U235 U238 O16 - scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - total - tracklength - - - 1 2 - total - flux - analog - - - 1 5 6 - U234 U235 U238 O16 - nu-scatter - analog - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,elastic) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,level) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,2n) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,na) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,nc) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,gamma) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,a) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,Xa) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - heating - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - damage-energy - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,n1) - tracklength - - - 1 2 - total - flux - tracklength - - - 1 2 - U234 U235 U238 O16 - (n,a0) - tracklength - - - 1 2 - total - flux - analog - - - 1 2 5 - U234 U235 U238 O16 - (n,nc) - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - U234 U235 U238 O16 - (n,n1) - analog - - - 1 2 - total - flux - analog - - - 1 2 5 - U234 U235 U238 O16 - (n,2n) - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 104 2 - total - flux - analog - - - 104 5 6 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 104 2 - total - flux - analog - - - 104 5 6 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - absorption - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - absorption - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - fission - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - fission - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - kappa-fission - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - tracklength - - - 104 2 - total - flux - analog - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 2 - total - flux - analog - - - 104 2 5 28 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - analog - - - 104 2 5 28 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - tracklength - - - 104 2 5 28 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - tracklength - - - 104 2 5 28 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 52 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission - analog - - - 104 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-fission - analog - - - 104 52 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog - - - 104 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - inverse-velocity - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - tracklength - - - 104 2 - total - flux - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - prompt-nu-fission - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 104 2 - total - flux - analog - - - 104 5 6 - Zr90 Zr91 Zr92 Zr94 Zr96 - scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - total - tracklength - - - 104 2 - total - flux - analog - - - 104 5 6 - Zr90 Zr91 Zr92 Zr94 Zr96 - nu-scatter - analog - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,elastic) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,level) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,2n) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,na) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,nc) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,gamma) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,a) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,Xa) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - heating - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - damage-energy - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,n1) - tracklength - - - 104 2 - total - flux - tracklength - - - 104 2 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,a0) - tracklength - - - 104 2 - total - flux - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,nc) - analog - - - 104 2 - total - flux - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,n1) - analog - - - 104 2 - total - flux - analog - - - 104 2 5 - Zr90 Zr91 Zr92 Zr94 Zr96 - (n,2n) - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - total - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - total - tracklength - - - 207 2 - total - flux - analog - - - 207 5 6 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - total - tracklength - - - 207 2 - total - flux - analog - - - 207 5 6 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - absorption - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - absorption - tracklength - - - 207 2 - H1 O16 B10 B11 - fission - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - fission - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - nu-fission - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - kappa-fission - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - scatter - tracklength - - - 207 2 - total - flux - analog - - - 207 2 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 2 - total - flux - analog - - - 207 2 5 28 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - analog - - - 207 2 5 28 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 2 5 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 2 5 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - analog - - - 207 2 5 - H1 O16 B10 B11 - nu-fission - analog - - - 207 2 5 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - scatter - tracklength - - - 207 2 5 28 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - scatter - tracklength - - - 207 2 5 28 - H1 O16 B10 B11 - scatter - analog - - - 207 2 5 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 52 - H1 O16 B10 B11 - nu-fission - analog - - - 207 5 - H1 O16 B10 B11 - nu-fission - analog - - - 207 52 - H1 O16 B10 B11 - prompt-nu-fission - analog - - - 207 5 - H1 O16 B10 B11 - prompt-nu-fission - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - inverse-velocity - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - prompt-nu-fission - tracklength - - - 207 2 - total - flux - analog - - - 207 2 5 - H1 O16 B10 B11 - prompt-nu-fission - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - total - tracklength - - - 207 2 - total - flux - analog - - - 207 5 6 - H1 O16 B10 B11 - scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - total - tracklength - - - 207 2 - total - flux - analog - - - 207 5 6 - H1 O16 B10 B11 - nu-scatter - analog - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,elastic) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,level) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,2n) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,na) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,nc) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,gamma) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,a) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,Xa) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - heating - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - damage-energy - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,n1) - tracklength - - - 207 2 - total - flux - tracklength - - - 207 2 - H1 O16 B10 B11 - (n,a0) - tracklength - - - 207 2 - total - flux - analog - - - 207 2 5 - H1 O16 B10 B11 - (n,nc) - analog - - - 207 2 - total - flux - analog - - - 207 2 5 - H1 O16 B10 B11 - (n,n1) - analog - - - 207 2 - total - flux - analog - - - 207 2 5 - H1 O16 B10 B11 - (n,2n) - analog - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 + + + 0.0 0.625 20000000.0 + + + 0.0 0.625 20000000.0 + + + 1 + + + 3 + + + 0.0 20000000.0 + + + 2 + + + 3 + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + absorption + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + absorption + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,2n) + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,3n) + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,4n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + absorption + tracklength + + + 1 2 + U234 U235 U238 O16 + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + nu-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + kappa-fission + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + scatter + tracklength + + + 1 2 + total + flux + analog + + + 1 2 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 30 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 5 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 5 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + U234 U235 U238 O16 + nu-fission + analog + + + 1 2 5 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + scatter + tracklength + + + 1 2 5 30 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + scatter + tracklength + + + 1 2 5 30 + U234 U235 U238 O16 + scatter + analog + + + 1 2 5 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 54 + U234 U235 U238 O16 + nu-fission + analog + + + 1 5 + U234 U235 U238 O16 + nu-fission + analog + + + 1 54 + U234 U235 U238 O16 + prompt-nu-fission + analog + + + 1 5 + U234 U235 U238 O16 + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + inverse-velocity + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + prompt-nu-fission + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + U234 U235 U238 O16 + prompt-nu-fission + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + U234 U235 U238 O16 + scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + total + tracklength + + + 1 2 + total + flux + analog + + + 1 5 6 + U234 U235 U238 O16 + nu-scatter + analog + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,elastic) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,level) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,2n) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,na) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,nc) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,gamma) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,a) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,Xa) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + heating + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + damage-energy + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,n1) + tracklength + + + 1 2 + total + flux + tracklength + + + 1 2 + U234 U235 U238 O16 + (n,a0) + tracklength + + + 1 2 + total + flux + analog + + + 1 2 5 + U234 U235 U238 O16 + (n,nc) + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + U234 U235 U238 O16 + (n,n1) + analog + + + 1 2 + total + flux + analog + + + 1 2 5 + U234 U235 U238 O16 + (n,2n) + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 106 2 + total + flux + analog + + + 106 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 106 2 + total + flux + analog + + + 106 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + absorption + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + absorption + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,2n) + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,3n) + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,4n) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + absorption + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + fission + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + fission + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + kappa-fission + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + tracklength + + + 106 2 + total + flux + analog + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 2 + total + flux + analog + + + 106 2 5 30 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + analog + + + 106 2 5 30 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + tracklength + + + 106 2 5 30 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + tracklength + + + 106 2 5 30 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 54 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission + analog + + + 106 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-fission + analog + + + 106 54 + Zr90 Zr91 Zr92 Zr94 Zr96 + prompt-nu-fission + analog + + + 106 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + prompt-nu-fission + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + inverse-velocity + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + prompt-nu-fission + tracklength + + + 106 2 + total + flux + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + prompt-nu-fission + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 106 2 + total + flux + analog + + + 106 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + total + tracklength + + + 106 2 + total + flux + analog + + + 106 5 6 + Zr90 Zr91 Zr92 Zr94 Zr96 + nu-scatter + analog + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,elastic) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,level) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,2n) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,na) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,nc) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,gamma) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,a) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,Xa) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + heating + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + damage-energy + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,n1) + tracklength + + + 106 2 + total + flux + tracklength + + + 106 2 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,a0) + tracklength + + + 106 2 + total + flux + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,nc) + analog + + + 106 2 + total + flux + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,n1) + analog + + + 106 2 + total + flux + analog + + + 106 2 5 + Zr90 Zr91 Zr92 Zr94 Zr96 + (n,2n) + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + total + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + total + tracklength + + + 211 2 + total + flux + analog + + + 211 5 6 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + total + tracklength + + + 211 2 + total + flux + analog + + + 211 5 6 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + absorption + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + absorption + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,2n) + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,3n) + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,4n) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + absorption + tracklength + + + 211 2 + H1 O16 B10 B11 + fission + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + fission + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + nu-fission + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + kappa-fission + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + scatter + tracklength + + + 211 2 + total + flux + analog + + + 211 2 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 2 + total + flux + analog + + + 211 2 5 30 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + analog + + + 211 2 5 30 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 2 5 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 2 5 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + analog + + + 211 2 5 + H1 O16 B10 B11 + nu-fission + analog + + + 211 2 5 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + scatter + tracklength + + + 211 2 5 30 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + scatter + tracklength + + + 211 2 5 30 + H1 O16 B10 B11 + scatter + analog + + + 211 2 5 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 54 + H1 O16 B10 B11 + nu-fission + analog + + + 211 5 + H1 O16 B10 B11 + nu-fission + analog + + + 211 54 + H1 O16 B10 B11 + prompt-nu-fission + analog + + + 211 5 + H1 O16 B10 B11 + prompt-nu-fission + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + inverse-velocity + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + prompt-nu-fission + tracklength + + + 211 2 + total + flux + analog + + + 211 2 5 + H1 O16 B10 B11 + prompt-nu-fission + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + total + tracklength + + + 211 2 + total + flux + analog + + + 211 5 6 + H1 O16 B10 B11 + scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + total + tracklength + + + 211 2 + total + flux + analog + + + 211 5 6 + H1 O16 B10 B11 + nu-scatter + analog + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,elastic) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,level) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,2n) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,na) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,nc) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,gamma) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,a) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,Xa) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + heating + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + damage-energy + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,n1) + tracklength + + + 211 2 + total + flux + tracklength + + + 211 2 + H1 O16 B10 B11 + (n,a0) + tracklength + + + 211 2 + total + flux + analog + + + 211 2 5 + H1 O16 B10 B11 + (n,nc) + analog + + + 211 2 + total + flux + analog + + + 211 2 5 + H1 O16 B10 B11 + (n,n1) + analog + + + 211 2 + total + flux + analog + + + 211 2 5 + H1 O16 B10 B11 + (n,2n) + analog + + + diff --git a/tests/regression_tests/mgxs_library_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_nuclides/results_true.dat index c0084547b1..c05ec9b48d 100644 --- a/tests/regression_tests/mgxs_library_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/results_true.dat @@ -1 +1 @@ -d36f8abb1131212063470d622dbedeae31602da71006389421bd5dba712c94fe96acbc8ded833cb237687fb1071c1a6c3e0ec67b18ce7cf0f7720451b86d993a \ No newline at end of file +d1e4ab2c0d85bb5da617db9c7a3731494114e2fd3ba75ae8eaa1f0a36648f73fcbcfb487390789b0124719cabdcbfa84af4bb118f11bdc548d1065e1b5af836a \ No newline at end of file diff --git a/tests/regression_tests/mgxs_library_specific_nuclides/__init__.py b/tests/regression_tests/mgxs_library_specific_nuclides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/mgxs_library_specific_nuclides/inputs_true.dat b/tests/regression_tests/mgxs_library_specific_nuclides/inputs_true.dat new file mode 100644 index 0000000000..dd9d1ceb07 --- /dev/null +++ b/tests/regression_tests/mgxs_library_specific_nuclides/inputs_true.dat @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + + + 1 2 3 + + + 0.0 0.625 20000000.0 + + + 1 + + + 0.0 0.625 20000000.0 + + + 1 + + + 3 + + + 0.0 20000000.0 + + + 2 + + + 3 + + + 383 2 + total + flux + tracklength + + + 1 2 + U235 total + total absorption (n,2n) (n,3n) (n,4n) fission nu-fission kappa-fission scatter inverse-velocity prompt-nu-fission (n,elastic) (n,level) (n,na) (n,nc) (n,gamma) (n,a) (n,Xa) heating damage-energy (n,n1) (n,a0) + tracklength + + + 383 2 + total + flux + analog + + + 1 5 6 + U235 total + scatter nu-scatter + analog + + + 1 2 + U235 total + nu-scatter + analog + + + 1 2 5 30 + U235 total + scatter nu-scatter + analog + + + 1 2 5 + U235 total + nu-scatter scatter nu-fission prompt-nu-fission (n,nc) (n,n1) (n,2n) + analog + + + 1 54 + U235 total + nu-fission prompt-nu-fission + analog + + + 1 5 + U235 total + nu-fission prompt-nu-fission + analog + + + 106 2 + Zr90 total + total absorption (n,2n) (n,3n) (n,4n) fission nu-fission kappa-fission scatter inverse-velocity prompt-nu-fission (n,elastic) (n,level) (n,na) (n,nc) (n,gamma) (n,a) (n,Xa) heating damage-energy (n,n1) (n,a0) + tracklength + + + 106 5 6 + Zr90 total + scatter nu-scatter + analog + + + 106 2 + Zr90 total + nu-scatter + analog + + + 106 2 5 30 + Zr90 total + scatter nu-scatter + analog + + + 106 2 5 + Zr90 total + nu-scatter scatter nu-fission prompt-nu-fission (n,nc) (n,n1) (n,2n) + analog + + + 106 54 + Zr90 total + nu-fission prompt-nu-fission + analog + + + 106 5 + Zr90 total + nu-fission prompt-nu-fission + analog + + + 251 2 + H1 total + total absorption (n,2n) (n,3n) (n,4n) fission nu-fission kappa-fission scatter inverse-velocity prompt-nu-fission (n,elastic) (n,level) (n,na) (n,nc) (n,gamma) (n,a) (n,Xa) heating damage-energy (n,n1) (n,a0) + tracklength + + + 251 5 6 + H1 total + scatter nu-scatter + analog + + + 251 2 + H1 total + nu-scatter + analog + + + 251 2 5 30 + H1 total + scatter nu-scatter + analog + + + 251 2 5 + H1 total + nu-scatter scatter nu-fission prompt-nu-fission (n,nc) (n,n1) (n,2n) + analog + + + 251 54 + H1 total + nu-fission prompt-nu-fission + analog + + + 251 5 + H1 total + nu-fission prompt-nu-fission + analog + + + diff --git a/tests/regression_tests/mgxs_library_specific_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_specific_nuclides/results_true.dat new file mode 100644 index 0000000000..0c44eb132c --- /dev/null +++ b/tests/regression_tests/mgxs_library_specific_nuclides/results_true.dat @@ -0,0 +1 @@ +efcd9fd6be2ed6c98bbe5279cbacdb287597fcbbc4ed49e164b07e0861f28877b1bef2ad82d70fdac95965f7ed0d0990f77c8545718836b1b55a16b4243208d0 \ No newline at end of file diff --git a/tests/regression_tests/mgxs_library_specific_nuclides/test.py b/tests/regression_tests/mgxs_library_specific_nuclides/test.py new file mode 100644 index 0000000000..61910e539e --- /dev/null +++ b/tests/regression_tests/mgxs_library_specific_nuclides/test.py @@ -0,0 +1,71 @@ +import hashlib + +import openmc +import openmc.mgxs +from openmc.examples import pwr_pin_cell + +from tests.testing_harness import PyAPITestHarness + + +class MGXSTestHarness(PyAPITestHarness): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Initialize a two-group structure + energy_groups = openmc.mgxs.EnergyGroups(group_edges=[0, 0.625, 20.e6]) + + # Initialize MGXS Library for a few cross section types + self.mgxs_lib = openmc.mgxs.Library(self._model.geometry) + self.mgxs_lib.by_nuclide = True + + # Test relevant MGXS types + relevant_MGXS_TYPES = [item for item in openmc.mgxs.MGXS_TYPES + if item != 'current'] + # Add in a subset of openmc.mgxs.ARBITRARY_VECTOR_TYPES and + # openmc.mgxs.ARBITRARY_MATRIX_TYPES so we can see the code works, + # but not use too much resources + relevant_MGXS_TYPES += [ + "(n,elastic)", "(n,level)", "(n,2n)", "(n,na)", "(n,nc)", + "(n,gamma)", "(n,a)", "(n,Xa)", "heating", "damage-energy", + "(n,n1)", "(n,a0)", "(n,nc) matrix", "(n,n1) matrix", + "(n,2n) matrix"] + self.mgxs_lib.mgxs_types = tuple(relevant_MGXS_TYPES) + self.mgxs_lib.energy_groups = energy_groups + self.mgxs_lib.legendre_order = 3 + self.mgxs_lib.domain_type = 'material' + self.mgxs_lib.nuclides = ['U235', 'Zr90', 'H1'] + self.mgxs_lib.build_library() + + # Add tallies + self.mgxs_lib.add_to_tallies_file(self._model.tallies, merge=True) + + def _get_results(self, hash_output=True): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + sp = openmc.StatePoint(self._sp_name) + + # Load the MGXS library from the statepoint + self.mgxs_lib.load_from_statepoint(sp) + + # Build a string from Pandas Dataframe for each MGXS + outstr = '' + for domain in self.mgxs_lib.domains: + for mgxs_type in self.mgxs_lib.mgxs_types: + mgxs = self.mgxs_lib.get_mgxs(domain, mgxs_type) + df = mgxs.get_pandas_dataframe() + outstr += df.to_string() + '\n' + + # Hash the results if necessary + if hash_output: + sha512 = hashlib.sha512() + sha512.update(outstr.encode('utf-8')) + outstr = sha512.hexdigest() + + return outstr + + +def test_mgxs_library_specific_nuclides(): + model = pwr_pin_cell() + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/microxs/__init.py__ b/tests/regression_tests/microxs/__init.py__ new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/microxs/test.py b/tests/regression_tests/microxs/test.py new file mode 100644 index 0000000000..70833bb39c --- /dev/null +++ b/tests/regression_tests/microxs/test.py @@ -0,0 +1,68 @@ +"""Test one-group cross section generation""" +from pathlib import Path + +import numpy as np +import pytest +import openmc +from openmc.deplete import MicroXS, get_microxs_and_flux + +from tests.regression_tests import config + +CHAIN_FILE = Path(__file__).parents[2] / "chain_simple.xml" + +@pytest.fixture(scope="module") +def model(): + fuel = openmc.Material(name="uo2") + fuel.add_nuclide("U235", 1.0) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cc", 10.4) + + sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(region=-sphere, fill=fuel) + geometry = openmc.Geometry([cell]) + + settings = openmc.Settings() + settings.particles = 1000 + settings.inactive = 5 + settings.batches = 10 + + return openmc.Model(geometry, settings=settings) + + +@pytest.mark.parametrize( + "domain_type, rr_mode", + [ + ("materials", "direct"), + ("materials", "flux"), + ("mesh", "direct"), + ("mesh", "flux"), + ] +) +def test_from_model(model, domain_type, rr_mode): + if domain_type == 'materials': + domains = list(model.geometry.get_all_materials().values()) + elif domain_type == 'mesh': + mesh = openmc.RegularMesh() + mesh.lower_left = (-10., -10.) + mesh.upper_right = (10., 10.) + mesh.dimension = (1, 1) + domains = mesh + nuclides = ['U235', 'O16', 'Xe135'] + kwargs = { + 'reaction_rate_mode': rr_mode, + 'chain_file': CHAIN_FILE, + 'path_statepoint': 'neutron_transport.h5', + } + if rr_mode == 'flux': + kwargs['energies'] = 'CASMO-40' + _, test_xs = get_microxs_and_flux(model, domains, nuclides, **kwargs) + if config['update']: + test_xs[0].to_csv(f'test_reference_{domain_type}_{rr_mode}.csv') + + # Make sure results match reference results + ref_xs = MicroXS.from_csv(f'test_reference_{domain_type}_{rr_mode}.csv') + np.testing.assert_allclose(test_xs[0].data, ref_xs.data, rtol=1e-11) + + # Make sure statepoint file was saved + assert Path('neutron_transport.h5').exists() + Path('neutron_transport.h5').unlink() diff --git a/tests/regression_tests/microxs/test_reference_materials_direct.csv b/tests/regression_tests/microxs/test_reference_materials_direct.csv new file mode 100644 index 0000000000..4a63fed85a --- /dev/null +++ b/tests/regression_tests/microxs/test_reference_materials_direct.csv @@ -0,0 +1,7 @@ +nuclides,reactions,groups,xs +U235,"(n,gamma)",1,0.1475718536187164 +U235,fission,1,1.2504996049257149 +O16,"(n,gamma)",1,0.00010981236259441559 +O16,fission,1,0.0 +Xe135,"(n,gamma)",1,0.014570546772870611 +Xe135,fission,1,0.0 diff --git a/tests/regression_tests/microxs/test_reference_materials_flux.csv b/tests/regression_tests/microxs/test_reference_materials_flux.csv new file mode 100644 index 0000000000..5eb29902e4 --- /dev/null +++ b/tests/regression_tests/microxs/test_reference_materials_flux.csv @@ -0,0 +1,7 @@ +nuclides,reactions,groups,xs +U235,"(n,gamma)",1,0.15003016703758473 +U235,fission,1,1.2646269005413537 +O16,"(n,gamma)",1,0.00012069778439640301 +O16,fission,1,0.0 +Xe135,"(n,gamma)",1,0.014820264774863562 +Xe135,fission,1,0.0 diff --git a/tests/regression_tests/microxs/test_reference_mesh_direct.csv b/tests/regression_tests/microxs/test_reference_mesh_direct.csv new file mode 100644 index 0000000000..60160ee513 --- /dev/null +++ b/tests/regression_tests/microxs/test_reference_mesh_direct.csv @@ -0,0 +1,7 @@ +nuclides,reactions,groups,xs +U235,"(n,gamma)",1,0.14757185361871633 +U235,fission,1,1.2504996049257142 +O16,"(n,gamma)",1,0.0001098123625944155 +O16,fission,1,0.0 +Xe135,"(n,gamma)",1,0.0145705467728706 +Xe135,fission,1,0.0 diff --git a/tests/regression_tests/microxs/test_reference_mesh_flux.csv b/tests/regression_tests/microxs/test_reference_mesh_flux.csv new file mode 100644 index 0000000000..5eb29902e4 --- /dev/null +++ b/tests/regression_tests/microxs/test_reference_mesh_flux.csv @@ -0,0 +1,7 @@ +nuclides,reactions,groups,xs +U235,"(n,gamma)",1,0.15003016703758473 +U235,fission,1,1.2646269005413537 +O16,"(n,gamma)",1,0.00012069778439640301 +O16,fission,1,0.0 +Xe135,"(n,gamma)",1,0.014820264774863562 +Xe135,fission,1,0.0 diff --git a/tests/regression_tests/model_xml/__init__.py b/tests/regression_tests/model_xml/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat new file mode 100644 index 0000000000..18c0552ceb --- /dev/null +++ b/tests/regression_tests/model_xml/adj_cell_rotation_inputs_true.dat @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + diff --git a/tests/regression_tests/model_xml/energy_laws_inputs_true.dat b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat new file mode 100644 index 0000000000..8c5191217d --- /dev/null +++ b/tests/regression_tests/model_xml/energy_laws_inputs_true.dat @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/model_xml/inputs_true.dat b/tests/regression_tests/model_xml/inputs_true.dat new file mode 100644 index 0000000000..c1ef136658 --- /dev/null +++ b/tests/regression_tests/model_xml/inputs_true.dat @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 14000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + 16 + + + neutron photon electron positron + + + 1 2 + current + + + 2 + Al27 total + total (n,gamma) + tracklength + + + 2 + Al27 total + total heating (n,gamma) + collision + + + 2 + Al27 total + total heating (n,gamma) + analog + + + diff --git a/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat new file mode 100644 index 0000000000..06abef5821 --- /dev/null +++ b/tests/regression_tests/model_xml/lattice_multiple_inputs_true.dat @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + 1.2 1.2 + 1 + 2 2 + -1.2 -1.2 + +2 1 +1 1 + + + 2.4 2.4 + 2 2 + -2.4 -2.4 + +4 4 +4 4 + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/model_xml/photon_production_inputs_true.dat b/tests/regression_tests/model_xml/photon_production_inputs_true.dat new file mode 100644 index 0000000000..07eebaa3e6 --- /dev/null +++ b/tests/regression_tests/model_xml/photon_production_inputs_true.dat @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 14000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + 1 + + + neutron photon electron positron + + + 1 2 + current + + + 2 + Al27 total + total (n,gamma) + tracklength + + + 2 + Al27 total + total heating (n,gamma) + collision + + + 2 + Al27 total + total heating (n,gamma) + analog + + + diff --git a/tests/regression_tests/model_xml/test.py b/tests/regression_tests/model_xml/test.py new file mode 100644 index 0000000000..c67a72ed37 --- /dev/null +++ b/tests/regression_tests/model_xml/test.py @@ -0,0 +1,100 @@ +from difflib import unified_diff +import glob +import filecmp +import os +from pathlib import Path + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness, colorize + +# use a few models from other tests to make sure the same results are +# produced when using a single model.xml file as input +from ..adj_cell_rotation.test import model as adj_cell_rotation_model +from ..lattice_multiple.test import model as lattice_multiple_model +from ..energy_laws.test import model as energy_laws_model +from ..photon_production.test import model as photon_production_model + + +class ModelXMLTestHarness(PyAPITestHarness): + """Accept a results file to check against and assume inputs_true is the contents of a model.xml file. + """ + def __init__(self, model=None, inputs_true=None, results_true=None): + statepoint_name = f'statepoint.{model.settings.batches}.h5' + super().__init__(statepoint_name, model, inputs_true) + + self.results_true = 'results_true.dat' if results_true is None else results_true + + def _build_inputs(self): + self._model.export_to_model_xml() + + def _get_inputs(self): + return open('model.xml').read() + + def _compare_results(self): + """Make sure the current results agree with the reference.""" + compare = filecmp.cmp('results_test.dat', self.results_true) + if not compare: + expected = open(self.results_true).readlines() + actual = open('results_test.dat').readlines() + diff = unified_diff(expected, actual, self.results_true, + 'results_test.dat') + print('Result differences:') + print(''.join(colorize(diff))) + os.rename('results_test.dat', 'results_error.dat') + assert compare, 'Results do not agree' + + def _cleanup(self): + super()._cleanup() + if os.path.exists('model.xml'): + os.remove('model.xml') + + +test_names = [ + 'adj_cell_rotation', + 'lattice_multiple', + 'energy_laws', + 'photon_production' +] + + +@pytest.mark.parametrize("test_name", test_names, ids=lambda test: test) +def test_model_xml(test_name, request): + openmc.reset_auto_ids() + + test_path = '../' + test_name + results = test_path + "/results_true.dat" + inputs = test_name + "_inputs_true.dat" + model_name = test_name + "_model" + harness = ModelXMLTestHarness(request.getfixturevalue(model_name), inputs, results) + harness.main() + +def test_input_arg(run_in_tmpdir): + + pincell = openmc.examples.pwr_pin_cell() + + pincell.settings.particles = 100 + + # export to separate XML files and run + pincell.export_to_xml() + openmc.run() + + # make sure the executable isn't falling back on the separate XMLs + for f in glob.glob('*.xml'): + os.remove(f) + # now export to a single XML file with a custom name + pincell.export_to_model_xml('pincell.xml') + assert Path('pincell.xml').exists() + + # run by specifying that single file + openmc.run(path_input='pincell.xml') + + # check that this works for plotting too + openmc.plot_geometry(path_input='pincell.xml') + + # now ensure we get an error for an incorrect filename, + # even in the presence of other, valid XML files + pincell.export_to_model_xml() + with pytest.raises(RuntimeError, match='ex-em-ell.xml'): + openmc.run(path_input='ex-em-ell.xml') \ No newline at end of file diff --git a/tests/regression_tests/multipole/inputs_true.dat b/tests/regression_tests/multipole/inputs_true.dat index 3bbcc20247..22a351240c 100644 --- a/tests/regression_tests/multipole/inputs_true.dat +++ b/tests/regression_tests/multipole/inputs_true.dat @@ -1,55 +1,54 @@ - - - - - - - 2.0 2.0 - 1 - 2 2 - -2.0 -2.0 - + + + + + + + + + + + + + + + + + + + + 2.0 2.0 + 1 + 2 2 + -2.0 -2.0 + 11 11 11 11 - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - -1 -1 -1 1 1 1 - - - true - 1000 - - - - - U235 O16 total - total fission (n,gamma) elastic (n,p) - - + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + -1 -1 -1 1 1 1 + + + true + 1000 + + + + U235 O16 total + total fission (n,gamma) elastic (n,p) + + + diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index 955f88a4a0..40b42b1a05 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -1,36 +1,36 @@ k-combined: -1.377711E+00 1.297376E-02 +1.315804E+00 7.070811E-02 tally 1: -3.861112E+00 -2.991150E+00 -2.787756E+00 -1.559383E+00 -5.422517E-01 -5.899488E-02 -4.575965E-01 -4.201819E-02 +3.840178E+00 +2.953217E+00 +2.769369E+00 +1.535605E+00 +5.400560E-01 +5.840168E-02 +4.595308E-01 +4.237758E-02 0.000000E+00 0.000000E+00 -2.250654E+01 -1.013609E+02 +2.283789E+01 +1.044032E+02 0.000000E+00 0.000000E+00 -6.830189E-04 -9.338756E-08 -2.248753E+01 -1.011895E+02 -1.147267E-05 -4.624497E-11 -3.580777E+02 -2.567385E+04 -2.787756E+00 -1.559383E+00 -2.157976E+00 -9.322753E-01 -3.530401E+02 -2.495723E+04 -1.147267E-05 -4.624497E-11 +6.960971E-04 +9.704386E-08 +2.281620E+01 +1.042051E+02 +3.667073E-05 +1.048567E-09 +3.655557E+02 +2.676173E+04 +2.769369E+00 +1.535605E+00 +2.199058E+00 +9.681253E-01 +3.604950E+02 +2.602657E+04 +3.667073E-05 +1.048567E-09 Cell ID = 11 Name = @@ -38,5 +38,6 @@ Cell Region = -1 Rotation = None Temperature = [500. 700. 0. 800.] + Density = None Translation = None Volume = None diff --git a/tests/regression_tests/multipole/test.py b/tests/regression_tests/multipole/test.py index 4c4353c841..2f7369a9db 100644 --- a/tests/regression_tests/multipole/test.py +++ b/tests/regression_tests/multipole/test.py @@ -53,7 +53,7 @@ def make_model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 1000 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-1, -1, -1], [1, 1, 1])) model.settings.temperature = {'tolerance': 1000, 'multipole': True} diff --git a/tests/regression_tests/ncrystal/__init__.py b/tests/regression_tests/ncrystal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/ncrystal/inputs_true.dat b/tests/regression_tests/ncrystal/inputs_true.dat new file mode 100644 index 0000000000..81ee2e312c --- /dev/null +++ b/tests/regression_tests/ncrystal/inputs_true.dat @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + fixed source + 100000 + 10 + + + 0 0 -20 + + + + 0.012 1.0 + + + + + + 1 + + + 0.0 0.017453292519943295 0.03490658503988659 0.05235987755982989 0.06981317007977318 0.08726646259971647 0.10471975511965978 0.12217304763960307 0.13962634015954636 0.15707963267948966 0.17453292519943295 0.19198621771937624 0.20943951023931956 0.22689280275926285 0.24434609527920614 0.2617993877991494 0.2792526803190927 0.29670597283903605 0.3141592653589793 0.33161255787892263 0.3490658503988659 0.3665191429188092 0.3839724354387525 0.4014257279586958 0.4188790204786391 0.4363323129985824 0.4537856055185257 0.47123889803846897 0.4886921905584123 0.5061454830783556 0.5235987755982988 0.5410520681182421 0.5585053606381855 0.5759586531581288 0.5934119456780721 0.6108652381980153 0.6283185307179586 0.6457718232379019 0.6632251157578453 0.6806784082777885 0.6981317007977318 0.7155849933176751 0.7330382858376184 0.7504915783575618 0.767944870877505 0.7853981633974483 0.8028514559173916 0.8203047484373349 0.8377580409572782 0.8552113334772214 0.8726646259971648 0.8901179185171081 0.9075712110370514 0.9250245035569946 0.9424777960769379 0.9599310885968813 0.9773843811168246 0.9948376736367679 1.0122909661567112 1.0297442586766545 1.0471975511965976 1.064650843716541 1.0821041362364843 1.0995574287564276 1.117010721276371 1.1344640137963142 1.1519173063162575 1.1693705988362009 1.1868238913561442 1.2042771838760873 1.2217304763960306 1.239183768915974 1.2566370614359172 1.2740903539558606 1.2915436464758039 1.3089969389957472 1.3264502315156905 1.3439035240356338 1.361356816555577 1.3788101090755203 1.3962634015954636 1.413716694115407 1.4311699866353502 1.4486232791552935 1.4660765716752369 1.4835298641951802 1.5009831567151235 1.5184364492350666 1.53588974175501 1.5533430342749532 1.5707963267948966 1.5882496193148399 1.6057029118347832 1.6231562043547265 1.6406094968746698 1.6580627893946132 1.6755160819145565 1.6929693744344996 1.710422666954443 1.7278759594743862 1.7453292519943295 1.7627825445142729 1.7802358370342162 1.7976891295541595 1.8151424220741028 1.8325957145940461 1.8500490071139892 1.8675022996339325 1.8849555921538759 1.9024088846738192 1.9198621771937625 1.9373154697137058 1.9547687622336491 1.9722220547535925 1.9896753472735358 2.007128639793479 2.0245819323134224 2.0420352248333655 2.059488517353309 2.076941809873252 2.0943951023931953 2.111848394913139 2.129301687433082 2.1467549799530254 2.1642082724729685 2.181661564992912 2.199114857512855 2.2165681500327987 2.234021442552742 2.251474735072685 2.2689280275926285 2.2863813201125716 2.303834612632515 2.321287905152458 2.3387411976724017 2.356194490192345 2.3736477827122884 2.3911010752322315 2.4085543677521746 2.426007660272118 2.443460952792061 2.4609142453120048 2.478367537831948 2.4958208303518914 2.5132741228718345 2.530727415391778 2.548180707911721 2.5656340004316642 2.5830872929516078 2.600540585471551 2.6179938779914944 2.6354471705114375 2.652900463031381 2.670353755551324 2.6878070480712677 2.705260340591211 2.722713633111154 2.7401669256310974 2.7576202181510405 2.775073510670984 2.792526803190927 2.8099800957108707 2.827433388230814 2.8448866807507573 2.8623399732707004 2.8797932657906435 2.897246558310587 2.91469985083053 2.9321531433504737 2.949606435870417 2.9670597283903604 2.9845130209103035 3.001966313430247 3.01941960595019 3.036872898470133 3.0543261909900767 3.07177948351002 3.0892327760299634 3.1066860685499065 3.12413936106985 3.141592653589793 + + + 1 + + + 1 2 3 + current + + + diff --git a/tests/regression_tests/ncrystal/results_true.dat b/tests/regression_tests/ncrystal/results_true.dat new file mode 100644 index 0000000000..28c02c51c4 --- /dev/null +++ b/tests/regression_tests/ncrystal/results_true.dat @@ -0,0 +1,181 @@ + surface polar low [rad] polar high [rad] cellfrom nuclide score mean std. dev. +0 1 0.00e+00 1.75e-02 1 total current 9.82e-01 1.43e-04 +1 1 1.75e-02 3.49e-02 1 total current 0.00e+00 0.00e+00 +2 1 3.49e-02 5.24e-02 1 total current 1.00e-06 1.00e-06 +3 1 5.24e-02 6.98e-02 1 total current 0.00e+00 0.00e+00 +4 1 6.98e-02 8.73e-02 1 total current 0.00e+00 0.00e+00 +5 1 8.73e-02 1.05e-01 1 total current 1.00e-06 1.00e-06 +6 1 1.05e-01 1.22e-01 1 total current 0.00e+00 0.00e+00 +7 1 1.22e-01 1.40e-01 1 total current 1.00e-06 1.00e-06 +8 1 1.40e-01 1.57e-01 1 total current 1.00e-06 1.00e-06 +9 1 1.57e-01 1.75e-01 1 total current 1.00e-06 1.00e-06 +10 1 1.75e-01 1.92e-01 1 total current 0.00e+00 0.00e+00 +11 1 1.92e-01 2.09e-01 1 total current 2.00e-06 1.33e-06 +12 1 2.09e-01 2.27e-01 1 total current 0.00e+00 0.00e+00 +13 1 2.27e-01 2.44e-01 1 total current 1.00e-06 1.00e-06 +14 1 2.44e-01 2.62e-01 1 total current 1.00e-06 1.00e-06 +15 1 2.62e-01 2.79e-01 1 total current 2.00e-06 1.33e-06 +16 1 2.79e-01 2.97e-01 1 total current 0.00e+00 0.00e+00 +17 1 2.97e-01 3.14e-01 1 total current 2.00e-06 2.00e-06 +18 1 3.14e-01 3.32e-01 1 total current 1.00e-06 1.00e-06 +19 1 3.32e-01 3.49e-01 1 total current 1.00e-06 1.00e-06 +20 1 3.49e-01 3.67e-01 1 total current 2.00e-06 1.33e-06 +21 1 3.67e-01 3.84e-01 1 total current 0.00e+00 0.00e+00 +22 1 3.84e-01 4.01e-01 1 total current 2.00e-06 1.33e-06 +23 1 4.01e-01 4.19e-01 1 total current 2.00e-06 1.33e-06 +24 1 4.19e-01 4.36e-01 1 total current 1.00e-06 1.00e-06 +25 1 4.36e-01 4.54e-01 1 total current 2.00e-06 2.00e-06 +26 1 4.54e-01 4.71e-01 1 total current 5.00e-06 2.24e-06 +27 1 4.71e-01 4.89e-01 1 total current 4.00e-06 1.63e-06 +28 1 4.89e-01 5.06e-01 1 total current 3.00e-06 1.53e-06 +29 1 5.06e-01 5.24e-01 1 total current 3.00e-06 1.53e-06 +30 1 5.24e-01 5.41e-01 1 total current 3.00e-06 1.53e-06 +31 1 5.41e-01 5.59e-01 1 total current 7.00e-06 2.13e-06 +32 1 5.59e-01 5.76e-01 1 total current 3.00e-06 1.53e-06 +33 1 5.76e-01 5.93e-01 1 total current 3.00e-06 1.53e-06 +34 1 5.93e-01 6.11e-01 1 total current 2.00e-06 1.33e-06 +35 1 6.11e-01 6.28e-01 1 total current 2.00e-06 1.33e-06 +36 1 6.28e-01 6.46e-01 1 total current 3.00e-06 2.13e-06 +37 1 6.46e-01 6.63e-01 1 total current 3.00e-06 1.53e-06 +38 1 6.63e-01 6.81e-01 1 total current 2.00e-06 1.33e-06 +39 1 6.81e-01 6.98e-01 1 total current 3.00e-06 1.53e-06 +40 1 6.98e-01 7.16e-01 1 total current 1.00e-06 1.00e-06 +41 1 7.16e-01 7.33e-01 1 total current 6.00e-06 2.67e-06 +42 1 7.33e-01 7.50e-01 1 total current 6.00e-06 2.21e-06 +43 1 7.50e-01 7.68e-01 1 total current 7.00e-06 3.35e-06 +44 1 7.68e-01 7.85e-01 1 total current 6.00e-06 2.67e-06 +45 1 7.85e-01 8.03e-01 1 total current 6.00e-06 2.21e-06 +46 1 8.03e-01 8.20e-01 1 total current 7.00e-06 2.13e-06 +47 1 8.20e-01 8.38e-01 1 total current 5.00e-06 2.24e-06 +48 1 8.38e-01 8.55e-01 1 total current 3.00e-06 1.53e-06 +49 1 8.55e-01 8.73e-01 1 total current 8.00e-06 3.27e-06 +50 1 8.73e-01 8.90e-01 1 total current 7.00e-06 2.13e-06 +51 1 8.90e-01 9.08e-01 1 total current 6.00e-06 2.21e-06 +52 1 9.08e-01 9.25e-01 1 total current 4.00e-06 2.21e-06 +53 1 9.25e-01 9.42e-01 1 total current 1.20e-05 3.27e-06 +54 1 9.42e-01 9.60e-01 1 total current 8.00e-06 3.89e-06 +55 1 9.60e-01 9.77e-01 1 total current 1.20e-05 4.42e-06 +56 1 9.77e-01 9.95e-01 1 total current 7.00e-06 3.67e-06 +57 1 9.95e-01 1.01e+00 1 total current 1.20e-05 3.89e-06 +58 1 1.01e+00 1.03e+00 1 total current 9.00e-06 2.33e-06 +59 1 1.03e+00 1.05e+00 1 total current 6.00e-06 2.67e-06 +60 1 1.05e+00 1.06e+00 1 total current 7.00e-06 2.13e-06 +61 1 1.06e+00 1.08e+00 1 total current 5.00e-06 2.24e-06 +62 1 1.08e+00 1.10e+00 1 total current 1.20e-05 2.91e-06 +63 1 1.10e+00 1.12e+00 1 total current 1.30e-05 3.35e-06 +64 1 1.12e+00 1.13e+00 1 total current 9.00e-06 3.14e-06 +65 1 1.13e+00 1.15e+00 1 total current 1.20e-05 3.89e-06 +66 1 1.15e+00 1.17e+00 1 total current 6.00e-06 2.21e-06 +67 1 1.17e+00 1.19e+00 1 total current 5.11e-03 4.20e-05 +68 1 1.19e+00 1.20e+00 1 total current 8.00e-06 3.27e-06 +69 1 1.20e+00 1.22e+00 1 total current 1.30e-05 4.48e-06 +70 1 1.22e+00 1.24e+00 1 total current 1.20e-05 3.89e-06 +71 1 1.24e+00 1.26e+00 1 total current 1.50e-05 4.28e-06 +72 1 1.26e+00 1.27e+00 1 total current 7.00e-06 3.00e-06 +73 1 1.27e+00 1.29e+00 1 total current 1.60e-05 3.71e-06 +74 1 1.29e+00 1.31e+00 1 total current 9.00e-06 3.79e-06 +75 1 1.31e+00 1.33e+00 1 total current 1.30e-05 2.60e-06 +76 1 1.33e+00 1.34e+00 1 total current 1.60e-05 3.06e-06 +77 1 1.34e+00 1.36e+00 1 total current 1.40e-05 2.67e-06 +78 1 1.36e+00 1.38e+00 1 total current 1.40e-05 6.86e-06 +79 1 1.38e+00 1.40e+00 1 total current 1.50e-05 4.28e-06 +80 1 1.40e+00 1.41e+00 1 total current 3.26e-03 6.93e-05 +81 1 1.41e+00 1.43e+00 1 total current 1.40e-05 3.71e-06 +82 1 1.43e+00 1.45e+00 1 total current 1.30e-05 3.00e-06 +83 1 1.45e+00 1.47e+00 1 total current 1.30e-05 3.67e-06 +84 1 1.47e+00 1.48e+00 1 total current 1.70e-05 5.59e-06 +85 1 1.48e+00 1.50e+00 1 total current 1.70e-05 3.67e-06 +86 1 1.50e+00 1.52e+00 1 total current 1.40e-05 3.40e-06 +87 1 1.52e+00 1.54e+00 1 total current 2.50e-05 4.53e-06 +88 1 1.54e+00 1.55e+00 1 total current 1.30e-05 5.39e-06 +89 1 1.55e+00 1.57e+00 1 total current 1.80e-05 4.67e-06 +90 1 1.57e+00 1.59e+00 1 total current 1.40e-05 4.52e-06 +91 1 1.59e+00 1.61e+00 1 total current 1.70e-05 4.23e-06 +92 1 1.61e+00 1.62e+00 1 total current 1.40e-05 3.71e-06 +93 1 1.62e+00 1.64e+00 1 total current 1.00e-05 2.11e-06 +94 1 1.64e+00 1.66e+00 1 total current 2.00e-05 4.22e-06 +95 1 1.66e+00 1.68e+00 1 total current 2.30e-05 5.59e-06 +96 1 1.68e+00 1.69e+00 1 total current 1.70e-05 6.51e-06 +97 1 1.69e+00 1.71e+00 1 total current 1.30e-05 3.00e-06 +98 1 1.71e+00 1.73e+00 1 total current 1.50e-05 4.01e-06 +99 1 1.73e+00 1.75e+00 1 total current 1.70e-05 3.96e-06 +100 1 1.75e+00 1.76e+00 1 total current 1.80e-05 5.12e-06 +101 1 1.76e+00 1.78e+00 1 total current 2.50e-05 6.54e-06 +102 1 1.78e+00 1.80e+00 1 total current 1.80e-05 3.59e-06 +103 1 1.80e+00 1.82e+00 1 total current 1.50e-05 4.01e-06 +104 1 1.82e+00 1.83e+00 1 total current 1.10e-05 4.07e-06 +105 1 1.83e+00 1.85e+00 1 total current 1.50e-05 4.01e-06 +106 1 1.85e+00 1.87e+00 1 total current 1.90e-05 4.82e-06 +107 1 1.87e+00 1.88e+00 1 total current 2.30e-05 3.96e-06 +108 1 1.88e+00 1.90e+00 1 total current 2.00e-05 3.94e-06 +109 1 1.90e+00 1.92e+00 1 total current 1.50e-05 3.42e-06 +110 1 1.92e+00 1.94e+00 1 total current 2.20e-05 5.12e-06 +111 1 1.94e+00 1.95e+00 1 total current 2.10e-05 5.86e-06 +112 1 1.95e+00 1.97e+00 1 total current 2.60e-05 4.27e-06 +113 1 1.97e+00 1.99e+00 1 total current 2.20e-05 4.16e-06 +114 1 1.99e+00 2.01e+00 1 total current 2.40e-05 5.42e-06 +115 1 2.01e+00 2.02e+00 1 total current 1.60e-05 4.52e-06 +116 1 2.02e+00 2.04e+00 1 total current 1.30e-05 3.35e-06 +117 1 2.04e+00 2.06e+00 1 total current 1.90e-05 4.07e-06 +118 1 2.06e+00 2.08e+00 1 total current 1.30e-05 3.00e-06 +119 1 2.08e+00 2.09e+00 1 total current 1.50e-05 4.28e-06 +120 1 2.09e+00 2.11e+00 1 total current 3.00e-05 4.94e-06 +121 1 2.11e+00 2.13e+00 1 total current 2.30e-05 5.39e-06 +122 1 2.13e+00 2.15e+00 1 total current 2.20e-05 4.67e-06 +123 1 2.15e+00 2.16e+00 1 total current 1.80e-05 4.67e-06 +124 1 2.16e+00 2.18e+00 1 total current 1.50e-05 4.01e-06 +125 1 2.18e+00 2.20e+00 1 total current 1.80e-05 4.16e-06 +126 1 2.20e+00 2.22e+00 1 total current 1.80e-05 4.42e-06 +127 1 2.22e+00 2.23e+00 1 total current 1.80e-05 5.54e-06 +128 1 2.23e+00 2.25e+00 1 total current 1.90e-05 4.33e-06 +129 1 2.25e+00 2.27e+00 1 total current 1.00e-05 3.33e-06 +130 1 2.27e+00 2.29e+00 1 total current 1.80e-05 4.42e-06 +131 1 2.29e+00 2.30e+00 1 total current 4.15e-03 5.40e-05 +132 1 2.30e+00 2.32e+00 1 total current 1.90e-05 2.33e-06 +133 1 2.32e+00 2.34e+00 1 total current 2.30e-05 3.96e-06 +134 1 2.34e+00 2.36e+00 1 total current 1.90e-05 3.48e-06 +135 1 2.36e+00 2.37e+00 1 total current 1.60e-05 3.71e-06 +136 1 2.37e+00 2.39e+00 1 total current 1.70e-05 4.48e-06 +137 1 2.39e+00 2.41e+00 1 total current 2.30e-05 4.48e-06 +138 1 2.41e+00 2.43e+00 1 total current 2.10e-05 3.48e-06 +139 1 2.43e+00 2.44e+00 1 total current 1.60e-05 2.21e-06 +140 1 2.44e+00 2.46e+00 1 total current 9.00e-06 2.77e-06 +141 1 2.46e+00 2.48e+00 1 total current 1.30e-05 3.00e-06 +142 1 2.48e+00 2.50e+00 1 total current 2.70e-05 3.67e-06 +143 1 2.50e+00 2.51e+00 1 total current 1.90e-05 4.07e-06 +144 1 2.51e+00 2.53e+00 1 total current 1.20e-05 4.16e-06 +145 1 2.53e+00 2.55e+00 1 total current 1.30e-05 2.13e-06 +146 1 2.55e+00 2.57e+00 1 total current 1.10e-05 2.33e-06 +147 1 2.57e+00 2.58e+00 1 total current 1.50e-05 3.07e-06 +148 1 2.58e+00 2.60e+00 1 total current 1.20e-05 2.49e-06 +149 1 2.60e+00 2.62e+00 1 total current 1.80e-05 5.54e-06 +150 1 2.62e+00 2.64e+00 1 total current 1.30e-05 3.67e-06 +151 1 2.64e+00 2.65e+00 1 total current 1.60e-05 3.40e-06 +152 1 2.65e+00 2.67e+00 1 total current 7.00e-06 3.35e-06 +153 1 2.67e+00 2.69e+00 1 total current 1.00e-05 2.98e-06 +154 1 2.69e+00 2.71e+00 1 total current 7.00e-06 3.35e-06 +155 1 2.71e+00 2.72e+00 1 total current 1.20e-05 2.91e-06 +156 1 2.72e+00 2.74e+00 1 total current 9.00e-06 2.33e-06 +157 1 2.74e+00 2.76e+00 1 total current 1.00e-05 3.33e-06 +158 1 2.76e+00 2.78e+00 1 total current 1.10e-05 3.14e-06 +159 1 2.78e+00 2.79e+00 1 total current 1.00e-05 3.33e-06 +160 1 2.79e+00 2.81e+00 1 total current 1.40e-05 4.76e-06 +161 1 2.81e+00 2.83e+00 1 total current 8.00e-06 2.91e-06 +162 1 2.83e+00 2.84e+00 1 total current 5.00e-06 2.69e-06 +163 1 2.84e+00 2.86e+00 1 total current 6.00e-06 2.21e-06 +164 1 2.86e+00 2.88e+00 1 total current 5.00e-06 1.67e-06 +165 1 2.88e+00 2.90e+00 1 total current 4.00e-06 2.21e-06 +166 1 2.90e+00 2.91e+00 1 total current 7.00e-06 2.13e-06 +167 1 2.91e+00 2.93e+00 1 total current 6.00e-06 2.67e-06 +168 1 2.93e+00 2.95e+00 1 total current 7.00e-06 2.13e-06 +169 1 2.95e+00 2.97e+00 1 total current 5.00e-06 1.67e-06 +170 1 2.97e+00 2.98e+00 1 total current 3.00e-06 1.53e-06 +171 1 2.98e+00 3.00e+00 1 total current 6.00e-06 2.21e-06 +172 1 3.00e+00 3.02e+00 1 total current 3.00e-06 1.53e-06 +173 1 3.02e+00 3.04e+00 1 total current 1.00e-05 2.98e-06 +174 1 3.04e+00 3.05e+00 1 total current 2.00e-06 1.33e-06 +175 1 3.05e+00 3.07e+00 1 total current 1.00e-06 1.00e-06 +176 1 3.07e+00 3.09e+00 1 total current 2.00e-06 1.33e-06 +177 1 3.09e+00 3.11e+00 1 total current 0.00e+00 0.00e+00 +178 1 3.11e+00 3.12e+00 1 total current 1.00e-06 1.00e-06 +179 1 3.12e+00 3.14e+00 1 total current 0.00e+00 0.00e+00 \ No newline at end of file diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py new file mode 100644 index 0000000000..8da05e1cfd --- /dev/null +++ b/tests/regression_tests/ncrystal/test.py @@ -0,0 +1,106 @@ +from math import pi +import filecmp +from difflib import unified_diff + +import numpy as np +import openmc +import openmc.lib +import pytest +import shutil + +from tests.testing_harness import PyAPITestHarness + +pytestmark = pytest.mark.skipif( + not shutil.which('ncrystal-config'), + reason="NCrystal is not installed.") + + +def pencil_beam_model(cfg, E0, N): + """Return an openmc.Model() object for a monoenergetic pencil + beam hitting a 1 mm sphere filled with the material defined by + the cfg string, and compute the angular distribution""" + + # Material definition + + m1 = openmc.Material.from_ncrystal(cfg) + materials = openmc.Materials([m1]) + + # Geometry definition + + sample_sphere = openmc.Sphere(r=0.1) + outer_sphere = openmc.Sphere(r=100, boundary_type="vacuum") + cell1 = openmc.Cell(region=-sample_sphere, fill=m1) + cell2_region = +sample_sphere & -outer_sphere + cell2 = openmc.Cell(region=cell2_region, fill=None) + geometry = openmc.Geometry([cell1, cell2]) + + # Source definition + + source = openmc.IndependentSource() + source.space = openmc.stats.Point((0, 0, -20)) + source.angle = openmc.stats.Monodirectional(reference_uvw=(0, 0, 1)) + source.energy = openmc.stats.Discrete([E0], [1.0]) + + # Execution settings + + settings = openmc.Settings() + settings.source = source + settings.run_mode = "fixed source" + settings.batches = 10 + settings.particles = N + + # Tally definition + + tally1 = openmc.Tally(name="angular distribution") + tally1.scores = ["current"] + filter1 = openmc.SurfaceFilter(sample_sphere) + filter2 = openmc.PolarFilter(np.linspace(0, pi, 180+1)) + filter3 = openmc.CellFromFilter(cell1) + tally1.filters = [filter1, filter2, filter3] + tallies = openmc.Tallies([tally1]) + + return openmc.Model(geometry, materials, settings, tallies) + + +class NCrystalTest(PyAPITestHarness): + def _get_results(self): + """Digest info in the statepoint and return as a string.""" + + # Read the statepoint file. + with openmc.StatePoint(self._sp_name) as sp: + tal = sp.get_tally(name='angular distribution') + df = tal.get_pandas_dataframe() + return df.to_string() + + +def test_ncrystal(): + n_particles = 100000 + T = 293.6 # K + E0 = 0.012 # eV + cfg = 'Al_sg225.ncmat' + test = pencil_beam_model(cfg, E0, n_particles) + harness = NCrystalTest('statepoint.10.h5', model=test) + harness.main() + + +def test_cfg_from_xml(): + """Make sure the cfg string is read by from_xml method""" + n_particles = 100000 + E0 = 0.012 # eV + cfg = 'Al_sg225.ncmat' + model = pencil_beam_model(cfg, E0, n_particles) + #export the original material generated with cfg string + model.materials.export_to_xml('materials.xml.orig') + expected = open('materials.xml.orig', 'r').readlines() + #read back the original material + mats_from_xml = openmc.Materials.from_xml('materials.xml.orig') + #export again + mats_from_xml.export_to_xml('materials.xml.after') + actual = open('materials.xml.after', 'r').readlines() + compare = filecmp.cmp('materials.xml.orig','materials.xml.after') + if not compare: + diff = unified_diff(expected, actual, 'materials.xml.orig', + 'materials.xml.after') + print('Input differences:') + print(''.join(diff)) + assert compare, 'Materials not read correctly from XML' diff --git a/tests/regression_tests/output/results_true.dat b/tests/regression_tests/output/results_true.dat index fe46748c80..97b997ae6b 100644 --- a/tests/regression_tests/output/results_true.dat +++ b/tests/regression_tests/output/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 diff --git a/tests/regression_tests/particle_restart_eigval/results_true.dat b/tests/regression_tests/particle_restart_eigval/results_true.dat index 64101d7706..3feeb5e74b 100644 --- a/tests/regression_tests/particle_restart_eigval/results_true.dat +++ b/tests/regression_tests/particle_restart_eigval/results_true.dat @@ -1,16 +1,16 @@ current batch: -1.100000E+01 +8.000000E+00 current generation: 1.000000E+00 particle id: -9.020000E+02 +6.000000E+01 run mode: eigenvalue particle weight: 1.000000E+00 particle energy: -3.691964E+06 +2.028153E+06 particle xyz: --5.047439E+01 2.730535E+01 -2.619863E+01 +-3.678172E+01 -6.073321E+01 2.756488E+01 particle uvw: --6.278670E-01 1.419818E-01 -7.652609E-01 +-3.284774E-01 -8.920284E-01 3.104639E-01 diff --git a/tests/regression_tests/particle_restart_eigval/settings.xml b/tests/regression_tests/particle_restart_eigval/settings.xml index 64b1a4dd45..de580e4c48 100644 --- a/tests/regression_tests/particle_restart_eigval/settings.xml +++ b/tests/regression_tests/particle_restart_eigval/settings.xml @@ -6,7 +6,7 @@ 12 5 1200 - + 1000000 -10 -10 -5 10 10 5 diff --git a/tests/regression_tests/particle_restart_eigval/test.py b/tests/regression_tests/particle_restart_eigval/test.py index e526e50038..bad3f158c0 100644 --- a/tests/regression_tests/particle_restart_eigval/test.py +++ b/tests/regression_tests/particle_restart_eigval/test.py @@ -2,5 +2,5 @@ from tests.testing_harness import ParticleRestartTestHarness def test_particle_restart_eigval(): - harness = ParticleRestartTestHarness('particle_11_902.h5') + harness = ParticleRestartTestHarness('particle_8_60.h5') harness.main() diff --git a/tests/regression_tests/periodic/inputs_true.dat b/tests/regression_tests/periodic/inputs_true.dat index c1f6d15631..9183db3c49 100644 --- a/tests/regression_tests/periodic/inputs_true.dat +++ b/tests/regression_tests/periodic/inputs_true.dat @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 4 - 0 - - - 0 0 0 5 5 0 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 4 + 0 + + + 0 0 0 5 5 0 + + + + diff --git a/tests/regression_tests/periodic/results_true.dat b/tests/regression_tests/periodic/results_true.dat index 6d58baccb0..626004b16b 100644 --- a/tests/regression_tests/periodic/results_true.dat +++ b/tests/regression_tests/periodic/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.654583E+00 1.501286E-02 +1.624889E+00 1.108153E-02 diff --git a/tests/regression_tests/periodic/test.py b/tests/regression_tests/periodic/test.py index 51a70a6aa7..73fe8a83e4 100644 --- a/tests/regression_tests/periodic/test.py +++ b/tests/regression_tests/periodic/test.py @@ -42,7 +42,7 @@ def box_model(): model.settings.particles = 1000 model.settings.batches = 4 model.settings.inactive = 0 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( (0, 0, 0), (5, 5, 0)) ) return model diff --git a/tests/regression_tests/periodic_6fold/inputs_true.dat b/tests/regression_tests/periodic_6fold/inputs_true.dat index 35bbcc0475..075cffc123 100644 --- a/tests/regression_tests/periodic_6fold/inputs_true.dat +++ b/tests/regression_tests/periodic_6fold/inputs_true.dat @@ -1,34 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 4 - 0 - - - 0 0 0 5 5 0 - - - + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 4 + 0 + + + 0 0 0 5 5 0 + + + + diff --git a/tests/regression_tests/periodic_6fold/results_true.dat b/tests/regression_tests/periodic_6fold/results_true.dat index def4d0c7ab..04aa308781 100644 --- a/tests/regression_tests/periodic_6fold/results_true.dat +++ b/tests/regression_tests/periodic_6fold/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.845885E+00 1.472487E-02 +1.848492E+00 2.933785E-03 diff --git a/tests/regression_tests/periodic_6fold/test.py b/tests/regression_tests/periodic_6fold/test.py index 555ab24f9f..272c65df57 100644 --- a/tests/regression_tests/periodic_6fold/test.py +++ b/tests/regression_tests/periodic_6fold/test.py @@ -1,5 +1,6 @@ +from math import sin, cos, pi + import openmc -import numpy as np import pytest from tests.testing_harness import PyAPITestHarness @@ -24,17 +25,14 @@ def model(): # (it essentially defines a circle of half-cylinders), but it is # designed so that periodic and reflective BCs will give different # answers. - theta1 = (-1/6 + 1/2) * np.pi - theta2 = (1/6 - 1/2) * np.pi - plane1 = openmc.Plane(a=np.cos(theta1), b=np.sin(theta1), - boundary_type='periodic') - plane2 = openmc.Plane(a=np.cos(theta2), b=np.sin(theta2), - boundary_type='periodic') + theta1 = (-1/6 + 1/2) * pi + theta2 = (1/6 - 1/2) * pi + plane1 = openmc.Plane(a=cos(theta1), b=sin(theta1), boundary_type='periodic') + plane2 = openmc.Plane(a=cos(theta2), b=sin(theta2), boundary_type='periodic') - x_max = openmc.XPlane(x0=5., boundary_type='reflective') + x_max = openmc.XPlane(5., boundary_type='reflective') - z_cyl = openmc.ZCylinder(x0=3*np.cos(np.pi/6), y0=3*np.sin(np.pi/6), - r=2.0) + z_cyl = openmc.ZCylinder(x0=3*cos(pi/6), y0=3*sin(pi/6), r=2.0) outside_cyl = openmc.Cell(1, fill=water, region=( +plane1 & +plane2 & -x_max & +z_cyl)) @@ -48,7 +46,7 @@ def model(): model.settings.particles = 1000 model.settings.batches = 4 model.settings.inactive = 0 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( (0, 0, 0), (5, 5, 0)) ) return model diff --git a/tests/regression_tests/periodic_hex/inputs_true.dat b/tests/regression_tests/periodic_hex/inputs_true.dat index 00e539d4bf..e65af3d940 100644 --- a/tests/regression_tests/periodic_hex/inputs_true.dat +++ b/tests/regression_tests/periodic_hex/inputs_true.dat @@ -1,24 +1,24 @@ - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + diff --git a/tests/regression_tests/periodic_hex/results_true.dat b/tests/regression_tests/periodic_hex/results_true.dat index 16512ec23f..eff00b6e16 100644 --- a/tests/regression_tests/periodic_hex/results_true.dat +++ b/tests/regression_tests/periodic_hex/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.271604E+00 1.159996E-02 +2.285622E+00 2.576768E-03 diff --git a/tests/regression_tests/periodic_hex/test.py b/tests/regression_tests/periodic_hex/test.py index a21819b1ad..db9f6cfd5b 100644 --- a/tests/regression_tests/periodic_hex/test.py +++ b/tests/regression_tests/periodic_hex/test.py @@ -12,8 +12,8 @@ def hex_model(): fuel.add_nuclide('U235', 1.0) fuel.set_density('g/cc', 4.5) - hex_region = openmc.model.hexagonal_prism(10.0, boundary_type='periodic') - cell = openmc.Cell(fill=fuel, region=hex_region) + hex_prism = openmc.model.HexagonalPrism(10.0, boundary_type='periodic') + cell = openmc.Cell(fill=fuel, region=-hex_prism) model.geometry = openmc.Geometry([cell]) # Define settings diff --git a/tests/regression_tests/photon_production/inputs_true.dat b/tests/regression_tests/photon_production/inputs_true.dat index 09f1fd290f..07eebaa3e6 100644 --- a/tests/regression_tests/photon_production/inputs_true.dat +++ b/tests/regression_tests/photon_production/inputs_true.dat @@ -1,68 +1,67 @@ - - - - - - - - - - - - - - - - - - - fixed source - 10000 - 1 - - - 0 0 0 - - - - 14000000.0 1.0 - - - ttb - true - - 1000.0 - - - - - - 1 - - - neutron photon electron positron - - - 1 2 - current - - - 2 - Al27 total - total (n,gamma) - tracklength - - - 2 - Al27 total - total heating (n,gamma) - collision - - - 2 - Al27 total - total heating (n,gamma) - analog - - + + + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 14000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + 1 + + + neutron photon electron positron + + + 1 2 + current + + + 2 + Al27 total + total (n,gamma) + tracklength + + + 2 + Al27 total + total heating (n,gamma) + collision + + + 2 + Al27 total + total heating (n,gamma) + analog + + + diff --git a/tests/regression_tests/photon_production/results_true.dat b/tests/regression_tests/photon_production/results_true.dat index 03fb80b81e..413f6f0ca4 100644 --- a/tests/regression_tests/photon_production/results_true.dat +++ b/tests/regression_tests/photon_production/results_true.dat @@ -1,8 +1,8 @@ tally 1: 8.610000E-01 7.413210E-01 -9.493000E-01 -9.011705E-01 +9.491000E-01 +9.007908E-01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -16,12 +16,12 @@ tally 2: 1.573004E+00 4.296434E-04 1.845934E-07 -2.337049E-01 -5.461796E-02 +2.350047E-01 +5.522722E-02 0.000000E+00 0.000000E+00 -2.337049E-01 -5.461796E-02 +2.350047E-01 +5.522722E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -53,40 +53,40 @@ tally 3: 4.104374E+12 4.296582E-04 1.846062E-07 -2.286000E-01 -5.225796E-02 -7.054033E+03 -4.975938E+07 +2.297000E-01 +5.276209E-02 +4.196651E+00 +1.761188E+01 0.000000E+00 0.000000E+00 -2.286000E-01 -5.225796E-02 -7.054033E+03 -4.975938E+07 +2.297000E-01 +5.276209E-02 +4.196651E+00 +1.761188E+01 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.764573E+05 -3.113718E+10 +1.774484E+05 +3.148794E+10 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.764573E+05 -3.113718E+10 +1.774484E+05 +3.148794E+10 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -7.691658E+03 -5.916160E+07 +1.474427E+04 +2.173936E+08 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -7.691658E+03 -5.916160E+07 +1.474427E+04 +2.173936E+08 0.000000E+00 0.000000E+00 tally 4: @@ -102,16 +102,16 @@ tally 4: 4.104374E+12 0.000000E+00 0.000000E+00 -2.286000E-01 -5.225796E-02 -7.054033E+03 -4.975938E+07 +2.297000E-01 +5.276209E-02 +4.196651E+00 +1.761188E+01 0.000000E+00 0.000000E+00 -2.286000E-01 -5.225796E-02 -7.054033E+03 -4.975938E+07 +2.297000E-01 +5.276209E-02 +4.196651E+00 +1.761188E+01 0.000000E+00 0.000000E+00 0.000000E+00 @@ -122,8 +122,8 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -1.764573E+05 -3.113718E+10 +1.774484E+05 +3.148794E+10 0.000000E+00 0.000000E+00 0.000000E+00 @@ -134,7 +134,7 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.691658E+03 -5.916160E+07 +1.474427E+04 +2.173936E+08 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/photon_production/test.py b/tests/regression_tests/photon_production/test.py index 107b53bbb6..150448a12f 100644 --- a/tests/regression_tests/photon_production/test.py +++ b/tests/regression_tests/photon_production/test.py @@ -27,7 +27,7 @@ def model(): inner_cyl_right.fill = mat model.geometry = openmc.Geometry([inner_cyl_left, inner_cyl_right, outer_cyl]) - source = openmc.Source() + source = openmc.IndependentSource() source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Monodirectional() source.energy = openmc.stats.Discrete([14.0e6], [1.0]) diff --git a/tests/regression_tests/photon_production_fission/inputs_true.dat b/tests/regression_tests/photon_production_fission/inputs_true.dat index c317f6273e..11a194e3ce 100644 --- a/tests/regression_tests/photon_production_fission/inputs_true.dat +++ b/tests/regression_tests/photon_production_fission/inputs_true.dat @@ -1,49 +1,48 @@ - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 2 - - - 0 0 0 - - - true - - - - - neutron photon - - - 1 - U235 total - fission heating-local - tracklength - - - 1 - U235 total - fission heating heating-local - collision - - - 1 - U235 total - fission heating heating-local - analog - - + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 2 + + + 0 0 0 + + + true + + + + neutron photon + + + 1 + U235 total + fission heating-local + tracklength + + + 1 + U235 total + fission heating heating-local + collision + + + 1 + U235 total + fission heating heating-local + analog + + + diff --git a/tests/regression_tests/photon_production_fission/results_true.dat b/tests/regression_tests/photon_production_fission/results_true.dat index ac91b63a13..af325d4b02 100644 --- a/tests/regression_tests/photon_production_fission/results_true.dat +++ b/tests/regression_tests/photon_production_fission/results_true.dat @@ -1,12 +1,12 @@ k-combined: -2.294874E+00 2.454640E-02 +2.297165E+00 1.955494E-02 tally 1: -2.691351E+00 -2.415076E+00 +2.696393E+00 +2.423937E+00 0.000000E+00 0.000000E+00 -2.691351E+00 -2.415076E+00 +2.696393E+00 +2.423937E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -18,52 +18,52 @@ tally 1: 0.000000E+00 0.000000E+00 tally 2: -2.654218E+00 -2.348806E+00 -4.260252E+08 -6.051306E+16 +2.672029E+00 +2.380251E+00 +4.288244E+08 +6.130569E+16 0.000000E+00 0.000000E+00 -2.654218E+00 -2.348806E+00 -4.260252E+08 -6.051306E+16 +2.672029E+00 +2.380251E+00 +4.288244E+08 +6.130569E+16 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.507232E+06 -2.096890E+12 +1.711908E+05 +9.769096E+09 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.507232E+06 -2.096890E+12 +1.711908E+05 +9.769096E+09 0.000000E+00 0.000000E+00 tally 3: -2.656221E+00 -2.351840E+00 -4.260252E+08 -6.051306E+16 +2.649127E+00 +2.339294E+00 +4.288244E+08 +6.130569E+16 0.000000E+00 0.000000E+00 -2.656221E+00 -2.351840E+00 -4.260252E+08 -6.051306E+16 +2.649127E+00 +2.339294E+00 +4.288244E+08 +6.130569E+16 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.507232E+06 -2.096890E+12 +1.711908E+05 +9.769096E+09 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.507232E+06 -2.096890E+12 +1.711908E+05 +9.769096E+09 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/photon_production_fission/test.py b/tests/regression_tests/photon_production_fission/test.py index 595d3c2d6c..96665b3086 100644 --- a/tests/regression_tests/photon_production_fission/test.py +++ b/tests/regression_tests/photon_production_fission/test.py @@ -20,7 +20,7 @@ def model(): model.settings.batches = 5 model.settings.inactive = 2 model.settings.photon_transport = True - model.settings.source = openmc.Source(space=openmc.stats.Point((0, 0, 0))) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point((0, 0, 0))) particle_filter = openmc.ParticleFilter(['neutron', 'photon']) tally_tracklength = openmc.Tally() diff --git a/tests/regression_tests/photon_source/inputs_true.dat b/tests/regression_tests/photon_source/inputs_true.dat index f7c9c24b4f..adaa5fb42d 100644 --- a/tests/regression_tests/photon_source/inputs_true.dat +++ b/tests/regression_tests/photon_source/inputs_true.dat @@ -1,45 +1,44 @@ - - - - - - - - - - - - - - - - - fixed source - 10000 - 1 - - - 0 0 0 - - - - 10000000.0 1.0 - - - ttb - true - - 1000.0 - - - - - - photon - - - 1 - flux (n,gamma) - - + + + + + + + + + + + + + + + + fixed source + 10000 + 1 + + + 0 0 0 + + + + 10000000.0 1.0 + + + ttb + true + + 1000.0 + + + + + photon + + + 1 + flux (n,gamma) + + + diff --git a/tests/regression_tests/photon_source/results_true.dat b/tests/regression_tests/photon_source/results_true.dat index fcc75cbca8..8d934afc66 100644 --- a/tests/regression_tests/photon_source/results_true.dat +++ b/tests/regression_tests/photon_source/results_true.dat @@ -1,5 +1,5 @@ tally 1: -2.263938E+02 -5.125417E+04 +2.263761E+02 +5.124615E+04 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index a2bfb90384..c2eb1476de 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -7,22 +7,21 @@ from tests.testing_harness import PyAPITestHarness class SourceTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) mat = openmc.Material() mat.set_density('g/cm3', 0.998207) mat.add_element('H', 0.111894) mat.add_element('O', 0.888106) - materials = openmc.Materials([mat]) - materials.export_to_xml() + self._model.materials = openmc.Materials([mat]) sphere = openmc.Sphere(r=1.0e9, boundary_type='reflective') inside_sphere = openmc.Cell() inside_sphere.region = -sphere inside_sphere.fill = mat - geometry = openmc.Geometry([inside_sphere]) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry([inside_sphere]) - source = openmc.Source() + source = openmc.IndependentSource() source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Isotropic() source.energy = openmc.stats.Discrete([10.0e6], [1.0]) @@ -36,16 +35,16 @@ class SourceTestHarness(PyAPITestHarness): settings.cutoff = {'energy_photon' : 1000.0} settings.run_mode = 'fixed source' settings.source = source - settings.export_to_xml() + self._model.settings = settings particle_filter = openmc.ParticleFilter('photon') tally = openmc.Tally() tally.filters = [particle_filter] tally.scores = ['flux', '(n,gamma)'] tallies = openmc.Tallies([tally]) - tallies.export_to_xml() + self._model.tallies = tallies def test_photon_source(): - harness = SourceTestHarness('statepoint.1.h5') + harness = SourceTestHarness('statepoint.1.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/plot/geometry.xml b/tests/regression_tests/plot/geometry.xml deleted file mode 100644 index 83619d9f78..0000000000 --- a/tests/regression_tests/plot/geometry.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tests/regression_tests/plot/model.xml b/tests/regression_tests/plot/model.xml new file mode 100644 index 0000000000..a63ff95daa --- /dev/null +++ b/tests/regression_tests/plot/model.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + plot + 1 + + 5 4 3 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + + + -10.0 10.0 + -10.0 10.0 + -10.0 0.0 5.0 7.5 8.75 10.0 + + + 2 + + + 1 + total + + + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + + + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + + + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + 0 0 0 + + + 0.0 0.0 0.0 + 20.0 20.0 10.0 + 100 100 10 + + + diff --git a/tests/regression_tests/plot/plots.xml b/tests/regression_tests/plot/plots.xml deleted file mode 100644 index ce63da1442..0000000000 --- a/tests/regression_tests/plot/plots.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - 0. 0. 0. - 25 25 - 200 200 - - - - - - 0. 0. 0. - 25 25 - 200 200 - - - - - - 0. 0. 0. - 25 25 - 200 200 - 0 0 0 - - - - 100 100 10 - 0. 0. 0. - 20 20 10 - - - diff --git a/tests/regression_tests/plot/results_true.dat b/tests/regression_tests/plot/results_true.dat index 9174202fba..1be60ad4fa 100644 --- a/tests/regression_tests/plot/results_true.dat +++ b/tests/regression_tests/plot/results_true.dat @@ -1 +1 @@ -f85c20735a0c08525fe48b19a8e075c074539ee6c8860268fa0cb515d842496b7086e5b94305ef78dcf2106bb193abdf438259ac8ff1d0245a2782eb6f5af873 \ No newline at end of file +6385d2969ed54d47a09f75112595fc034a16f551346c92d87ed9e7841881baaee8e56190c347e97c3f919ef677bb5b858960d60fe985a00e2848a3bfbef7a12b \ No newline at end of file diff --git a/tests/regression_tests/plot/test.py b/tests/regression_tests/plot/test.py index 2fd6a5b795..30d9ab0d08 100644 --- a/tests/regression_tests/plot/test.py +++ b/tests/regression_tests/plot/test.py @@ -1,60 +1,7 @@ -import glob -import hashlib -import os - -import h5py -import openmc - -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config -class PlotTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC plotting tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - def _test_output_created(self): - """Make sure *.png has been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.png'): - # Add PNG output to results - with open(fname, 'rb') as fh: - outstr += fh.read() - elif fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - - def test_plot(): harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', 'plot_4.h5')) diff --git a/tests/regression_tests/plot_overlaps/geometry.xml b/tests/regression_tests/plot_overlaps/geometry.xml deleted file mode 100644 index 7a9f1fb41f..0000000000 --- a/tests/regression_tests/plot_overlaps/geometry.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/tests/regression_tests/plot_overlaps/materials.xml b/tests/regression_tests/plot_overlaps/materials.xml deleted file mode 100644 index 90b3542675..0000000000 --- a/tests/regression_tests/plot_overlaps/materials.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/tests/regression_tests/plot_overlaps/model.xml b/tests/regression_tests/plot_overlaps/model.xml new file mode 100644 index 0000000000..e3b65d45d4 --- /dev/null +++ b/tests/regression_tests/plot_overlaps/model.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + plot + 1 + + 5 4 3 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + + true + + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + + true + 255 211 0 + + + 0.0 0.0 0.0 + 25.0 25.0 + 200 200 + 0 0 0 + + + 0.0 0.0 0.0 + 20.0 20.0 10.0 + 100 100 10 + + + diff --git a/tests/regression_tests/plot_overlaps/plots.xml b/tests/regression_tests/plot_overlaps/plots.xml deleted file mode 100644 index 28064f58fb..0000000000 --- a/tests/regression_tests/plot_overlaps/plots.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - 0. 0. 0. - 25 25 - 200 200 - - - true - - - - 0. 0. 0. - 25 25 - 200 200 - - true - 255 211 0 - - - - 0. 0. 0. - 25 25 - 200 200 - 0 0 0 - - - - 100 100 10 - 0. 0. 0. - 20 20 10 - - - diff --git a/tests/regression_tests/plot_overlaps/results_true.dat b/tests/regression_tests/plot_overlaps/results_true.dat index cb04daaa4c..93ee6769fc 100644 --- a/tests/regression_tests/plot_overlaps/results_true.dat +++ b/tests/regression_tests/plot_overlaps/results_true.dat @@ -1 +1 @@ -926065ceb2a9b8292fe6270317c38c4373473cfea19d2a8392a32e5ece8e314c04b9f032921d987bd195ae4b6f674d359b0e38302e6ae4c93b4ac9573a384ac6 \ No newline at end of file +125ce40fbff3e02e7f5f36a92a2e37abce9407154c825b6617242a4685dfc33c63041145b1f3114334338fbb86188331195ddc319ffce58fab2943d8e60f6da3 \ No newline at end of file diff --git a/tests/regression_tests/plot_overlaps/settings.xml b/tests/regression_tests/plot_overlaps/settings.xml deleted file mode 100644 index adf256d2d4..0000000000 --- a/tests/regression_tests/plot_overlaps/settings.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - plot - - - 5 4 3 - -10 -10 -10 - 10 10 10 - - 1 - - diff --git a/tests/regression_tests/plot_overlaps/test.py b/tests/regression_tests/plot_overlaps/test.py index cf23abc27f..0828c6e255 100644 --- a/tests/regression_tests/plot_overlaps/test.py +++ b/tests/regression_tests/plot_overlaps/test.py @@ -1,60 +1,7 @@ -import glob -import hashlib -import os - -import h5py -import openmc - -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config -class PlotTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC plotting tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - def _test_output_created(self): - """Make sure *.png has been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.png'): - # Add PNG output to results - with open(fname, 'rb') as fh: - outstr += fh.read() - elif fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - - def test_plot_overlap(): harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png', 'plot_4.h5')) diff --git a/tests/regression_tests/plot_projections/__init__.py b/tests/regression_tests/plot_projections/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/plot_projections/geometry.xml b/tests/regression_tests/plot_projections/geometry.xml new file mode 100644 index 0000000000..a648dfe92f --- /dev/null +++ b/tests/regression_tests/plot_projections/geometry.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tests/regression_tests/plot/materials.xml b/tests/regression_tests/plot_projections/materials.xml similarity index 80% rename from tests/regression_tests/plot/materials.xml rename to tests/regression_tests/plot_projections/materials.xml index 90b3542675..35f0de3128 100644 --- a/tests/regression_tests/plot/materials.xml +++ b/tests/regression_tests/plot_projections/materials.xml @@ -8,12 +8,12 @@ - + - + diff --git a/tests/regression_tests/plot_projections/plots.xml b/tests/regression_tests/plot_projections/plots.xml new file mode 100644 index 0000000000..50d129cd97 --- /dev/null +++ b/tests/regression_tests/plot_projections/plots.xml @@ -0,0 +1,90 @@ + + + + + 0. 0. 0. + 20. 20. 20. + 200 200 + + + + 70 + + + + 0. 0. 0. + 10. 10. 0. + 25 25 + 200 200 + + 90 + 4 + example1 + + + + 0. 0. 0. + 20. 20. 20. + 200 200 + 240 240 240 + example2.png + 2 + + + + 0. 0. 0. + 0. 10.0 20. + 200 200 + 110 240 240 + example3.png + + + + 0. 0. 0. + 10. 10. 10. + 25 25 + 200 200 + 25.0 + 2 + orthographic_example1 + + + + + + + 0. 0. 0. + 10. 10. 10. + 200 200 + phong.png + 1 3 + + + + + + + 0. 0. 0. + 10. 10. 10. + 0.5 + 200 200 + phong_diffuse.png + 1 3 + + + + + + + 0. 0. 0. + 10. 10. 10. + 0. 10. 10. + 200 200 + phong_move_light.png + 1 3 + + + + + + diff --git a/tests/regression_tests/plot_projections/results_true.dat b/tests/regression_tests/plot_projections/results_true.dat new file mode 100644 index 0000000000..d5c6a7a58b --- /dev/null +++ b/tests/regression_tests/plot_projections/results_true.dat @@ -0,0 +1 @@ +6b90dfcf3059f86d623bb6496bb92d5b6ea2788b79639b61f865b31b503b84df9af64e59eacb04ccb02a225cfdb51bb7fa4b4f71e8e6ea20b2714266b34886ce \ No newline at end of file diff --git a/tests/regression_tests/plot/settings.xml b/tests/regression_tests/plot_projections/settings.xml similarity index 100% rename from tests/regression_tests/plot/settings.xml rename to tests/regression_tests/plot_projections/settings.xml diff --git a/tests/regression_tests/plot/tallies.xml b/tests/regression_tests/plot_projections/tallies.xml similarity index 100% rename from tests/regression_tests/plot/tallies.xml rename to tests/regression_tests/plot_projections/tallies.xml diff --git a/tests/regression_tests/plot_projections/test.py b/tests/regression_tests/plot_projections/test.py new file mode 100644 index 0000000000..37a6ecf28d --- /dev/null +++ b/tests/regression_tests/plot_projections/test.py @@ -0,0 +1,9 @@ +from tests.testing_harness import PlotTestHarness +from tests.regression_tests import config + +def test_plot(): + harness = PlotTestHarness(('plot_1.png', 'example1.png', 'example2.png', + 'example3.png', 'orthographic_example1.png', + 'phong.png', 'phong_diffuse.png', + 'phong_move_light.png')) + harness.main() diff --git a/tests/regression_tests/plot_voxel/geometry.xml b/tests/regression_tests/plot_voxel/geometry.xml deleted file mode 100644 index 83619d9f78..0000000000 --- a/tests/regression_tests/plot_voxel/geometry.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tests/regression_tests/plot_voxel/materials.xml b/tests/regression_tests/plot_voxel/materials.xml deleted file mode 100644 index 90b3542675..0000000000 --- a/tests/regression_tests/plot_voxel/materials.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/tests/regression_tests/plot_voxel/model.xml b/tests/regression_tests/plot_voxel/model.xml new file mode 100644 index 0000000000..7b0e854c57 --- /dev/null +++ b/tests/regression_tests/plot_voxel/model.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + plot + 1 + + 5 4 3 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + + + 0.0 0.0 0.0 + 20.0 20.0 10.0 + 50 50 10 + + + diff --git a/tests/regression_tests/plot_voxel/plots.xml b/tests/regression_tests/plot_voxel/plots.xml deleted file mode 100644 index 833329b427..0000000000 --- a/tests/regression_tests/plot_voxel/plots.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - 50 50 10 - 0. 0. 0. - 20 20 10 - - - diff --git a/tests/regression_tests/plot_voxel/settings.xml b/tests/regression_tests/plot_voxel/settings.xml deleted file mode 100644 index adf256d2d4..0000000000 --- a/tests/regression_tests/plot_voxel/settings.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - plot - - - 5 4 3 - -10 -10 -10 - 10 10 10 - - 1 - - diff --git a/tests/regression_tests/plot_voxel/test.py b/tests/regression_tests/plot_voxel/test.py index c4fa20c80f..e034decf79 100644 --- a/tests/regression_tests/plot_voxel/test.py +++ b/tests/regression_tests/plot_voxel/test.py @@ -1,62 +1,11 @@ -import glob -import hashlib -import os -from subprocess import check_call - -import h5py -import openmc import pytest -from tests.testing_harness import TestHarness +from tests.testing_harness import PlotTestHarness from tests.regression_tests import config vtk = pytest.importorskip('vtk') -class PlotVoxelTestHarness(TestHarness): - """Specialized TestHarness for running OpenMC voxel plot tests.""" - def __init__(self, plot_names): - super().__init__(None) - self._plot_names = plot_names - - def _run_openmc(self): - openmc.plot_geometry(openmc_exec=config['exe']) - - check_call(['../../../scripts/openmc-voxel-to-vtk'] + - glob.glob('plot_4.h5')) - - def _test_output_created(self): - """Make sure plots have been created.""" - for fname in self._plot_names: - assert os.path.exists(fname), 'Plot output file does not exist.' - - def _cleanup(self): - super()._cleanup() - for fname in self._plot_names: - if os.path.exists(fname): - os.remove(fname) - - def _get_results(self): - """Return a string hash of the plot files.""" - outstr = bytes() - - for fname in self._plot_names: - if fname.endswith('.h5'): - # Add voxel data to results - with h5py.File(fname, 'r') as fh: - outstr += fh.attrs['filetype'] - outstr += fh.attrs['num_voxels'].tobytes() - outstr += fh.attrs['lower_left'].tobytes() - outstr += fh.attrs['voxel_width'].tobytes() - outstr += fh['data'][()].tobytes() - - # Hash the information and return. - sha512 = hashlib.sha512() - sha512.update(outstr) - outstr = sha512.hexdigest() - - return outstr - def test_plot_voxel(): - harness = PlotVoxelTestHarness(('plot_4.h5', 'plot.vti')) + harness = PlotTestHarness(('plot_4.h5', 'plot.vti'), voxel_convert_checks=['plot_4.h5']) harness.main() diff --git a/tests/regression_tests/ptables_off/results_true.dat b/tests/regression_tests/ptables_off/results_true.dat index 7c1d187bb6..3742748625 100644 --- a/tests/regression_tests/ptables_off/results_true.dat +++ b/tests/regression_tests/ptables_off/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.042885E-01 1.073889E-02 +2.968228E-01 1.770320E-03 diff --git a/tests/regression_tests/pulse_height/__init__.py b/tests/regression_tests/pulse_height/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/pulse_height/inputs_true.dat b/tests/regression_tests/pulse_height/inputs_true.dat new file mode 100644 index 0000000000..590928e435 --- /dev/null +++ b/tests/regression_tests/pulse_height/inputs_true.dat @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + fixed source + 100 + 5 + + + 0.0 0.0 0.0 + + + 1000000.0 1.0 + + + true + + + + 1 + + + 0.0 10000.0 20000.0 30000.0 40000.0 50000.0 60000.0 70000.0 80000.0 90000.0 100000.0 110000.0 120000.0 130000.0 140000.0 150000.0 160000.0 170000.0 180000.0 190000.0 200000.0 210000.0 220000.0 230000.0 240000.0 250000.0 260000.0 270000.0 280000.0 290000.0 300000.0 310000.0 320000.0 330000.0 340000.0 350000.0 360000.0 370000.0 380000.0 390000.0 400000.0 410000.0 420000.0 430000.0 440000.0 450000.0 460000.0 470000.0 480000.0 490000.0 500000.0 510000.0 520000.0 530000.0 540000.0 550000.0 560000.0 570000.0 580000.0 590000.0 600000.0 610000.0 620000.0 630000.0 640000.0 650000.0 660000.0 670000.0 680000.0 690000.0 700000.0 710000.0 720000.0 730000.0 740000.0 750000.0 760000.0 770000.0 780000.0 790000.0 800000.0 810000.0 820000.0 830000.0 840000.0 850000.0 860000.0 870000.0 880000.0 890000.0 900000.0 910000.0 920000.0 930000.0 940000.0 950000.0 960000.0 970000.0 980000.0 990000.0 1000000.0 + + + 1 2 + pulse-height + + + diff --git a/tests/regression_tests/pulse_height/results_true.dat b/tests/regression_tests/pulse_height/results_true.dat new file mode 100644 index 0000000000..c57e8ff1c8 --- /dev/null +++ b/tests/regression_tests/pulse_height/results_true.dat @@ -0,0 +1,201 @@ +tally 1: +4.140000E+00 +3.443000E+00 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +3.000000E-02 +5.000000E-04 +2.000000E-02 +4.000000E-04 +2.000000E-02 +4.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.000000E-02 +3.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.000000E-02 +6.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +2.000000E-02 +2.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +2.000000E-02 +4.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-02 +2.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +3.000000E-02 +5.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-02 +2.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +2.000000E-02 +2.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +2.000000E-02 +2.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +3.000000E-02 +5.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +1.000000E-02 +1.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-02 +2.000000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-01 +8.600000E-03 diff --git a/tests/regression_tests/pulse_height/test.py b/tests/regression_tests/pulse_height/test.py new file mode 100644 index 0000000000..90d960f664 --- /dev/null +++ b/tests/regression_tests/pulse_height/test.py @@ -0,0 +1,53 @@ +import numpy as np +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def sphere_model(): + + model = openmc.model.Model() + + # Define materials + NaI = openmc.Material() + NaI.set_density('g/cc', 3.7) + NaI.add_element('Na', 1.0) + NaI.add_element('I', 1.0) + + model.materials = openmc.Materials([NaI]) + + # Define geometry: two spheres in each other + s1 = openmc.Sphere(r=1) + s2 = openmc.Sphere(r=2, boundary_type='vacuum') + inner_sphere = openmc.Cell(name='inner sphere', fill=NaI, region=-s1) + outer_sphere = openmc.Cell(name='outer sphere', region=+s1 & -s2) + model.geometry = openmc.Geometry([inner_sphere, outer_sphere]) + + # Define settings + model.settings.run_mode = 'fixed source' + model.settings.batches = 5 + model.settings.particles = 100 + model.settings.photon_transport = True + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), + energy=openmc.stats.Discrete([1e6], [1]), + particle='photon' + ) + + # Define tallies + tally = openmc.Tally(name="pht tally") + tally.scores = ['pulse-height'] + cell_filter = openmc.CellFilter(inner_sphere) + energy_filter = openmc.EnergyFilter(np.linspace(0, 1_000_000, 101)) + tally.filters = [cell_filter, energy_filter] + model.tallies = [tally] + + return model + + + +def test_pulse_height(sphere_model): + harness = PyAPITestHarness('statepoint.5.h5', sphere_model) + harness.main() diff --git a/tests/regression_tests/quadric_surfaces/results_true.dat b/tests/regression_tests/quadric_surfaces/results_true.dat index 8dc59df029..6fb569f522 100644 --- a/tests/regression_tests/quadric_surfaces/results_true.dat +++ b/tests/regression_tests/quadric_surfaces/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.213534E+00 1.386061E-02 +1.216213E+00 2.789559E-02 diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/__init__.py b/tests/regression_tests/random_ray_adjoint_fixed_source/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/inputs_true.dat b/tests/regression_tests/random_ray_adjoint_fixed_source/inputs_true.dat new file mode 100644 index 0000000000..0adfc54884 --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/inputs_true.dat @@ -0,0 +1,246 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 500 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + true + naive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/results_true.dat b/tests/regression_tests/random_ray_adjoint_fixed_source/results_true.dat new file mode 100644 index 0000000000..e9aa9015b3 --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.790516E+04 +6.740859E+08 +tally 2: +6.885455E+04 +9.482551E+08 +tally 3: +1.956327E+05 +7.654468E+09 diff --git a/tests/regression_tests/random_ray_adjoint_fixed_source/test.py b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py new file mode 100644 index 0000000000..6c2790fa09 --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_fixed_source/test.py @@ -0,0 +1,22 @@ +import os + +from openmc.examples import random_ray_three_region_cube + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_adjoint_fixed_source(): + model = random_ray_three_region_cube() + model.settings.random_ray['adjoint'] = True + model.settings.random_ray['volume_estimator'] = 'naive' + model.settings.particles = 500 + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_adjoint_k_eff/__init__.py b/tests/regression_tests/random_ray_adjoint_k_eff/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_adjoint_k_eff/inputs_true.dat b/tests/regression_tests/random_ray_adjoint_k_eff/inputs_true.dat new file mode 100644 index 0000000000..073348c41e --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_k_eff/inputs_true.dat @@ -0,0 +1,110 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + true + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_adjoint_k_eff/results_true.dat b/tests/regression_tests/random_ray_adjoint_k_eff/results_true.dat new file mode 100644 index 0000000000..657c841b56 --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_k_eff/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +1.006640E+00 1.812969E-03 +tally 1: +6.684129E+00 +8.939821E+00 +2.685967E+00 +1.443592E+00 +0.000000E+00 +0.000000E+00 +6.358774E+00 +8.091444E+00 +9.687217E-01 +1.878029E-01 +0.000000E+00 +0.000000E+00 +5.963160E+00 +7.117108E+00 +1.932332E-01 +7.473914E-03 +0.000000E+00 +0.000000E+00 +5.137593E+00 +5.283310E+00 +1.714616E-01 +5.884834E-03 +1.086218E-06 +2.361752E-13 +4.857253E+00 +4.719856E+00 +5.689580E-02 +6.476286E-04 +2.989356E-03 +1.787808E-06 +4.830516E+00 +4.666801E+00 +7.203015E-03 +1.037676E-05 +3.620020E+00 +2.620927E+00 +5.161382E+00 +5.328124E+00 +6.786255E-02 +9.210763E-04 +5.531943E+00 +6.120553E+00 +5.414034E+00 +5.864661E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.632338E+00 +6.347626E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.682608E+00 +6.462382E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.310716E+00 +5.645180E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.945409E+00 +4.893171E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.842688E+00 +4.690352E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.117198E+00 +5.237280E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.938711E+00 +9.633345E+00 +2.835258E+00 +1.608212E+00 +0.000000E+00 +0.000000E+00 +6.549505E+00 +8.584036E+00 +1.015138E+00 +2.061993E-01 +0.000000E+00 +0.000000E+00 +6.050651E+00 +7.327711E+00 +1.992816E-01 +7.948424E-03 +0.000000E+00 +0.000000E+00 +5.113981E+00 +5.234801E+00 +1.732323E-01 +6.006619E-03 +1.097435E-06 +2.410627E-13 +4.837033E+00 +4.680541E+00 +5.760042E-02 +6.637112E-04 +3.026377E-03 +1.832205E-06 +4.827049E+00 +4.660105E+00 +7.319913E-03 +1.071647E-05 +3.678770E+00 +2.706730E+00 +5.175337E+00 +5.356957E+00 +6.923046E-02 +9.586177E-04 +5.643451E+00 +6.370016E+00 +6.693323E+00 +8.964322E+00 +2.753307E+00 +1.516683E+00 +0.000000E+00 +0.000000E+00 +6.358384E+00 +8.090233E+00 +9.912008E-01 +1.965868E-01 +0.000000E+00 +0.000000E+00 +5.957484E+00 +7.103246E+00 +1.974033E-01 +7.798286E-03 +0.000000E+00 +0.000000E+00 +5.130744E+00 +5.268844E+00 +1.749233E-01 +6.123348E-03 +1.108148E-06 +2.457474E-13 +4.857340E+00 +4.720019E+00 +5.816659E-02 +6.768049E-04 +3.056125E-03 +1.868351E-06 +4.830629E+00 +4.667018E+00 +7.366289E-03 +1.085264E-05 +3.702077E+00 +2.741125E+00 +5.164864E+00 +5.335279E+00 +6.947917E-02 +9.655086E-04 +5.663725E+00 +6.415806E+00 diff --git a/tests/regression_tests/random_ray_adjoint_k_eff/test.py b/tests/regression_tests/random_ray_adjoint_k_eff/test.py new file mode 100644 index 0000000000..44cf1182ae --- /dev/null +++ b/tests/regression_tests/random_ray_adjoint_k_eff/test.py @@ -0,0 +1,20 @@ +import os + +from openmc.examples import random_ray_lattice + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_basic(): + model = random_ray_lattice() + model.settings.random_ray['adjoint'] = True + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_auto_convert/__init__.py b/tests/regression_tests/random_ray_auto_convert/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_auto_convert/infinite_medium/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert/infinite_medium/inputs_true.dat new file mode 100644 index 0000000000..464c89a5df --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/infinite_medium/inputs_true.dat @@ -0,0 +1,64 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + linear + + + 2 2 + -0.63 -0.63 + 0.63 0.63 + + + diff --git a/tests/regression_tests/random_ray_auto_convert/infinite_medium/results_true.dat b/tests/regression_tests/random_ray_auto_convert/infinite_medium/results_true.dat new file mode 100644 index 0000000000..c7584ab647 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/infinite_medium/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.797820E-01 1.054725E-02 diff --git a/tests/regression_tests/random_ray_auto_convert/material_wise/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert/material_wise/inputs_true.dat new file mode 100644 index 0000000000..464c89a5df --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/material_wise/inputs_true.dat @@ -0,0 +1,64 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + linear + + + 2 2 + -0.63 -0.63 + 0.63 0.63 + + + diff --git a/tests/regression_tests/random_ray_auto_convert/material_wise/results_true.dat b/tests/regression_tests/random_ray_auto_convert/material_wise/results_true.dat new file mode 100644 index 0000000000..d544a27df6 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/material_wise/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.356667E-01 6.637270E-03 diff --git a/tests/regression_tests/random_ray_auto_convert/stochastic_slab/inputs_true.dat b/tests/regression_tests/random_ray_auto_convert/stochastic_slab/inputs_true.dat new file mode 100644 index 0000000000..464c89a5df --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/stochastic_slab/inputs_true.dat @@ -0,0 +1,64 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + linear + + + 2 2 + -0.63 -0.63 + 0.63 0.63 + + + diff --git a/tests/regression_tests/random_ray_auto_convert/stochastic_slab/results_true.dat b/tests/regression_tests/random_ray_auto_convert/stochastic_slab/results_true.dat new file mode 100644 index 0000000000..75a10a2247 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/stochastic_slab/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.551716E-01 8.117378E-03 diff --git a/tests/regression_tests/random_ray_auto_convert/test.py b/tests/regression_tests/random_ray_auto_convert/test.py new file mode 100644 index 0000000000..fa7f2f17f4 --- /dev/null +++ b/tests/regression_tests/random_ray_auto_convert/test.py @@ -0,0 +1,54 @@ +import os + +import openmc +from openmc.examples import pwr_pin_cell +from openmc import RegularMesh +from openmc.utility_funcs import change_directory +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("method", ["material_wise", "stochastic_slab", "infinite_medium"]) +def test_random_ray_auto_convert(method): + with change_directory(method): + openmc.reset_auto_ids() + + # Start with a normal continuous energy model + model = pwr_pin_cell() + + # Convert to a multi-group model + model.convert_to_multigroup( + method=method, groups='CASMO-2', nparticles=30, + overwrite_mgxs_library=False, mgxs_path="mgxs.h5" + ) + + # Convert to a random ray model + model.convert_to_random_ray() + + # Set the number of particles + model.settings.particles = 100 + + # Overlay a basic 2x2 mesh + n = 2 + mesh = RegularMesh() + mesh.dimension = (n, n) + bbox = model.geometry.bounding_box + mesh.lower_left = (bbox.lower_left[0], bbox.lower_left[1]) + mesh.upper_right = (bbox.upper_right[0], bbox.upper_right[1]) + model.settings.random_ray['source_region_meshes'] = [ + (mesh, [model.geometry.root_universe])] + + # Set the source shape to linear + model.settings.random_ray['source_shape'] = 'linear' + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/__init__.py b/tests/regression_tests/random_ray_diagonal_stabilization/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat new file mode 100644 index 0000000000..47325ebd7d --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/inputs_true.dat @@ -0,0 +1,65 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 20 + 15 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + multi-group + + + + -0.63 -0.63 -1.0 0.63 0.63 1.0 + + + 30.0 + 150.0 + + + + + + linear + 0.5 + + + 2 2 + -0.63 -0.63 + 0.63 0.63 + + + diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/results_true.dat b/tests/regression_tests/random_ray_diagonal_stabilization/results_true.dat new file mode 100644 index 0000000000..f27ad46b46 --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +7.201808E-01 1.506596E-02 diff --git a/tests/regression_tests/random_ray_diagonal_stabilization/test.py b/tests/regression_tests/random_ray_diagonal_stabilization/test.py new file mode 100644 index 0000000000..c7a1c9f7cd --- /dev/null +++ b/tests/regression_tests/random_ray_diagonal_stabilization/test.py @@ -0,0 +1,61 @@ +import os + +from openmc.examples import pwr_pin_cell +from openmc import RegularMesh + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_diagonal_stabilization(): + # Start with a normal continuous energy model + model = pwr_pin_cell() + + # Convert to a multi-group model, with 70 group XS + # and transport correction enabled. This will generate + # MGXS data with some negatives on the diagonal, in order + # to trigger diagonal correction. + model.convert_to_multigroup( + method='material_wise', groups='CASMO-70', nparticles=30, + overwrite_mgxs_library=True, mgxs_path="mgxs.h5", correction='P0' + ) + + # Convert to a random ray model + model.convert_to_random_ray() + + # Set the number of particles + model.settings.particles = 100 + + # Overlay a basic 2x2 mesh + n = 2 + mesh = RegularMesh() + mesh.dimension = (n, n) + bbox = model.geometry.bounding_box + mesh.lower_left = (bbox.lower_left[0], bbox.lower_left[1]) + mesh.upper_right = (bbox.upper_right[0], bbox.upper_right[1]) + model.settings.random_ray['source_region_meshes'] = [ + (mesh, [model.geometry.root_universe])] + + # Set the source shape to linear + model.settings.random_ray['source_shape'] = 'linear' + + # Explicitly set the diagonal stabilization rho (default is otherwise 1.0). + # Note that if we set this to 0.0 (thus distabling stabilization), the + # problem should fail due to instability, so this is actually a good test + # problem. + model.settings.random_ray['diagonal_stabilization_rho'] = 0.5 + + # If rho was 0.0, the instability would cause failure after iteration 14, + # so we go a little past that. + model.settings.inactive = 15 + model.settings.batches = 20 + + harness = MGXSTestHarness('statepoint.20.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_entropy/__init__.py b/tests/regression_tests/random_ray_entropy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_entropy/geometry.xml b/tests/regression_tests/random_ray_entropy/geometry.xml new file mode 100644 index 0000000000..4c87bbbfb9 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/geometry.xml @@ -0,0 +1,88 @@ + + + + + + 12.5 12.5 12.5 + 8 8 8 + 0.0 0.0 0.0 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 + + + + + + + + diff --git a/tests/regression_tests/random_ray_entropy/materials.xml b/tests/regression_tests/random_ray_entropy/materials.xml new file mode 100644 index 0000000000..5a6f93414b --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/materials.xml @@ -0,0 +1,8 @@ + + + mgxs.h5 + + + + + diff --git a/tests/regression_tests/random_ray_entropy/mgxs.h5 b/tests/regression_tests/random_ray_entropy/mgxs.h5 new file mode 100644 index 0000000000..6ce80bf713 Binary files /dev/null and b/tests/regression_tests/random_ray_entropy/mgxs.h5 differ diff --git a/tests/regression_tests/random_ray_entropy/results_true.dat b/tests/regression_tests/random_ray_entropy/results_true.dat new file mode 100644 index 0000000000..2542545387 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/results_true.dat @@ -0,0 +1,13 @@ +k-combined: +1.000000E+00 0.000000E+00 +entropy: +8.863421E+00 +8.933584E+00 +8.960553E+00 +8.967921E+00 +8.976016E+00 +8.981856E+00 +8.983670E+00 +8.986584E+00 +8.987732E+00 +8.988186E+00 diff --git a/tests/regression_tests/random_ray_entropy/settings.xml b/tests/regression_tests/random_ray_entropy/settings.xml new file mode 100644 index 0000000000..81deaa7751 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/settings.xml @@ -0,0 +1,17 @@ + + + eigenvalue + 100 + 10 + 5 + multi-group + + + + 0.0 0.0 0.0 100.0 100.0 100.0 + + + 40.0 + 400.0 + + diff --git a/tests/regression_tests/random_ray_entropy/test.py b/tests/regression_tests/random_ray_entropy/test.py new file mode 100644 index 0000000000..a3cba65ad0 --- /dev/null +++ b/tests/regression_tests/random_ray_entropy/test.py @@ -0,0 +1,33 @@ +import glob +import os + +from openmc import StatePoint + +from tests.testing_harness import TestHarness + + +class EntropyTestHarness(TestHarness): + def _get_results(self): + """Digest info in the statepoint and return as a string.""" + # Read the statepoint file. + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0] + with StatePoint(statepoint) as sp: + # Write out k-combined. + outstr = 'k-combined:\n' + outstr += '{:12.6E} {:12.6E}\n'.format(sp.keff.n, sp.keff.s) + + # Write out entropy data. + outstr += 'entropy:\n' + results = ['{:12.6E}'.format(x) for x in sp.entropy] + outstr += '\n'.join(results) + '\n' + + return outstr + +''' +# This test is adapted from "Monte Carlo power iteration: Entropy and spatial correlations," +M. Nowak et al. The cross sections are defined explicitly so that the value for entropy +is exactly 9 and the eigenvalue is exactly 1. +''' +def test_entropy(): + harness = EntropyTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_domain/__init__.py b/tests/regression_tests/random_ray_fixed_source_domain/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat new file mode 100644 index 0000000000..9f1987f3ac --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/cell/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + cell + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_domain/cell/results_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/cell/results_true.dat new file mode 100644 index 0000000000..6da51a711b --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/cell/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.934460E-01 +7.058894E-02 +tally 2: +3.206214E-02 +2.063370E-04 +tally 3: +2.096411E-03 +8.804924E-07 diff --git a/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat new file mode 100644 index 0000000000..b4f57dbfa8 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/material/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + material + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_domain/material/results_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/material/results_true.dat new file mode 100644 index 0000000000..6da51a711b --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/material/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.934460E-01 +7.058894E-02 +tally 2: +3.206214E-02 +2.063370E-04 +tally 3: +2.096411E-03 +8.804924E-07 diff --git a/tests/regression_tests/random_ray_fixed_source_domain/test.py b/tests/regression_tests/random_ray_fixed_source_domain/test.py new file mode 100644 index 0000000000..5885a92009 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/test.py @@ -0,0 +1,51 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("domain_type", ["cell", "material", "universe"]) +def test_random_ray_fixed_source(domain_type): + with change_directory(domain_type): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + + # Based on the parameter, we need to adjust + # the particle source constraints + source = model.settings.source[0] + constraints = source.constraints + + if domain_type == 'cell': + cells = model.geometry.get_all_cells() + for key, cell in cells.items(): + print(cell.name) + if cell.name == 'infinite source region': + constraints['domain_type'] = 'cell' + constraints['domain_ids'] = [cell.id] + elif domain_type == 'material': + materials = model.materials + for material in materials: + if material.name == 'source': + constraints['domain_type'] = 'material' + constraints['domain_ids'] = [material.id] + elif domain_type == 'universe': + universes = model.geometry.get_all_universes() + for key, universe in universes.items(): + if universe.name == 'source universe': + constraints['domain_type'] = 'universe' + constraints['domain_ids'] = [universe.id] + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat new file mode 100644 index 0000000000..ab91f74e50 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/universe/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_domain/universe/results_true.dat b/tests/regression_tests/random_ray_fixed_source_domain/universe/results_true.dat new file mode 100644 index 0000000000..6da51a711b --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_domain/universe/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.934460E-01 +7.058894E-02 +tally 2: +3.206214E-02 +2.063370E-04 +tally 3: +2.096411E-03 +8.804924E-07 diff --git a/tests/regression_tests/random_ray_fixed_source_linear/__init__.py b/tests/regression_tests/random_ray_fixed_source_linear/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat new file mode 100644 index 0000000000..220fa7db64 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear/results_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear/results_true.dat new file mode 100644 index 0000000000..e90d6bfdcb --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.339086E+00 +2.747305E-01 +tally 2: +1.089827E-01 +6.069324E-04 +tally 3: +7.300831E-03 +2.715940E-06 diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat new file mode 100644 index 0000000000..f8c4430852 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear_xy + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/results_true.dat b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/results_true.dat new file mode 100644 index 0000000000..f4c8546aae --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_linear/linear_xy/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.335703E+00 +2.742866E-01 +tally 2: +1.081884E-01 +5.983316E-04 +tally 3: +7.295389E-03 +2.711859E-06 diff --git a/tests/regression_tests/random_ray_fixed_source_linear/test.py b/tests/regression_tests/random_ray_fixed_source_linear/test.py new file mode 100644 index 0000000000..99211024e6 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_linear/test.py @@ -0,0 +1,29 @@ +import os + +import numpy as np +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("shape", ["linear", "linear_xy"]) +def test_random_ray_fixed_source_linear(shape): + with change_directory(shape): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.random_ray['source_shape'] = shape + model.settings.inactive = 20 + model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/__init__.py b/tests/regression_tests/random_ray_fixed_source_mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat new file mode 100644 index 0000000000..c84e544fcc --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_mesh/flat/inputs_true.dat @@ -0,0 +1,271 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 30 + 15 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + + + + + + + + flat + + + 24 24 24 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 36 36 36 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 30 30 30 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/flat/results_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/flat/results_true.dat new file mode 100644 index 0000000000..0b0eaa4479 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_mesh/flat/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +1.750296E+00 +2.051829E-01 +tally 2: +8.045199E-02 +4.384408E-04 +tally 3: +5.216828E-03 +1.840861E-06 diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat new file mode 100644 index 0000000000..05c4846e6b --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_mesh/linear/inputs_true.dat @@ -0,0 +1,271 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 30 + 15 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + + + + + + + + linear + + + 24 24 24 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 36 36 36 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 30 30 30 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/linear/results_true.dat b/tests/regression_tests/random_ray_fixed_source_mesh/linear/results_true.dat new file mode 100644 index 0000000000..23754a1573 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_mesh/linear/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +1.780361E+00 +2.137912E-01 +tally 2: +8.230400E-02 +4.596391E-04 +tally 3: +5.207797E-03 +1.834531E-06 diff --git a/tests/regression_tests/random_ray_fixed_source_mesh/test.py b/tests/regression_tests/random_ray_fixed_source_mesh/test.py new file mode 100644 index 0000000000..0b93b2a7a6 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_mesh/test.py @@ -0,0 +1,53 @@ +import os + +import openmc +from openmc.examples import random_ray_three_region_cube +from openmc.utility_funcs import change_directory +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def make_mesh(dim): + width = 30.0 + mesh = openmc.RegularMesh() + mesh.dimension = (dim, dim, dim) + mesh.lower_left = (0.0, 0.0, 0.0) + mesh.upper_right = (width, width, width) + return mesh + + +@pytest.mark.parametrize("shape", ["flat", "linear"]) +def test_random_ray_fixed_source_mesh(shape): + with change_directory(shape): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + + # We will apply three different mesh resolutions to three different domain types + source_universe = model.geometry.get_universes_by_name('source universe')[0] + void_cell = model.geometry.get_cells_by_name('infinite void region')[0] + absorber_mat = model.geometry.get_materials_by_name('absorber')[0] + + model.settings.random_ray['source_region_meshes'] = [ + (make_mesh(24), [source_universe]), + (make_mesh(36), [void_cell]), + (make_mesh(30), [absorber_mat]) + ] + + # We also test flat/linear source shapes to ensure they are both + # working correctly with the mesh overlay logic + model.settings.random_ray['source_shape'] = shape + + model.settings.inactive = 15 + model.settings.batches = 30 + + harness = MGXSTestHarness('statepoint.30.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat new file mode 100644 index 0000000000..0c870e1006 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_normalization/False/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + false + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/False/results_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/False/results_true.dat new file mode 100644 index 0000000000..d8f78493ce --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_normalization/False/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +6.840321E+01 +9.376316E+02 +tally 2: +4.976182E+02 +4.970407E+04 +tally 3: +2.382441E+01 +1.137148E+02 diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat new file mode 100644 index 0000000000..ab91f74e50 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_normalization/True/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/True/results_true.dat b/tests/regression_tests/random_ray_fixed_source_normalization/True/results_true.dat new file mode 100644 index 0000000000..6da51a711b --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_normalization/True/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.934460E-01 +7.058894E-02 +tally 2: +3.206214E-02 +2.063370E-04 +tally 3: +2.096411E-03 +8.804924E-07 diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/__init__.py b/tests/regression_tests/random_ray_fixed_source_normalization/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_fixed_source_normalization/test.py b/tests/regression_tests/random_ray_fixed_source_normalization/test.py new file mode 100644 index 0000000000..3fa4ba2a63 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_normalization/test.py @@ -0,0 +1,27 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("normalize", [True, False]) +def test_random_ray_fixed_source(normalize): + with change_directory(str(normalize)): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.random_ray['volume_normalized_flux_tallies'] = normalize + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/__init__.py b/tests/regression_tests/random_ray_fixed_source_subcritical/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat new file mode 100644 index 0000000000..0c05a71df3 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/inputs_true.dat @@ -0,0 +1,140 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 9 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 12 +12 13 + + + + + + + + + + + + + + + + + + + + + fixed source + 30 + 125 + 100 + + + 1.134 -1.26 -1.0 1.26 -1.134 1.0 + + + 2e-05 0.0735 20.0 200.0 2000.0 750000.0 2000000.0 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 + + + universe + 9 + + + multi-group + + 40.0 + 40.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + false + flat + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/flat/results_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/results_true.dat new file mode 100644 index 0000000000..4d2fb6c579 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/flat/results_true.dat @@ -0,0 +1,169 @@ +tally 1: +1.591301E+02 +1.016825E+03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.916980E+01 +1.404518E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.036992E+01 +1.662864E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.464728E+01 +2.434669E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.260923E+01 +1.109616E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.443775E+01 +3.580658E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.708323E+01 +1.318058E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.004495E+02 +1.610586E+03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.321594E+01 +2.147152E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.573858E+01 +2.655227E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.944079E+01 +3.474938E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.758548E+01 +1.328297E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.536577E+01 +3.644652E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.824884E+01 +1.368347E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.073355E+02 +4.630895E+02 +1.263975E+01 +6.427678E+00 +3.076263E+01 +3.807359E+01 +4.995302E+01 +1.001379E+02 +2.355060E+00 +2.226283E-01 +5.731746E+00 +1.318712E+00 +1.959843E+01 +1.538887E+01 +2.005479E-01 +1.611455E-03 +4.880935E-01 +9.545263E-03 +2.315398E+01 +2.148264E+01 +2.441408E-01 +2.388612E-03 +5.941898E-01 +1.414866E-02 +5.319554E+01 +1.134034E+02 +1.967517E-01 +1.551313E-03 +4.788601E-01 +9.189243E-03 +1.192041E+02 +5.698491E+02 +5.811654E-02 +1.356369E-04 +1.438053E-01 +8.304781E-04 +8.044764E+01 +2.602455E+02 +3.474828E-01 +4.908566E-03 +9.665057E-01 +3.797493E-02 +1.561720E+02 +9.788256E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.840869E+01 +1.368656E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.029506E+01 +1.650985E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.467632E+01 +2.440891E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.301033E+01 +1.126277E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.608414E+01 +3.702737E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.881578E+01 +1.396463E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat new file mode 100644 index 0000000000..a67495bf16 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/inputs_true.dat @@ -0,0 +1,140 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 8 +8 8 8 8 8 8 8 8 8 9 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 12 +12 13 + + + + + + + + + + + + + + + + + + + + + fixed source + 30 + 125 + 100 + + + 1.134 -1.26 -1.0 1.26 -1.134 1.0 + + + 2e-05 0.0735 20.0 200.0 2000.0 750000.0 2000000.0 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 0.14285714285714285 + + + universe + 9 + + + multi-group + + 40.0 + 40.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + false + linear_xy + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/results_true.dat b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/results_true.dat new file mode 100644 index 0000000000..bf602d3e2d --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/linear_xy/results_true.dat @@ -0,0 +1,169 @@ +tally 1: +1.583465E+02 +1.007029E+03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.891526E+01 +1.392807E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.028338E+01 +1.649224E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.453687E+01 +2.413498E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.235813E+01 +1.099243E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.394406E+01 +3.543513E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.676284E+01 +1.303104E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.007858E+02 +1.616216E+03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.319709E+01 +2.146301E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.566730E+01 +2.640697E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.934703E+01 +3.453026E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.737215E+01 +1.318580E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.495275E+01 +3.613373E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.799251E+01 +1.356178E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.066414E+02 +4.570291E+02 +1.254014E+01 +6.325641E+00 +3.052020E+01 +3.746918E+01 +4.977302E+01 +9.940548E+01 +2.345790E+00 +2.208444E-01 +5.709184E+00 +1.308146E+00 +1.953082E+01 +1.528292E+01 +1.998234E-01 +1.599792E-03 +4.863302E-01 +9.476180E-03 +2.307196E+01 +2.133059E+01 +2.432481E-01 +2.371074E-03 +5.920173E-01 +1.404478E-02 +5.299863E+01 +1.125576E+02 +1.959914E-01 +1.539191E-03 +4.770098E-01 +9.117441E-03 +1.186133E+02 +5.641060E+02 +5.781230E-02 +1.341759E-04 +1.430525E-01 +8.215327E-04 +7.995734E+01 +2.570099E+02 +3.452934E-01 +4.844059E-03 +9.604162E-01 +3.747587E-02 +1.556795E+02 +9.726232E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.816726E+01 +1.357531E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.018480E+01 +1.633551E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.453476E+01 +2.413615E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.269380E+01 +1.113102E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.550216E+01 +3.658264E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.846320E+01 +1.379625E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/random_ray_fixed_source_subcritical/test.py b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py new file mode 100644 index 0000000000..e2f3cf1758 --- /dev/null +++ b/tests/regression_tests/random_ray_fixed_source_subcritical/test.py @@ -0,0 +1,133 @@ +import os + +import openmc +from openmc.examples import random_ray_lattice +from openmc.utility_funcs import change_directory +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("shape", ["flat", "linear_xy"]) +def test_random_ray_fixed_source_subcritical(shape): + with change_directory(shape): + openmc.reset_auto_ids() + + # The general strategy is to reuse the random_ray_lattice model, + # but redfine some of the geometry to make it a good + # subcritical multiplication problem. We then also add in + # a fixed source term. + + model = random_ray_lattice() + + # Begin by updating the random ray settings for fixed source + settings = model.settings + settings.random_ray['source_shape'] = shape + settings.run_mode = 'fixed source' + settings.particles = 30 + settings.random_ray['distance_active'] = 40.0 + settings.random_ray['distance_inactive'] = 40.0 + settings.random_ray['volume_normalized_flux_tallies'] = False + + # This problem needs about 2k iterations to converge, + # but for regression testing we only need a few hundred + # to ensure things are working as expected. With + # only 100 inactive batches, tallies will still be off + # by 3x or more. For validation against MGMC, be sure + # to increase the batch counts. + settings.batches = 125 + settings.inactive = 100 + + ######################################## + # Define the alternative geometry + + pitch = 1.26 + + for material in model.materials: + if material.name == 'Water': + water = material + + # The new geometry replaces two of the fuel pins with + # moderator, reducing k-eff to around 0.84. We also + # add a special universe in the corner of one of the moderator + # regions to use as a domain constraint for the source + moderator_infinite = openmc.Cell(fill=water, name='moderator infinite') + mu = openmc.Universe(cells=[moderator_infinite]) + + moderator_infinite2 = openmc.Cell(fill=water, name='moderator infinite 2') + mu2 = openmc.Universe(cells=[moderator_infinite2]) + + n_sub = 10 + + lattice = openmc.RectLattice() + lattice.lower_left = [-pitch/2.0, -pitch/2.0] + lattice.pitch = [pitch/n_sub, pitch/n_sub] + lattice.universes = [[mu] * n_sub for _ in range(n_sub)] + + lattice2 = openmc.RectLattice() + lattice2.lower_left = [-pitch/2.0, -pitch/2.0] + lattice2.pitch = [pitch/n_sub, pitch/n_sub] + lattice2.universes = [[mu] * n_sub for _ in range(n_sub)] + lattice2.universes[n_sub-1][n_sub-1] = mu2 + + mod_lattice_cell = openmc.Cell(fill=lattice) + mod_lattice_uni = openmc.Universe(cells=[mod_lattice_cell]) + + mod_lattice_cell2 = openmc.Cell(fill=lattice2) + mod_lattice_uni2 = openmc.Universe(cells=[mod_lattice_cell2]) + + lattice2x2 = openmc.RectLattice() + lattice2x2.lower_left = [-pitch, -pitch] + lattice2x2.pitch = [pitch, pitch] + + universes = model.geometry.get_all_universes() + for universe in universes.values(): + if universe.name == 'pincell': + pincell = universe + + lattice2x2.universes = [ + [pincell, mod_lattice_uni], + [mod_lattice_uni, mod_lattice_uni2] + ] + + box = openmc.model.RectangularPrism( + pitch*2, pitch*2, boundary_type='reflective') + + assembly = openmc.Cell(fill=lattice2x2, region=-box, name='assembly') + + root = openmc.Universe(name='root universe', cells=[assembly]) + model.geometry = openmc.Geometry(root) + + ######################################## + # Define the fixed source term + + s = 1.0 / 7.0 + strengths = [s, s, s, s, s, s, s] + midpoints = [2.0e-5, 0.0735, 20.0, 2.0e2, 2.0e3, 0.75e6, 2.0e6] + energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) + + lower_left_src = [pitch - pitch/10.0, -pitch, -1.0] + upper_right_src = [pitch, -pitch + pitch/10.0, 1.0] + spatial_distribution = openmc.stats.Box( + lower_left_src, upper_right_src, only_fissionable=False) + + settings.source = openmc.IndependentSource( + space=spatial_distribution, + energy=energy_distribution, + constraints={'domains': [mu2]}, + strength=1.0 + ) + + ######################################## + # Run test + + harness = MGXSTestHarness('statepoint.125.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_halton_samples/__init__.py b/tests/regression_tests/random_ray_halton_samples/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_halton_samples/inputs_true.dat b/tests/regression_tests/random_ray_halton_samples/inputs_true.dat new file mode 100644 index 0000000000..36d5f6f227 --- /dev/null +++ b/tests/regression_tests/random_ray_halton_samples/inputs_true.dat @@ -0,0 +1,110 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + halton + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_halton_samples/results_true.dat b/tests/regression_tests/random_ray_halton_samples/results_true.dat new file mode 100644 index 0000000000..b62398935b --- /dev/null +++ b/tests/regression_tests/random_ray_halton_samples/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +8.388051E-01 7.383265E-03 +tally 1: +5.033308E+00 +5.072162E+00 +1.917335E+00 +7.360725E-01 +4.666410E+00 +4.360038E+00 +2.851812E+00 +1.629362E+00 +4.365590E-01 +3.818884E-02 +1.062497E+00 +2.262071E-01 +1.697621E+00 +5.829333E-01 +5.639912E-02 +6.427568E-04 +1.372642E-01 +3.807294E-03 +2.376683E+00 +1.151027E+00 +8.060903E-02 +1.323179E-03 +1.961862E-01 +7.837693E-03 +7.145452E+00 +1.037540E+01 +8.551803E-02 +1.486269E-03 +2.081363E-01 +8.803955E-03 +2.053205E+01 +8.469498E+01 +3.235618E-02 +2.102891E-04 +8.006311E-02 +1.287559E-03 +1.326545E+01 +3.519484E+01 +1.867471E-01 +6.975133E-03 +5.194275E-01 +5.396284E-02 +7.558115E+00 +1.142535E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.386211E+00 +2.294414E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.827274E+00 +6.782305E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.702858E+00 +1.489752E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.475537E+00 +1.133971E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.828685E+01 +6.719606E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.143600E+01 +2.615734E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.590713E+00 +4.224107E+00 +1.705847E+00 +5.831967E-01 +4.151691E+00 +3.454497E+00 +2.730853E+00 +1.495413E+00 +4.072633E-01 +3.325944E-02 +9.911973E-01 +1.970084E-01 +1.664732E+00 +5.598668E-01 +5.385645E-02 +5.858353E-04 +1.310758E-01 +3.470126E-03 +2.312239E+00 +1.088485E+00 +7.662778E-02 +1.195422E-03 +1.864967E-01 +7.080943E-03 +7.105766E+00 +1.025960E+01 +8.287512E-02 +1.396474E-03 +2.017039E-01 +8.272053E-03 +2.099024E+01 +8.854251E+01 +3.191885E-02 +2.048368E-04 +7.898095E-02 +1.254175E-03 +1.355820E+01 +3.676862E+01 +1.815102E-01 +6.590727E-03 +5.048614E-01 +5.098890E-02 +5.093659E+00 +5.192360E+00 +1.874632E+00 +7.031793E-01 +4.562478E+00 +4.165199E+00 +2.870214E+00 +1.650126E+00 +4.244069E-01 +3.608745E-02 +1.032921E+00 +2.137598E-01 +1.703400E+00 +5.873029E-01 +5.464557E-02 +6.042855E-04 +1.329964E-01 +3.579413E-03 +2.389118E+00 +1.163674E+00 +7.832237E-02 +1.251254E-03 +1.906210E-01 +7.411654E-03 +7.162707E+00 +1.042515E+01 +8.273831E-02 +1.391799E-03 +2.013709E-01 +8.244359E-03 +2.043145E+01 +8.383557E+01 +3.096158E-02 +1.924116E-04 +7.661226E-02 +1.178098E-03 +1.314148E+01 +3.454143E+01 +1.771732E-01 +6.279887E-03 +4.927984E-01 +4.858410E-02 diff --git a/tests/regression_tests/random_ray_halton_samples/test.py b/tests/regression_tests/random_ray_halton_samples/test.py new file mode 100644 index 0000000000..478b650264 --- /dev/null +++ b/tests/regression_tests/random_ray_halton_samples/test.py @@ -0,0 +1,19 @@ +import os + +from openmc.examples import random_ray_lattice + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + +def test_random_ray_halton_samples(): + model = random_ray_lattice() + model.settings.random_ray['sample_method'] = 'halton' + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_k_eff/__init__.py b/tests/regression_tests/random_ray_k_eff/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_k_eff/inputs_true.dat b/tests/regression_tests/random_ray_k_eff/inputs_true.dat new file mode 100644 index 0000000000..545bd1d457 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff/inputs_true.dat @@ -0,0 +1,109 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_k_eff/results_true.dat b/tests/regression_tests/random_ray_k_eff/results_true.dat new file mode 100644 index 0000000000..37eca77f3c --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +8.400321E-01 8.023358E-03 +tally 1: +5.086559E+00 +5.180935E+00 +1.885166E+00 +7.115503E-01 +4.588116E+00 +4.214784E+00 +2.860400E+00 +1.639328E+00 +4.245221E-01 +3.610929E-02 +1.033202E+00 +2.138892E-01 +1.692631E+00 +5.793966E-01 +5.445818E-02 +5.996625E-04 +1.325403E-01 +3.552030E-03 +2.372248E+00 +1.146944E+00 +7.808142E-02 +1.242278E-03 +1.900346E-01 +7.358491E-03 +7.134949E+00 +1.034824E+01 +8.272647E-02 +1.391871E-03 +2.013421E-01 +8.244788E-03 +2.043539E+01 +8.389902E+01 +3.099367E-02 +1.930673E-04 +7.669167E-02 +1.182113E-03 +1.313212E+01 +3.449537E+01 +1.764293E-01 +6.225586E-03 +4.907293E-01 +4.816400E-02 +7.567715E+00 +1.145439E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.383194E+00 +2.290468E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.819672E+00 +6.726158E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.693683E+00 +1.480961E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.453758E+00 +1.128171E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.823561E+01 +6.681652E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.137517E+01 +2.588512E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.601916E+00 +4.242624E+00 +1.719723E+00 +5.923465E-01 +4.185462E+00 +3.508695E+00 +2.730305E+00 +1.494324E+00 +4.108214E-01 +3.383938E-02 +9.998572E-01 +2.004436E-01 +1.660852E+00 +5.570432E-01 +5.428709E-02 +5.947848E-04 +1.321239E-01 +3.523137E-03 +2.306069E+00 +1.082855E+00 +7.697031E-02 +1.206036E-03 +1.873303E-01 +7.143815E-03 +7.075194E+00 +1.017519E+01 +8.322052E-02 +1.408294E-03 +2.025446E-01 +8.342071E-03 +2.094832E+01 +8.816715E+01 +3.234739E-02 +2.101889E-04 +8.004135E-02 +1.286945E-03 +1.357413E+01 +3.685983E+01 +1.861680E-01 +6.934827E-03 +5.178169E-01 +5.365102E-02 +5.072149E+00 +5.151429E+00 +1.916643E+00 +7.358710E-01 +4.664726E+00 +4.358845E+00 +2.859464E+00 +1.638250E+00 +4.332944E-01 +3.763170E-02 +1.054552E+00 +2.229070E-01 +1.693008E+00 +5.796671E-01 +5.561096E-02 +6.247543E-04 +1.353459E-01 +3.700658E-03 +2.368860E+00 +1.143296E+00 +7.951819E-02 +1.286690E-03 +1.935314E-01 +7.621557E-03 +7.119587E+00 +1.030023E+01 +8.428175E-02 +1.442931E-03 +2.051274E-01 +8.547243E-03 +2.046758E+01 +8.418768E+01 +3.181946E-02 +2.034766E-04 +7.873502E-02 +1.245847E-03 +1.325834E+01 +3.515919E+01 +1.832838E-01 +6.720555E-03 +5.097947E-01 +5.199331E-02 diff --git a/tests/regression_tests/random_ray_k_eff/test.py b/tests/regression_tests/random_ray_k_eff/test.py new file mode 100644 index 0000000000..8dd0dd9155 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff/test.py @@ -0,0 +1,19 @@ +import os + +from openmc.examples import random_ray_lattice + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_basic(): + model = random_ray_lattice() + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_k_eff_mesh/__init__.py b/tests/regression_tests/random_ray_k_eff_mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat b/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat new file mode 100644 index 0000000000..98badea18d --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh/inputs_true.dat @@ -0,0 +1,119 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + + + + + + + + 40 40 + -1.26 -1.26 + 1.26 1.26 + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_k_eff_mesh/results_true.dat b/tests/regression_tests/random_ray_k_eff_mesh/results_true.dat new file mode 100644 index 0000000000..83209044ba --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +8.379203E-01 8.057199E-03 +tally 1: +5.080172E+00 +5.167984E+00 +1.880341E+00 +7.079266E-01 +4.576373E+00 +4.193319E+00 +2.859914E+00 +1.638769E+00 +4.243332E-01 +3.607732E-02 +1.032742E+00 +2.136998E-01 +1.692643E+00 +5.794069E-01 +5.445214E-02 +5.995213E-04 +1.325256E-01 +3.551193E-03 +2.372336E+00 +1.147031E+00 +7.807378E-02 +1.242019E-03 +1.900160E-01 +7.356955E-03 +7.135636E+00 +1.035026E+01 +8.273225E-02 +1.392069E-03 +2.013562E-01 +8.245961E-03 +2.044034E+01 +8.394042E+01 +3.100485E-02 +1.932097E-04 +7.671934E-02 +1.182985E-03 +1.313652E+01 +3.451846E+01 +1.764978E-01 +6.230420E-03 +4.909196E-01 +4.820140E-02 +7.585874E+00 +1.150936E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.386790E+00 +2.295327E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.820058E+00 +6.729239E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.694013E+00 +1.481363E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.453818E+00 +1.128202E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.823642E+01 +6.682239E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.137903E+01 +2.590265E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.589529E+00 +4.219985E+00 +1.712446E+00 +5.873675E-01 +4.167750E+00 +3.479202E+00 +2.728285E+00 +1.492132E+00 +4.104063E-01 +3.377132E-02 +9.988468E-01 +2.000405E-01 +1.660506E+00 +5.567967E-01 +5.427391E-02 +5.944708E-04 +1.320918E-01 +3.521277E-03 +2.305889E+00 +1.082691E+00 +7.695793E-02 +1.205644E-03 +1.873002E-01 +7.141490E-03 +7.076637E+00 +1.017946E+01 +8.323139E-02 +1.408687E-03 +2.025710E-01 +8.344398E-03 +2.095897E+01 +8.825741E+01 +3.236513E-02 +2.104220E-04 +8.008525E-02 +1.288373E-03 +1.358006E+01 +3.689205E+01 +1.862562E-01 +6.941428E-03 +5.180621E-01 +5.370209E-02 +5.067386E+00 +5.141748E+00 +1.912704E+00 +7.328484E-01 +4.655140E+00 +4.340941E+00 +2.858992E+00 +1.637705E+00 +4.331050E-01 +3.759875E-02 +1.054091E+00 +2.227118E-01 +1.692974E+00 +5.796396E-01 +5.560487E-02 +6.246120E-04 +1.353311E-01 +3.699815E-03 +2.368737E+00 +1.143184E+00 +7.950086E-02 +1.286135E-03 +1.934892E-01 +7.618269E-03 +7.119767E+00 +1.030095E+01 +8.427628E-02 +1.442764E-03 +2.051141E-01 +8.546249E-03 +2.047651E+01 +8.426275E+01 +3.183786E-02 +2.037156E-04 +7.878057E-02 +1.247311E-03 +1.326415E+01 +3.519010E+01 +1.833688E-01 +6.726832E-03 +5.100312E-01 +5.204188E-02 diff --git a/tests/regression_tests/random_ray_k_eff_mesh/test.py b/tests/regression_tests/random_ray_k_eff_mesh/test.py new file mode 100644 index 0000000000..cffdaf8bb4 --- /dev/null +++ b/tests/regression_tests/random_ray_k_eff_mesh/test.py @@ -0,0 +1,36 @@ +import os + +import openmc +from openmc.examples import random_ray_lattice + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_k_eff_mesh(): + model = random_ray_lattice() + + # The model already has some geometrical subdivisions + # up to a 10x10 grid in the moderator region. So, we + # increase the resolution 40x40 applied over the full + # 2x2 lattice. + pitch = 1.26 + dim = 40 + mesh = openmc.RegularMesh() + mesh.dimension = (dim, dim) + mesh.lower_left = (-pitch, -pitch) + mesh.upper_right = (pitch, pitch) + + root = model.geometry.root_universe + + model.settings.random_ray['source_region_meshes'] = [(mesh, [root])] + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_linear/__init__.py b/tests/regression_tests/random_ray_linear/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_linear/linear/inputs_true.dat b/tests/regression_tests/random_ray_linear/linear/inputs_true.dat new file mode 100644 index 0000000000..a43a66e71c --- /dev/null +++ b/tests/regression_tests/random_ray_linear/linear/inputs_true.dat @@ -0,0 +1,110 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 40 + 20 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + linear + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_linear/linear/results_true.dat b/tests/regression_tests/random_ray_linear/linear/results_true.dat new file mode 100644 index 0000000000..4c0e14370e --- /dev/null +++ b/tests/regression_tests/random_ray_linear/linear/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +1.095967E+00 1.543581E-02 +tally 1: +2.548108E+01 +3.269093E+01 +9.271804E+00 +4.327275E+00 +2.256572E+01 +2.563210E+01 +1.816107E+01 +1.653421E+01 +2.659203E+00 +3.544951E-01 +6.471969E+00 +2.099810E+00 +1.364193E+01 +9.308675E+00 +4.362828E-01 +9.521133E-03 +1.061825E+00 +5.639730E-02 +1.746102E+01 +1.524680E+01 +5.733016E-01 +1.643671E-02 +1.395301E+00 +9.736092E-02 +4.539598E+01 +1.030472E+02 +5.263055E-01 +1.385088E-02 +1.280938E+00 +8.204609E-02 +9.945736E+01 +4.946716E+02 +1.505228E-01 +1.133424E-03 +3.724582E-01 +6.939732E-03 +5.324914E+01 +1.418809E+02 +7.219589E-01 +2.614875E-02 +2.008092E+00 +2.022988E-01 +4.188246E+01 +8.843469E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.224363E+01 +2.481131E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.436097E+01 +1.031496E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.901248E+01 +1.807657E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.559337E+01 +1.039424E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.802992E+01 +3.874925E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.506602E+01 +1.016497E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.207833E+01 +2.455068E+01 +8.139772E+00 +3.337974E+00 +1.981058E+01 +1.977210E+01 +1.710407E+01 +1.466648E+01 +2.542287E+00 +3.240467E-01 +6.187418E+00 +1.919453E+00 +1.342690E+01 +9.017908E+00 +4.362263E-01 +9.518695E-03 +1.061688E+00 +5.638286E-02 +1.713924E+01 +1.469065E+01 +5.714028E-01 +1.632839E-02 +1.390680E+00 +9.671931E-02 +4.539193E+01 +1.030326E+02 +5.345254E-01 +1.428719E-02 +1.300944E+00 +8.463057E-02 +1.013270E+02 +5.134168E+02 +1.555517E-01 +1.210145E-03 +3.849017E-01 +7.409480E-03 +5.377836E+01 +1.446537E+02 +7.374053E-01 +2.722908E-02 +2.051056E+00 +2.106567E-01 +2.522726E+01 +3.202007E+01 +9.366659E+00 +4.412278E+00 +2.279657E+01 +2.613561E+01 +1.803368E+01 +1.629756E+01 +2.696490E+00 +3.643066E-01 +6.562716E+00 +2.157928E+00 +1.357447E+01 +9.216150E+00 +4.437305E-01 +9.847287E-03 +1.079951E+00 +5.832923E-02 +1.735848E+01 +1.506775E+01 +5.825173E-01 +1.696803E-02 +1.417731E+00 +1.005081E-01 +4.519297E+01 +1.021280E+02 +5.357712E-01 +1.435322E-02 +1.303976E+00 +8.502167E-02 +9.934508E+01 +4.935314E+02 +1.538761E-01 +1.184229E-03 +3.807556E-01 +7.250804E-03 +5.335839E+01 +1.424221E+02 +7.404823E-01 +2.747396E-02 +2.059614E+00 +2.125512E-01 diff --git a/tests/regression_tests/random_ray_linear/linear_xy/inputs_true.dat b/tests/regression_tests/random_ray_linear/linear_xy/inputs_true.dat new file mode 100644 index 0000000000..7f76f2fd1c --- /dev/null +++ b/tests/regression_tests/random_ray_linear/linear_xy/inputs_true.dat @@ -0,0 +1,110 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.126 0.126 + 10 10 + -0.63 -0.63 + +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 + + + 1.26 1.26 + 2 2 + -1.26 -1.26 + +2 2 +2 5 + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 40 + 20 + multi-group + + 100.0 + 20.0 + + + -1.26 -1.26 -1 1.26 1.26 1 + + + true + linear_xy + + + + + 2 2 + -1.26 -1.26 + 1.26 1.26 + + + 1 + + + 1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0 + + + 1 2 + flux fission nu-fission + analog + + + diff --git a/tests/regression_tests/random_ray_linear/linear_xy/results_true.dat b/tests/regression_tests/random_ray_linear/linear_xy/results_true.dat new file mode 100644 index 0000000000..abfd03c067 --- /dev/null +++ b/tests/regression_tests/random_ray_linear/linear_xy/results_true.dat @@ -0,0 +1,171 @@ +k-combined: +1.104727E+00 1.593303E-02 +tally 1: +2.566934E+01 +3.317503E+01 +9.417202E+00 +4.465518E+00 +2.291958E+01 +2.645097E+01 +1.823903E+01 +1.667438E+01 +2.679931E+00 +3.600420E-01 +6.522415E+00 +2.132667E+00 +1.365623E+01 +9.327448E+00 +4.370682E-01 +9.554968E-03 +1.063736E+00 +5.659772E-02 +1.750634E+01 +1.532609E+01 +5.762870E-01 +1.660889E-02 +1.402567E+00 +9.838082E-02 +4.543609E+01 +1.032286E+02 +5.271287E-01 +1.389456E-02 +1.282941E+00 +8.230483E-02 +9.881678E+01 +4.882586E+02 +1.487616E-01 +1.106634E-03 +3.681003E-01 +6.775702E-03 +5.260781E+01 +1.384126E+02 +7.018594E-01 +2.464953E-02 +1.952187E+00 +1.907001E-01 +4.184779E+01 +8.826530E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.226932E+01 +2.486554E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.441107E+01 +1.038682E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.909194E+01 +1.822767E+01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.579319E+01 +1.048559E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.833276E+01 +3.901410E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.528911E+01 +1.025740E+02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.218278E+01 +2.477434E+01 +8.203467E+00 +3.389915E+00 +1.996560E+01 +2.007976E+01 +1.714818E+01 +1.473927E+01 +2.551034E+00 +3.262450E-01 +6.208707E+00 +1.932474E+00 +1.343470E+01 +9.027502E+00 +4.363259E-01 +9.522121E-03 +1.061930E+00 +5.640315E-02 +1.717034E+01 +1.474381E+01 +5.729720E-01 +1.641841E-02 +1.394499E+00 +9.725251E-02 +4.542715E+01 +1.031896E+02 +5.348128E-01 +1.430232E-02 +1.301643E+00 +8.472019E-02 +1.009076E+02 +5.091264E+02 +1.543547E-01 +1.191320E-03 +3.819398E-01 +7.294216E-03 +5.342751E+01 +1.427427E+02 +7.255554E-01 +2.633014E-02 +2.018096E+00 +2.037021E-01 +2.540053E+01 +3.247261E+01 +9.483956E+00 +4.526477E+00 +2.308205E+01 +2.681205E+01 +1.812760E+01 +1.646855E+01 +2.717374E+00 +3.700301E-01 +6.613545E+00 +2.191830E+00 +1.362672E+01 +9.286907E+00 +4.458292E-01 +9.940508E-03 +1.085059E+00 +5.888142E-02 +1.744511E+01 +1.521876E+01 +5.866632E-01 +1.721091E-02 +1.427821E+00 +1.019468E-01 +4.538426E+01 +1.029941E+02 +5.385934E-01 +1.450495E-02 +1.310845E+00 +8.592045E-02 +9.921424E+01 +4.921945E+02 +1.532444E-01 +1.174272E-03 +3.791926E-01 +7.189835E-03 +5.301633E+01 +1.405571E+02 +7.285745E-01 +2.655093E-02 +2.026493E+00 +2.054102E-01 diff --git a/tests/regression_tests/random_ray_linear/test.py b/tests/regression_tests/random_ray_linear/test.py new file mode 100644 index 0000000000..510c57de8c --- /dev/null +++ b/tests/regression_tests/random_ray_linear/test.py @@ -0,0 +1,28 @@ +import os + +import openmc +from openmc.examples import random_ray_lattice +from openmc.utility_funcs import change_directory +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("shape", ["linear", "linear_xy"]) +def test_random_ray_source(shape): + with change_directory(shape): + openmc.reset_auto_ids() + model = random_ray_lattice() + model.settings.random_ray['source_shape'] = shape + model.settings.inactive = 20 + model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_low_density/__init__.py b/tests/regression_tests/random_ray_low_density/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_low_density/inputs_true.dat b/tests/regression_tests/random_ray_low_density/inputs_true.dat new file mode 100644 index 0000000000..ab91f74e50 --- /dev/null +++ b/tests/regression_tests/random_ray_low_density/inputs_true.dat @@ -0,0 +1,244 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_low_density/results_true.dat b/tests/regression_tests/random_ray_low_density/results_true.dat new file mode 100644 index 0000000000..a4b3ee1bcd --- /dev/null +++ b/tests/regression_tests/random_ray_low_density/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.973607E-01 +7.155477E-02 +tally 2: +3.206216E-02 +2.063375E-04 +tally 3: +2.096415E-03 +8.804963E-07 diff --git a/tests/regression_tests/random_ray_low_density/test.py b/tests/regression_tests/random_ray_low_density/test.py new file mode 100644 index 0000000000..1b4ffb7818 --- /dev/null +++ b/tests/regression_tests/random_ray_low_density/test.py @@ -0,0 +1,60 @@ +import os + +import numpy as np +import openmc +from openmc.examples import random_ray_three_region_cube + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_low_density(): + model = random_ray_three_region_cube() + + # Rebuild the MGXS library to have a material with very + # low macroscopic cross sections + ebins = [1e-5, 20.0e6] + groups = openmc.mgxs.EnergyGroups(group_edges=ebins) + + void_sigma_a = 4.0e-6 + void_sigma_s = 3.0e-4 + void_mat_data = openmc.XSdata('void', groups) + void_mat_data.order = 0 + void_mat_data.set_total([void_sigma_a + void_sigma_s]) + void_mat_data.set_absorption([void_sigma_a]) + void_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[void_sigma_s]]]), 0, 3)) + + absorber_sigma_a = 0.75 + absorber_sigma_s = 0.25 + absorber_mat_data = openmc.XSdata('absorber', groups) + absorber_mat_data.order = 0 + absorber_mat_data.set_total([absorber_sigma_a + absorber_sigma_s]) + absorber_mat_data.set_absorption([absorber_sigma_a]) + absorber_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[absorber_sigma_s]]]), 0, 3)) + + multiplier = 0.0000001 + source_sigma_a = void_sigma_a * multiplier + source_sigma_s = void_sigma_s * multiplier + source_mat_data = openmc.XSdata('source', groups) + source_mat_data.order = 0 + source_mat_data.set_total([source_sigma_a + source_sigma_s]) + source_mat_data.set_absorption([source_sigma_a]) + source_mat_data.set_scatter_matrix( + np.rollaxis(np.array([[[source_sigma_s]]]), 0, 3)) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.add_xsdatas( + [source_mat_data, void_mat_data, absorber_mat_data]) + mg_cross_sections_file.export_to_hdf5() + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_point_source_locator/__init__.py b/tests/regression_tests/random_ray_point_source_locator/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat b/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat new file mode 100644 index 0000000000..088f803bfa --- /dev/null +++ b/tests/regression_tests/random_ray_point_source_locator/inputs_true.dat @@ -0,0 +1,253 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 30 + 15 + + + 2.5 2.5 2.5 + + + 100.0 1.0 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + + + + 30 30 30 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_point_source_locator/results_true.dat b/tests/regression_tests/random_ray_point_source_locator/results_true.dat new file mode 100644 index 0000000000..8c6f358dd3 --- /dev/null +++ b/tests/regression_tests/random_ray_point_source_locator/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.633923E+00 +2.948228E+00 +tally 2: +1.440456E-01 +3.293984E-03 +tally 3: +9.425207E-03 +1.089748E-05 diff --git a/tests/regression_tests/random_ray_point_source_locator/test.py b/tests/regression_tests/random_ray_point_source_locator/test.py new file mode 100644 index 0000000000..fd3d8a18fe --- /dev/null +++ b/tests/regression_tests/random_ray_point_source_locator/test.py @@ -0,0 +1,44 @@ +import os + +import openmc +from openmc.examples import random_ray_three_region_cube + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_point_source_locator(): + model = random_ray_three_region_cube() + + # Overlay subdivided SR mesh to reduce resolution from 2.5cm -> 1cm + width = 30.0 + mesh = openmc.RegularMesh() + mesh.dimension = (30, 30, 30) + mesh.lower_left = (0.0, 0.0, 0.0) + mesh.upper_right = (width, width, width) + model.settings.random_ray['source_region_meshes'] = [ + (mesh, [model.geometry.root_universe]), + ] + + # Define a point source + strengths = [1.0] + midpoints = [100.0] + energy_distribution = openmc.stats.Discrete(x=midpoints, p=strengths) + spatial_distribution = openmc.stats.Point([2.5, 2.5, 2.5]) + source = openmc.IndependentSource( + energy=energy_distribution, space=spatial_distribution, strength=3.14) + model.settings.source = [source] + + # Settings + model.settings.inactive = 15 + model.settings.batches = 30 + + harness = MGXSTestHarness('statepoint.30.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_void/__init__.py b/tests/regression_tests/random_ray_void/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_void/flat/inputs_true.dat b/tests/regression_tests/random_ray_void/flat/inputs_true.dat new file mode 100644 index 0000000000..aa28e7b68b --- /dev/null +++ b/tests/regression_tests/random_ray_void/flat/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + flat + + + + + 1 + + + 2 + + + 3 + + + 6 + flux + tracklength + + + 5 + flux + tracklength + + + 4 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_void/flat/results_true.dat b/tests/regression_tests/random_ray_void/flat/results_true.dat new file mode 100644 index 0000000000..bd2f2d3b41 --- /dev/null +++ b/tests/regression_tests/random_ray_void/flat/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.354630E+00 +2.777456E-01 +tally 2: +1.056204E-01 +5.741779E-04 +tally 3: +7.286803E-03 +2.706427E-06 diff --git a/tests/regression_tests/random_ray_void/linear/inputs_true.dat b/tests/regression_tests/random_ray_void/linear/inputs_true.dat new file mode 100644 index 0000000000..e4b2f22fa2 --- /dev/null +++ b/tests/regression_tests/random_ray_void/linear/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear + + + + + 1 + + + 2 + + + 3 + + + 6 + flux + tracklength + + + 5 + flux + tracklength + + + 4 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_void/linear/results_true.dat b/tests/regression_tests/random_ray_void/linear/results_true.dat new file mode 100644 index 0000000000..befa507a59 --- /dev/null +++ b/tests/regression_tests/random_ray_void/linear/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.356818E+00 +2.782542E-01 +tally 2: +1.082843E-01 +6.028734E-04 +tally 3: +7.302705E-03 +2.718076E-06 diff --git a/tests/regression_tests/random_ray_void/test.py b/tests/regression_tests/random_ray_void/test.py new file mode 100644 index 0000000000..b48a7794d7 --- /dev/null +++ b/tests/regression_tests/random_ray_void/test.py @@ -0,0 +1,72 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("shape", ["flat", "linear"]) +def test_random_ray_void(shape): + with change_directory(shape): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + + # There is some different logic for void depending on linear + # vs. flat, so we test both + model.settings.random_ray['source_shape'] = shape + + # As we are testing linear sources, need to have more than + # 10 inactive batches so the moments start getting computed + model.settings.inactive = 20 + model.settings.batches = 40 + + # Begin by getting handles to the cells, and setting the + # source and void areas to have no fill. We leave the absorber + # as solid. + absorber_cell = model.geometry.get_cells_by_name( + 'infinite absorber region', matching=True)[0] + void_cell = model.geometry.get_cells_by_name( + 'infinite void region', matching=True)[0] + source_cell = model.geometry.get_cells_by_name( + 'infinite source region', matching=True)[0] + + void_cell.fill = None + source_cell.fill = None + + # We also need to redefine all three tallies to use cell + # filters instead of material ones + estimator = 'tracklength' + absorber_filter = openmc.CellFilter(absorber_cell) + absorber_tally = openmc.Tally(name="Absorber Tally") + absorber_tally.filters = [absorber_filter] + absorber_tally.scores = ['flux'] + absorber_tally.estimator = estimator + + void_filter = openmc.CellFilter(void_cell) + void_tally = openmc.Tally(name="Void Tally") + void_tally.filters = [void_filter] + void_tally.scores = ['flux'] + void_tally.estimator = estimator + + source_filter = openmc.CellFilter(source_cell) + source_tally = openmc.Tally(name="Source Tally") + source_tally.filters = [source_filter] + source_tally.scores = ['flux'] + source_tally.estimator = estimator + + tallies = openmc.Tallies([source_tally, void_tally, absorber_tally]) + model.tallies = tallies + + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator/__init__.py b/tests/regression_tests/random_ray_volume_estimator/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat new file mode 100644 index 0000000000..8e8a8ed9b8 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/hybrid/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + hybrid + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat new file mode 100644 index 0000000000..6da51a711b --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/hybrid/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.934460E-01 +7.058894E-02 +tally 2: +3.206214E-02 +2.063370E-04 +tally 3: +2.096411E-03 +8.804924E-07 diff --git a/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat new file mode 100644 index 0000000000..1e25b97da6 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/naive/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + naive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat new file mode 100644 index 0000000000..f8d6d10b00 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/naive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +5.935538E-01 +7.061433E-02 +tally 2: +3.263210E-02 +2.134164E-04 +tally 3: +2.107977E-03 +8.905227E-07 diff --git a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat new file mode 100644 index 0000000000..78c1626976 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/inputs_true.dat @@ -0,0 +1,245 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + simulation_averaged + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat new file mode 100644 index 0000000000..5f29758607 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/simulation_averaged/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +-5.745886E+02 +9.758367E+04 +tally 2: +2.971927E-02 +1.827222E-04 +tally 3: +1.978393E-03 +7.951531E-07 diff --git a/tests/regression_tests/random_ray_volume_estimator/test.py b/tests/regression_tests/random_ray_volume_estimator/test.py new file mode 100644 index 0000000000..fba4bbbbe6 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator/test.py @@ -0,0 +1,30 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("estimator", ["hybrid", + "simulation_averaged", + "naive" + ]) +def test_random_ray_volume_estimator(estimator): + with change_directory(estimator): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.random_ray['volume_estimator'] = estimator + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/__init__.py b/tests/regression_tests/random_ray_volume_estimator_linear/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat new file mode 100644 index 0000000000..47a8a71824 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/inputs_true.dat @@ -0,0 +1,246 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear + hybrid + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat new file mode 100644 index 0000000000..e90d6bfdcb --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/hybrid/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.339086E+00 +2.747305E-01 +tally 2: +1.089827E-01 +6.069324E-04 +tally 3: +7.300831E-03 +2.715940E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat new file mode 100644 index 0000000000..80a9ada4d5 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/naive/inputs_true.dat @@ -0,0 +1,246 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear + naive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat new file mode 100644 index 0000000000..5258ffd9c8 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/naive/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.339567E+00 +2.748423E-01 +tally 2: +1.085878E-01 +6.024509E-04 +tally 3: +7.299803E-03 +2.741867E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat new file mode 100644 index 0000000000..4f032a62a8 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/inputs_true.dat @@ -0,0 +1,246 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 40 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + linear + simulation_averaged + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat new file mode 100644 index 0000000000..1e8aa9fb75 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/simulation_averaged/results_true.dat @@ -0,0 +1,9 @@ +tally 1: +2.670850E+02 +4.432939E+05 +tally 2: +1.116994E-01 +6.491358E-04 +tally 3: +7.564527E-03 +2.947794E-06 diff --git a/tests/regression_tests/random_ray_volume_estimator_linear/test.py b/tests/regression_tests/random_ray_volume_estimator_linear/test.py new file mode 100644 index 0000000000..94a14f3ad3 --- /dev/null +++ b/tests/regression_tests/random_ray_volume_estimator_linear/test.py @@ -0,0 +1,32 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import TolerantPyAPITestHarness + + +class MGXSTestHarness(TolerantPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("estimator", ["hybrid", + "simulation_averaged", + "naive" + ]) +def test_random_ray_volume_estimator_linear(estimator): + with change_directory(estimator): + openmc.reset_auto_ids() + model = random_ray_three_region_cube() + model.settings.random_ray['source_shape'] = 'linear' + model.settings.random_ray['volume_estimator'] = estimator + model.settings.inactive = 20 + model.settings.batches = 40 + harness = MGXSTestHarness('statepoint.40.h5', model) + harness.main() diff --git a/tests/regression_tests/reflective_plane/results_true.dat b/tests/regression_tests/reflective_plane/results_true.dat index a55747acc4..a4d6edb677 100644 --- a/tests/regression_tests/reflective_plane/results_true.dat +++ b/tests/regression_tests/reflective_plane/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.275423E+00 4.746681E-03 +2.279066E+00 4.793565E-03 diff --git a/tests/regression_tests/resonance_scattering/inputs_true.dat b/tests/regression_tests/resonance_scattering/inputs_true.dat index 57a5e1a4b9..ebe6a5dbe8 100644 --- a/tests/regression_tests/resonance_scattering/inputs_true.dat +++ b/tests/regression_tests/resonance_scattering/inputs_true.dat @@ -1,34 +1,34 @@ - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - -4 -4 -4 4 4 4 - - - - true - rvs - 1.0 - 210.0 - U238 U235 Pu239 - - + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + -4 -4 -4 4 4 4 + + + + true + rvs + 1.0 + 210.0 + U238 U235 Pu239 + + + diff --git a/tests/regression_tests/resonance_scattering/results_true.dat b/tests/regression_tests/resonance_scattering/results_true.dat index 2535885fbf..72f0933b83 100644 --- a/tests/regression_tests/resonance_scattering/results_true.dat +++ b/tests/regression_tests/resonance_scattering/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.463226E+00 1.142843E-02 +1.462428E+00 1.828903E-02 diff --git a/tests/regression_tests/resonance_scattering/test.py b/tests/regression_tests/resonance_scattering/test.py index e77dd92582..24629b12b7 100644 --- a/tests/regression_tests/resonance_scattering/test.py +++ b/tests/regression_tests/resonance_scattering/test.py @@ -4,7 +4,8 @@ from tests.testing_harness import PyAPITestHarness class ResonanceScatteringTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Materials mat = openmc.Material(material_id=1) mat.set_density('g/cc', 1.0) @@ -13,15 +14,13 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): mat.add_nuclide('Pu239', 0.02) mat.add_nuclide('H1', 20.0) - mats_file = openmc.Materials([mat]) - mats_file.export_to_xml() + self._model.materials = openmc.Materials([mat]) # Geometry dumb_surface = openmc.XPlane(100, boundary_type='reflective') c1 = openmc.Cell(cell_id=1, fill=mat, region=-dumb_surface) root_univ = openmc.Universe(universe_id=0, cells=[c1]) - geometry = openmc.Geometry(root_univ) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root_univ) # Resonance elastic scattering settings res_scat_settings = { @@ -36,12 +35,13 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): settings.batches = 10 settings.inactive = 5 settings.particles = 1000 - settings.source = openmc.source.Source( + settings.source = openmc.IndependentSource( space=openmc.stats.Box([-4, -4, -4], [4, 4, 4])) settings.resonance_scattering = res_scat_settings - settings.export_to_xml() + self._model.settings = settings def test_resonance_scattering(): - harness = ResonanceScatteringTestHarness('statepoint.10.h5') + harness = ResonanceScatteringTestHarness('statepoint.10.h5', + model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/rotation/results_true.dat b/tests/regression_tests/rotation/results_true.dat index f4a3cfcccb..6db3d329a0 100644 --- a/tests/regression_tests/rotation/results_true.dat +++ b/tests/regression_tests/rotation/results_true.dat @@ -1,2 +1,2 @@ k-combined: -4.132181E-01 1.130226E-02 +4.459219E-01 1.899168E-02 diff --git a/tests/regression_tests/salphabeta/inputs_true.dat b/tests/regression_tests/salphabeta/inputs_true.dat index 29d8065cf1..56f4be3759 100644 --- a/tests/regression_tests/salphabeta/inputs_true.dat +++ b/tests/regression_tests/salphabeta/inputs_true.dat @@ -1,60 +1,60 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 400 - 5 - 0 - - - -4 -4 -4 4 4 4 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 400 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/regression_tests/salphabeta/results_true.dat b/tests/regression_tests/salphabeta/results_true.dat index 1c22db3a09..75a81075e9 100644 --- a/tests/regression_tests/salphabeta/results_true.dat +++ b/tests/regression_tests/salphabeta/results_true.dat @@ -1,2 +1,2 @@ k-combined: -8.214164E-01 2.262776E-02 +8.628529E-01 3.120924E-02 diff --git a/tests/regression_tests/salphabeta/test.py b/tests/regression_tests/salphabeta/test.py index fd0486118f..11bf3bc842 100644 --- a/tests/regression_tests/salphabeta/test.py +++ b/tests/regression_tests/salphabeta/test.py @@ -67,7 +67,7 @@ def make_model(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 400 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-4, -4, -4], [4, 4, 4])) return model diff --git a/tests/regression_tests/score_current/inputs_true.dat b/tests/regression_tests/score_current/inputs_true.dat index 2b81f508f1..42c2d3df2e 100644 --- a/tests/regression_tests/score_current/inputs_true.dat +++ b/tests/regression_tests/score_current/inputs_true.dat @@ -1,55 +1,54 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 3 3 3 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - 1 - - - 0.0 0.253 20000000.0 - - - 1 - current - - - 1 2 - current - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 3 3 3 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + 1 + + + 0.0 0.253 20000000.0 + + + 1 + current + + + 1 2 + current + + + diff --git a/tests/regression_tests/score_current/results_true.dat b/tests/regression_tests/score_current/results_true.dat index 6793466954..6bb74445ce 100644 --- a/tests/regression_tests/score_current/results_true.dat +++ b/tests/regression_tests/score_current/results_true.dat @@ -1,652 +1,652 @@ k-combined: -7.952381E-01 3.714273E-02 +7.729082E-01 3.775399E-02 tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.400000E-01 -4.016000E-03 -1.760000E-01 -6.368000E-03 +1.200000E-01 +2.924000E-03 +1.740000E-01 +6.270000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.140000E-01 -2.714000E-03 -1.590000E-01 -5.517000E-03 -1.800000E-01 -6.524000E-03 +1.200000E-01 +3.044000E-03 +1.510000E-01 +4.615000E-03 +1.710000E-01 +6.067000E-03 +0.000000E+00 +0.000000E+00 +6.500000E-02 +9.390000E-04 +1.410000E-01 +4.001000E-03 +1.740000E-01 +6.270000E-03 +1.200000E-01 +2.924000E-03 +1.830000E-01 +6.879000E-03 +1.310000E-01 +3.541000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.750000E-01 +6.659000E-03 +2.820000E-01 +1.605400E-02 +1.980000E-01 +7.932000E-03 +0.000000E+00 +0.000000E+00 +9.400000E-02 +1.912000E-03 +2.200000E-01 +9.958000E-03 +1.310000E-01 +3.541000E-03 +1.830000E-01 +6.879000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.110000E-01 +2.639000E-03 +1.360000E-01 +3.890000E-03 +1.770000E-01 +6.551000E-03 0.000000E+00 0.000000E+00 6.700000E-02 -9.270000E-04 +9.310000E-04 1.570000E-01 -5.131000E-03 -1.760000E-01 -6.368000E-03 -1.400000E-01 -4.016000E-03 -1.570000E-01 -5.019000E-03 -1.210000E-01 -2.997000E-03 +4.975000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.780000E-01 -6.838000E-03 -2.440000E-01 -1.203200E-02 -2.120000E-01 -9.028000E-03 -0.000000E+00 -0.000000E+00 -8.800000E-02 -1.644000E-03 -2.360000E-01 -1.139000E-02 -1.210000E-01 -2.997000E-03 -1.570000E-01 -5.019000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.070000E-01 -2.375000E-03 -1.490000E-01 -4.621000E-03 -1.640000E-01 -5.476000E-03 -0.000000E+00 -0.000000E+00 -5.800000E-02 -7.240000E-04 -1.290000E-01 -3.357000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.470000E-01 -4.583000E-03 -2.610000E-01 -1.376900E-02 -1.590000E-01 -5.517000E-03 -1.140000E-01 -2.714000E-03 -1.630000E-01 -5.645000E-03 -1.090000E-01 -2.419000E-03 -1.820000E-01 -6.750000E-03 -0.000000E+00 -0.000000E+00 -1.060000E-01 -2.530000E-03 -2.230000E-01 -1.002300E-02 -2.610000E-01 -1.376900E-02 -1.470000E-01 -4.583000E-03 -2.820000E-01 -1.613400E-02 +1.480000E-01 +4.910000E-03 +2.720000E-01 +1.490600E-02 +1.510000E-01 +4.615000E-03 +1.200000E-01 +3.044000E-03 1.800000E-01 -6.690000E-03 -2.440000E-01 -1.203200E-02 -1.780000E-01 -6.838000E-03 -2.870000E-01 -1.696700E-02 -1.800000E-01 -6.556000E-03 -2.430000E-01 -1.211900E-02 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.451000E-03 -5.570000E-01 -7.415500E-02 -1.800000E-01 -6.690000E-03 -2.820000E-01 -1.613400E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.621000E-03 -1.070000E-01 -2.375000E-03 -1.500000E-01 -4.604000E-03 -1.100000E-01 -2.552000E-03 -1.800000E-01 -6.518000E-03 -0.000000E+00 -0.000000E+00 +6.720000E-03 1.050000E-01 -2.345000E-03 -2.150000E-01 -9.355000E-03 +2.285000E-03 +1.790000E-01 +6.499000E-03 +0.000000E+00 +0.000000E+00 +9.300000E-02 +1.927000E-03 +2.160000E-01 +9.418000E-03 +2.720000E-01 +1.490600E-02 +1.480000E-01 +4.910000E-03 +2.680000E-01 +1.445800E-02 +1.480000E-01 +4.488000E-03 +2.820000E-01 +1.605400E-02 +1.750000E-01 +6.659000E-03 +2.510000E-01 +1.283100E-02 +1.870000E-01 +7.731000E-03 +2.340000E-01 +1.105000E-02 +0.000000E+00 +0.000000E+00 +2.110000E-01 +9.215000E-03 +5.470000E-01 +7.228300E-02 +1.480000E-01 +4.488000E-03 +2.680000E-01 +1.445800E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.340000E-01 -3.818000E-03 -1.690000E-01 -5.911000E-03 -1.090000E-01 -2.419000E-03 -1.630000E-01 -5.645000E-03 +1.360000E-01 +3.890000E-03 +1.110000E-01 +2.639000E-03 +1.540000E-01 +4.804000E-03 +1.280000E-01 +3.302000E-03 +2.200000E-01 +9.834000E-03 +0.000000E+00 +0.000000E+00 +9.900000E-02 +2.127000E-03 +1.860000E-01 +7.198000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.430000E-01 +4.337000E-03 +1.580000E-01 +5.104000E-03 +1.050000E-01 +2.285000E-03 +1.800000E-01 +6.720000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 1.860000E-01 -6.998000E-03 +7.178000E-03 0.000000E+00 0.000000E+00 -7.200000E-02 -1.090000E-03 -1.720000E-01 -6.062000E-03 -1.690000E-01 -5.911000E-03 -1.340000E-01 -3.818000E-03 -1.600000E-01 -5.454000E-03 -1.060000E-01 -2.328000E-03 -1.800000E-01 -6.556000E-03 -2.870000E-01 -1.696700E-02 +7.000000E-02 +1.020000E-03 +1.610000E-01 +5.237000E-03 +1.580000E-01 +5.104000E-03 +1.430000E-01 +4.337000E-03 +1.580000E-01 +5.254000E-03 +1.230000E-01 +3.091000E-03 +1.870000E-01 +7.731000E-03 +2.510000E-01 +1.283100E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.060000E-01 -8.670000E-03 +1.980000E-01 +7.934000E-03 0.000000E+00 0.000000E+00 -1.100000E-01 -2.558000E-03 -2.340000E-01 -1.113000E-02 -1.060000E-01 -2.328000E-03 -1.600000E-01 -5.454000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-01 -2.552000E-03 -1.500000E-01 -4.604000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.730000E-01 -6.077000E-03 -0.000000E+00 -0.000000E+00 -6.200000E-02 -8.400000E-04 -1.340000E-01 -3.634000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.600000E-01 -5.202000E-03 -2.170000E-01 -9.735000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.565000E-03 -2.360000E-01 -1.145400E-02 -1.570000E-01 -5.131000E-03 -6.700000E-02 -9.270000E-04 -1.460000E-01 -4.382000E-03 -6.800000E-02 -1.030000E-03 -2.170000E-01 -9.735000E-03 -1.600000E-01 -5.202000E-03 -2.130000E-01 -9.111000E-03 -1.360000E-01 -4.056000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.070000E-01 -8.827000E-03 -4.970000E-01 -5.443700E-02 -2.360000E-01 -1.139000E-02 -8.800000E-02 -1.644000E-03 -1.890000E-01 -7.267000E-03 -1.000000E-01 -2.154000E-03 -1.360000E-01 -4.056000E-03 -2.130000E-01 -9.111000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.400000E-01 -3.972000E-03 -2.360000E-01 -1.118200E-02 -1.290000E-01 -3.357000E-03 -5.800000E-02 -7.240000E-04 -1.650000E-01 -5.497000E-03 -4.500000E-02 -4.390000E-04 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.380000E-01 -1.167000E-02 -5.080000E-01 -5.729800E-02 -2.360000E-01 -1.145400E-02 -1.490000E-01 -4.565000E-03 -2.400000E-01 -1.169000E-02 -1.700000E-01 -5.850000E-03 -2.230000E-01 -1.002300E-02 -1.060000E-01 -2.530000E-03 -2.020000E-01 -8.370000E-03 -1.010000E-01 -2.179000E-03 -5.080000E-01 -5.729800E-02 -2.380000E-01 -1.167000E-02 -4.940000E-01 -5.315400E-02 -2.280000E-01 -1.061000E-02 -4.970000E-01 -5.443700E-02 -2.070000E-01 -8.827000E-03 -5.320000E-01 -6.384600E-02 -2.240000E-01 -1.040600E-02 -5.570000E-01 -7.415500E-02 -2.130000E-01 -9.451000E-03 -5.020000E-01 -5.764600E-02 -2.250000E-01 -1.096300E-02 -2.280000E-01 -1.061000E-02 -4.940000E-01 -5.315400E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.118200E-02 -1.400000E-01 -3.972000E-03 -2.130000E-01 -9.133000E-03 -1.660000E-01 -5.668000E-03 +9.500000E-02 +1.949000E-03 2.150000E-01 -9.355000E-03 -1.050000E-01 -2.345000E-03 -2.110000E-01 -8.999000E-03 -1.030000E-01 -2.191000E-03 +9.289000E-03 +1.230000E-01 +3.091000E-03 +1.580000E-01 +5.254000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.280000E-01 +3.302000E-03 +1.540000E-01 +4.804000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.640000E-01 +5.424000E-03 +0.000000E+00 +0.000000E+00 +6.200000E-02 +8.020000E-04 +1.500000E-01 +4.588000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.590000E-01 -5.087000E-03 -2.500000E-01 -1.274600E-02 1.700000E-01 -5.850000E-03 -2.400000E-01 -1.169000E-02 +5.884000E-03 +2.280000E-01 +1.084800E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.720000E-01 -6.062000E-03 -7.200000E-02 -1.090000E-03 -1.440000E-01 -4.264000E-03 -7.000000E-02 -1.284000E-03 -2.500000E-01 -1.274600E-02 -1.590000E-01 -5.087000E-03 -2.530000E-01 -1.284500E-02 -1.390000E-01 -4.087000E-03 -2.240000E-01 -1.040600E-02 -5.320000E-01 -6.384600E-02 +1.540000E-01 +4.856000E-03 +2.250000E-01 +1.047100E-02 +1.410000E-01 +4.001000E-03 +6.500000E-02 +9.390000E-04 +1.360000E-01 +3.852000E-03 +6.400000E-02 +8.700000E-04 +2.280000E-01 +1.084800E-02 +1.700000E-01 +5.884000E-03 +2.230000E-01 +1.001100E-02 +1.410000E-01 +4.185000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.980000E-01 +8.038000E-03 +5.030000E-01 +5.613900E-02 +2.200000E-01 +9.958000E-03 +9.400000E-02 +1.912000E-03 +2.140000E-01 +9.376000E-03 +7.300000E-02 +1.091000E-03 +1.410000E-01 +4.185000E-03 +2.230000E-01 +1.001100E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.808000E-03 +2.340000E-01 +1.119400E-02 +1.570000E-01 +4.975000E-03 +6.700000E-02 +9.310000E-04 +1.600000E-01 +5.134000E-03 +5.300000E-02 +6.110000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.310000E-01 +1.084100E-02 +5.390000E-01 +6.215100E-02 +2.250000E-01 +1.047100E-02 +1.540000E-01 +4.856000E-03 +2.280000E-01 +1.059800E-02 +1.600000E-01 +5.214000E-03 +2.160000E-01 +9.418000E-03 +9.300000E-02 +1.927000E-03 +2.510000E-01 +1.271500E-02 +1.030000E-01 +2.229000E-03 +5.390000E-01 +6.215100E-02 +2.310000E-01 +1.084100E-02 +4.700000E-01 +4.972200E-02 +2.190000E-01 +9.821000E-03 +5.030000E-01 +5.613900E-02 +1.980000E-01 +8.038000E-03 +5.100000E-01 +5.929000E-02 +2.140000E-01 +9.406000E-03 +5.470000E-01 +7.228300E-02 +2.110000E-01 +9.215000E-03 +5.160000E-01 +5.892200E-02 +2.550000E-01 +1.480500E-02 +2.190000E-01 +9.821000E-03 +4.700000E-01 +4.972200E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 2.340000E-01 -1.113000E-02 -1.100000E-01 -2.558000E-03 -2.070000E-01 -8.695000E-03 -1.160000E-01 -3.278000E-03 -1.390000E-01 -4.087000E-03 -2.530000E-01 -1.284500E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.660000E-01 -5.668000E-03 -2.130000E-01 -9.133000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.340000E-01 -3.634000E-03 -6.200000E-02 -8.400000E-04 +1.119400E-02 +1.540000E-01 +4.808000E-03 +2.170000E-01 +9.509000E-03 1.600000E-01 -5.186000E-03 -5.400000E-02 -6.340000E-04 +5.156000E-03 +1.860000E-01 +7.198000E-03 +9.900000E-02 +2.127000E-03 +2.290000E-01 +1.058700E-02 +9.000000E-02 +1.780000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.120000E-01 -2.592000E-03 -1.720000E-01 -6.066000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.220000E-01 -3.024000E-03 -1.370000E-01 -3.775000E-03 -6.800000E-02 -1.030000E-03 -1.460000E-01 -4.382000E-03 +1.550000E-01 +4.913000E-03 +2.380000E-01 +1.153400E-02 1.600000E-01 -5.186000E-03 -0.000000E+00 -0.000000E+00 -1.720000E-01 -6.066000E-03 -1.120000E-01 -2.592000E-03 -1.260000E-01 -3.360000E-03 -1.250000E-01 -3.287000E-03 +5.214000E-03 +2.280000E-01 +1.059800E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.580000E-01 -5.108000E-03 -2.510000E-01 -1.270100E-02 -1.000000E-01 -2.154000E-03 -1.890000E-01 -7.267000E-03 -1.710000E-01 -5.975000E-03 -0.000000E+00 -0.000000E+00 -1.250000E-01 -3.287000E-03 -1.260000E-01 -3.360000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.230000E-01 -3.143000E-03 -1.300000E-01 -3.454000E-03 -4.500000E-02 -4.390000E-04 -1.650000E-01 -5.497000E-03 -1.380000E-01 -3.946000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.840000E-01 -6.872000E-03 -2.440000E-01 -1.221200E-02 -1.370000E-01 -3.775000E-03 -1.220000E-01 -3.024000E-03 -1.560000E-01 -4.952000E-03 -1.210000E-01 -3.039000E-03 -1.010000E-01 -2.179000E-03 -2.020000E-01 -8.370000E-03 -1.800000E-01 -6.542000E-03 -0.000000E+00 -0.000000E+00 -2.440000E-01 -1.221200E-02 -1.840000E-01 -6.872000E-03 -2.430000E-01 -1.197500E-02 -1.700000E-01 -6.050000E-03 -2.510000E-01 -1.270100E-02 -1.580000E-01 -5.108000E-03 -2.750000E-01 -1.589500E-02 -1.680000E-01 -5.858000E-03 -2.250000E-01 -1.096300E-02 -5.020000E-01 -5.764600E-02 -2.260000E-01 -1.039600E-02 -0.000000E+00 -0.000000E+00 -1.700000E-01 -6.050000E-03 -2.430000E-01 -1.197500E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.300000E-01 -3.454000E-03 -1.230000E-01 -3.143000E-03 -1.590000E-01 -5.219000E-03 -1.270000E-01 -3.447000E-03 -1.030000E-01 -2.191000E-03 -2.110000E-01 -8.999000E-03 -2.090000E-01 -9.091000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.150000E-01 -2.821000E-03 1.610000E-01 -5.499000E-03 -1.210000E-01 -3.039000E-03 -1.560000E-01 -4.952000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 +5.237000E-03 7.000000E-02 -1.284000E-03 -1.440000E-01 -4.264000E-03 -1.580000E-01 -5.002000E-03 -0.000000E+00 -0.000000E+00 -1.610000E-01 -5.499000E-03 -1.150000E-01 -2.821000E-03 -1.630000E-01 -5.583000E-03 -1.270000E-01 -3.315000E-03 -1.680000E-01 -5.858000E-03 -2.750000E-01 -1.589500E-02 +1.020000E-03 +1.410000E-01 +4.071000E-03 +6.100000E-02 +8.350000E-04 +2.380000E-01 +1.153400E-02 +1.550000E-01 +4.913000E-03 +2.320000E-01 +1.100400E-02 +1.460000E-01 +4.498000E-03 +2.140000E-01 +9.406000E-03 +5.100000E-01 +5.929000E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.160000E-01 -3.278000E-03 -2.070000E-01 -8.695000E-03 -1.840000E-01 -6.908000E-03 -0.000000E+00 -0.000000E+00 -1.270000E-01 -3.315000E-03 -1.630000E-01 -5.583000E-03 +2.150000E-01 +9.289000E-03 +9.500000E-02 +1.949000E-03 +1.870000E-01 +7.267000E-03 +1.030000E-01 +2.543000E-03 +1.460000E-01 +4.498000E-03 +2.320000E-01 +1.100400E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.270000E-01 -3.447000E-03 -1.590000E-01 -5.219000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.400000E-02 -6.340000E-04 1.600000E-01 -5.186000E-03 -1.920000E-01 -7.646000E-03 +5.156000E-03 +2.170000E-01 +9.509000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.500000E-01 +4.588000E-03 +6.200000E-02 +8.020000E-04 +1.290000E-01 +3.487000E-03 +6.000000E-02 +8.060000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.090000E-01 +2.437000E-03 +1.650000E-01 +5.655000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.065000E-03 +1.570000E-01 +4.999000E-03 +6.400000E-02 +8.700000E-04 +1.360000E-01 +3.852000E-03 +1.750000E-01 +6.299000E-03 +0.000000E+00 +0.000000E+00 +1.650000E-01 +5.655000E-03 +1.090000E-01 +2.437000E-03 +1.480000E-01 +4.674000E-03 +1.320000E-01 +3.548000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.440000E-01 +4.282000E-03 +2.190000E-01 +9.683000E-03 +7.300000E-02 +1.091000E-03 +2.140000E-01 +9.376000E-03 +1.830000E-01 +6.803000E-03 +0.000000E+00 +0.000000E+00 +1.320000E-01 +3.548000E-03 +1.480000E-01 +4.674000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.350000E-01 +3.895000E-03 +1.430000E-01 +4.297000E-03 +5.300000E-02 +6.110000E-04 +1.600000E-01 +5.134000E-03 +1.380000E-01 +4.220000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-01 +7.412000E-03 +2.420000E-01 +1.208400E-02 +1.570000E-01 +4.999000E-03 +1.230000E-01 +3.065000E-03 +1.730000E-01 +6.279000E-03 +1.330000E-01 +3.621000E-03 +1.030000E-01 +2.229000E-03 +2.510000E-01 +1.271500E-02 +2.100000E-01 +8.970000E-03 +0.000000E+00 +0.000000E+00 +2.420000E-01 +1.208400E-02 +1.900000E-01 +7.412000E-03 +2.610000E-01 +1.399900E-02 +2.000000E-01 +8.410000E-03 +2.190000E-01 +9.683000E-03 +1.440000E-01 +4.282000E-03 +2.690000E-01 +1.478100E-02 +1.540000E-01 +4.986000E-03 +2.550000E-01 +1.480500E-02 +5.160000E-01 +5.892200E-02 +2.310000E-01 +1.098900E-02 +0.000000E+00 +0.000000E+00 +2.000000E-01 +8.410000E-03 +2.610000E-01 +1.399900E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.430000E-01 +4.297000E-03 +1.350000E-01 +3.895000E-03 +1.760000E-01 +6.534000E-03 +1.170000E-01 +2.807000E-03 +9.000000E-02 +1.780000E-03 +2.290000E-01 +1.058700E-02 +2.080000E-01 +8.854000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.305000E-03 +1.760000E-01 +6.598000E-03 +1.330000E-01 +3.621000E-03 +1.730000E-01 +6.279000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.100000E-02 +8.350000E-04 +1.410000E-01 +4.071000E-03 +1.800000E-01 +6.574000E-03 +0.000000E+00 +0.000000E+00 +1.760000E-01 +6.598000E-03 +1.230000E-01 +3.305000E-03 +1.690000E-01 +6.135000E-03 +1.220000E-01 +3.344000E-03 +1.540000E-01 +4.986000E-03 +2.690000E-01 +1.478100E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.030000E-01 +2.543000E-03 +1.870000E-01 +7.267000E-03 +1.690000E-01 +5.731000E-03 +0.000000E+00 +0.000000E+00 +1.220000E-01 +3.344000E-03 +1.690000E-01 +6.135000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.170000E-01 +2.807000E-03 +1.760000E-01 +6.534000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.000000E-02 +8.060000E-04 +1.290000E-01 +3.487000E-03 +1.790000E-01 +6.747000E-03 0.000000E+00 0.000000E+00 tally 2: @@ -660,12 +660,12 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.400000E-01 -4.016000E-03 +1.200000E-01 +2.924000E-03 0.000000E+00 0.000000E+00 -1.760000E-01 -6.368000E-03 +1.740000E-01 +6.270000E-03 0.000000E+00 0.000000E+00 0.000000E+00 @@ -676,16 +676,112 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.140000E-01 -2.714000E-03 +1.200000E-01 +3.044000E-03 0.000000E+00 0.000000E+00 -1.590000E-01 -5.517000E-03 +1.510000E-01 +4.615000E-03 0.000000E+00 0.000000E+00 -1.800000E-01 -6.524000E-03 +1.710000E-01 +6.067000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.500000E-02 +9.390000E-04 +0.000000E+00 +0.000000E+00 +1.410000E-01 +4.001000E-03 +0.000000E+00 +0.000000E+00 +1.740000E-01 +6.270000E-03 +0.000000E+00 +0.000000E+00 +1.200000E-01 +2.924000E-03 +0.000000E+00 +0.000000E+00 +1.830000E-01 +6.879000E-03 +0.000000E+00 +0.000000E+00 +1.310000E-01 +3.541000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.750000E-01 +6.659000E-03 +0.000000E+00 +0.000000E+00 +2.820000E-01 +1.605400E-02 +0.000000E+00 +0.000000E+00 +1.980000E-01 +7.932000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.400000E-02 +1.912000E-03 +0.000000E+00 +0.000000E+00 +2.200000E-01 +9.958000E-03 +0.000000E+00 +0.000000E+00 +1.310000E-01 +3.541000E-03 +0.000000E+00 +0.000000E+00 +1.830000E-01 +6.879000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.110000E-01 +2.639000E-03 +0.000000E+00 +0.000000E+00 +1.360000E-01 +3.890000E-03 +0.000000E+00 +0.000000E+00 +1.770000E-01 +6.551000E-03 0.000000E+00 0.000000E+00 0.000000E+00 @@ -693,251 +789,115 @@ tally 2: 0.000000E+00 0.000000E+00 6.700000E-02 -9.270000E-04 +9.310000E-04 0.000000E+00 0.000000E+00 1.570000E-01 -5.131000E-03 +4.975000E-03 0.000000E+00 0.000000E+00 -1.760000E-01 -6.368000E-03 0.000000E+00 0.000000E+00 -1.400000E-01 -4.016000E-03 0.000000E+00 0.000000E+00 -1.570000E-01 -5.019000E-03 0.000000E+00 0.000000E+00 -1.210000E-01 -2.997000E-03 0.000000E+00 0.000000E+00 +1.480000E-01 +4.910000E-03 0.000000E+00 0.000000E+00 +2.720000E-01 +1.490600E-02 0.000000E+00 0.000000E+00 +1.510000E-01 +4.615000E-03 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -1.780000E-01 -6.838000E-03 -0.000000E+00 -0.000000E+00 -2.440000E-01 -1.203200E-02 -0.000000E+00 -0.000000E+00 -2.120000E-01 -9.028000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.800000E-02 -1.644000E-03 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.139000E-02 -0.000000E+00 -0.000000E+00 -1.210000E-01 -2.997000E-03 -0.000000E+00 -0.000000E+00 -1.570000E-01 -5.019000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.070000E-01 -2.375000E-03 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.621000E-03 -0.000000E+00 -0.000000E+00 -1.640000E-01 -5.476000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.800000E-02 -7.240000E-04 -0.000000E+00 -0.000000E+00 -1.290000E-01 -3.357000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.470000E-01 -4.583000E-03 -0.000000E+00 -0.000000E+00 -2.610000E-01 -1.376900E-02 -0.000000E+00 -0.000000E+00 -1.590000E-01 -5.517000E-03 -0.000000E+00 -0.000000E+00 -1.140000E-01 -2.714000E-03 -0.000000E+00 -0.000000E+00 -1.630000E-01 -5.645000E-03 -0.000000E+00 -0.000000E+00 -1.090000E-01 -2.419000E-03 -0.000000E+00 -0.000000E+00 -1.820000E-01 -6.750000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.060000E-01 -2.530000E-03 -0.000000E+00 -0.000000E+00 -2.230000E-01 -1.002300E-02 -0.000000E+00 -0.000000E+00 -2.610000E-01 -1.376900E-02 -0.000000E+00 -0.000000E+00 -1.470000E-01 -4.583000E-03 -0.000000E+00 -0.000000E+00 -2.820000E-01 -1.613400E-02 +1.200000E-01 +3.044000E-03 0.000000E+00 0.000000E+00 1.800000E-01 -6.690000E-03 -0.000000E+00 -0.000000E+00 -2.440000E-01 -1.203200E-02 -0.000000E+00 -0.000000E+00 -1.780000E-01 -6.838000E-03 -0.000000E+00 -0.000000E+00 -2.870000E-01 -1.696700E-02 -0.000000E+00 -0.000000E+00 -1.800000E-01 -6.556000E-03 -0.000000E+00 -0.000000E+00 -2.430000E-01 -1.211900E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.451000E-03 -0.000000E+00 -0.000000E+00 -5.570000E-01 -7.415500E-02 -0.000000E+00 -0.000000E+00 -1.800000E-01 -6.690000E-03 -0.000000E+00 -0.000000E+00 -2.820000E-01 -1.613400E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.621000E-03 -0.000000E+00 -0.000000E+00 -1.070000E-01 -2.375000E-03 -0.000000E+00 -0.000000E+00 -1.500000E-01 -4.604000E-03 -0.000000E+00 -0.000000E+00 -1.100000E-01 -2.552000E-03 -0.000000E+00 -0.000000E+00 -1.800000E-01 -6.518000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 +6.720000E-03 0.000000E+00 0.000000E+00 1.050000E-01 -2.345000E-03 +2.285000E-03 0.000000E+00 0.000000E+00 -2.150000E-01 -9.355000E-03 +1.790000E-01 +6.499000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.300000E-02 +1.927000E-03 +0.000000E+00 +0.000000E+00 +2.160000E-01 +9.418000E-03 +0.000000E+00 +0.000000E+00 +2.720000E-01 +1.490600E-02 +0.000000E+00 +0.000000E+00 +1.480000E-01 +4.910000E-03 +0.000000E+00 +0.000000E+00 +2.680000E-01 +1.445800E-02 +0.000000E+00 +0.000000E+00 +1.480000E-01 +4.488000E-03 +0.000000E+00 +0.000000E+00 +2.820000E-01 +1.605400E-02 +0.000000E+00 +0.000000E+00 +1.750000E-01 +6.659000E-03 +0.000000E+00 +0.000000E+00 +2.510000E-01 +1.283100E-02 +0.000000E+00 +0.000000E+00 +1.870000E-01 +7.731000E-03 +0.000000E+00 +0.000000E+00 +2.340000E-01 +1.105000E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.110000E-01 +9.215000E-03 +0.000000E+00 +0.000000E+00 +5.470000E-01 +7.228300E-02 +0.000000E+00 +0.000000E+00 +1.480000E-01 +4.488000E-03 +0.000000E+00 +0.000000E+00 +2.680000E-01 +1.445800E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -948,20 +908,60 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.340000E-01 -3.818000E-03 +1.360000E-01 +3.890000E-03 0.000000E+00 0.000000E+00 -1.690000E-01 -5.911000E-03 +1.110000E-01 +2.639000E-03 0.000000E+00 0.000000E+00 -1.090000E-01 -2.419000E-03 +1.540000E-01 +4.804000E-03 0.000000E+00 0.000000E+00 -1.630000E-01 -5.645000E-03 +1.280000E-01 +3.302000E-03 +0.000000E+00 +0.000000E+00 +2.200000E-01 +9.834000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.900000E-02 +2.127000E-03 +0.000000E+00 +0.000000E+00 +1.860000E-01 +7.198000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.430000E-01 +4.337000E-03 +0.000000E+00 +0.000000E+00 +1.580000E-01 +5.104000E-03 +0.000000E+00 +0.000000E+00 +1.050000E-01 +2.285000E-03 +0.000000E+00 +0.000000E+00 +1.800000E-01 +6.720000E-03 0.000000E+00 0.000000E+00 0.000000E+00 @@ -973,43 +973,43 @@ tally 2: 0.000000E+00 0.000000E+00 1.860000E-01 -6.998000E-03 +7.178000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -7.200000E-02 -1.090000E-03 +7.000000E-02 +1.020000E-03 0.000000E+00 0.000000E+00 -1.720000E-01 -6.062000E-03 +1.610000E-01 +5.237000E-03 0.000000E+00 0.000000E+00 -1.690000E-01 -5.911000E-03 +1.580000E-01 +5.104000E-03 0.000000E+00 0.000000E+00 -1.340000E-01 -3.818000E-03 +1.430000E-01 +4.337000E-03 0.000000E+00 0.000000E+00 -1.600000E-01 -5.454000E-03 +1.580000E-01 +5.254000E-03 0.000000E+00 0.000000E+00 -1.060000E-01 -2.328000E-03 +1.230000E-01 +3.091000E-03 0.000000E+00 0.000000E+00 -1.800000E-01 -6.556000E-03 +1.870000E-01 +7.731000E-03 0.000000E+00 0.000000E+00 -2.870000E-01 -1.696700E-02 +2.510000E-01 +1.283100E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1020,356 +1020,28 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -2.060000E-01 -8.670000E-03 +1.980000E-01 +7.934000E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.100000E-01 -2.558000E-03 -0.000000E+00 -0.000000E+00 -2.340000E-01 -1.113000E-02 -0.000000E+00 -0.000000E+00 -1.060000E-01 -2.328000E-03 -0.000000E+00 -0.000000E+00 -1.600000E-01 -5.454000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-01 -2.552000E-03 -0.000000E+00 -0.000000E+00 -1.500000E-01 -4.604000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.730000E-01 -6.077000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.200000E-02 -8.400000E-04 -0.000000E+00 -0.000000E+00 -1.340000E-01 -3.634000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.600000E-01 -5.202000E-03 -0.000000E+00 -0.000000E+00 -2.170000E-01 -9.735000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.565000E-03 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.145400E-02 -0.000000E+00 -0.000000E+00 -1.570000E-01 -5.131000E-03 -0.000000E+00 -0.000000E+00 -6.700000E-02 -9.270000E-04 -0.000000E+00 -0.000000E+00 -1.460000E-01 -4.382000E-03 -0.000000E+00 -0.000000E+00 -6.800000E-02 -1.030000E-03 -0.000000E+00 -0.000000E+00 -2.170000E-01 -9.735000E-03 -0.000000E+00 -0.000000E+00 -1.600000E-01 -5.202000E-03 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.111000E-03 -0.000000E+00 -0.000000E+00 -1.360000E-01 -4.056000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.070000E-01 -8.827000E-03 -0.000000E+00 -0.000000E+00 -4.970000E-01 -5.443700E-02 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.139000E-02 -0.000000E+00 -0.000000E+00 -8.800000E-02 -1.644000E-03 -0.000000E+00 -0.000000E+00 -1.890000E-01 -7.267000E-03 -0.000000E+00 -0.000000E+00 -1.000000E-01 -2.154000E-03 -0.000000E+00 -0.000000E+00 -1.360000E-01 -4.056000E-03 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.111000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.400000E-01 -3.972000E-03 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.118200E-02 -0.000000E+00 -0.000000E+00 -1.290000E-01 -3.357000E-03 -0.000000E+00 -0.000000E+00 -5.800000E-02 -7.240000E-04 -0.000000E+00 -0.000000E+00 -1.650000E-01 -5.497000E-03 -0.000000E+00 -0.000000E+00 -4.500000E-02 -4.390000E-04 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.380000E-01 -1.167000E-02 -0.000000E+00 -0.000000E+00 -5.080000E-01 -5.729800E-02 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.145400E-02 -0.000000E+00 -0.000000E+00 -1.490000E-01 -4.565000E-03 -0.000000E+00 -0.000000E+00 -2.400000E-01 -1.169000E-02 -0.000000E+00 -0.000000E+00 -1.700000E-01 -5.850000E-03 -0.000000E+00 -0.000000E+00 -2.230000E-01 -1.002300E-02 -0.000000E+00 -0.000000E+00 -1.060000E-01 -2.530000E-03 -0.000000E+00 -0.000000E+00 -2.020000E-01 -8.370000E-03 -0.000000E+00 -0.000000E+00 -1.010000E-01 -2.179000E-03 -0.000000E+00 -0.000000E+00 -5.080000E-01 -5.729800E-02 -0.000000E+00 -0.000000E+00 -2.380000E-01 -1.167000E-02 -0.000000E+00 -0.000000E+00 -4.940000E-01 -5.315400E-02 -0.000000E+00 -0.000000E+00 -2.280000E-01 -1.061000E-02 -0.000000E+00 -0.000000E+00 -4.970000E-01 -5.443700E-02 -0.000000E+00 -0.000000E+00 -2.070000E-01 -8.827000E-03 -0.000000E+00 -0.000000E+00 -5.320000E-01 -6.384600E-02 -0.000000E+00 -0.000000E+00 -2.240000E-01 -1.040600E-02 -0.000000E+00 -0.000000E+00 -5.570000E-01 -7.415500E-02 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.451000E-03 -0.000000E+00 -0.000000E+00 -5.020000E-01 -5.764600E-02 -0.000000E+00 -0.000000E+00 -2.250000E-01 -1.096300E-02 -0.000000E+00 -0.000000E+00 -2.280000E-01 -1.061000E-02 -0.000000E+00 -0.000000E+00 -4.940000E-01 -5.315400E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.360000E-01 -1.118200E-02 -0.000000E+00 -0.000000E+00 -1.400000E-01 -3.972000E-03 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.133000E-03 -0.000000E+00 -0.000000E+00 -1.660000E-01 -5.668000E-03 +9.500000E-02 +1.949000E-03 0.000000E+00 0.000000E+00 2.150000E-01 -9.355000E-03 +9.289000E-03 0.000000E+00 0.000000E+00 -1.050000E-01 -2.345000E-03 +1.230000E-01 +3.091000E-03 0.000000E+00 0.000000E+00 -2.110000E-01 -8.999000E-03 -0.000000E+00 -0.000000E+00 -1.030000E-01 -2.191000E-03 +1.580000E-01 +5.254000E-03 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1380,20 +1052,52 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.590000E-01 -5.087000E-03 +1.280000E-01 +3.302000E-03 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.804000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.640000E-01 +5.424000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.200000E-02 +8.020000E-04 +0.000000E+00 +0.000000E+00 +1.500000E-01 +4.588000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 0.000000E+00 0.000000E+00 -2.500000E-01 -1.274600E-02 0.000000E+00 0.000000E+00 1.700000E-01 -5.850000E-03 +5.884000E-03 0.000000E+00 0.000000E+00 -2.400000E-01 -1.169000E-02 +2.280000E-01 +1.084800E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1404,44 +1108,228 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.720000E-01 -6.062000E-03 +1.540000E-01 +4.856000E-03 0.000000E+00 0.000000E+00 -7.200000E-02 -1.090000E-03 +2.250000E-01 +1.047100E-02 0.000000E+00 0.000000E+00 -1.440000E-01 -4.264000E-03 +1.410000E-01 +4.001000E-03 0.000000E+00 0.000000E+00 -7.000000E-02 -1.284000E-03 +6.500000E-02 +9.390000E-04 0.000000E+00 0.000000E+00 -2.500000E-01 -1.274600E-02 +1.360000E-01 +3.852000E-03 0.000000E+00 0.000000E+00 -1.590000E-01 -5.087000E-03 +6.400000E-02 +8.700000E-04 0.000000E+00 0.000000E+00 -2.530000E-01 -1.284500E-02 +2.280000E-01 +1.084800E-02 0.000000E+00 0.000000E+00 -1.390000E-01 -4.087000E-03 +1.700000E-01 +5.884000E-03 0.000000E+00 0.000000E+00 -2.240000E-01 -1.040600E-02 +2.230000E-01 +1.001100E-02 0.000000E+00 0.000000E+00 -5.320000E-01 -6.384600E-02 +1.410000E-01 +4.185000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.980000E-01 +8.038000E-03 +0.000000E+00 +0.000000E+00 +5.030000E-01 +5.613900E-02 +0.000000E+00 +0.000000E+00 +2.200000E-01 +9.958000E-03 +0.000000E+00 +0.000000E+00 +9.400000E-02 +1.912000E-03 +0.000000E+00 +0.000000E+00 +2.140000E-01 +9.376000E-03 +0.000000E+00 +0.000000E+00 +7.300000E-02 +1.091000E-03 +0.000000E+00 +0.000000E+00 +1.410000E-01 +4.185000E-03 +0.000000E+00 +0.000000E+00 +2.230000E-01 +1.001100E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.808000E-03 +0.000000E+00 +0.000000E+00 +2.340000E-01 +1.119400E-02 +0.000000E+00 +0.000000E+00 +1.570000E-01 +4.975000E-03 +0.000000E+00 +0.000000E+00 +6.700000E-02 +9.310000E-04 +0.000000E+00 +0.000000E+00 +1.600000E-01 +5.134000E-03 +0.000000E+00 +0.000000E+00 +5.300000E-02 +6.110000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.310000E-01 +1.084100E-02 +0.000000E+00 +0.000000E+00 +5.390000E-01 +6.215100E-02 +0.000000E+00 +0.000000E+00 +2.250000E-01 +1.047100E-02 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.856000E-03 +0.000000E+00 +0.000000E+00 +2.280000E-01 +1.059800E-02 +0.000000E+00 +0.000000E+00 +1.600000E-01 +5.214000E-03 +0.000000E+00 +0.000000E+00 +2.160000E-01 +9.418000E-03 +0.000000E+00 +0.000000E+00 +9.300000E-02 +1.927000E-03 +0.000000E+00 +0.000000E+00 +2.510000E-01 +1.271500E-02 +0.000000E+00 +0.000000E+00 +1.030000E-01 +2.229000E-03 +0.000000E+00 +0.000000E+00 +5.390000E-01 +6.215100E-02 +0.000000E+00 +0.000000E+00 +2.310000E-01 +1.084100E-02 +0.000000E+00 +0.000000E+00 +4.700000E-01 +4.972200E-02 +0.000000E+00 +0.000000E+00 +2.190000E-01 +9.821000E-03 +0.000000E+00 +0.000000E+00 +5.030000E-01 +5.613900E-02 +0.000000E+00 +0.000000E+00 +1.980000E-01 +8.038000E-03 +0.000000E+00 +0.000000E+00 +5.100000E-01 +5.929000E-02 +0.000000E+00 +0.000000E+00 +2.140000E-01 +9.406000E-03 +0.000000E+00 +0.000000E+00 +5.470000E-01 +7.228300E-02 +0.000000E+00 +0.000000E+00 +2.110000E-01 +9.215000E-03 +0.000000E+00 +0.000000E+00 +5.160000E-01 +5.892200E-02 +0.000000E+00 +0.000000E+00 +2.550000E-01 +1.480500E-02 +0.000000E+00 +0.000000E+00 +2.190000E-01 +9.821000E-03 +0.000000E+00 +0.000000E+00 +4.700000E-01 +4.972200E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1453,67 +1341,35 @@ tally 2: 0.000000E+00 0.000000E+00 2.340000E-01 -1.113000E-02 +1.119400E-02 0.000000E+00 0.000000E+00 -1.100000E-01 -2.558000E-03 +1.540000E-01 +4.808000E-03 0.000000E+00 0.000000E+00 -2.070000E-01 -8.695000E-03 -0.000000E+00 -0.000000E+00 -1.160000E-01 -3.278000E-03 -0.000000E+00 -0.000000E+00 -1.390000E-01 -4.087000E-03 -0.000000E+00 -0.000000E+00 -2.530000E-01 -1.284500E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.660000E-01 -5.668000E-03 -0.000000E+00 -0.000000E+00 -2.130000E-01 -9.133000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.340000E-01 -3.634000E-03 -0.000000E+00 -0.000000E+00 -6.200000E-02 -8.400000E-04 +2.170000E-01 +9.509000E-03 0.000000E+00 0.000000E+00 1.600000E-01 -5.186000E-03 +5.156000E-03 0.000000E+00 0.000000E+00 -5.400000E-02 -6.340000E-04 +1.860000E-01 +7.198000E-03 +0.000000E+00 +0.000000E+00 +9.900000E-02 +2.127000E-03 +0.000000E+00 +0.000000E+00 +2.290000E-01 +1.058700E-02 +0.000000E+00 +0.000000E+00 +9.000000E-02 +1.780000E-03 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1524,356 +1380,68 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.120000E-01 -2.592000E-03 +1.550000E-01 +4.913000E-03 0.000000E+00 0.000000E+00 -1.720000E-01 -6.066000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.220000E-01 -3.024000E-03 -0.000000E+00 -0.000000E+00 -1.370000E-01 -3.775000E-03 -0.000000E+00 -0.000000E+00 -6.800000E-02 -1.030000E-03 -0.000000E+00 -0.000000E+00 -1.460000E-01 -4.382000E-03 +2.380000E-01 +1.153400E-02 0.000000E+00 0.000000E+00 1.600000E-01 -5.186000E-03 +5.214000E-03 0.000000E+00 0.000000E+00 +2.280000E-01 +1.059800E-02 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.720000E-01 -6.066000E-03 0.000000E+00 0.000000E+00 -1.120000E-01 -2.592000E-03 0.000000E+00 0.000000E+00 -1.260000E-01 -3.360000E-03 -0.000000E+00 -0.000000E+00 -1.250000E-01 -3.287000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.580000E-01 -5.108000E-03 -0.000000E+00 -0.000000E+00 -2.510000E-01 -1.270100E-02 -0.000000E+00 -0.000000E+00 -1.000000E-01 -2.154000E-03 -0.000000E+00 -0.000000E+00 -1.890000E-01 -7.267000E-03 -0.000000E+00 -0.000000E+00 -1.710000E-01 -5.975000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.250000E-01 -3.287000E-03 -0.000000E+00 -0.000000E+00 -1.260000E-01 -3.360000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.230000E-01 -3.143000E-03 -0.000000E+00 -0.000000E+00 -1.300000E-01 -3.454000E-03 -0.000000E+00 -0.000000E+00 -4.500000E-02 -4.390000E-04 -0.000000E+00 -0.000000E+00 -1.650000E-01 -5.497000E-03 -0.000000E+00 -0.000000E+00 -1.380000E-01 -3.946000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.840000E-01 -6.872000E-03 -0.000000E+00 -0.000000E+00 -2.440000E-01 -1.221200E-02 -0.000000E+00 -0.000000E+00 -1.370000E-01 -3.775000E-03 -0.000000E+00 -0.000000E+00 -1.220000E-01 -3.024000E-03 -0.000000E+00 -0.000000E+00 -1.560000E-01 -4.952000E-03 -0.000000E+00 -0.000000E+00 -1.210000E-01 -3.039000E-03 -0.000000E+00 -0.000000E+00 -1.010000E-01 -2.179000E-03 -0.000000E+00 -0.000000E+00 -2.020000E-01 -8.370000E-03 -0.000000E+00 -0.000000E+00 -1.800000E-01 -6.542000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.440000E-01 -1.221200E-02 -0.000000E+00 -0.000000E+00 -1.840000E-01 -6.872000E-03 -0.000000E+00 -0.000000E+00 -2.430000E-01 -1.197500E-02 -0.000000E+00 -0.000000E+00 -1.700000E-01 -6.050000E-03 -0.000000E+00 -0.000000E+00 -2.510000E-01 -1.270100E-02 -0.000000E+00 -0.000000E+00 -1.580000E-01 -5.108000E-03 -0.000000E+00 -0.000000E+00 -2.750000E-01 -1.589500E-02 -0.000000E+00 -0.000000E+00 -1.680000E-01 -5.858000E-03 -0.000000E+00 -0.000000E+00 -2.250000E-01 -1.096300E-02 -0.000000E+00 -0.000000E+00 -5.020000E-01 -5.764600E-02 -0.000000E+00 -0.000000E+00 -2.260000E-01 -1.039600E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.700000E-01 -6.050000E-03 -0.000000E+00 -0.000000E+00 -2.430000E-01 -1.197500E-02 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.300000E-01 -3.454000E-03 -0.000000E+00 -0.000000E+00 -1.230000E-01 -3.143000E-03 -0.000000E+00 -0.000000E+00 -1.590000E-01 -5.219000E-03 -0.000000E+00 -0.000000E+00 -1.270000E-01 -3.447000E-03 -0.000000E+00 -0.000000E+00 -1.030000E-01 -2.191000E-03 -0.000000E+00 -0.000000E+00 -2.110000E-01 -8.999000E-03 -0.000000E+00 -0.000000E+00 -2.090000E-01 -9.091000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.150000E-01 -2.821000E-03 0.000000E+00 0.000000E+00 1.610000E-01 -5.499000E-03 -0.000000E+00 -0.000000E+00 -1.210000E-01 -3.039000E-03 -0.000000E+00 -0.000000E+00 -1.560000E-01 -4.952000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 +5.237000E-03 0.000000E+00 0.000000E+00 7.000000E-02 -1.284000E-03 +1.020000E-03 0.000000E+00 0.000000E+00 -1.440000E-01 -4.264000E-03 +1.410000E-01 +4.071000E-03 0.000000E+00 0.000000E+00 -1.580000E-01 -5.002000E-03 +6.100000E-02 +8.350000E-04 0.000000E+00 0.000000E+00 +2.380000E-01 +1.153400E-02 0.000000E+00 0.000000E+00 +1.550000E-01 +4.913000E-03 0.000000E+00 0.000000E+00 -1.610000E-01 -5.499000E-03 +2.320000E-01 +1.100400E-02 0.000000E+00 0.000000E+00 -1.150000E-01 -2.821000E-03 +1.460000E-01 +4.498000E-03 0.000000E+00 0.000000E+00 -1.630000E-01 -5.583000E-03 +2.140000E-01 +9.406000E-03 0.000000E+00 0.000000E+00 -1.270000E-01 -3.315000E-03 -0.000000E+00 -0.000000E+00 -1.680000E-01 -5.858000E-03 -0.000000E+00 -0.000000E+00 -2.750000E-01 -1.589500E-02 +5.100000E-01 +5.929000E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1884,28 +1452,28 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.160000E-01 -3.278000E-03 +2.150000E-01 +9.289000E-03 0.000000E+00 0.000000E+00 -2.070000E-01 -8.695000E-03 +9.500000E-02 +1.949000E-03 0.000000E+00 0.000000E+00 -1.840000E-01 -6.908000E-03 +1.870000E-01 +7.267000E-03 0.000000E+00 0.000000E+00 +1.030000E-01 +2.543000E-03 0.000000E+00 0.000000E+00 +1.460000E-01 +4.498000E-03 0.000000E+00 0.000000E+00 -1.270000E-01 -3.315000E-03 -0.000000E+00 -0.000000E+00 -1.630000E-01 -5.583000E-03 +2.320000E-01 +1.100400E-02 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1916,32 +1484,464 @@ tally 2: 0.000000E+00 0.000000E+00 0.000000E+00 -1.270000E-01 -3.447000E-03 -0.000000E+00 -0.000000E+00 -1.590000E-01 -5.219000E-03 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.400000E-02 -6.340000E-04 -0.000000E+00 -0.000000E+00 1.600000E-01 -5.186000E-03 +5.156000E-03 0.000000E+00 0.000000E+00 -1.920000E-01 -7.646000E-03 +2.170000E-01 +9.509000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.500000E-01 +4.588000E-03 +0.000000E+00 +0.000000E+00 +6.200000E-02 +8.020000E-04 +0.000000E+00 +0.000000E+00 +1.290000E-01 +3.487000E-03 +0.000000E+00 +0.000000E+00 +6.000000E-02 +8.060000E-04 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.090000E-01 +2.437000E-03 +0.000000E+00 +0.000000E+00 +1.650000E-01 +5.655000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.065000E-03 +0.000000E+00 +0.000000E+00 +1.570000E-01 +4.999000E-03 +0.000000E+00 +0.000000E+00 +6.400000E-02 +8.700000E-04 +0.000000E+00 +0.000000E+00 +1.360000E-01 +3.852000E-03 +0.000000E+00 +0.000000E+00 +1.750000E-01 +6.299000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.650000E-01 +5.655000E-03 +0.000000E+00 +0.000000E+00 +1.090000E-01 +2.437000E-03 +0.000000E+00 +0.000000E+00 +1.480000E-01 +4.674000E-03 +0.000000E+00 +0.000000E+00 +1.320000E-01 +3.548000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.440000E-01 +4.282000E-03 +0.000000E+00 +0.000000E+00 +2.190000E-01 +9.683000E-03 +0.000000E+00 +0.000000E+00 +7.300000E-02 +1.091000E-03 +0.000000E+00 +0.000000E+00 +2.140000E-01 +9.376000E-03 +0.000000E+00 +0.000000E+00 +1.830000E-01 +6.803000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.320000E-01 +3.548000E-03 +0.000000E+00 +0.000000E+00 +1.480000E-01 +4.674000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.350000E-01 +3.895000E-03 +0.000000E+00 +0.000000E+00 +1.430000E-01 +4.297000E-03 +0.000000E+00 +0.000000E+00 +5.300000E-02 +6.110000E-04 +0.000000E+00 +0.000000E+00 +1.600000E-01 +5.134000E-03 +0.000000E+00 +0.000000E+00 +1.380000E-01 +4.220000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-01 +7.412000E-03 +0.000000E+00 +0.000000E+00 +2.420000E-01 +1.208400E-02 +0.000000E+00 +0.000000E+00 +1.570000E-01 +4.999000E-03 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.065000E-03 +0.000000E+00 +0.000000E+00 +1.730000E-01 +6.279000E-03 +0.000000E+00 +0.000000E+00 +1.330000E-01 +3.621000E-03 +0.000000E+00 +0.000000E+00 +1.030000E-01 +2.229000E-03 +0.000000E+00 +0.000000E+00 +2.510000E-01 +1.271500E-02 +0.000000E+00 +0.000000E+00 +2.100000E-01 +8.970000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.420000E-01 +1.208400E-02 +0.000000E+00 +0.000000E+00 +1.900000E-01 +7.412000E-03 +0.000000E+00 +0.000000E+00 +2.610000E-01 +1.399900E-02 +0.000000E+00 +0.000000E+00 +2.000000E-01 +8.410000E-03 +0.000000E+00 +0.000000E+00 +2.190000E-01 +9.683000E-03 +0.000000E+00 +0.000000E+00 +1.440000E-01 +4.282000E-03 +0.000000E+00 +0.000000E+00 +2.690000E-01 +1.478100E-02 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.986000E-03 +0.000000E+00 +0.000000E+00 +2.550000E-01 +1.480500E-02 +0.000000E+00 +0.000000E+00 +5.160000E-01 +5.892200E-02 +0.000000E+00 +0.000000E+00 +2.310000E-01 +1.098900E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-01 +8.410000E-03 +0.000000E+00 +0.000000E+00 +2.610000E-01 +1.399900E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.430000E-01 +4.297000E-03 +0.000000E+00 +0.000000E+00 +1.350000E-01 +3.895000E-03 +0.000000E+00 +0.000000E+00 +1.760000E-01 +6.534000E-03 +0.000000E+00 +0.000000E+00 +1.170000E-01 +2.807000E-03 +0.000000E+00 +0.000000E+00 +9.000000E-02 +1.780000E-03 +0.000000E+00 +0.000000E+00 +2.290000E-01 +1.058700E-02 +0.000000E+00 +0.000000E+00 +2.080000E-01 +8.854000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.305000E-03 +0.000000E+00 +0.000000E+00 +1.760000E-01 +6.598000E-03 +0.000000E+00 +0.000000E+00 +1.330000E-01 +3.621000E-03 +0.000000E+00 +0.000000E+00 +1.730000E-01 +6.279000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.100000E-02 +8.350000E-04 +0.000000E+00 +0.000000E+00 +1.410000E-01 +4.071000E-03 +0.000000E+00 +0.000000E+00 +1.800000E-01 +6.574000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.760000E-01 +6.598000E-03 +0.000000E+00 +0.000000E+00 +1.230000E-01 +3.305000E-03 +0.000000E+00 +0.000000E+00 +1.690000E-01 +6.135000E-03 +0.000000E+00 +0.000000E+00 +1.220000E-01 +3.344000E-03 +0.000000E+00 +0.000000E+00 +1.540000E-01 +4.986000E-03 +0.000000E+00 +0.000000E+00 +2.690000E-01 +1.478100E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.030000E-01 +2.543000E-03 +0.000000E+00 +0.000000E+00 +1.870000E-01 +7.267000E-03 +0.000000E+00 +0.000000E+00 +1.690000E-01 +5.731000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.220000E-01 +3.344000E-03 +0.000000E+00 +0.000000E+00 +1.690000E-01 +6.135000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.170000E-01 +2.807000E-03 +0.000000E+00 +0.000000E+00 +1.760000E-01 +6.534000E-03 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.000000E-02 +8.060000E-04 +0.000000E+00 +0.000000E+00 +1.290000E-01 +3.487000E-03 +0.000000E+00 +0.000000E+00 +1.790000E-01 +6.747000E-03 0.000000E+00 0.000000E+00 0.000000E+00 diff --git a/tests/regression_tests/score_current/test.py b/tests/regression_tests/score_current/test.py index 1309584d2c..a338a66266 100644 --- a/tests/regression_tests/score_current/test.py +++ b/tests/regression_tests/score_current/test.py @@ -16,12 +16,12 @@ def model(): zr.add_nuclide('Zr90', 1.0) model.materials.extend([fuel, zr]) - box1 = openmc.model.rectangular_prism(10.0, 10.0) - box2 = openmc.model.rectangular_prism(20.0, 20.0, boundary_type='reflective') + box1 = openmc.model.RectangularPrism(10.0, 10.0) + box2 = openmc.model.RectangularPrism(20.0, 20.0, boundary_type='reflective') top = openmc.ZPlane(z0=10.0, boundary_type='vacuum') bottom = openmc.ZPlane(z0=-10.0, boundary_type='vacuum') - cell1 = openmc.Cell(fill=fuel, region=box1 & +bottom & -top) - cell2 = openmc.Cell(fill=zr, region=~box1 & box2 & +bottom & -top) + cell1 = openmc.Cell(fill=fuel, region=-box1 & +bottom & -top) + cell2 = openmc.Cell(fill=zr, region=+box1 & -box2 & +bottom & -top) model.geometry = openmc.Geometry([cell1, cell2]) model.settings.batches = 5 diff --git a/tests/regression_tests/seed/results_true.dat b/tests/regression_tests/seed/results_true.dat index 1f70e15137..ff34071c10 100644 --- a/tests/regression_tests/seed/results_true.dat +++ b/tests/regression_tests/seed/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.961599E-01 2.921488E-03 +3.015003E-01 5.094212E-03 diff --git a/tests/regression_tests/source/inputs_true.dat b/tests/regression_tests/source/inputs_true.dat index 131d20ac12..9f10b79d6b 100644 --- a/tests/regression_tests/source/inputs_true.dat +++ b/tests/regression_tests/source/inputs_true.dat @@ -1,150 +1,150 @@ - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - - - -4.0 -1.0 3.0 0.2 0.3 0.5 - - - -2.0 0.0 2.0 0.2 0.3 0.2 - - - - - -1.0 0.0 1.0 0.5 0.25 0.25 - - - - - - - - -4.0 -4.0 -4.0 4.0 4.0 4.0 - - - - - - - 1.2 -2.3 0.781 - - - - 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 - - - - - - - 0.7853981633974483 1.5707963267948966 2.356194490192345 0.3 0.4 0.3 - - - - - - 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 - - - - - - - - -2.0 0.0 2.0 0.2 0.3 0.2 - - - - - 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 - - - - - - - - -2.0 0.0 2.0 0.2 0.3 0.2 - - - - - - - - - - - - - 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 - - - - - - - - - 0.7853981633974483 1.5707963267948966 2.356194490192345 0.3 0.4 0.3 - - - - - - - - - - - - - - 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 - - - - + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + + + -4.0 -1.0 3.0 0.2 0.3 0.5 + + + -2.0 0.0 2.0 0.2 0.3 0.2 + + + + + -1.0 0.0 1.0 0.5 0.25 0.25 + + + + + + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + + + + + 1.2 -2.3 0.781 + + + + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 + + + + + + + 0.7071067811865476 0.0 -0.7071067811865475 0.3 0.4 0.3 + + + + + + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 + + + + + + + + -2.0 0.0 2.0 0.2 0.3 0.2 + + + + + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 + + + + + + + + -2.0 0.0 2.0 0.2 0.3 0.2 + + + + + + + + + + + + + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 + + + + + + + + + 0.7071067811865476 0.0 -0.7071067811865475 0.3 0.4 0.3 + + + + + + + + + + + + + + 1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23 + + + + + diff --git a/tests/regression_tests/source/results_true.dat b/tests/regression_tests/source/results_true.dat index 62eaf6eff9..951075bbb9 100644 --- a/tests/regression_tests/source/results_true.dat +++ b/tests/regression_tests/source/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.865754E-01 6.762423E-03 +3.034717E-01 2.799386E-03 diff --git a/tests/regression_tests/source/test.py b/tests/regression_tests/source/test.py index 31debde474..96efa77eb7 100644 --- a/tests/regression_tests/source/test.py +++ b/tests/regression_tests/source/test.py @@ -1,4 +1,4 @@ -from math import pi +from math import pi, cos import numpy as np import openmc @@ -7,12 +7,12 @@ from tests.testing_harness import PyAPITestHarness class SourceTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) mat1 = openmc.Material(material_id=1, temperature=294) mat1.set_density('g/cm3', 4.5) - mat1.add_nuclide(openmc.Nuclide('U235'), 1.0) - materials = openmc.Materials([mat1]) - materials.export_to_xml() + mat1.add_nuclide('U235', 1.0) + self._model.materials = openmc.Materials([mat1]) sphere = openmc.Sphere(surface_id=1, r=10.0, boundary_type='vacuum') inside_sphere = openmc.Cell(cell_id=1) @@ -21,9 +21,7 @@ class SourceTestHarness(PyAPITestHarness): root = openmc.Universe(universe_id=0) root.add_cell(inside_sphere) - geometry = openmc.Geometry() - geometry.root_universe = root - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root) # Create an array of different sources x_dist = openmc.stats.Uniform(-3., 3.) @@ -32,22 +30,22 @@ class SourceTestHarness(PyAPITestHarness): r_dist = openmc.stats.Uniform(2., 3.) r_dist1 = openmc.stats.PowerLaw(2., 3., 1.) r_dist2 = openmc.stats.PowerLaw(2., 3., 2.) - theta_dist = openmc.stats.Discrete([pi/4, pi/2, 3*pi/4], - [0.3, 0.4, 0.3]) + cos_theta_dist = openmc.stats.Discrete([cos(pi/4), 0.0, cos(3*pi/4)], + [0.3, 0.4, 0.3]) phi_dist = openmc.stats.Uniform(0.0, 2*pi) spatial1 = openmc.stats.CartesianIndependent(x_dist, y_dist, z_dist) spatial2 = openmc.stats.Box([-4., -4., -4.], [4., 4., 4.]) spatial3 = openmc.stats.Point([1.2, -2.3, 0.781]) - spatial4 = openmc.stats.SphericalIndependent(r_dist, theta_dist, - phi_dist, + spatial4 = openmc.stats.SphericalIndependent(r_dist, cos_theta_dist, + phi_dist, origin=(1., 1., 0.)) - spatial5 = openmc.stats.CylindricalIndependent(r_dist, phi_dist, + spatial5 = openmc.stats.CylindricalIndependent(r_dist, phi_dist, z_dist, origin=(1., 1., 0.)) - spatial6 = openmc.stats.SphericalIndependent(r_dist2, theta_dist, - phi_dist, + spatial6 = openmc.stats.SphericalIndependent(r_dist2, cos_theta_dist, + phi_dist, origin=(1., 1., 0.)) - spatial7 = openmc.stats.CylindricalIndependent(r_dist1, phi_dist, + spatial7 = openmc.stats.CylindricalIndependent(r_dist1, phi_dist, z_dist, origin=(1., 1., 0.)) @@ -57,7 +55,10 @@ class SourceTestHarness(PyAPITestHarness): angle2 = openmc.stats.Monodirectional(reference_uvw=[0., 1., 0.]) angle3 = openmc.stats.Isotropic() - E = np.logspace(0, 7) + # Note that the definition for E is equivalent to logspace(0, 7) but we + # manually take powers because of last-digit differences that may cause + # test failures with different versions of numpy + E = np.array([10**x for x in np.linspace(0, 7)]) p = np.sin(np.linspace(0., pi)) p /= sum(np.diff(E)*p[:-1]) energy1 = openmc.stats.Maxwell(1.2895e6) @@ -67,23 +68,23 @@ class SourceTestHarness(PyAPITestHarness): time1 = openmc.stats.Uniform(2, 5) - source1 = openmc.Source(spatial1, angle1, energy1, strength=0.3) - source2 = openmc.Source(spatial2, angle2, energy2, strength=0.1) - source3 = openmc.Source(spatial3, angle3, energy3, strength=0.1) - source4 = openmc.Source(spatial4, angle3, energy3, strength=0.1) - source5 = openmc.Source(spatial5, angle3, energy3, strength=0.1) - source6 = openmc.Source(spatial5, angle3, energy4, strength=0.1) - source7 = openmc.Source(spatial6, angle3, energy4, time1, strength=0.1) - source8 = openmc.Source(spatial7, angle3, energy4, time1, strength=0.1) + source1 = openmc.IndependentSource(spatial1, angle1, energy1, strength=0.3) + source2 = openmc.IndependentSource(spatial2, angle2, energy2, strength=0.1) + source3 = openmc.IndependentSource(spatial3, angle3, energy3, strength=0.1) + source4 = openmc.IndependentSource(spatial4, angle3, energy3, strength=0.1) + source5 = openmc.IndependentSource(spatial5, angle3, energy3, strength=0.1) + source6 = openmc.IndependentSource(spatial5, angle3, energy4, strength=0.1) + source7 = openmc.IndependentSource(spatial6, angle3, energy4, time1, strength=0.1) + source8 = openmc.IndependentSource(spatial7, angle3, energy4, time1, strength=0.1) settings = openmc.Settings() settings.batches = 10 settings.inactive = 5 settings.particles = 1000 settings.source = [source1, source2, source3, source4, source5, source6, source7, source8] - settings.export_to_xml() + self._model.settings = settings def test_source(): - harness = SourceTestHarness('statepoint.10.h5') + harness = SourceTestHarness('statepoint.10.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/source_dlopen/inputs_true.dat b/tests/regression_tests/source_dlopen/inputs_true.dat index 23878ac207..9b4601d9f3 100644 --- a/tests/regression_tests/source_dlopen/inputs_true.dat +++ b/tests/regression_tests/source_dlopen/inputs_true.dat @@ -1,23 +1,35 @@ - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - 0 - - + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + 0 + + + + + 1 + + + 0.0 2000.0 1000000.0 + + + 1 2 + flux + + + diff --git a/tests/regression_tests/source_dlopen/results_true.dat b/tests/regression_tests/source_dlopen/results_true.dat index e69de29bb2..1cddaf802c 100644 --- a/tests/regression_tests/source_dlopen/results_true.dat +++ b/tests/regression_tests/source_dlopen/results_true.dat @@ -0,0 +1,5 @@ +tally 1: +1.445856E+04 +2.090732E+07 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/source_dlopen/source_sampling.cpp b/tests/regression_tests/source_dlopen/source_sampling.cpp index 2c6de6d85e..fc61ef1fdd 100644 --- a/tests/regression_tests/source_dlopen/source_sampling.cpp +++ b/tests/regression_tests/source_dlopen/source_sampling.cpp @@ -5,8 +5,7 @@ #include "openmc/random_lcg.h" #include "openmc/source.h" -class CustomSource : public openmc::Source -{ +class CustomSource : public openmc::Source { openmc::SourceSite sample(uint64_t* seed) const { openmc::SourceSite particle; @@ -20,16 +19,17 @@ class CustomSource : public openmc::Source particle.r.z = 0.; // angle particle.u = {1.0, 0.0, 0.0}; - particle.E = 14.08e6; + particle.E = 1.00e3; particle.delayed_group = 0; return particle; } }; -// A function to create a unique pointer to an instance of this class when generated -// via a plugin call using dlopen/dlsym. -// You must have external C linkage here otherwise dlopen will not find the file -extern "C" std::unique_ptr openmc_create_source(std::string parameters) +// A function to create a unique pointer to an instance of this class when +// generated via a plugin call using dlopen/dlsym. You must have external C +// linkage here otherwise dlopen will not find the file +extern "C" std::unique_ptr openmc_create_source( + std::string parameters) { return std::make_unique(); } diff --git a/tests/regression_tests/source_dlopen/test.py b/tests/regression_tests/source_dlopen/test.py index 41690224df..0581d6deec 100644 --- a/tests/regression_tests/source_dlopen/test.py +++ b/tests/regression_tests/source_dlopen/test.py @@ -18,7 +18,7 @@ def compile_source(request): openmc_dir = Path(str(request.config.rootdir)) / 'build' with open('CMakeLists.txt', 'w') as f: f.write(textwrap.dedent(""" - cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_sources CXX) add_library(source SHARED source_sampling.cpp) find_package(OpenMC REQUIRED HINTS {}) @@ -61,9 +61,18 @@ def model(): model.settings.particles = 1000 model.settings.run_mode = 'fixed source' + tally = openmc.Tally() + mat_filter = openmc.MaterialFilter([natural_lead]) + # energy filter with two bins 0 eV - 1 keV and 1 keV - 1 MeV the second bin + # of the energy filter (last two entries in the tally results) should be + # zero + energy_filter = openmc.EnergyFilter([0.0, 2e3, 1e6]) + tally.filters = [mat_filter, energy_filter] + tally.scores = ['flux'] + model.tallies = openmc.Tallies([tally]) + # custom source from shared library - source = openmc.Source() - source.library = 'build/libsource.so' + source = openmc.CompiledSource('build/libsource.so') model.settings.source = source return model diff --git a/tests/regression_tests/source_file/results_true.dat b/tests/regression_tests/source_file/results_true.dat index 0d8600fedc..359e0526e6 100644 --- a/tests/regression_tests/source_file/results_true.dat +++ b/tests/regression_tests/source_file/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.009416E-01 3.229998E-03 +2.827397E-01 1.150437E-03 diff --git a/tests/regression_tests/source_mcpl_file/__init__.py b/tests/regression_tests/source_mcpl_file/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/statepoint_batch/geometry.xml b/tests/regression_tests/source_mcpl_file/geometry.xml similarity index 100% rename from tests/regression_tests/statepoint_batch/geometry.xml rename to tests/regression_tests/source_mcpl_file/geometry.xml diff --git a/tests/regression_tests/infinite_cell/materials.xml b/tests/regression_tests/source_mcpl_file/materials.xml similarity index 58% rename from tests/regression_tests/infinite_cell/materials.xml rename to tests/regression_tests/source_mcpl_file/materials.xml index 6acd8df74b..2472a74717 100644 --- a/tests/regression_tests/infinite_cell/materials.xml +++ b/tests/regression_tests/source_mcpl_file/materials.xml @@ -6,9 +6,4 @@ - - - - - diff --git a/tests/regression_tests/source_mcpl_file/results_true.dat b/tests/regression_tests/source_mcpl_file/results_true.dat new file mode 100644 index 0000000000..3ba1a45200 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +2.827397E-01 1.150438E-03 diff --git a/tests/regression_tests/infinite_cell/settings.xml b/tests/regression_tests/source_mcpl_file/settings.xml similarity index 77% rename from tests/regression_tests/infinite_cell/settings.xml rename to tests/regression_tests/source_mcpl_file/settings.xml index 70b4e802f8..bb08e6fd77 100644 --- a/tests/regression_tests/infinite_cell/settings.xml +++ b/tests/regression_tests/source_mcpl_file/settings.xml @@ -1,15 +1,14 @@ - eigenvalue + + 10 5 1000 - -4 -4 -4 4 4 4 - diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py new file mode 100644 index 0000000000..6e668ef191 --- /dev/null +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +import pytest +import glob +import os +import shutil +from tests.testing_harness import * + +pytestmark = pytest.mark.skipif( + shutil.which("mcpl-config") is None, + reason="mcpl-config command not found in PATH; MCPL is likely not available." +) + +settings1=""" + + eigenvalue + + + 10 + 5 + 1000 + + + -4 -4 -4 4 4 4 + + + +""" + +settings2 = """ + + eigenvalue + 10 + 5 + 1000 + + source.10.{} + + +""" + + +class SourceFileTestHarness(TestHarness): + def execute_test(self): + """Run OpenMC with the appropriate arguments and check the outputs.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + def update_results(self): + """Update the results_true using the current version of OpenMC.""" + try: + self._run_openmc() + self._test_output_created() + self._run_openmc_restart() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + + def _test_output_created(self): + """Make sure statepoint and source files have been created.""" + statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name)) + assert len(statepoint) == 1, 'Either multiple or no statepoint files ' \ + 'exist.' + assert statepoint[0].endswith('h5'), \ + 'Statepoint file is not a HDF5 file.' + + source = glob.glob(os.path.join(os.getcwd(), 'source.10.mcpl*')) + assert len(source) == 1, 'Either multiple or no source files exist.' + assert source[0].endswith('mcpl') or source[0].endswith('mcpl.gz'), \ + 'Source file is not a MCPL file.' + + def _run_openmc_restart(self): + # Get the name of the source file. + source = glob.glob(os.path.join(os.getcwd(), 'source.10.*')) + + # Write the new settings.xml file. + with open('settings.xml','w') as fh: + fh.write(settings2.format(source[0].split('.')[-1])) + + # Run OpenMC. + self._run_openmc() + + def _cleanup(self): + TestHarness._cleanup(self) + output = glob.glob(os.path.join(os.getcwd(), 'source.*')) + with open('settings.xml','w') as fh: + fh.write(settings1) + + +def test_source_file(): + harness = SourceFileTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/source_parameterized_dlopen/inputs_true.dat b/tests/regression_tests/source_parameterized_dlopen/inputs_true.dat index f4a0eba737..088d65ada3 100644 --- a/tests/regression_tests/source_parameterized_dlopen/inputs_true.dat +++ b/tests/regression_tests/source_parameterized_dlopen/inputs_true.dat @@ -1,23 +1,35 @@ - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - 0 - - + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + 0 + + + + + 1 + + + 0.0 2000.0 1000000.0 + + + 1 2 + flux + + + diff --git a/tests/regression_tests/source_parameterized_dlopen/results_true.dat b/tests/regression_tests/source_parameterized_dlopen/results_true.dat index e69de29bb2..1cddaf802c 100644 --- a/tests/regression_tests/source_parameterized_dlopen/results_true.dat +++ b/tests/regression_tests/source_parameterized_dlopen/results_true.dat @@ -0,0 +1,5 @@ +tally 1: +1.445856E+04 +2.090732E+07 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/source_parameterized_dlopen/test.py b/tests/regression_tests/source_parameterized_dlopen/test.py index c613cde4b3..151fb37356 100644 --- a/tests/regression_tests/source_parameterized_dlopen/test.py +++ b/tests/regression_tests/source_parameterized_dlopen/test.py @@ -18,7 +18,7 @@ def compile_source(request): openmc_dir = Path(str(request.config.rootdir)) / 'build' with open('CMakeLists.txt', 'w') as f: f.write(textwrap.dedent(""" - cmake_minimum_required(VERSION 3.3 FATAL_ERROR) + cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(openmc_sources CXX) add_library(source SHARED parameterized_source_sampling.cpp) find_package(OpenMC REQUIRED HINTS {}) @@ -61,9 +61,17 @@ def model(): model.settings.particles = 1000 model.settings.run_mode = 'fixed source' + tally = openmc.Tally() + mat_filter = openmc.MaterialFilter([natural_lead]) + # energy filter with two bins 0 eV - 1 keV and 1 keV - 1 MeV + # the second bin shouldn't have any results + energy_filter = openmc.EnergyFilter([0.0, 2e3, 1e6]) + tally.filters = [mat_filter, energy_filter] + tally.scores = ['flux'] + model.tallies = openmc.Tallies([tally]) + # custom source from shared library - source = openmc.Source() - source.library = 'build/libsource.so' + source = openmc.CompiledSource('build/libsource.so') source.parameters = '1e3' model.settings.source = source diff --git a/tests/regression_tests/sourcepoint_batch/results_true.dat b/tests/regression_tests/sourcepoint_batch/results_true.dat index 4cd4e623d7..3665bdd08d 100644 --- a/tests/regression_tests/sourcepoint_batch/results_true.dat +++ b/tests/regression_tests/sourcepoint_batch/results_true.dat @@ -1,3 +1,3 @@ k-combined: -3.003690E-01 3.923324E-03 --5.424825E+00 -4.990262E+00 -6.395029E+00 +2.920435E-01 9.109227E-04 +1.101997E+00 -8.197502E+00 4.294606E+00 diff --git a/tests/regression_tests/sourcepoint_latest/results_true.dat b/tests/regression_tests/sourcepoint_latest/results_true.dat index fe46748c80..97b997ae6b 100644 --- a/tests/regression_tests/sourcepoint_latest/results_true.dat +++ b/tests/regression_tests/sourcepoint_latest/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 diff --git a/tests/regression_tests/sourcepoint_restart/results_true.dat b/tests/regression_tests/sourcepoint_restart/results_true.dat index 965dc45b17..c20b5f2a0a 100644 --- a/tests/regression_tests/sourcepoint_restart/results_true.dat +++ b/tests/regression_tests/sourcepoint_restart/results_true.dat @@ -1,14 +1,78 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 tally 1: -1.400000E-02 -5.000000E-05 -5.637968E-03 -7.140222E-06 +1.300000E-02 +3.900000E-05 +5.833114E-03 +7.476880E-06 0.000000E+00 0.000000E+00 -8.891645E-04 -4.440759E-07 +1.164000E-03 +5.072152E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.110000E-04 +1.108363E-02 +2.889480E-05 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 +2.000000E-03 +2.000000E-06 +2.043490E-03 +1.793389E-06 +2.000000E-03 +2.000000E-06 +2.930717E-04 +8.589100E-08 +2.000000E-02 +9.000000E-05 +9.334862E-03 +1.853104E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.500000E-02 +1.430000E-04 +1.110224E-02 +2.872840E-05 +0.000000E+00 +0.000000E+00 +5.833203E-04 +1.701353E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +1.100000E-05 +1.752967E-03 +1.027501E-06 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -18,41 +82,61 @@ tally 1: 0.000000E+00 0.000000E+00 1.800000E-02 -7.200000E-05 -8.618270E-03 -1.620709E-05 +7.600000E-05 +7.300761E-03 +1.254112E-05 0.000000E+00 0.000000E+00 -2.884266E-04 -8.318989E-08 +5.841969E-04 +1.706438E-07 0.000000E+00 0.000000E+00 -8.903211E-04 -7.926717E-07 +5.861433E-04 +3.435640E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.300000E-02 -2.290000E-04 -1.685675E-02 -6.105228E-05 +3.000000E-02 +1.820000E-04 +1.518122E-02 +4.757233E-05 0.000000E+00 0.000000E+00 -6.007379E-04 -3.608860E-07 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 +2.927374E-04 +8.569516E-08 +2.000000E-03 +2.000000E-06 +2.631167E-03 +3.503358E-06 1.000000E-03 1.000000E-06 -2.884266E-04 -8.318989E-08 -1.500000E-02 -6.300000E-05 -7.665313E-03 -1.617331E-05 +2.920435E-04 +8.528942E-08 +2.500000E-02 +1.390000E-04 +9.343205E-03 +1.910189E-05 +0.000000E+00 +0.000000E+00 +2.039724E-03 +1.271217E-06 +1.000000E-03 +1.000000E-06 +5.861433E-04 +3.435640E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.400000E-02 +1.340000E-04 +1.080336E-02 +2.533338E-05 +0.000000E+00 +0.000000E+00 +2.914595E-04 +8.494867E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -61,66 +145,62 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +1.400000E-02 +5.200000E-05 +6.711472E-03 +1.216306E-05 +0.000000E+00 +0.000000E+00 +5.847809E-04 +1.709846E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.100000E-02 -6.900000E-05 -5.663296E-03 -1.118218E-05 0.000000E+00 0.000000E+00 -1.198138E-03 -8.972573E-07 0.000000E+00 0.000000E+00 -5.989880E-04 -3.587866E-07 +1.700000E-02 +7.900000E-05 +6.413106E-03 +1.186604E-05 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.900000E-02 +1.770000E-04 +1.314196E-02 +3.678739E-05 +0.000000E+00 +0.000000E+00 +1.167073E-03 +5.110971E-07 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 1.000000E-03 1.000000E-06 0.000000E+00 0.000000E+00 -1.300000E-02 -6.300000E-05 -5.687181E-03 -1.121031E-05 +2.900000E-02 +1.830000E-04 +1.372758E-02 +4.360498E-05 0.000000E+00 0.000000E+00 -5.963216E-04 -1.778036E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -6.100000E-05 -1.040584E-02 -2.402892E-05 -0.000000E+00 -0.000000E+00 -5.879206E-04 -1.728865E-07 -0.000000E+00 -0.000000E+00 -8.903211E-04 -7.926717E-07 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -4.200000E-02 -3.880000E-04 -1.781042E-02 -7.329257E-05 -0.000000E+00 -0.000000E+00 -5.990419E-04 -1.794256E-07 +5.858090E-04 +1.715862E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -130,105 +210,41 @@ tally 1: 0.000000E+00 0.000000E+00 2.200000E-02 -1.220000E-04 -9.854350E-03 -2.671897E-05 +1.000000E-04 +9.631311E-03 +2.001285E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 +5.829191E-04 +3.397947E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +1.800000E-05 +4.094439E-03 +4.622331E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.200000E-02 -4.400000E-05 -5.595730E-03 -9.201460E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.000000E-03 -1.000000E-05 -2.391548E-03 -1.608867E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 +1.170949E-03 +1.371123E-06 1.000000E-03 1.000000E-06 0.000000E+00 0.000000E+00 -2.600000E-02 -1.920000E-04 -1.211360E-02 -3.507456E-05 -0.000000E+00 -0.000000E+00 -8.763472E-04 -4.224562E-07 -0.000000E+00 -0.000000E+00 -1.799697E-03 -1.260606E-06 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -3.500000E-02 -2.750000E-04 -1.685749E-02 -6.486072E-05 -0.000000E+00 -0.000000E+00 -9.011069E-04 -8.119936E-07 -0.000000E+00 -0.000000E+00 -1.494965E-03 -1.523738E-06 -3.000000E-03 -5.000000E-06 -2.995479E-04 -8.972896E-08 -2.600000E-02 -1.600000E-04 -1.275573E-02 -3.540061E-05 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 -2.000000E-03 -2.000000E-06 -1.794352E-03 -1.254260E-06 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.800000E-05 -4.799173E-03 -8.824671E-06 +9.000000E-03 +5.100000E-05 +4.664405E-03 +1.087264E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -241,158 +257,62 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +2.200000E-02 +1.300000E-04 +9.644227E-03 +2.300481E-05 +0.000000E+00 +0.000000E+00 +8.771587E-04 +4.270487E-07 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.500000E-02 +1.630000E-04 +1.257283E-02 +3.946813E-05 +0.000000E+00 +0.000000E+00 +1.171259E-03 +8.583084E-07 +0.000000E+00 +0.000000E+00 +1.462299E-03 +1.112414E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.400000E-02 +1.400000E-04 +1.138156E-02 +3.565057E-05 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 9.000000E-03 -3.100000E-05 -4.430081E-03 -6.185529E-06 +2.100000E-05 +4.383252E-03 +6.073597E-06 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -3.291067E-03 -4.033124E-06 -1.000000E-03 -1.000000E-06 -5.971427E-04 -1.782961E-07 -2.800000E-02 -1.660000E-04 -1.217803E-02 -3.047875E-05 -0.000000E+00 -0.000000E+00 -9.011069E-04 -8.119936E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.300000E-02 -2.930000E-04 -1.600563E-02 -6.991498E-05 -0.000000E+00 -0.000000E+00 -5.962677E-04 -1.777713E-07 -0.000000E+00 -0.000000E+00 -8.958696E-04 -4.469905E-07 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -1.800000E-02 -8.200000E-05 -8.048487E-03 -1.779097E-05 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.000000E-03 -5.000000E-06 -2.664923E-03 -2.009218E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -1.600000E-02 -7.000000E-05 -7.128932E-03 -1.345969E-05 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.200000E-02 -2.360000E-04 -1.452343E-02 -4.653003E-05 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.000000E-02 -2.160000E-04 -1.327819E-02 -4.230384E-05 -0.000000E+00 -0.000000E+00 -8.958157E-04 -2.675003E-07 -0.000000E+00 -0.000000E+00 -5.768531E-04 -3.327596E-07 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -2.100000E-02 -1.150000E-04 -1.005499E-02 -2.671941E-05 -0.000000E+00 -0.000000E+00 -1.192535E-03 -7.110852E-07 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -1.200000E-02 -4.600000E-05 -6.223479E-03 -9.948264E-06 -0.000000E+00 -0.000000E+00 -2.884266E-04 -8.318989E-08 +8.707460E-04 +7.581986E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -403,236 +323,220 @@ tally 1: 0.000000E+00 1.100000E-02 3.500000E-05 -5.341464E-03 -7.422119E-06 +6.410606E-03 +1.066242E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.700000E-02 -8.500000E-05 -7.089406E-03 -1.347399E-05 -0.000000E+00 -0.000000E+00 -5.768531E-04 -3.327596E-07 -0.000000E+00 -0.000000E+00 -8.764011E-04 -4.224885E-07 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -2.300000E-02 -1.430000E-04 -1.185149E-02 -3.938904E-05 -0.000000E+00 -0.000000E+00 -2.080112E-03 -1.332746E-06 -0.000000E+00 -0.000000E+00 -1.198138E-03 -8.972573E-07 -1.000000E-03 -1.000000E-06 -5.989880E-04 -3.587866E-07 -3.000000E-02 -2.020000E-04 -1.190088E-02 -2.985574E-05 -0.000000E+00 -0.000000E+00 -5.971427E-04 -1.782961E-07 -0.000000E+00 -0.000000E+00 -2.651470E-03 -2.516819E-06 -4.000000E-03 -6.000000E-06 -0.000000E+00 -0.000000E+00 -1.200000E-02 -3.800000E-05 -5.332038E-03 -7.371181E-06 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -1.000000E-03 -1.000000E-06 -2.995479E-04 -8.972896E-08 -3.000000E-03 -3.000000E-06 -2.087327E-03 -1.859777E-06 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.100000E-02 -2.510000E-04 -1.448290E-02 -4.809715E-05 -0.000000E+00 -0.000000E+00 -1.162053E-03 -8.367836E-07 -0.000000E+00 -0.000000E+00 -1.475335E-03 -1.140030E-06 -1.000000E-03 -1.000000E-06 -2.884266E-04 -8.318989E-08 -3.200000E-02 -2.220000E-04 -1.427003E-02 -4.164437E-05 -0.000000E+00 -0.000000E+00 -8.855692E-04 -2.614860E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -2.400000E-05 -3.864325E-03 -4.535592E-06 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -2.065114E-03 -1.468511E-06 -3.000000E-03 -3.000000E-06 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 -1.170401E-03 -6.850581E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.000000E-05 -4.121850E-03 -5.320749E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.100000E-02 -2.170000E-04 -1.483273E-02 -4.914676E-05 -0.000000E+00 -0.000000E+00 -8.763472E-04 -4.224562E-07 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.300000E-02 -2.310000E-04 -1.399272E-02 -4.084044E-05 -0.000000E+00 -0.000000E+00 -1.499382E-03 -1.168447E-06 -1.000000E-03 -1.000000E-06 -8.930414E-04 -4.419952E-07 +8.756564E-04 +4.254898E-07 1.000000E-03 1.000000E-06 0.000000E+00 0.000000E+00 1.600000E-02 -7.000000E-05 -7.077099E-03 -1.371332E-05 +6.000000E-05 +7.287540E-03 +1.279808E-05 0.000000E+00 0.000000E+00 -2.994940E-04 -8.969666E-08 +5.822922E-04 +1.695337E-07 0.000000E+00 0.000000E+00 -1.497740E-03 -2.243224E-06 +1.172287E-03 +1.374256E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +3.400000E-02 +2.820000E-04 +1.603656E-02 +6.229803E-05 +0.000000E+00 +0.000000E+00 +1.460479E-03 +7.689660E-07 +1.000000E-03 +1.000000E-06 +1.165972E-03 +6.797578E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-02 +8.500000E-05 +7.880322E-03 +1.387762E-05 +0.000000E+00 +0.000000E+00 +8.755466E-04 +4.261064E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.400000E-05 +4.666802E-03 +4.926489E-06 +0.000000E+00 +0.000000E+00 +5.829191E-04 +3.397947E-07 +0.000000E+00 +0.000000E+00 +8.759908E-04 +4.256857E-07 +2.000000E-03 +2.000000E-06 +0.000000E+00 +0.000000E+00 +1.500000E-02 +5.900000E-05 +8.180347E-03 +1.622409E-05 +0.000000E+00 +0.000000E+00 +2.902487E-04 +8.424429E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.300000E-02 +1.390000E-04 +7.593262E-03 +1.708292E-05 +0.000000E+00 +0.000000E+00 +1.750169E-03 +1.193182E-06 +1.000000E-03 +1.000000E-06 +1.461465E-03 +7.684663E-07 2.000000E-03 4.000000E-06 +2.914595E-04 +8.494867E-08 +2.300000E-02 +1.350000E-04 +9.639670E-03 +2.161898E-05 +0.000000E+00 +0.000000E+00 +8.781869E-04 +4.288534E-07 +0.000000E+00 +0.000000E+00 +8.707460E-04 +7.581986E-07 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.090000E-04 +9.045520E-03 +2.171457E-05 +0.000000E+00 +0.000000E+00 +5.851152E-04 +1.711804E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +7.000000E-05 +1.167023E-02 +2.840320E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +5.854747E-04 +3.427806E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.000000E-03 +5.000000E-06 +1.171618E-03 +6.863446E-07 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.300000E-02 +4.500000E-05 +6.428153E-03 +9.064469E-06 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +9.000000E-05 +8.185938E-03 +1.967120E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +2.300000E-05 +4.665341E-03 +5.426897E-06 0.000000E+00 0.000000E+00 -1.000000E-02 -3.400000E-05 -3.528506E-03 -4.251243E-06 0.000000E+00 0.000000E+00 -5.879206E-04 -1.728865E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -642,13 +546,285 @@ tally 1: 0.000000E+00 0.000000E+00 4.000000E-03 -6.000000E-06 -9.002859E-04 -4.506150E-07 +4.000000E-06 +3.497088E-03 +3.563633E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +9.000000E-06 +2.333045E-03 +1.870613E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.172287E-03 +1.374256E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.200000E-05 +6.413648E-03 +1.136518E-05 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.000000E-02 +1.200000E-04 +7.007604E-03 +1.379504E-05 +0.000000E+00 +0.000000E+00 +5.851152E-04 +1.711804E-07 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +1.700000E-02 +7.900000E-05 +7.281401E-03 +1.600845E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.000000E-03 +1.900000E-05 +3.218959E-03 +4.543526E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +1.900000E-05 +4.380921E-03 +4.182826E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.050000E-04 +9.932207E-03 +2.153598E-05 +0.000000E+00 +0.000000E+00 +5.854747E-04 +3.427806E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +7.400000E-05 +7.301011E-03 +1.358176E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.804973E-04 +3.369772E-07 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.300000E-02 +1.610000E-04 +9.647933E-03 +2.657983E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +1.900000E-05 +2.915422E-03 +2.203274E-06 +0.000000E+00 +0.000000E+00 +8.775182E-04 +4.280701E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +9.000000E-06 +2.044244E-03 +1.796182E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +9.000000E-05 +6.718632E-03 +1.203946E-05 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +2.700000E-05 +4.675703E-03 +5.128811E-06 +0.000000E+00 +0.000000E+00 +2.914595E-04 +8.494867E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +2.914595E-04 +8.494867E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.600000E-05 +4.670349E-03 +6.133014E-06 +0.000000E+00 +0.000000E+00 0.000000E+00 0.000000E+00 -8.939164E-04 -4.425201E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -658,45 +834,77 @@ tally 1: 0.000000E+00 0.000000E+00 1.500000E-02 -5.500000E-05 -7.400046E-03 -1.140009E-05 -0.000000E+00 -0.000000E+00 -5.990419E-04 -1.794256E-07 -0.000000E+00 -0.000000E+00 -8.903211E-04 -7.926717E-07 +6.100000E-05 +8.751735E-03 +1.922421E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.800000E-02 -7.400000E-05 -7.695028E-03 -1.396744E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.000000E-03 -1.000000E-06 -5.990959E-04 -3.589159E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.400000E-02 -1.260000E-04 -1.220791E-02 -3.565074E-05 +1.400000E-02 +5.000000E-05 +6.126605E-03 +9.620966E-06 0.000000E+00 0.000000E+00 -2.995479E-04 -8.972896E-08 +5.835031E-04 +1.702381E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-03 +2.000000E-06 +5.847809E-04 +1.709846E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-03 +2.000000E-06 +8.719569E-04 +4.219258E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +2.700000E-05 +4.085939E-03 +4.936434E-06 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -707,206 +915,14 @@ tally 1: 0.000000E+00 9.000000E-03 2.500000E-05 -3.572884E-03 -4.101061E-06 +5.846700E-03 +9.739696E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -5.935474E-04 -3.522985E-07 -1.000000E-03 -1.000000E-06 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.800000E-05 -5.382607E-03 -1.128619E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -4.600000E-05 -6.554978E-03 -9.995681E-06 -0.000000E+00 -0.000000E+00 -2.884266E-04 -8.318989E-08 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.000000E-03 -1.100000E-05 -3.868525E-03 -3.829197E-06 -0.000000E+00 -0.000000E+00 -5.935474E-04 -3.522985E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -5.989880E-04 -3.587866E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -3.100000E-05 -4.132972E-03 -4.495389E-06 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -1.000000E-03 -1.000000E-06 -2.967737E-04 -8.807464E-08 -2.000000E-02 -9.200000E-05 -8.597951E-03 -1.801722E-05 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -3.200000E-05 -6.509749E-03 -9.403130E-06 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -5.990959E-04 -3.589159E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -2.884266E-04 -8.318989E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.000000E-03 -2.100000E-05 -3.890163E-03 -6.545535E-06 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -0.000000E+00 -0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -914,33 +930,17 @@ tally 1: 0.000000E+00 0.000000E+00 1.300000E-02 -4.300000E-05 -6.485840E-03 -1.112994E-05 +3.900000E-05 +6.128455E-03 +1.013669E-05 0.000000E+00 0.000000E+00 -5.989880E-04 -3.587866E-07 +5.851152E-04 +1.711804E-07 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -4.900000E-05 -7.442524E-03 -1.164759E-05 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -1.000000E-03 -1.000000E-06 -5.768531E-04 -3.327596E-07 +1.172287E-03 +1.374256E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -962,11 +962,11 @@ tally 1: 0.000000E+00 0.000000E+00 tally 2: -5.649702E-01 -6.385502E-02 -6.158199E-01 -7.586795E-02 -3.579551E+00 -2.563396E+00 -3.982345E+01 -3.172536E+02 +5.554367E-01 +6.170503E-02 +6.055520E-01 +7.334109E-02 +3.526894E+00 +2.488065E+00 +3.925114E+01 +3.081807E+02 diff --git a/tests/regression_tests/statepoint_batch/results_true.dat b/tests/regression_tests/statepoint_batch/results_true.dat deleted file mode 100644 index cde417d0e6..0000000000 --- a/tests/regression_tests/statepoint_batch/results_true.dat +++ /dev/null @@ -1,2 +0,0 @@ -k-combined: -2.985923E-01 2.864193E-03 diff --git a/tests/regression_tests/statepoint_batch/test.py b/tests/regression_tests/statepoint_batch/test.py deleted file mode 100644 index 323b28fc65..0000000000 --- a/tests/regression_tests/statepoint_batch/test.py +++ /dev/null @@ -1,18 +0,0 @@ -from tests.testing_harness import TestHarness - - -class StatepointTestHarness(TestHarness): - def __init__(self): - super().__init__(None) - - def _test_output_created(self): - """Make sure statepoint files have been created.""" - sps = ('statepoint.03.h5', 'statepoint.06.h5', 'statepoint.09.h5') - for sp in sps: - self._sp_name = sp - TestHarness._test_output_created(self) - - -def test_statepoint_batch(): - harness = StatepointTestHarness() - harness.main() diff --git a/tests/regression_tests/statepoint_restart/results_true.dat b/tests/regression_tests/statepoint_restart/results_true.dat index 0809b902e4..b919b30005 100644 --- a/tests/regression_tests/statepoint_restart/results_true.dat +++ b/tests/regression_tests/statepoint_restart/results_true.dat @@ -1,18 +1,114 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 tally 1: -1.400000E-02 -5.000000E-05 -1.400000E-02 -5.000000E-05 -5.637968E-03 -7.140222E-06 +1.300000E-02 +3.900000E-05 +1.300000E-02 +3.900000E-05 +5.833114E-03 +7.476880E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -8.891645E-04 -4.440759E-07 +1.164000E-03 +5.072152E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.110000E-04 +2.100000E-02 +1.110000E-04 +1.108363E-02 +2.889480E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 +2.000000E-03 +2.000000E-06 +3.000000E-03 +5.000000E-06 +2.043490E-03 +1.793389E-06 +2.000000E-03 +2.000000E-06 +2.000000E-03 +2.000000E-06 +2.930717E-04 +8.589100E-08 +2.000000E-02 +9.000000E-05 +2.000000E-02 +9.000000E-05 +9.334862E-03 +1.853104E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.500000E-02 +1.430000E-04 +2.500000E-02 +1.430000E-04 +1.110224E-02 +2.872840E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.833203E-04 +1.701353E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +1.100000E-05 +5.000000E-03 +1.100000E-05 +1.752967E-03 +1.027501E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -26,59 +122,89 @@ tally 1: 0.000000E+00 0.000000E+00 1.800000E-02 -7.200000E-05 +7.600000E-05 1.800000E-02 -7.200000E-05 -8.618270E-03 -1.620709E-05 +7.600000E-05 +7.300761E-03 +1.254112E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.884266E-04 -8.318989E-08 +5.841969E-04 +1.706438E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -8.903211E-04 -7.926717E-07 +5.861433E-04 +3.435640E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -3.300000E-02 -2.290000E-04 -3.300000E-02 -2.290000E-04 -1.685675E-02 -6.105228E-05 +3.000000E-02 +1.820000E-04 +3.000000E-02 +1.820000E-04 +1.518122E-02 +4.757233E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -6.007379E-04 -3.608860E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 +2.927374E-04 +8.569516E-08 +2.000000E-03 +2.000000E-06 +2.000000E-03 +2.000000E-06 +2.631167E-03 +3.503358E-06 1.000000E-03 1.000000E-06 1.000000E-03 1.000000E-06 -2.884266E-04 -8.318989E-08 -1.500000E-02 -6.300000E-05 -1.500000E-02 -6.300000E-05 -7.665313E-03 -1.617331E-05 +2.920435E-04 +8.528942E-08 +2.500000E-02 +1.390000E-04 +2.500000E-02 +1.390000E-04 +9.343205E-03 +1.910189E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.039724E-03 +1.271217E-06 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +5.861433E-04 +3.435640E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.400000E-02 +1.340000E-04 +2.400000E-02 +1.340000E-04 +1.080336E-02 +2.533338E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.914595E-04 +8.494867E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -91,96 +217,90 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +1.400000E-02 +5.200000E-05 +1.400000E-02 +5.200000E-05 +6.711472E-03 +1.216306E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.847809E-04 +1.709846E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.100000E-02 -6.900000E-05 -1.100000E-02 -6.900000E-05 -5.663296E-03 -1.118218E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.198138E-03 -8.972573E-07 +0.000000E+00 +0.000000E+00 +1.700000E-02 +7.900000E-05 +1.700000E-02 +7.900000E-05 +6.413106E-03 +1.186604E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -5.989880E-04 -3.587866E-07 +5.861433E-04 +3.435640E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.900000E-02 +1.770000E-04 +2.900000E-02 +1.770000E-04 +1.314196E-02 +3.678739E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.167073E-03 +5.110971E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 1.000000E-03 1.000000E-06 1.000000E-03 1.000000E-06 0.000000E+00 0.000000E+00 -1.300000E-02 -6.300000E-05 -1.300000E-02 -6.300000E-05 -5.687181E-03 -1.121031E-05 +2.900000E-02 +1.830000E-04 +2.900000E-02 +1.830000E-04 +1.372758E-02 +4.360498E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -5.963216E-04 -1.778036E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -6.100000E-05 -1.500000E-02 -6.100000E-05 -1.040584E-02 -2.402892E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.879206E-04 -1.728865E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.903211E-04 -7.926717E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -4.200000E-02 -3.880000E-04 -4.200000E-02 -3.880000E-04 -1.781042E-02 -7.329257E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.990419E-04 -1.794256E-07 +5.858090E-04 +1.715862E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -194,11 +314,11 @@ tally 1: 0.000000E+00 0.000000E+00 2.200000E-02 -1.220000E-04 +1.000000E-04 2.200000E-02 -1.220000E-04 -9.854350E-03 -2.671897E-05 +1.000000E-04 +9.631311E-03 +2.001285E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -209,20 +329,20 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +5.829191E-04 +3.397947E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -4.400000E-05 -1.200000E-02 -4.400000E-05 -5.595730E-03 -9.201460E-06 +8.000000E-03 +1.800000E-05 +8.000000E-03 +1.800000E-05 +4.094439E-03 +4.622331E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -233,116 +353,20 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.000000E-03 -1.000000E-05 -6.000000E-03 -1.000000E-05 -2.391548E-03 -1.608867E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 +1.170949E-03 +1.371123E-06 1.000000E-03 1.000000E-06 1.000000E-03 1.000000E-06 0.000000E+00 0.000000E+00 -2.600000E-02 -1.920000E-04 -2.600000E-02 -1.920000E-04 -1.211360E-02 -3.507456E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.763472E-04 -4.224562E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.799697E-03 -1.260606E-06 -2.000000E-03 -2.000000E-06 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -3.500000E-02 -2.750000E-04 -3.500000E-02 -2.750000E-04 -1.685749E-02 -6.486072E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -9.011069E-04 -8.119936E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.494965E-03 -1.523738E-06 -3.000000E-03 -5.000000E-06 -3.000000E-03 -5.000000E-06 -2.995479E-04 -8.972896E-08 -2.600000E-02 -1.600000E-04 -2.600000E-02 -1.600000E-04 -1.275573E-02 -3.540061E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 -2.000000E-03 -2.000000E-06 -2.000000E-03 -2.000000E-06 -1.794352E-03 -1.254260E-06 -2.000000E-03 -2.000000E-06 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.800000E-05 -1.000000E-02 -3.800000E-05 -4.799173E-03 -8.824671E-06 +9.000000E-03 +5.100000E-05 +9.000000E-03 +5.100000E-05 +4.664405E-03 +1.087264E-05 0.000000E+00 0.000000E+00 0.000000E+00 @@ -361,234 +385,90 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 +2.200000E-02 +1.300000E-04 +2.200000E-02 +1.300000E-04 +9.644227E-03 +2.300481E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.771587E-04 +4.270487E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.500000E-02 +1.630000E-04 +2.500000E-02 +1.630000E-04 +1.257283E-02 +3.946813E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.171259E-03 +8.583084E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.462299E-03 +1.112414E-06 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.400000E-02 +1.400000E-04 +2.400000E-02 +1.400000E-04 +1.138156E-02 +3.565057E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 9.000000E-03 -3.100000E-05 +2.100000E-05 9.000000E-03 -3.100000E-05 -4.430081E-03 -6.185529E-06 +2.100000E-05 +4.383252E-03 +6.073597E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -2.000000E-03 -4.000000E-06 -3.291067E-03 -4.033124E-06 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -5.971427E-04 -1.782961E-07 -2.800000E-02 -1.660000E-04 -2.800000E-02 -1.660000E-04 -1.217803E-02 -3.047875E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -9.011069E-04 -8.119936E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.300000E-02 -2.930000E-04 -3.300000E-02 -2.930000E-04 -1.600563E-02 -6.991498E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.962677E-04 -1.777713E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.958696E-04 -4.469905E-07 -2.000000E-03 -2.000000E-06 -2.000000E-03 -2.000000E-06 -0.000000E+00 -0.000000E+00 -1.800000E-02 -8.200000E-05 -1.800000E-02 -8.200000E-05 -8.048487E-03 -1.779097E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.000000E-03 -5.000000E-06 -3.000000E-03 -5.000000E-06 -2.664923E-03 -2.009218E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -1.600000E-02 -7.000000E-05 -1.600000E-02 -7.000000E-05 -7.128932E-03 -1.345969E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.200000E-02 -2.360000E-04 -3.200000E-02 -2.360000E-04 -1.452343E-02 -4.653003E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.000000E-02 -2.160000E-04 -3.000000E-02 -2.160000E-04 -1.327819E-02 -4.230384E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.958157E-04 -2.675003E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.768531E-04 -3.327596E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -2.100000E-02 -1.150000E-04 -2.100000E-02 -1.150000E-04 -1.005499E-02 -2.671941E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.192535E-03 -7.110852E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -1.200000E-02 -4.600000E-05 -1.200000E-02 -4.600000E-05 -6.223479E-03 -9.948264E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.884266E-04 -8.318989E-08 +8.707460E-04 +7.581986E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -605,308 +485,20 @@ tally 1: 3.500000E-05 1.100000E-02 3.500000E-05 -5.341464E-03 -7.422119E-06 +6.410606E-03 +1.066242E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.700000E-02 -8.500000E-05 -1.700000E-02 -8.500000E-05 -7.089406E-03 -1.347399E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.768531E-04 -3.327596E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.764011E-04 -4.224885E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -0.000000E+00 -0.000000E+00 -2.300000E-02 -1.430000E-04 -2.300000E-02 -1.430000E-04 -1.185149E-02 -3.938904E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.080112E-03 -1.332746E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.198138E-03 -8.972573E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -5.989880E-04 -3.587866E-07 -3.000000E-02 -2.020000E-04 -3.000000E-02 -2.020000E-04 -1.190088E-02 -2.985574E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.971427E-04 -1.782961E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.651470E-03 -2.516819E-06 -4.000000E-03 -6.000000E-06 -4.000000E-03 -6.000000E-06 -0.000000E+00 -0.000000E+00 -1.200000E-02 -3.800000E-05 -1.200000E-02 -3.800000E-05 -5.332038E-03 -7.371181E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.995479E-04 -8.972896E-08 -3.000000E-03 -3.000000E-06 -3.000000E-03 -3.000000E-06 -2.087327E-03 -1.859777E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.100000E-02 -2.510000E-04 -3.100000E-02 -2.510000E-04 -1.448290E-02 -4.809715E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.162053E-03 -8.367836E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.475335E-03 -1.140030E-06 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.884266E-04 -8.318989E-08 -3.200000E-02 -2.220000E-04 -3.200000E-02 -2.220000E-04 -1.427003E-02 -4.164437E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.855692E-04 -2.614860E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -2.400000E-05 -1.000000E-02 -2.400000E-05 -3.864325E-03 -4.535592E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.065114E-03 -1.468511E-06 -3.000000E-03 -3.000000E-06 -3.000000E-03 -3.000000E-06 -0.000000E+00 -0.000000E+00 1.000000E-03 1.000000E-06 1.000000E-03 1.000000E-06 -1.170401E-03 -6.850581E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.000000E-05 -1.000000E-02 -3.000000E-05 -4.121850E-03 -5.320749E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.100000E-02 -2.170000E-04 -3.100000E-02 -2.170000E-04 -1.483273E-02 -4.914676E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.763472E-04 -4.224562E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.300000E-02 -2.310000E-04 -3.300000E-02 -2.310000E-04 -1.399272E-02 -4.084044E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.499382E-03 -1.168447E-06 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -8.930414E-04 -4.419952E-07 +8.756564E-04 +4.254898E-07 1.000000E-03 1.000000E-06 1.000000E-03 @@ -914,41 +506,305 @@ tally 1: 0.000000E+00 0.000000E+00 1.600000E-02 -7.000000E-05 +6.000000E-05 1.600000E-02 +6.000000E-05 +7.287540E-03 +1.279808E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.822922E-04 +1.695337E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.172287E-03 +1.374256E-06 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +3.400000E-02 +2.820000E-04 +3.400000E-02 +2.820000E-04 +1.603656E-02 +6.229803E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.460479E-03 +7.689660E-07 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +1.165972E-03 +6.797578E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.900000E-02 +8.500000E-05 +1.900000E-02 +8.500000E-05 +7.880322E-03 +1.387762E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.755466E-04 +4.261064E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.400000E-05 +8.000000E-03 +2.400000E-05 +4.666802E-03 +4.926489E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.829191E-04 +3.397947E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.759908E-04 +4.256857E-07 +2.000000E-03 +2.000000E-06 +2.000000E-03 +2.000000E-06 +0.000000E+00 +0.000000E+00 +1.500000E-02 +5.900000E-05 +1.500000E-02 +5.900000E-05 +8.180347E-03 +1.622409E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.902487E-04 +8.424429E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.300000E-02 +1.390000E-04 +2.300000E-02 +1.390000E-04 +7.593262E-03 +1.708292E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.750169E-03 +1.193182E-06 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +1.461465E-03 +7.684663E-07 +2.000000E-03 +4.000000E-06 +2.000000E-03 +4.000000E-06 +2.914595E-04 +8.494867E-08 +2.300000E-02 +1.350000E-04 +2.300000E-02 +1.350000E-04 +9.639670E-03 +2.161898E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.781869E-04 +4.288534E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.707460E-04 +7.581986E-07 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.090000E-04 +2.100000E-02 +1.090000E-04 +9.045520E-03 +2.171457E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.851152E-04 +1.711804E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 7.000000E-05 -7.077099E-03 -1.371332E-05 +1.800000E-02 +7.000000E-05 +1.167023E-02 +2.840320E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.994940E-04 -8.969666E-08 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +5.854747E-04 +3.427806E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.497740E-03 -2.243224E-06 -2.000000E-03 -4.000000E-06 -2.000000E-03 -4.000000E-06 0.000000E+00 0.000000E+00 -1.000000E-02 -3.400000E-05 -1.000000E-02 -3.400000E-05 -3.528506E-03 -4.251243E-06 +3.000000E-03 +5.000000E-06 +3.000000E-03 +5.000000E-06 +1.171618E-03 +6.863446E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.300000E-02 +4.500000E-05 +1.300000E-02 +4.500000E-05 +6.428153E-03 +9.064469E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +9.000000E-05 +1.800000E-02 +9.000000E-05 +8.185938E-03 +1.967120E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +2.300000E-05 +9.000000E-03 +2.300000E-05 +4.665341E-03 +5.426897E-06 +0.000000E+00 +0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -5.879206E-04 -1.728865E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -962,17 +818,425 @@ tally 1: 0.000000E+00 0.000000E+00 4.000000E-03 -6.000000E-06 +4.000000E-06 4.000000E-03 -6.000000E-06 -9.002859E-04 -4.506150E-07 +4.000000E-06 +3.497088E-03 +3.563633E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +9.000000E-06 +5.000000E-03 +9.000000E-06 +2.333045E-03 +1.870613E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.172287E-03 +1.374256E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.200000E-05 +8.000000E-03 +2.200000E-05 +6.413648E-03 +1.136518E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.000000E-02 +1.200000E-04 +2.000000E-02 +1.200000E-04 +7.007604E-03 +1.379504E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.851152E-04 +1.711804E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.861433E-04 +3.435640E-07 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +1.700000E-02 +7.900000E-05 +1.700000E-02 +7.900000E-05 +7.281401E-03 +1.600845E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.000000E-03 +1.900000E-05 +7.000000E-03 +1.900000E-05 +3.218959E-03 +4.543526E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +1.900000E-05 +9.000000E-03 +1.900000E-05 +4.380921E-03 +4.182826E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.100000E-02 +1.050000E-04 +2.100000E-02 +1.050000E-04 +9.932207E-03 +2.153598E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.854747E-04 +3.427806E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +7.400000E-05 +1.800000E-02 +7.400000E-05 +7.301011E-03 +1.358176E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.804973E-04 +3.369772E-07 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +0.000000E+00 +0.000000E+00 +2.300000E-02 +1.610000E-04 +2.300000E-02 +1.610000E-04 +9.647933E-03 +2.657983E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +1.900000E-05 +9.000000E-03 +1.900000E-05 +2.915422E-03 +2.203274E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.775182E-04 +4.280701E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.000000E-03 +9.000000E-06 +5.000000E-03 +9.000000E-06 +2.044244E-03 +1.796182E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.800000E-02 +9.000000E-05 +1.800000E-02 +9.000000E-05 +6.718632E-03 +1.203946E-05 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.927374E-04 +8.569516E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.100000E-02 +2.700000E-05 +1.100000E-02 +2.700000E-05 +4.675703E-03 +5.128811E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.914595E-04 +8.494867E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +2.914595E-04 +8.494867E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.000000E-03 +1.000000E-06 +1.000000E-03 +1.000000E-06 +2.920435E-04 +8.528942E-08 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.000000E-03 +2.600000E-05 +8.000000E-03 +2.600000E-05 +4.670349E-03 +6.133014E-06 +0.000000E+00 +0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -8.939164E-04 -4.425201E-07 0.000000E+00 0.000000E+00 0.000000E+00 @@ -986,65 +1250,113 @@ tally 1: 0.000000E+00 0.000000E+00 1.500000E-02 -5.500000E-05 +6.100000E-05 1.500000E-02 -5.500000E-05 -7.400046E-03 -1.140009E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.990419E-04 -1.794256E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -8.903211E-04 -7.926717E-07 +6.100000E-05 +8.751735E-03 +1.922421E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.800000E-02 -7.400000E-05 -1.800000E-02 -7.400000E-05 -7.695028E-03 -1.396744E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -5.990959E-04 -3.589159E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.400000E-02 -1.260000E-04 -2.400000E-02 -1.260000E-04 -1.220791E-02 -3.565074E-05 +1.400000E-02 +5.000000E-05 +1.400000E-02 +5.000000E-05 +6.126605E-03 +9.620966E-06 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -2.995479E-04 -8.972896E-08 +5.835031E-04 +1.702381E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-03 +2.000000E-06 +2.000000E-03 +2.000000E-06 +5.847809E-04 +1.709846E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.000000E-03 +2.000000E-06 +2.000000E-03 +2.000000E-06 +8.719569E-04 +4.219258E-07 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.000000E-03 +2.700000E-05 +9.000000E-03 +2.700000E-05 +4.085939E-03 +4.936434E-06 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.930717E-04 +8.589100E-08 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1061,8 +1373,8 @@ tally 1: 2.500000E-05 9.000000E-03 2.500000E-05 -3.572884E-03 -4.101061E-06 +5.846700E-03 +9.739696E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1073,294 +1385,6 @@ tally 1: 0.000000E+00 0.000000E+00 0.000000E+00 -5.935474E-04 -3.522985E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-02 -3.800000E-05 -1.000000E-02 -3.800000E-05 -5.382607E-03 -1.128619E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -4.600000E-05 -1.200000E-02 -4.600000E-05 -6.554978E-03 -9.995681E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.884266E-04 -8.318989E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.000000E-03 -1.100000E-05 -7.000000E-03 -1.100000E-05 -3.868525E-03 -3.829197E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.935474E-04 -3.522985E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -5.989880E-04 -3.587866E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.994940E-04 -8.969666E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.100000E-02 -3.100000E-05 -1.100000E-02 -3.100000E-05 -4.132972E-03 -4.495389E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.967737E-04 -8.807464E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -6.007379E-04 -3.608860E-07 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.967737E-04 -8.807464E-08 -2.000000E-02 -9.200000E-05 -2.000000E-02 -9.200000E-05 -8.597951E-03 -1.801722E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.989880E-04 -3.587866E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.200000E-02 -3.200000E-05 -1.200000E-02 -3.200000E-05 -6.509749E-03 -9.403130E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -3.003690E-04 -9.022151E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -5.990959E-04 -3.589159E-07 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -2.884266E-04 -8.318989E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -7.000000E-03 -2.100000E-05 -7.000000E-03 -2.100000E-05 -3.890163E-03 -6.545535E-06 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.995479E-04 -8.972896E-08 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1370,47 +1394,23 @@ tally 1: 0.000000E+00 0.000000E+00 1.300000E-02 -4.300000E-05 +3.900000E-05 1.300000E-02 -4.300000E-05 -6.485840E-03 -1.112994E-05 +3.900000E-05 +6.128455E-03 +1.013669E-05 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -5.989880E-04 -3.587866E-07 +5.851152E-04 +1.711804E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -4.900000E-05 -1.500000E-02 -4.900000E-05 -7.442524E-03 -1.164759E-05 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -2.994940E-04 -8.969666E-08 -1.000000E-03 -1.000000E-06 -1.000000E-03 -1.000000E-06 -5.768531E-04 -3.327596E-07 +1.172287E-03 +1.374256E-06 0.000000E+00 0.000000E+00 0.000000E+00 @@ -1442,11 +1442,11 @@ tally 1: 0.000000E+00 0.000000E+00 tally 2: -5.649702E-01 -6.385502E-02 -6.158199E-01 -7.586795E-02 -3.579551E+00 -2.563396E+00 -3.982345E+01 -3.172536E+02 +5.554367E-01 +6.170503E-02 +6.055520E-01 +7.334109E-02 +3.526894E+00 +2.488065E+00 +3.925114E+01 +3.081807E+02 diff --git a/tests/regression_tests/statepoint_restart/test.py b/tests/regression_tests/statepoint_restart/test.py index 4575607f7d..82e514da87 100644 --- a/tests/regression_tests/statepoint_restart/test.py +++ b/tests/regression_tests/statepoint_restart/test.py @@ -1,11 +1,10 @@ -import glob -import os +from pathlib import Path import openmc from tests.testing_harness import TestHarness from tests.regression_tests import config - +from tests import cdtemp class StatepointRestartTestHarness(TestHarness): def __init__(self, final_sp, restart_sp): @@ -42,7 +41,7 @@ class StatepointRestartTestHarness(TestHarness): def _run_openmc_restart(self): # Get the name of the statepoint file. - statepoint = glob.glob(os.path.join(os.getcwd(), self._restart_sp)) + statepoint = list(Path.cwd().glob(self._restart_sp)) assert len(statepoint) == 1 statepoint = statepoint[0] @@ -59,3 +58,37 @@ def test_statepoint_restart(): harness = StatepointRestartTestHarness('statepoint.10.h5', 'statepoint.07.h5') harness.main() + + +def test_batch_check(request, capsys): + xmls = list(request.path.parent.glob('*.xml')) + + with cdtemp(xmls): + model = openmc.Model.from_xml() + model.settings.particles = 100 + + # run the model + sp_file = model.run(export_model_xml=False) + assert sp_file is not None + + # run a restart with the resulting statepoint + # and the settings unchanged + model.settings.batches = 6 + # ensure we capture output only from the next run + capsys.readouterr() + sp_file = model.run(export_model_xml=False, restart_file=sp_file) + # indicates that a new statepoint file was not created + assert sp_file is None + + output = capsys.readouterr().out + assert "WARNING" in output + assert "The number of batches specified for simulation" in output + + # update the number of batches and run again, + # this restart run should be successful + model.settings.batches = 15 + model.settings.statepoint = {} + sp_file = model.run(export_model_xml=False, restart_file=sp_file) + + sp = openmc.StatePoint(sp_file) + assert sp.n_batches == 15 diff --git a/tests/regression_tests/statepoint_sourcesep/results_true.dat b/tests/regression_tests/statepoint_sourcesep/results_true.dat index fe46748c80..97b997ae6b 100644 --- a/tests/regression_tests/statepoint_sourcesep/results_true.dat +++ b/tests/regression_tests/statepoint_sourcesep/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 diff --git a/tests/regression_tests/stride/__init__.py b/tests/regression_tests/stride/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/stride/inputs_true.dat b/tests/regression_tests/stride/inputs_true.dat new file mode 100644 index 0000000000..ebae53c05a --- /dev/null +++ b/tests/regression_tests/stride/inputs_true.dat @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + -4 -4 -4 4 4 4 + + + 1529170 + + diff --git a/tests/regression_tests/stride/results_true.dat b/tests/regression_tests/stride/results_true.dat new file mode 100644 index 0000000000..825de37667 --- /dev/null +++ b/tests/regression_tests/stride/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +2.953207E-01 2.874356E-03 diff --git a/tests/regression_tests/stride/test.py b/tests/regression_tests/stride/test.py new file mode 100644 index 0000000000..f911af1f5f --- /dev/null +++ b/tests/regression_tests/stride/test.py @@ -0,0 +1,29 @@ +import pytest +import openmc + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + u = openmc.Material() + u.add_nuclide('U235', 1.0) + u.set_density('g/cm3', 4.5) + sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=u, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 1000 + model.settings.stride = 1_529_170 + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-4, -4, -4], [4, 4, 4]) + ) + return model + + +def test_seed(model): + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/surface_source/inputs_true_read.dat b/tests/regression_tests/surface_source/inputs_true_read.dat deleted file mode 100644 index 3a122c737b..0000000000 --- a/tests/regression_tests/surface_source/inputs_true_read.dat +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - surface_source_true.h5 - - 1 - - - - - 3 - - - 1 - flux - - diff --git a/tests/regression_tests/surface_source/inputs_true_read_h5.dat b/tests/regression_tests/surface_source/inputs_true_read_h5.dat new file mode 100644 index 0000000000..321c11d42b --- /dev/null +++ b/tests/regression_tests/surface_source/inputs_true_read_h5.dat @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + surface_source_true.h5 + + 1 + + + + 3 + + + 1 + flux + + + diff --git a/tests/regression_tests/surface_source/inputs_true_read_mcpl.dat b/tests/regression_tests/surface_source/inputs_true_read_mcpl.dat new file mode 100644 index 0000000000..2ee76f618d --- /dev/null +++ b/tests/regression_tests/surface_source/inputs_true_read_mcpl.dat @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + surface_source_true.mcpl + + 1 + + + + 3 + + + 1 + flux + + + diff --git a/tests/regression_tests/surface_source/inputs_true_write.dat b/tests/regression_tests/surface_source/inputs_true_write.dat deleted file mode 100644 index 48dde9900a..0000000000 --- a/tests/regression_tests/surface_source/inputs_true_write.dat +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - 0 0 0 - - - - 1 - 1000 - - 1 - - - - - 3 - - - 1 - flux - - diff --git a/tests/regression_tests/surface_source/inputs_true_write_h5.dat b/tests/regression_tests/surface_source/inputs_true_write_h5.dat new file mode 100644 index 0000000000..10e3af0a7e --- /dev/null +++ b/tests/regression_tests/surface_source/inputs_true_write_h5.dat @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + 0 0 0 + + + + 1 + 1000 + + 1 + + + + 3 + + + 1 + flux + + + diff --git a/tests/regression_tests/surface_source/inputs_true_write_mcpl.dat b/tests/regression_tests/surface_source/inputs_true_write_mcpl.dat new file mode 100644 index 0000000000..e9758144d9 --- /dev/null +++ b/tests/regression_tests/surface_source/inputs_true_write_mcpl.dat @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + 0 0 0 + + + + 1 + true + 1000 + + 1 + + + + 3 + + + 1 + flux + + + diff --git a/tests/regression_tests/surface_source/surface_source_true.h5 b/tests/regression_tests/surface_source/surface_source_true.h5 index 2ea48b2ac1..2c2a19038f 100644 Binary files a/tests/regression_tests/surface_source/surface_source_true.h5 and b/tests/regression_tests/surface_source/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source/surface_source_true.mcpl b/tests/regression_tests/surface_source/surface_source_true.mcpl new file mode 100644 index 0000000000..ca4b7c2c42 Binary files /dev/null and b/tests/regression_tests/surface_source/surface_source_true.mcpl differ diff --git a/tests/regression_tests/surface_source/test.py b/tests/regression_tests/surface_source/test.py index dc60cfd3c7..13966b26d9 100644 --- a/tests/regression_tests/surface_source/test.py +++ b/tests/regression_tests/surface_source/test.py @@ -7,13 +7,43 @@ import pytest import openmc from tests.testing_harness import PyAPITestHarness +from tests.regression_tests import config + + +def mcpl_to_array(filepath): + import mcpl + + source = [] + with mcpl.MCPLFile(filepath) as f: + for p in f.particles: + source.append( + [ + *tuple(p.position), + *tuple(p.direction), + 1.0e6 * p.ekin, + 1.0e-3 * p.time, + p.weight, + p.pdgcode, + ] + ) + return np.sort(np.array(source), axis=0) + + +def assert_structured_arrays_close(arr1, arr2, rtol=1e-5, atol=1e-8): + assert arr1.dtype == arr2.dtype + + for field in arr1.dtype.names: + data1, data2 = arr1[field], arr2[field] + if data1.dtype.names: + assert_structured_arrays_close(data1, data2, rtol=rtol, atol=atol) + else: + np.testing.assert_allclose(data1, data2, rtol=rtol, atol=atol) @pytest.fixture def model(request): openmc.reset_auto_ids() - marker = request.node.get_closest_marker("surf_source_op") - surf_source_op = marker.args[0] + operation, file_format = request.node.get_closest_marker("params").args openmc_model = openmc.model.Model() @@ -42,15 +72,19 @@ def model(request): openmc_model.settings.batches = 10 openmc_model.settings.seed = 1 - if surf_source_op == 'write': + if operation == 'write': point = openmc.stats.Point((0, 0, 0)) - pt_src = openmc.Source(space=point) + pt_src = openmc.IndependentSource(space=point) openmc_model.settings.source = pt_src - openmc_model.settings.surf_source_write = {'surface_ids': [1], - 'max_particles': 1000} - elif surf_source_op == 'read': - openmc_model.settings.surf_source_read = {'path': 'surface_source_true.h5'} + surf_source_write_settings = {'surface_ids': [1], + 'max_particles': 1000} + if file_format == "mcpl": + surf_source_write_settings["mcpl"] = True + + openmc_model.settings.surf_source_write = surf_source_write_settings + elif operation == 'read': + openmc_model.settings.surf_source_read = {'path': f"surface_source_true.{file_format}"} # Tallies tal = openmc.Tally() @@ -63,28 +97,30 @@ def model(request): class SurfaceSourceTestHarness(PyAPITestHarness): + def __init__(self, statepoint_name, model=None, inputs_true=None, file_format="h5"): + super().__init__(statepoint_name, model, inputs_true) + self.file_format = file_format + def _test_output_created(self): - """Make sure surface_source.h5 has also been created.""" + """Make sure the surface_source file has also been created.""" super()._test_output_created() - # Check if 'surface_source.h5' has been created. if self._model.settings.surf_source_write: - assert os.path.exists('surface_source.h5'), \ + assert os.path.exists(f"surface_source.{self.file_format}"), \ 'Surface source file does not exist.' def _compare_output(self): """Make sure the current surface_source.h5 agree with the reference.""" if self._model.settings.surf_source_write: - with h5py.File("surface_source_true.h5", 'r') as f: - source_true = f['source_bank'][()] - # Convert dtye from mixed to a float for comparison assertion - source_true.dtype = 'float64' - with h5py.File("surface_source.h5", 'r') as f: - source_test = f['source_bank'][()] - # Convert dtye from mixed to a float for comparison assertion - source_test.dtype = 'float64' - np.testing.assert_allclose(np.sort(source_true), - np.sort(source_test), - atol=1e-07) + if self.file_format == "h5": + with h5py.File("surface_source_true.h5", 'r') as f: + source_true = np.sort(f['source_bank'][()]) + with h5py.File("surface_source.h5", 'r') as f: + source_test = np.sort(f['source_bank'][()]) + assert_structured_arrays_close(source_true, source_test, atol=1e-07) + elif self.file_format == "mcpl": + source_true = mcpl_to_array("surface_source_true.mcpl") + source_test = mcpl_to_array("surface_source.mcpl") + np.testing.assert_allclose(source_true, source_test, rtol=1e-5, atol=1e-7) def execute_test(self): """Build input XMLs, run OpenMC, check output and results.""" @@ -105,28 +141,56 @@ class SurfaceSourceTestHarness(PyAPITestHarness): def _overwrite_results(self): """Overwrite the results_true with the results_test.""" shutil.copyfile('results_test.dat', 'results_true.dat') - if os.path.exists('surface_source.h5'): - shutil.copyfile('surface_source.h5', 'surface_source_true.h5') + if os.path.exists(f"surface_source.{self.file_format}"): + shutil.copyfile(f"surface_source.{self.file_format}", f"surface_source_true.{self.file_format}") def _cleanup(self): """Delete statepoints, tally, and test files.""" super()._cleanup() - fs = 'surface_source.h5' + fs = f"surface_source.{self.file_format}" if os.path.exists(fs): os.remove(fs) -@pytest.mark.surf_source_op('write') -def test_surface_source_write(model): - harness = SurfaceSourceTestHarness('statepoint.10.h5', - model, - 'inputs_true_write.dat') +@pytest.mark.params('write', 'h5') +def test_surface_source_write(model, monkeypatch, request): + monkeypatch.setitem(config, "mpi_np", "1") # Results generated with 1 MPI process + operation, file_format = request.node.get_closest_marker("params").args + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, f"inputs_true_{operation}_{file_format}.dat", + file_format=file_format + ) harness.main() -@pytest.mark.surf_source_op('read') -def test_surface_source_read(model): - harness = SurfaceSourceTestHarness('statepoint.10.h5', - model, - 'inputs_true_read.dat') +@pytest.mark.params('read', 'h5') +def test_surface_source_read(model, request): + operation, file_format = request.node.get_closest_marker("params").args + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, f"inputs_true_{operation}_{file_format}.dat", + file_format=file_format + ) + harness.main() + + +@pytest.mark.skipif(shutil.which("mcpl-config") is None, reason="MCPL is not available.") +@pytest.mark.params('write', 'mcpl') +def test_surface_source_write_mcpl(model, monkeypatch, request): + monkeypatch.setitem(config, "mpi_np", "1") # Results generated with 1 MPI process + operation, file_format = request.node.get_closest_marker("params").args + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, f"inputs_true_{operation}_{file_format}.dat", + file_format=file_format + ) + harness.main() + + +@pytest.mark.skipif(shutil.which("mcpl-config") is None, reason="MCPL is not available.") +@pytest.mark.params('read', 'mcpl') +def test_surface_source_read_mcpl(model, request): + operation, file_format = request.node.get_closest_marker("params").args + harness = SurfaceSourceTestHarness( + "statepoint.10.h5", model, f"inputs_true_{operation}_{file_format}.dat", + file_format=file_format + ) harness.main() diff --git a/tests/regression_tests/surface_source_write/__init__.py b/tests/regression_tests/surface_source_write/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/surface_source_write/_visualize.py b/tests/regression_tests/surface_source_write/_visualize.py new file mode 100644 index 0000000000..73340cae06 --- /dev/null +++ b/tests/regression_tests/surface_source_write/_visualize.py @@ -0,0 +1,66 @@ +"""Helper script to visualize the surface_source.h5 files created with this test. +""" + +import h5py +import matplotlib.pyplot as plt + + +if __name__ == "__main__": + + # Select an option + # "show": 3D visualization using matplotlib + # "savefig": 2D representation using matplotlib and storing the fig under plot_2d.png + option = "show" + # option = "savefig" + + # Select the case from its folder name + folder = "case-20" + + # Reading the surface source file + with h5py.File(f"{folder}/surface_source_true.h5", "r") as fp: + source_bank = fp["source_bank"][()] + r_xs = source_bank['r']['x'] + r_ys = source_bank['r']['y'] + r_zs = source_bank['r']['z'] + + print("Size of the source bank: ", len(source_bank)) + + # Select data range to visualize + idx_1 = 0 + idx_2 = -1 + + # Show 3D representation + if option == "show": + + fig = plt.figure(figsize=(10, 10)) + ax1 = fig.add_subplot(projection="3d", proj_type="ortho") + ax1.scatter(r_xs[idx_1:idx_2], r_ys[idx_1:idx_2], r_zs[idx_1:idx_2], marker=".") + ax1.view_init(0, 0) + ax1.xaxis.set_ticklabels([]) + ax1.set_ylabel("y-axis [cm]") + ax1.set_zlabel("z-axis [cm]") + ax1.set_aspect("equal", "box") + + plt.show() + + # Save 2D representations + elif option == "savefig": + + fig = plt.figure(figsize=(14, 5)) + ax1 = fig.add_subplot(121, projection="3d", proj_type="ortho") + ax1.scatter(r_xs[idx_1:idx_2], r_ys[idx_1:idx_2], r_zs[idx_1:idx_2], marker=".") + ax1.view_init(0, 0) + ax1.xaxis.set_ticklabels([]) + ax1.set_ylabel("y-axis [cm]") + ax1.set_zlabel("z-axis [cm]") + ax1.set_aspect("equal", "box") + + ax2 = fig.add_subplot(122, projection="3d", proj_type="ortho") + ax2.scatter(r_xs[idx_1:idx_2], r_ys[idx_1:idx_2], r_zs[idx_1:idx_2], marker=".") + ax2.view_init(90, -90) + ax2.zaxis.set_ticklabels([]) + ax2.set_xlabel("x-axis [cm]") + ax2.set_ylabel("y-axis [cm]") + ax2.set_aspect("equal", "box") + + plt.savefig("plot_2d.png") diff --git a/tests/regression_tests/surface_source_write/case-01/inputs_true.dat b/tests/regression_tests/surface_source_write/case-01/inputs_true.dat new file mode 100644 index 0000000000..2a67b03dd2 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-01/inputs_true.dat @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-01/results_true.dat b/tests/regression_tests/surface_source_write/case-01/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-01/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-01/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-01/surface_source_true.h5 new file mode 100644 index 0000000000..a43646158b Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-01/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-02/inputs_true.dat b/tests/regression_tests/surface_source_write/case-02/inputs_true.dat new file mode 100644 index 0000000000..527f076b9c --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-02/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 8 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-02/results_true.dat b/tests/regression_tests/surface_source_write/case-02/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-02/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-02/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-02/surface_source_true.h5 new file mode 100644 index 0000000000..5a73e7ab2a Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-02/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-03/inputs_true.dat b/tests/regression_tests/surface_source_write/case-03/inputs_true.dat new file mode 100644 index 0000000000..58c4e0a240 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-03/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-03/results_true.dat b/tests/regression_tests/surface_source_write/case-03/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-03/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-03/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-03/surface_source_true.h5 new file mode 100644 index 0000000000..228f5a7d0a Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-03/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-04/inputs_true.dat b/tests/regression_tests/surface_source_write/case-04/inputs_true.dat new file mode 100644 index 0000000000..46701aea7e --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-04/inputs_true.dat @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-04/results_true.dat b/tests/regression_tests/surface_source_write/case-04/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-04/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-04/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-04/surface_source_true.h5 new file mode 100644 index 0000000000..c276af40f4 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-04/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-05/inputs_true.dat b/tests/regression_tests/surface_source_write/case-05/inputs_true.dat new file mode 100644 index 0000000000..c420d797ce --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-05/inputs_true.dat @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-05/results_true.dat b/tests/regression_tests/surface_source_write/case-05/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-05/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-05/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-05/surface_source_true.h5 new file mode 100644 index 0000000000..c276af40f4 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-05/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-06/inputs_true.dat b/tests/regression_tests/surface_source_write/case-06/inputs_true.dat new file mode 100644 index 0000000000..e02d5e90c1 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-06/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-06/results_true.dat b/tests/regression_tests/surface_source_write/case-06/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-06/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-06/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-06/surface_source_true.h5 new file mode 100644 index 0000000000..6946938adb Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-06/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-07/inputs_true.dat b/tests/regression_tests/surface_source_write/case-07/inputs_true.dat new file mode 100644 index 0000000000..a4588c8d08 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-07/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-07/results_true.dat b/tests/regression_tests/surface_source_write/case-07/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-07/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-07/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-07/surface_source_true.h5 new file mode 100644 index 0000000000..c276af40f4 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-07/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-08/inputs_true.dat b/tests/regression_tests/surface_source_write/case-08/inputs_true.dat new file mode 100644 index 0000000000..ecf3a6a2e5 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-08/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-08/results_true.dat b/tests/regression_tests/surface_source_write/case-08/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-08/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-08/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-08/surface_source_true.h5 new file mode 100644 index 0000000000..94cd377d40 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-08/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-09/inputs_true.dat b/tests/regression_tests/surface_source_write/case-09/inputs_true.dat new file mode 100644 index 0000000000..5d60f9dbeb --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-09/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-09/results_true.dat b/tests/regression_tests/surface_source_write/case-09/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-09/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-09/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-09/surface_source_true.h5 new file mode 100644 index 0000000000..ebce87b36a Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-09/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-10/inputs_true.dat b/tests/regression_tests/surface_source_write/case-10/inputs_true.dat new file mode 100644 index 0000000000..1940826c23 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-10/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-10/results_true.dat b/tests/regression_tests/surface_source_write/case-10/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-10/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-10/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-10/surface_source_true.h5 new file mode 100644 index 0000000000..fded9d987e Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-10/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-11/inputs_true.dat b/tests/regression_tests/surface_source_write/case-11/inputs_true.dat new file mode 100644 index 0000000000..a4feff1b1f --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-11/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-11/results_true.dat b/tests/regression_tests/surface_source_write/case-11/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-11/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-11/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-11/surface_source_true.h5 new file mode 100644 index 0000000000..b5b6036201 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-11/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-12/inputs_true.dat b/tests/regression_tests/surface_source_write/case-12/inputs_true.dat new file mode 100644 index 0000000000..c069f425c7 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-12/inputs_true.dat @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-12/results_true.dat b/tests/regression_tests/surface_source_write/case-12/results_true.dat new file mode 100644 index 0000000000..ad927bdf30 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-12/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.929000E-02 8.212396E-03 diff --git a/tests/regression_tests/surface_source_write/case-12/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-12/surface_source_true.h5 new file mode 100644 index 0000000000..6c6925daba Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-12/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-13/inputs_true.dat b/tests/regression_tests/surface_source_write/case-13/inputs_true.dat new file mode 100644 index 0000000000..2a93fdb4d6 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-13/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-13/results_true.dat b/tests/regression_tests/surface_source_write/case-13/results_true.dat new file mode 100644 index 0000000000..ad927bdf30 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-13/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.929000E-02 8.212396E-03 diff --git a/tests/regression_tests/surface_source_write/case-13/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-13/surface_source_true.h5 new file mode 100644 index 0000000000..6c6925daba Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-13/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-14/inputs_true.dat b/tests/regression_tests/surface_source_write/case-14/inputs_true.dat new file mode 100644 index 0000000000..893f8ddc15 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-14/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-14/results_true.dat b/tests/regression_tests/surface_source_write/case-14/results_true.dat new file mode 100644 index 0000000000..ad927bdf30 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-14/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.929000E-02 8.212396E-03 diff --git a/tests/regression_tests/surface_source_write/case-14/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-14/surface_source_true.h5 new file mode 100644 index 0000000000..6c6925daba Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-14/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-15/inputs_true.dat b/tests/regression_tests/surface_source_write/case-15/inputs_true.dat new file mode 100644 index 0000000000..875a2fb0bb --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-15/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-15/results_true.dat b/tests/regression_tests/surface_source_write/case-15/results_true.dat new file mode 100644 index 0000000000..ad927bdf30 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-15/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.929000E-02 8.212396E-03 diff --git a/tests/regression_tests/surface_source_write/case-15/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-15/surface_source_true.h5 new file mode 100644 index 0000000000..5bdb39b2a7 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-15/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-16/inputs_true.dat b/tests/regression_tests/surface_source_write/case-16/inputs_true.dat new file mode 100644 index 0000000000..347855e902 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-16/inputs_true.dat @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-16/results_true.dat b/tests/regression_tests/surface_source_write/case-16/results_true.dat new file mode 100644 index 0000000000..cd0619c1fe --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-16/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.403371E+00 1.456192E-02 diff --git a/tests/regression_tests/surface_source_write/case-16/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-16/surface_source_true.h5 new file mode 100644 index 0000000000..79ac8e1a65 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-16/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-17/inputs_true.dat b/tests/regression_tests/surface_source_write/case-17/inputs_true.dat new file mode 100644 index 0000000000..95d0a67124 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-17/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-17/results_true.dat b/tests/regression_tests/surface_source_write/case-17/results_true.dat new file mode 100644 index 0000000000..cd0619c1fe --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-17/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.403371E+00 1.456192E-02 diff --git a/tests/regression_tests/surface_source_write/case-17/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-17/surface_source_true.h5 new file mode 100644 index 0000000000..aadcc3fa40 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-17/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-18/inputs_true.dat b/tests/regression_tests/surface_source_write/case-18/inputs_true.dat new file mode 100644 index 0000000000..807c72ae65 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-18/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-18/results_true.dat b/tests/regression_tests/surface_source_write/case-18/results_true.dat new file mode 100644 index 0000000000..cd0619c1fe --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-18/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.403371E+00 1.456192E-02 diff --git a/tests/regression_tests/surface_source_write/case-18/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-18/surface_source_true.h5 new file mode 100644 index 0000000000..e5a7619c55 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-18/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-19/inputs_true.dat b/tests/regression_tests/surface_source_write/case-19/inputs_true.dat new file mode 100644 index 0000000000..42aa78a096 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-19/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-19/results_true.dat b/tests/regression_tests/surface_source_write/case-19/results_true.dat new file mode 100644 index 0000000000..cd0619c1fe --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-19/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.403371E+00 1.456192E-02 diff --git a/tests/regression_tests/surface_source_write/case-19/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-19/surface_source_true.h5 new file mode 100644 index 0000000000..db8a49dcee Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-19/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-20/inputs_true.dat b/tests/regression_tests/surface_source_write/case-20/inputs_true.dat new file mode 100644 index 0000000000..4c7a3f11d9 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-20/inputs_true.dat @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-20/results_true.dat b/tests/regression_tests/surface_source_write/case-20/results_true.dat new file mode 100644 index 0000000000..44293cf091 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-20/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.266853E+00 4.552028E-02 diff --git a/tests/regression_tests/surface_source_write/case-20/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-20/surface_source_true.h5 new file mode 100644 index 0000000000..01d2abf61c Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-20/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-21/inputs_true.dat b/tests/regression_tests/surface_source_write/case-21/inputs_true.dat new file mode 100644 index 0000000000..71f9aae6a9 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-21/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-21/results_true.dat b/tests/regression_tests/surface_source_write/case-21/results_true.dat new file mode 100644 index 0000000000..44293cf091 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-21/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.266853E+00 4.552028E-02 diff --git a/tests/regression_tests/surface_source_write/case-21/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-21/surface_source_true.h5 new file mode 100644 index 0000000000..4e5dcb446c Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-21/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-a01/inputs_true.dat b/tests/regression_tests/surface_source_write/case-a01/inputs_true.dat new file mode 100644 index 0000000000..e9840be875 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-a01/inputs_true.dat @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 1 2 3 + 200 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-a01/results_true.dat b/tests/regression_tests/surface_source_write/case-a01/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-a01/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-a01/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-a01/surface_source_true.h5 new file mode 100644 index 0000000000..da36fc505e Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-a01/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d01/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d01/inputs_true.dat new file mode 100644 index 0000000000..04703acfed --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d01/inputs_true.dat @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d01/results_true.dat b/tests/regression_tests/surface_source_write/case-d01/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d01/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d01/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d01/surface_source_true.h5 new file mode 100644 index 0000000000..9a66315ffe Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d01/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d02/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d02/inputs_true.dat new file mode 100644 index 0000000000..d8b4e68033 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d02/inputs_true.dat @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 1 + 300 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d02/results_true.dat b/tests/regression_tests/surface_source_write/case-d02/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d02/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d02/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d02/surface_source_true.h5 new file mode 100644 index 0000000000..2910629425 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d02/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d03/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d03/inputs_true.dat new file mode 100644 index 0000000000..d769184aa5 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d03/inputs_true.dat @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d03/results_true.dat b/tests/regression_tests/surface_source_write/case-d03/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d03/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d03/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d03/surface_source_true.h5 new file mode 100644 index 0000000000..0af7160d4b Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d03/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d04/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d04/inputs_true.dat new file mode 100644 index 0000000000..abefc26927 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d04/inputs_true.dat @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 1 + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d04/results_true.dat b/tests/regression_tests/surface_source_write/case-d04/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d04/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d04/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d04/surface_source_true.h5 new file mode 100644 index 0000000000..90ab00748b Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d04/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d05/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d05/inputs_true.dat new file mode 100644 index 0000000000..8b46b2b4ea --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d05/inputs_true.dat @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d05/results_true.dat b/tests/regression_tests/surface_source_write/case-d05/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d05/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d05/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d05/surface_source_true.h5 new file mode 100644 index 0000000000..2f55216798 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d05/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d06/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d06/inputs_true.dat new file mode 100644 index 0000000000..ca52cf455b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d06/inputs_true.dat @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d06/results_true.dat b/tests/regression_tests/surface_source_write/case-d06/results_true.dat new file mode 100644 index 0000000000..26b9e30a3b --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d06/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.288719E-01 6.877101E-03 diff --git a/tests/regression_tests/surface_source_write/case-d06/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d06/surface_source_true.h5 new file mode 100644 index 0000000000..27227ad49c Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d06/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d07/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d07/inputs_true.dat new file mode 100644 index 0000000000..40c439fe69 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d07/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 101 102 103 104 105 106 + 300 + 7 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d07/results_true.dat b/tests/regression_tests/surface_source_write/case-d07/results_true.dat new file mode 100644 index 0000000000..5a4ea66898 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d07/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.947197E-01 3.711779E-02 diff --git a/tests/regression_tests/surface_source_write/case-d07/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d07/surface_source_true.h5 new file mode 100644 index 0000000000..fd9f0dc572 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d07/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-d08/inputs_true.dat b/tests/regression_tests/surface_source_write/case-d08/inputs_true.dat new file mode 100644 index 0000000000..c81d21b979 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d08/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -4 -4 -20 4 4 20 + + + true + + + + 101 102 103 104 105 106 + 300 + 8 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-d08/results_true.dat b/tests/regression_tests/surface_source_write/case-d08/results_true.dat new file mode 100644 index 0000000000..5a4ea66898 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-d08/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +9.947197E-01 3.711779E-02 diff --git a/tests/regression_tests/surface_source_write/case-d08/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-d08/surface_source_true.h5 new file mode 100644 index 0000000000..fd9f0dc572 Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-d08/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-e01/inputs_true.dat b/tests/regression_tests/surface_source_write/case-e01/inputs_true.dat new file mode 100644 index 0000000000..46701aea7e --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e01/inputs_true.dat @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 2 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-e01/results_true.dat b/tests/regression_tests/surface_source_write/case-e01/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e01/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-e01/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-e01/surface_source_true.h5 new file mode 100644 index 0000000000..bbfbd152bc Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-e01/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-e02/inputs_true.dat b/tests/regression_tests/surface_source_write/case-e02/inputs_true.dat new file mode 100644 index 0000000000..a4588c8d08 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e02/inputs_true.dat @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-e02/results_true.dat b/tests/regression_tests/surface_source_write/case-e02/results_true.dat new file mode 100644 index 0000000000..d4d1d1e5ad --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e02/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +5.642735E-02 1.494035E-02 diff --git a/tests/regression_tests/surface_source_write/case-e02/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-e02/surface_source_true.h5 new file mode 100644 index 0000000000..bbfbd152bc Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-e02/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/case-e03/inputs_true.dat b/tests/regression_tests/surface_source_write/case-e03/inputs_true.dat new file mode 100644 index 0000000000..2a93fdb4d6 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e03/inputs_true.dat @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 5 + 1 + + + -2.0 -2.0 -2.0 2.0 2.0 2.0 + + + true + + + + 4 5 6 7 8 9 + 300 + 3 + + 1 + + diff --git a/tests/regression_tests/surface_source_write/case-e03/results_true.dat b/tests/regression_tests/surface_source_write/case-e03/results_true.dat new file mode 100644 index 0000000000..ad927bdf30 --- /dev/null +++ b/tests/regression_tests/surface_source_write/case-e03/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.929000E-02 8.212396E-03 diff --git a/tests/regression_tests/surface_source_write/case-e03/surface_source_true.h5 b/tests/regression_tests/surface_source_write/case-e03/surface_source_true.h5 new file mode 100644 index 0000000000..22e108745d Binary files /dev/null and b/tests/regression_tests/surface_source_write/case-e03/surface_source_true.h5 differ diff --git a/tests/regression_tests/surface_source_write/test.py b/tests/regression_tests/surface_source_write/test.py new file mode 100644 index 0000000000..f144eb82a7 --- /dev/null +++ b/tests/regression_tests/surface_source_write/test.py @@ -0,0 +1,1125 @@ +"""Test the 'surface_source_write' setting. + +Results +------- + +All results are generated using only 1 MPI process. + +All results are generated using 1 thread except for "test_consistency_low_realization_number". +This specific test verifies that when the number of realization (i.e., point being candidate +to be stored) is lower than the capacity, results are reproducible even with multiple +threads (i.e., there is no potential thread competition that would produce different +results in that case). + +All results are generated using the history-based mode except for cases e01 to e03. + +All results are visually verified using the '_visualize.py' script in the regression test folder. + +OpenMC models +------------- + +Four OpenMC models with CSG-only geometries are used to cover the transmission, vacuum, +reflective and periodic Boundary Conditions (BC): + +- model_1: cylindrical core in 2 boxes (vacuum and transmission BC), +- model_2: cylindrical core in 1 box (vacuum BC), +- model_3: cylindrical core in 1 box (reflective BC), +- model_4: cylindrical core in 1 box (periodic BC). + +Two models including DAGMC geometries are also used, based on the mesh file 'dagmc.h5m' +available from tests/regression_tests/dagmc/legacy: + +- model_dagmc_1: model adapted from tests/regression_tests/dagmc/legacy, +- model_dagmc_2: model_dagmc_1 contained in two CSG boxes to introduce multiple level of coordinates. + +Test cases +---------- + +Test cases using CSG-only geometries: + +======== ======= ========= ========================= ===== =================================== +Folder Model Surface Cell BC* Expected particles +======== ======= ========= ========================= ===== =================================== +case-01 model_1 No No T+V Particles crossing any surface in + the model +case-02 model_1 1 No T Particles crossing this surface + only +case-03 model_1 Multiple No T Particles crossing the declared + surfaces +case-04 model_1 Multiple cell (lower universe) T Particles crossing the declared + surfaces that come from or are + coming to the cell +case-05 model_1 Multiple cell (root universe) T Particles crossing the declared + surfaces that come from or are + coming to the cell +case-06 model_1 No cell (lower universe) T Particles crossing any surface that + come from or are coming to the cell +case-07 model_1 No cell (root universe) T Particles crossing any surface that + come from or are coming to the cell +case-08 model_1 No cellfrom (lower universe) T Particles crossing any surface that + come from the cell +case-09 model_1 No cellto (lower universe) T Particles crossing any surface that + are coming to the cell +case-10 model_1 No cellfrom (root universe) T Particles crossing any surface that + come from the cell +case-11 model_1 No cellto (root universe) T Particles crossing any surface that + are coming to the cell +case-12 model_2 Multiple No V Particles crossing the declared + surfaces +case-13 model_2 Multiple cell (root universe) V Particles crossing any surface that + come from or are coming to the cell +case-14 model_2 Multiple cellfrom (root universe) V Particles crossing any surface that + are coming to the cell +case-15 model_2 Multiple cellto (root universe) V None +case-16 model_3 Multiple No R Particles crossing the declared + surfaces +case-17 model_3 Multiple cell (root universe) R None +case-18 model_3 Multiple cellfrom (root universe) R None +case-19 model_3 Multiple cellto (root universe) R None +case-20 model_4 1 No P+R Particles crossing the declared + periodic surface +case-21 model_4 1 cell (root universe) P+R None +======== ======= ========= ========================= ===== =================================== + +*: BC stands for Boundary Conditions, T for Transmission, R for Reflective, and V for Vacuum. + +An additional case, called 'case-a01', is used to check that the results are comparable when +the number of threads is set to 2 if the number of realization is lower than the capacity. + +Cases e01 to e03 are the event-based cases corresponding to the history-based cases 04, 07 and 13, +respectively. + +Test cases using DAGMC geometries: + +======== ============= ========= ===================== ===== =================================== +Folder Model Surface Cell BC* Expected particles +======== ============= ========= ===================== ===== =================================== +case-d01 model_dagmc_1 No No T+V Particles crossing any surface in + the model +case-d02 model_dagmc_1 1 No T Particles crossing this surface + only +case-d03 model_dagmc_1 No cell T Particles crossing any surface that + come from or are coming to the cell +case-d04 model_dagmc_1 1 cell T Particles crossing the declared + surface that come from or are + coming to the cell +case-d05 model_dagmc_1 No cellfrom T Particles crossing any surface that + come from the cell +case-d06 model_dagmc_1 No cellto T Particles crossing any surface that + are coming to the cell +case-d07 model_dagmc_2 Multiple cell (lower universe) T Particles crossing the declared + surfaces that come from or are + coming to the cell +case-d08 model_dagmc_2 Multiple cell (root universe) T Particles crossing the declared + surfaces that come from or are + coming to the cell +======== ============= ========= ===================== ===== =================================== + +*: BC stands for Boundary Conditions, T for Transmission, and V for Vacuum. + +Notes: + +- The test cases list is non-exhaustive compared to the number of possible combinations. + Test cases have been selected based on use and internal code logic. +- Cases 08 to 11 are testing that the feature still works even if the level of coordinates + before and after crossing a surface is different, +- Tests on boundary conditions are not performed on DAGMC models as the logic is shared + with CSG-only models, +- Cases that should return an error are tested in the 'test_exceptions' unit test + from 'unit_tests/surface_source_write/test.py'. + +TODO: + +- Test with a lattice. + +""" + +import os +import shutil +from pathlib import Path + +import h5py +import numpy as np +import openmc +import openmc.lib +import pytest + +from tests.testing_harness import PyAPITestHarness +from tests.regression_tests import config + + +@pytest.fixture(scope="function") +def single_thread(monkeypatch): + """Set the number of OMP threads to 1 for the test.""" + monkeypatch.setenv("OMP_NUM_THREADS", "1") + + +@pytest.fixture(scope="function") +def two_threads(monkeypatch): + """Set the number of OMP threads to 2 for the test.""" + monkeypatch.setenv("OMP_NUM_THREADS", "2") + + +@pytest.fixture(scope="function") +def single_process(monkeypatch): + """Set the number of MPI process to 1 for the test.""" + monkeypatch.setitem(config, "mpi_np", "1") + + +@pytest.fixture(scope="module") +def model_1(): + """Cylindrical core contained in a first box which is contained in a larger box. + A lower universe is used to describe the interior of the first box which + contains the core and its surrounding space. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + fuel = openmc.Material() + fuel.add_nuclide("U234", 0.0004524) + fuel.add_nuclide("U235", 0.0506068) + fuel.add_nuclide("U238", 0.9487090) + fuel.add_nuclide("U236", 0.0002318) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cm3", 11.0) + + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + + # ============================================================================= + # Geometry + # ============================================================================= + + # ----------------------------------------------------------------------------- + # Cylindrical core + # ----------------------------------------------------------------------------- + + # Parameters + core_radius = 2.0 + core_height = 4.0 + + # Surfaces + core_cylinder = openmc.ZCylinder(r=core_radius) + core_lower_plane = openmc.ZPlane(-core_height / 2.0) + core_upper_plane = openmc.ZPlane(core_height / 2.0) + + # Region + core_region = -core_cylinder & +core_lower_plane & -core_upper_plane + + # Cells + core = openmc.Cell(fill=fuel, region=core_region) + outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane + outside_core = openmc.Cell(fill=water, region=outside_core_region) + + # Universe + inside_box1_universe = openmc.Universe(cells=[core, outside_core]) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 6.0 + + # Surfaces + box1_rpp = openmc.model.RectangularParallelepiped( + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + ) + + # Cell + box1 = openmc.Cell(fill=inside_box1_universe, region=-box1_rpp) + + # ----------------------------------------------------------------------------- + # Box 2 + # ----------------------------------------------------------------------------- + + # Parameters + box2_size = 8 + + # Surfaces + box2_rpp = openmc.model.RectangularParallelepiped( + -box2_size / 2.0, box2_size / 2.0, + -box2_size / 2.0, box2_size / 2.0, + -box2_size / 2.0, box2_size / 2.0, + boundary_type="vacuum" + ) + + # Cell + box2 = openmc.Cell(fill=water, region=-box2_rpp & +box1_rpp) + + # Register geometry + model.geometry = openmc.Geometry([box1, box2]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + bounds = [ + -core_radius, + -core_radius, + -core_height / 2.0, + core_radius, + core_radius, + core_height / 2.0, + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource( + space=distribution, constraints={'fissionable': True}) + + return model + + +@pytest.fixture +def model_2(): + """Cylindrical core contained in a box. + A lower universe is used to describe the interior of the box which + contains the core and its surrounding space. + + The box is defined with vacuum boundary conditions. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + fuel = openmc.Material() + fuel.add_nuclide("U234", 0.0004524) + fuel.add_nuclide("U235", 0.0506068) + fuel.add_nuclide("U238", 0.9487090) + fuel.add_nuclide("U236", 0.0002318) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cm3", 11.0) + + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + + # ============================================================================= + # Geometry + # ============================================================================= + + # ----------------------------------------------------------------------------- + # Cylindrical core + # ----------------------------------------------------------------------------- + + # Parameters + core_radius = 2.0 + core_height = 4.0 + + # Surfaces + core_cylinder = openmc.ZCylinder(r=core_radius) + core_lower_plane = openmc.ZPlane(-core_height / 2.0) + core_upper_plane = openmc.ZPlane(core_height / 2.0) + + # Region + core_region = -core_cylinder & +core_lower_plane & -core_upper_plane + + # Cells + core = openmc.Cell(fill=fuel, region=core_region) + outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane + outside_core = openmc.Cell(fill=water, region=outside_core_region) + + # Universe + inside_box1_universe = openmc.Universe(cells=[core, outside_core]) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 6.0 + + # Surfaces + box1_rpp = openmc.model.RectangularParallelepiped( + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + boundary_type="vacuum" + ) + + # Cell + box1 = openmc.Cell(fill=inside_box1_universe, region=-box1_rpp) + + # Register geometry + model.geometry = openmc.Geometry([box1]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + bounds = [ + -core_radius, + -core_radius, + -core_height / 2.0, + core_radius, + core_radius, + core_height / 2.0, + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource( + space=distribution, constraints={'fissionable': True}) + + return model + + +@pytest.fixture +def model_3(): + """Cylindrical core contained in a box. + A lower universe is used to describe the interior of the box which + contains the core and its surrounding space. + + The box is defined with reflective boundary conditions. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + fuel = openmc.Material() + fuel.add_nuclide("U234", 0.0004524) + fuel.add_nuclide("U235", 0.0506068) + fuel.add_nuclide("U238", 0.9487090) + fuel.add_nuclide("U236", 0.0002318) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cm3", 11.0) + + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + + # ============================================================================= + # Geometry + # ============================================================================= + + # ----------------------------------------------------------------------------- + # Cylindrical core + # ----------------------------------------------------------------------------- + + # Parameters + core_radius = 2.0 + core_height = 4.0 + + # Surfaces + core_cylinder = openmc.ZCylinder(r=core_radius) + core_lower_plane = openmc.ZPlane(-core_height / 2.0) + core_upper_plane = openmc.ZPlane(core_height / 2.0) + + # Region + core_region = -core_cylinder & +core_lower_plane & -core_upper_plane + + # Cells + core = openmc.Cell(fill=fuel, region=core_region) + outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane + outside_core = openmc.Cell(fill=water, region=outside_core_region) + + # Universe + inside_box1_universe = openmc.Universe(cells=[core, outside_core]) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 6.0 + + # Surfaces + box1_rpp = openmc.model.RectangularParallelepiped( + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + -box1_size / 2.0, box1_size / 2.0, + boundary_type="reflective" + ) + + # Cell + box1 = openmc.Cell(fill=inside_box1_universe, region=-box1_rpp) + + # Register geometry + model.geometry = openmc.Geometry([box1]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + bounds = [ + -core_radius, + -core_radius, + -core_height / 2.0, + core_radius, + core_radius, + core_height / 2.0, + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource( + space=distribution, constraints={'fissionable': True}) + + return model + + +@pytest.fixture +def model_4(): + """Cylindrical core contained in a box. + A lower universe is used to describe the interior of the box which + contains the core and its surrounding space. + + The box is defined with a pair of periodic boundary with reflective + boundaries. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + fuel = openmc.Material() + fuel.add_nuclide("U234", 0.0004524) + fuel.add_nuclide("U235", 0.0506068) + fuel.add_nuclide("U238", 0.9487090) + fuel.add_nuclide("U236", 0.0002318) + fuel.add_nuclide("O16", 2.0) + fuel.set_density("g/cm3", 11.0) + + water = openmc.Material() + water.add_nuclide("H1", 2.0) + water.add_nuclide("O16", 1.0) + water.set_density("g/cm3", 1.0) + + # ============================================================================= + # Geometry + # ============================================================================= + + # ----------------------------------------------------------------------------- + # Cylindrical core + # ----------------------------------------------------------------------------- + + # Parameters + core_radius = 2.0 + core_height = 4.0 + + # Surfaces + core_cylinder = openmc.ZCylinder(r=core_radius) + core_lower_plane = openmc.ZPlane(-core_height / 2.0) + core_upper_plane = openmc.ZPlane(core_height / 2.0) + + # Region + core_region = -core_cylinder & +core_lower_plane & -core_upper_plane + + # Cells + core = openmc.Cell(fill=fuel, region=core_region) + outside_core_region = +core_cylinder | -core_lower_plane | +core_upper_plane + outside_core = openmc.Cell(fill=water, region=outside_core_region) + + # Universe + inside_box1_universe = openmc.Universe(cells=[core, outside_core]) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 6.0 + + # Surfaces + box1_lower_plane = openmc.ZPlane(-box1_size / 2.0, boundary_type="periodic") + box1_upper_plane = openmc.ZPlane(box1_size / 2.0, boundary_type="periodic") + box1_left_plane = openmc.XPlane(-box1_size / 2.0, boundary_type="reflective") + box1_right_plane = openmc.XPlane(box1_size / 2.0, boundary_type="reflective") + box1_rear_plane = openmc.YPlane(-box1_size / 2.0, boundary_type="reflective") + box1_front_plane = openmc.YPlane(box1_size / 2.0, boundary_type="reflective") + + # Region + box1_region = ( + +box1_lower_plane + & -box1_upper_plane + & +box1_left_plane + & -box1_right_plane + & +box1_rear_plane + & -box1_front_plane + ) + + # Cell + box1 = openmc.Cell(fill=inside_box1_universe, region=box1_region) + + # Register geometry + model.geometry = openmc.Geometry([box1]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + bounds = [ + -core_radius, + -core_radius, + -core_height / 2.0, + core_radius, + core_radius, + core_height / 2.0, + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource( + space=distribution, constraints={'fissionable': True}) + + return model + + +def return_surface_source_data(filepath): + """Read a surface source file and return a sorted array composed + of flatten arrays of source data for each surface source point. + + Parameters + ---------- + filepath : str + Path to the surface source file + + Returns + ------- + data : np.array + Sorted array composed of flatten arrays of source data for + each surface source point + + """ + data = [] + keys = [] + + # Read source file + source = openmc.read_source_file(filepath) + + for point in source: + r = point.r + u = point.u + e = point.E + time = point.time + wgt = point.wgt + delayed_group = point.delayed_group + surf_id = point.surf_id + particle = point.particle + key = ( + f"{r[0]:.10e} {r[1]:.10e} {r[2]:.10e} {u[0]:.10e} {u[1]:.10e} {u[2]:.10e}" + f"{e:.10e} {time:.10e} {wgt:.10e} {delayed_group} {surf_id} {particle}" + ) + keys.append(key) + values = [*r, *u, e, time, wgt, delayed_group, surf_id, particle] + assert len(values) == 12 + data.append(values) + + data = np.array(data) + keys = np.array(keys) + sorted_idx = np.argsort(keys) + + return data[sorted_idx] + + +class SurfaceSourceWriteTestHarness(PyAPITestHarness): + def __init__(self, statepoint_name, model=None, inputs_true=None, workdir=None): + super().__init__(statepoint_name, model, inputs_true) + self.workdir = workdir + + def _test_output_created(self): + """Make sure surface_source.h5 has also been created.""" + super()._test_output_created() + if self._model.settings.surf_source_write: + assert os.path.exists( + "surface_source.h5" + ), "Surface source file has not been created." + + def _compare_output(self): + """Compare surface_source.h5 files.""" + if self._model.settings.surf_source_write: + source_true = return_surface_source_data("surface_source_true.h5") + source_test = return_surface_source_data("surface_source.h5") + np.testing.assert_allclose(source_true, source_test, rtol=1e-07) + + def main(self): + """Accept commandline arguments and either run or update tests.""" + if config["build_inputs"]: + self.build_inputs() + elif config["update"]: + self.update_results() + else: + self.execute_test() + + def build_inputs(self): + """Build inputs.""" + base_dir = os.getcwd() + try: + os.chdir(self.workdir) + self._build_inputs() + finally: + os.chdir(base_dir) + + def execute_test(self): + """Build inputs, run OpenMC, and verify correct results.""" + base_dir = os.getcwd() + try: + os.chdir(self.workdir) + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._compare_inputs() + self._run_openmc() + self._test_output_created() + self._compare_output() + results = self._get_results() + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + os.chdir(base_dir) + + def update_results(self): + """Update results_true.dat and inputs_true.dat""" + base_dir = os.getcwd() + try: + os.chdir(self.workdir) + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._overwrite_inputs() + self._run_openmc() + self._test_output_created() + results = self._get_results() + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + os.chdir(base_dir) + + def _overwrite_results(self): + """Also add the 'surface_source.h5' file during overwriting.""" + super()._overwrite_results() + if os.path.exists("surface_source.h5"): + shutil.copyfile("surface_source.h5", "surface_source_true.h5") + + def _cleanup(self): + """Also remove the 'surface_source.h5' file while cleaning.""" + super()._cleanup() + fs = "surface_source.h5" + if os.path.exists(fs): + os.remove(fs) + + +@pytest.mark.skipif(config["event"] is True, reason="Results from history-based mode.") +@pytest.mark.parametrize( + "folder, model_name, parameter", + [ + ("case-01", "model_1", {"max_particles": 300}), + ("case-02", "model_1", {"max_particles": 300, "surface_ids": [8]}), + ( + "case-03", + "model_1", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9]}, + ), + ( + "case-04", + "model_1", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 2}, + ), + ( + "case-05", + "model_1", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 3}, + ), + ("case-06", "model_1", {"max_particles": 300, "cell": 2}), + ("case-07", "model_1", {"max_particles": 300, "cell": 3}), + ("case-08", "model_1", {"max_particles": 300, "cellfrom": 2}), + ("case-09", "model_1", {"max_particles": 300, "cellto": 2}), + ("case-10", "model_1", {"max_particles": 300, "cellfrom": 3}), + ("case-11", "model_1", {"max_particles": 300, "cellto": 3}), + ( + "case-12", + "model_2", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9]}, + ), + ( + "case-13", + "model_2", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 3}, + ), + ( + "case-14", + "model_2", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cellfrom": 3}, + ), + ( + "case-15", + "model_2", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cellto": 3}, + ), + ( + "case-16", + "model_3", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9]}, + ), + ( + "case-17", + "model_3", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 3}, + ), + ( + "case-18", + "model_3", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cellfrom": 3}, + ), + ( + "case-19", + "model_3", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cellto": 3}, + ), + ( + "case-20", + "model_4", + {"max_particles": 300, "surface_ids": [4]}, + ), + ( + "case-21", + "model_4", + {"max_particles": 300, "surface_ids": [4], "cell": 3}, + ), + ], +) +def test_surface_source_cell_history_based( + folder, model_name, parameter, single_thread, single_process, request +): + """Test on history-based results for CSG-only geometries.""" + assert os.environ["OMP_NUM_THREADS"] == "1" + assert config["mpi_np"] == "1" + model = request.getfixturevalue(model_name) + model.settings.surf_source_write = parameter + harness = SurfaceSourceWriteTestHarness( + "statepoint.5.h5", model=model, workdir=folder + ) + harness.main() + + +@pytest.mark.skipif(config["event"] is True, reason="Results from history-based mode.") +def test_consistency_low_realization_number(model_1, two_threads, single_process): + """The objective is to test that the results produced, in a case where + the number of potential realization (particle storage) is low + compared to the capacity of storage, are still consistent. + + This configuration ensures that the competition between threads does not + occur and that the content of the source file created can be compared. + + """ + assert os.environ["OMP_NUM_THREADS"] == "2" + assert config["mpi_np"] == "1" + model_1.settings.surf_source_write = { + "max_particles": 200, + "surface_ids": [1, 2, 3], + "cellfrom": 2, + } + harness = SurfaceSourceWriteTestHarness( + "statepoint.5.h5", model=model_1, workdir="case-a01" + ) + harness.main() + + +@pytest.mark.skipif(config["event"] is False, reason="Results from event-based mode.") +@pytest.mark.parametrize( + "folder, model_name, parameter", + [ + ( + "case-e01", + "model_1", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 2}, + ), + ("case-e02", "model_1", {"max_particles": 300, "cell": 3}), + ( + "case-e03", + "model_2", + {"max_particles": 300, "surface_ids": [4, 5, 6, 7, 8, 9], "cell": 3}, + ), + ], +) +def test_surface_source_cell_event_based( + folder, model_name, parameter, single_thread, single_process, request +): + """Test on event-based results for CSG-only geometries.""" + assert os.environ["OMP_NUM_THREADS"] == "1" + assert config["mpi_np"] == "1" + model = request.getfixturevalue(model_name) + model.settings.surf_source_write = parameter + harness = SurfaceSourceWriteTestHarness( + "statepoint.5.h5", model=model, workdir=folder + ) + harness.main() + + +@pytest.fixture(scope="module") +def model_dagmc_1(): + """Model based on the mesh file 'dagmc.h5m' available from + tests/regression_tests/dagmc/legacy. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + u235 = openmc.Material(name="no-void fuel") + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) + u235.id = 40 + + water = openmc.Material(name="water") + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.id = 41 + + materials = openmc.Materials([u235, water]) + model.materials = materials + + # ============================================================================= + # Geometry + # ============================================================================= + + dagmc_univ = openmc.DAGMCUniverse(Path("../../dagmc/legacy/dagmc.h5m")) + model.geometry = openmc.Geometry(dagmc_univ) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20]) + model.settings.source = openmc.IndependentSource( + space=source_box, constraints={'fissionable': True}) + + return model + + +@pytest.fixture(scope="module") +def model_dagmc_2(): + """Model based on the mesh file 'dagmc.h5m' available from + tests/regression_tests/dagmc/legacy. + + This model corresponds to the model_dagmc_1 contained in two boxes to introduce + multiple level of coordinates from CSG geometry. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + u235 = openmc.Material(name="no-void fuel") + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) + u235.id = 40 + + water = openmc.Material(name="water") + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.id = 41 + + materials = openmc.Materials([u235, water]) + model.materials = materials + + # ============================================================================= + # Geometry + # ============================================================================= + + dagmc_univ = openmc.DAGMCUniverse(Path("../../dagmc/legacy/dagmc.h5m")) + + # ----------------------------------------------------------------------------- + # Box 1 + # ----------------------------------------------------------------------------- + + # Parameters + box1_size = 44 + + # Surfaces + box1_lower_plane = openmc.ZPlane(-box1_size / 2.0, surface_id=101) + box1_upper_plane = openmc.ZPlane(box1_size / 2.0, surface_id=102) + box1_left_plane = openmc.XPlane(-box1_size / 2.0, surface_id=103) + box1_right_plane = openmc.XPlane(box1_size / 2.0, surface_id=104) + box1_rear_plane = openmc.YPlane(-box1_size / 2.0, surface_id=105) + box1_front_plane = openmc.YPlane(box1_size / 2.0, surface_id=106) + + # Region + box1_region = ( + +box1_lower_plane + & -box1_upper_plane + & +box1_left_plane + & -box1_right_plane + & +box1_rear_plane + & -box1_front_plane + ) + + # Cell + box1 = openmc.Cell(fill=dagmc_univ, region=box1_region, cell_id=8) + + # ----------------------------------------------------------------------------- + # Box 2 + # ----------------------------------------------------------------------------- + + # Parameters + box2_size = 48 + + # Surfaces + box2_lower_plane = openmc.ZPlane( + -box2_size / 2.0, boundary_type="vacuum", surface_id=107 + ) + box2_upper_plane = openmc.ZPlane( + box2_size / 2.0, boundary_type="vacuum", surface_id=108 + ) + box2_left_plane = openmc.XPlane( + -box2_size / 2.0, boundary_type="vacuum", surface_id=109 + ) + box2_right_plane = openmc.XPlane( + box2_size / 2.0, boundary_type="vacuum", surface_id=110 + ) + box2_rear_plane = openmc.YPlane( + -box2_size / 2.0, boundary_type="vacuum", surface_id=111 + ) + box2_front_plane = openmc.YPlane( + box2_size / 2.0, boundary_type="vacuum", surface_id=112 + ) + + # Region + inside_box2 = ( + +box2_lower_plane + & -box2_upper_plane + & +box2_left_plane + & -box2_right_plane + & +box2_rear_plane + & -box2_front_plane + ) + outside_box1 = ( + -box1_lower_plane + | +box1_upper_plane + | -box1_left_plane + | +box1_right_plane + | -box1_rear_plane + | +box1_front_plane + ) + + box2_region = inside_box2 & outside_box1 + + # Cell + box2 = openmc.Cell(fill=water, region=box2_region, cell_id=9) + + # Register geometry + model.geometry = openmc.Geometry([box1, box2]) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 100 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20]) + model.settings.source = openmc.IndependentSource( + space=source_box, constraints={'fissionable': True}) + + return model + + +@pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) +@pytest.mark.skipif(config["event"] is True, reason="Results from history-based mode.") +@pytest.mark.parametrize( + "folder, model_name, parameter", + [ + ("case-d01", "model_dagmc_1", {"max_particles": 300}), + ("case-d02", "model_dagmc_1", {"max_particles": 300, "surface_ids": [1]}), + ("case-d03", "model_dagmc_1", {"max_particles": 300, "cell": 2}), + ( + "case-d04", + "model_dagmc_1", + {"max_particles": 300, "surface_ids": [1], "cell": 2}, + ), + ("case-d05", "model_dagmc_1", {"max_particles": 300, "cellfrom": 2}), + ("case-d06", "model_dagmc_1", {"max_particles": 300, "cellto": 2}), + ( + "case-d07", + "model_dagmc_2", + { + "max_particles": 300, + "surface_ids": [101, 102, 103, 104, 105, 106], + "cell": 7, + }, + ), + ( + "case-d08", + "model_dagmc_2", + { + "max_particles": 300, + "surface_ids": [101, 102, 103, 104, 105, 106], + "cell": 8, + }, + ), + ], +) +def test_surface_source_cell_dagmc( + folder, model_name, parameter, single_thread, single_process, request +): + """Test on models with DAGMC geometries.""" + assert os.environ["OMP_NUM_THREADS"] == "1" + assert config["mpi_np"] == "1" + model = request.getfixturevalue(model_name) + model.settings.surf_source_write = parameter + harness = SurfaceSourceWriteTestHarness( + "statepoint.5.h5", model=model, workdir=folder + ) + harness.main() diff --git a/tests/regression_tests/surface_tally/inputs_true.dat b/tests/regression_tests/surface_tally/inputs_true.dat index 96639a0761..2b070c5a3f 100644 --- a/tests/regression_tests/surface_tally/inputs_true.dat +++ b/tests/regression_tests/surface_tally/inputs_true.dat @@ -1,103 +1,105 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 0 - - - -0.62992 -0.62992 -1 0.62992 0.62992 1 - - - - - - - 13 - - - 14 - - - 0.0 4000000.0 20000000.0 - - - 0.0 0.7853981633974483 3.141592653589793 - - - 0.0 0.7853981633974483 3.141592653589793 - - - 1 - - - 14 - - - 13 - - - 2 - - - 3 - - - 5 6 1 2 3 - current - - - 5 4 1 2 3 - current - - - 7 8 1 2 3 - current - - - 7 4 1 2 3 - current - - - 4 1 2 3 - current - - - 10 1 2 3 - current - - - 11 1 - current - - - 11 1 - current - - + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 0 + + + -0.62992 -0.62992 -1 0.62992 0.62992 1 + + + true + + + + + + 1 + + + 2 + + + 0.0 4000000.0 20000000.0 + + + 0.0 0.7853981633974483 3.141592653589793 + + + 0.0 0.7853981633974483 3.141592653589793 + + + 1 + + + 2 + + + 1 + + + 2 + + + 3 + + + 5 6 1 2 3 + current + + + 5 4 1 2 3 + current + + + 7 8 1 2 3 + current + + + 7 4 1 2 3 + current + + + 4 1 2 3 + current + + + 10 1 2 3 + current + + + 11 1 + current + + + 11 1 + current + + + diff --git a/tests/regression_tests/surface_tally/results_true.dat b/tests/regression_tests/surface_tally/results_true.dat index cfd70a0137..70d5cad2c6 100644 --- a/tests/regression_tests/surface_tally/results_true.dat +++ b/tests/regression_tests/surface_tally/results_true.dat @@ -1,52 +1,52 @@ mean,std. dev. -1.9600000e-02,1.5719768e-03 -7.3700000e-02,1.9382122e-03 -1.7130000e-01,4.0907755e-03 -6.3490000e-01,8.8197380e-03 -2.5000000e-03,5.2174919e-04 -6.0000000e-03,1.6799471e-03 -1.0600000e-02,1.8749815e-03 -4.0200000e-02,1.6852300e-03 -1.9600000e-02,1.5719768e-03 -7.3700000e-02,1.9382122e-03 -1.7130000e-01,4.0907755e-03 -6.3490000e-01,8.8197380e-03 -2.5000000e-03,5.2174919e-04 -6.0000000e-03,1.6799471e-03 -1.0600000e-02,1.8749815e-03 -4.0200000e-02,1.6852300e-03 -4.7000000e-03,9.1954095e-04 -4.5400000e-02,2.4413111e-03 -4.2800000e-02,2.2150997e-03 -4.2460000e-01,7.3017502e-03 +2.4100000e-02,2.1052844e-03 +6.7900000e-02,2.3211587e-03 +1.6860000e-01,4.4800794e-03 +6.4710000e-01,9.1826527e-03 +2.2000000e-03,3.8873013e-04 +5.5000000e-03,1.0979779e-03 +1.2500000e-02,1.5438048e-03 +4.4700000e-02,1.9035055e-03 +2.4100000e-02,2.1052844e-03 +6.7900000e-02,2.3211587e-03 +1.6860000e-01,4.4800794e-03 +6.4710000e-01,9.1826527e-03 +2.2000000e-03,3.8873013e-04 +5.5000000e-03,1.0979779e-03 +1.2500000e-02,1.5438048e-03 +4.4700000e-02,1.9035055e-03 +7.3000000e-03,9.8938814e-04 +3.6600000e-02,2.5086517e-03 +4.1600000e-02,2.4864075e-03 +4.2380000e-01,9.6087923e-03 +1.0000000e-04,1.0000000e-04 +1.5000000e-03,4.5338235e-04 +3.0000000e-04,1.5275252e-04 +1.7300000e-02,1.4609738e-03 +-7.3000000e-03,9.8938814e-04 +-3.6600000e-02,2.5086517e-03 +-4.1600000e-02,2.4864075e-03 +-4.2380000e-01,9.6087923e-03 +-1.0000000e-04,1.0000000e-04 +-1.5000000e-03,4.5338235e-04 +-3.0000000e-04,1.5275252e-04 +-1.7300000e-02,1.4609738e-03 +1.6800000e-02,1.5902481e-03 +3.1300000e-02,3.8094911e-03 +1.2700000e-01,5.0990195e-03 +2.2330000e-01,9.3631073e-03 +2.1000000e-03,3.7859389e-04 +4.0000000e-03,1.0540926e-03 +1.2200000e-02,1.6110728e-03 +2.7400000e-02,1.6613248e-03 0.0000000e+00,0.0000000e+00 -1.6000000e-03,2.6666667e-04 -4.0000000e-04,1.6329932e-04 -1.5700000e-02,1.1551816e-03 --4.7000000e-03,9.1954095e-04 --4.5400000e-02,2.4413111e-03 --4.2800000e-02,2.2150997e-03 --4.2460000e-01,7.3017502e-03 +-3.2000000e-02,1.5634719e-03 0.0000000e+00,0.0000000e+00 --1.6000000e-03,2.6666667e-04 --4.0000000e-04,1.6329932e-04 --1.5700000e-02,1.1551816e-03 -1.4900000e-02,1.5235193e-03 -2.8300000e-02,2.0925795e-03 -1.2850000e-01,4.0613353e-03 -2.1030000e-01,5.4955538e-03 -2.5000000e-03,5.2174919e-04 -4.4000000e-03,1.4847372e-03 -1.0200000e-02,1.8903263e-03 -2.4500000e-02,1.7966017e-03 +-3.5400000e-01,7.5938572e-03 0.0000000e+00,0.0000000e+00 --3.0900000e-02,2.0518285e-03 +-3.0000000e-03,6.4978629e-04 0.0000000e+00,0.0000000e+00 --3.4370000e-01,4.5093730e-03 -0.0000000e+00,0.0000000e+00 --2.9000000e-03,8.0897741e-04 -0.0000000e+00,0.0000000e+00 --2.2200000e-02,2.0210009e-03 +-2.1800000e-02,1.4892205e-03 0.0000000e+00,0.0000000e+00 0.0000000e+00,0.0000000e+00 0.0000000e+00,0.0000000e+00 diff --git a/tests/regression_tests/surface_tally/test.py b/tests/regression_tests/surface_tally/test.py index 96199ec2a4..e496ac0f65 100644 --- a/tests/regression_tests/surface_tally/test.py +++ b/tests/regression_tests/surface_tally/test.py @@ -6,7 +6,8 @@ from tests.testing_harness import PyAPITestHarness class SurfaceTallyTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Instantiate some Materials and register the appropriate Nuclides uo2 = openmc.Material(name='UO2 fuel at 2.4% wt enrichment') uo2.set_density('g/cc', 10.0) @@ -21,8 +22,7 @@ class SurfaceTallyTestHarness(PyAPITestHarness): borated_water.add_nuclide('O16', 1.0) # Instantiate a Materials collection and export to XML - materials_file = openmc.Materials([uo2, borated_water]) - materials_file.export_to_xml() + self._model.materials = openmc.Materials([uo2, borated_water]) # Instantiate ZCylinder surfaces fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=1, @@ -61,8 +61,7 @@ class SurfaceTallyTestHarness(PyAPITestHarness): root_univ.add_cell(root_cell) # Instantiate a Geometry, register the root Universe - geometry = openmc.Geometry(root_univ) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root_univ) # Instantiate a Settings object, set all runtime parameters settings_file = openmc.Settings() @@ -73,10 +72,10 @@ class SurfaceTallyTestHarness(PyAPITestHarness): # Create an initial uniform spatial source distribution bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] - uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:],\ - only_fissionable=True) - settings_file.source = openmc.source.Source(space=uniform_dist) - settings_file.export_to_xml() + uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:],) + settings_file.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) + self._model.settings = settings_file # Tallies file tallies_file = openmc.Tallies() @@ -107,19 +106,19 @@ class SurfaceTallyTestHarness(PyAPITestHarness): # Create partial current tallies from water to fuel # Filters - cell_from_filter = openmc.CellFromFilter(water) + mat_from_filter = openmc.MaterialFromFilter(borated_water) cell_filter = openmc.CellFilter(fuel) # Cell to cell filters for partial current cell_to_cell_tally = openmc.Tally(name=str('water_to_fuel_1')) - cell_to_cell_tally.filters = [cell_from_filter, cell_filter, \ + cell_to_cell_tally.filters = [mat_from_filter, cell_filter, \ energy_filter, polar_filter, azimuthal_filter] cell_to_cell_tally.scores = ['current'] tallies_file.append(cell_to_cell_tally) # Cell from + surface filters for partial current cell_to_cell_tally = openmc.Tally(name=str('water_to_fuel_2')) - cell_to_cell_tally.filters = [cell_from_filter, surface_filter, \ + cell_to_cell_tally.filters = [mat_from_filter, surface_filter, \ energy_filter, polar_filter, azimuthal_filter] cell_to_cell_tally.scores = ['current'] tallies_file.append(cell_to_cell_tally) @@ -156,7 +155,7 @@ class SurfaceTallyTestHarness(PyAPITestHarness): surf_tally3.scores = ['current'] tallies_file.append(surf_tally3) - tallies_file.export_to_xml() + self._model.tallies = tallies_file def _get_results(self): """Digest info in the statepoint and return as a string.""" @@ -164,9 +163,8 @@ class SurfaceTallyTestHarness(PyAPITestHarness): sp = openmc.StatePoint(self._sp_name) # Extract the tally data as a Pandas DataFrame. - df = pd.DataFrame() - for t in sp.tallies.values(): - df = df.append(t.get_pandas_dataframe(), ignore_index=True) + tally_dfs = [t.get_pandas_dataframe() for t in sp.tallies.values()] + df = pd.concat(tally_dfs, ignore_index=True) # Extract the relevant data as a CSV string. cols = ('mean', 'std. dev.') @@ -175,5 +173,5 @@ class SurfaceTallyTestHarness(PyAPITestHarness): def test_surface_tally(): - harness = SurfaceTallyTestHarness('statepoint.10.h5') + harness = SurfaceTallyTestHarness('statepoint.10.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/survival_biasing/results_true.dat b/tests/regression_tests/survival_biasing/results_true.dat index b9868f3d3a..932414e98b 100644 --- a/tests/regression_tests/survival_biasing/results_true.dat +++ b/tests/regression_tests/survival_biasing/results_true.dat @@ -1,20 +1,20 @@ k-combined: -9.826269E-01 1.626276E-02 +9.517646E-01 1.303111E-02 tally 1: -4.212303E+01 -3.550855E+02 -1.760388E+01 -6.205773E+01 -2.165888E+00 -9.392975E-01 -1.873459E+00 -7.025989E-01 -4.850267E+00 -4.708899E+00 -3.397795E-02 -2.310621E-04 -3.628554E+08 -2.635633E+16 +4.164635E+01 +3.470110E+02 +1.724300E+01 +5.949580E+01 +2.124917E+00 +9.034789E-01 +1.844790E+00 +6.809026E-01 +4.784396E+00 +4.579552E+00 +3.348849E-02 +2.243613E-04 +3.573090E+08 +2.554338E+16 tally 2: -1.760388E+01 -6.205773E+01 +1.724300E+01 +5.949580E+01 diff --git a/tests/regression_tests/tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat index bc9efe97ef..40829f865a 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -1,38 +1,187 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 @@ -50,12 +199,12 @@ 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 @@ -73,12 +222,12 @@ 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 @@ -100,12 +249,12 @@ 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 @@ -127,402 +276,228 @@ 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 400 - 5 - 0 - - - -160 -160 -183 160 160 183 - - - - - - - 2 2 - -182.07 -182.07 - 182.07 182.07 - - - -3.14159 -1.885 -0.6283 0.6283 1.885 3.14159 - - - 1 - - - 10 21 22 23 - - - 1 2 3 4 5 6 - - - 0.0 0.253 1000.0 1000000.0 20000000.0 - - - 0.0 0.253 1000.0 1000000.0 20000000.0 - - - 1 2 3 4 - - - -1.0 -0.5 0.0 0.5 1.0 - - - 0.0 0.6283 1.2566 1.885 2.5132 3.14159 - - - 4 - - - 4 - - - 1 2 3 4 6 8 - - - 1 2 5 3 6 - - - 10 21 22 23 60 - - - 21 22 23 27 28 29 60 - - - 1 - flux - tracklength - - - 1 - flux - analog - - - 1 2 - flux - tracklength - - - 3 - total - - - 4 - U235 O16 total - delayed-nu-fission decay-rate - - - 5 - total - - - 6 - scatter - - - 5 6 - scatter nu-fission - - - 7 - total - - - 8 - scatter nu-scatter - - - 8 2 - scatter nu-scatter - - - 9 - flux - tracklength - - - 9 - flux - analog - - - 9 2 - flux - tracklength - - - 10 - scatter nu-scatter - analog - - - 11 - scatter nu-scatter flux total - analog - - - 11 - flux total - collision - - - 11 - flux total - tracklength - - - 12 - total - - - 15 - scatter - - - 13 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - tracklength - - - 13 - U235 O16 total - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - tracklength - - - 13 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - analog - - - 13 - U235 O16 total - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - analog - - - 13 - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - collision - - - 13 - U235 O16 total - absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate - collision - - - 14 - flux - tracklength - - - 14 - flux - analog - - - 14 - flux - collision - - - 13 - all - total - tracklength - - - 13 - all - total - collision - - - 2 - all - total - tracklength - - - 2 - U235 - total - tracklength - - - H1-production H2-production H3-production He3-production He4-production heating damage-energy - - + + + + + + + + + + + + + + + + + + + + + eigenvalue + 400 + 5 + 0 + + + -160 -160 -183 160 160 183 + + + + + + 2 2 + -182.07 -182.07 + 182.07 182.07 + + + -3.14159 -1.885 -0.6283 0.6283 1.885 3.14159 + + + 1 + + + 10 21 22 23 + + + 1 2 3 4 5 6 + + + 0.0 0.253 1000.0 1000000.0 20000000.0 + + + 0.0 0.253 1000.0 1000000.0 20000000.0 + + + 1 2 3 4 + + + -1.0 -0.5 0.0 0.5 1.0 + + + 0.0 0.6283 1.2566 1.885 2.5132 3.14159 + + + 4 + + + 4 + + + 1 2 3 4 6 8 + + + 1 2 5 3 6 + + + 10 21 22 23 60 + + + 21 22 23 27 28 29 60 + + + 1 + flux + tracklength + + + 1 + flux + analog + + + 1 2 + flux + tracklength + + + 3 + total + + + 4 + U235 O16 total + delayed-nu-fission decay-rate + + + 5 + total + + + 6 + scatter + + + 5 6 + scatter nu-fission + + + 7 + total + + + 8 + scatter nu-scatter + + + 8 2 + scatter nu-scatter + + + 9 + flux + tracklength + + + 9 + flux + analog + + + 9 2 + flux + tracklength + + + 10 + scatter nu-scatter + analog + + + 11 + scatter nu-scatter flux total + analog + + + 11 + flux total + collision + + + 11 + flux total + tracklength + + + 12 + total + + + 15 + scatter + + + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + tracklength + + + 13 + U235 O16 total + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + tracklength + + + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + analog + + + 13 + U235 O16 total + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + analog + + + 13 + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + collision + + + 13 + U235 O16 total + absorption delayed-nu-fission events fission inverse-velocity kappa-fission (n,2n) (n,n1) (n,gamma) nu-fission scatter elastic total prompt-nu-fission fission-q-prompt fission-q-recoverable decay-rate + collision + + + 14 + flux + tracklength + + + 14 + flux + analog + + + 14 + flux + collision + + + H1-production H2-production H3-production He3-production He4-production heating damage-energy + + + diff --git a/tests/regression_tests/tallies/results_true.dat b/tests/regression_tests/tallies/results_true.dat index c73ff33a43..1d3aca4b07 100644 --- a/tests/regression_tests/tallies/results_true.dat +++ b/tests/regression_tests/tallies/results_true.dat @@ -1 +1 @@ -de09325b517aad9f58940e5fcd53b005c57ea008a15699769ab91aba723de141014101e55be2621e34c369beefb6db8c6cf847e241ee26e73e394c0f155138b0 \ No newline at end of file +d01c3accd5b4de2aa166a77df28cfe42f5738a44c2480752fcfae7564a507362fff006b6dffb7b1dfe248e14bacef0070cabacea5d75c5996653e5605f7c7384 \ No newline at end of file diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index 7746a4e241..d20067ed33 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -13,7 +13,7 @@ def test_tallies(): model.settings.batches = 5 model.settings.inactive = 0 model.settings.particles = 400 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-160, -160, -183], [160, 160, 183])) azimuthal_bins = (-3.14159, -1.8850, -0.6283, 0.6283, 1.8850, 3.14159) @@ -40,7 +40,7 @@ def test_tallies(): cellborn_tally = Tally() cellborn_tally.filters = [ - CellbornFilter((model.geometry.get_all_cells()[10], + CellBornFilter((model.geometry.get_all_cells()[10], model.geometry.get_all_cells()[21], 22, 23))] # Test both Cell objects and ids cellborn_tally.scores = ['total'] @@ -154,17 +154,6 @@ def test_tallies(): flux_tallies[1].estimator = 'analog' flux_tallies[2].estimator = 'collision' - all_nuclide_tallies = [Tally() for i in range(4)] - for t in all_nuclide_tallies: - t.filters = [cell_filter] - t.estimator = 'tracklength' - t.nuclides = ['all'] - t.scores = ['total'] - all_nuclide_tallies[1].estimator = 'collision' - all_nuclide_tallies[2].filters = [mesh_filter] - all_nuclide_tallies[3].filters = [mesh_filter] - all_nuclide_tallies[3].nuclides = ['U235'] - fusion_tally = Tally() fusion_tally.scores = ['H1-production', 'H2-production', 'H3-production', 'He3-production', 'He4-production', 'heating', 'damage-energy'] @@ -180,11 +169,10 @@ def test_tallies(): cellborn_tally, dg_tally, energy_tally, energyout_tally, transfer_tally, material_tally, mu_tally1, mu_tally2, polar_tally1, polar_tally2, polar_tally3, legendre_tally, - harmonics_tally, harmonics_tally2, harmonics_tally3, + harmonics_tally, harmonics_tally2, harmonics_tally3, universe_tally, collision_tally] model.tallies += score_tallies model.tallies += flux_tallies - model.tallies += all_nuclide_tallies model.tallies.append(fusion_tally) harness.main() diff --git a/tests/regression_tests/tally_aggregation/inputs_true.dat b/tests/regression_tests/tally_aggregation/inputs_true.dat index 80356f7119..7351b230c8 100644 --- a/tests/regression_tests/tally_aggregation/inputs_true.dat +++ b/tests/regression_tests/tally_aggregation/inputs_true.dat @@ -1,57 +1,56 @@ - - - - - - 1.2 1.2 - 1 - 2 2 - -1.2 -1.2 - + + + + + + + + + + + + + + + + + + + + + + 1.2 1.2 + 1 + 2 2 + -1.2 -1.2 + 1 1 1 1 - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - - - - - 0.0 0.253 1000.0 1000000.0 20000000.0 - - - 1 - - - 1 2 - U234 U235 U238 - nu-fission total - - + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + + 0.0 0.253 1000.0 1000000.0 20000000.0 + + + 1 + + + 1 2 + U234 U235 U238 + nu-fission total + + + diff --git a/tests/regression_tests/tally_aggregation/results_true.dat b/tests/regression_tests/tally_aggregation/results_true.dat index a86eff895d..ee6263373a 100644 --- a/tests/regression_tests/tally_aggregation/results_true.dat +++ b/tests/regression_tests/tally_aggregation/results_true.dat @@ -1,97 +1,97 @@ -[[1.6100557e-05 5.2845844e-04] - [3.1178030e-01 1.6963068e-01] - [1.8103970e-02 7.1792000e-01]], [[1.6327747e-05 5.3562414e-04] - [3.2299959e-01 1.7506573e-01] - [1.8518775e-02 7.2159429e-01]], [[1.5779072e-05 5.7847880e-04] - [3.1206099e-01 1.6937953e-01] - [1.7615699e-02 6.9887963e-01]], [[1.6129040e-05 5.2214564e-04] - [3.3116549e-01 1.7924984e-01] - [1.8339236e-02 7.1809645e-01]][[2.5070599e-07 4.1541177e-05] - [8.8868371e-03 4.3482360e-03] - [4.2929455e-04 5.1166511e-03]], [[2.6348920e-07 4.0911978e-05] - [9.5560259e-03 4.6948487e-03] - [5.1500008e-04 8.2022796e-03]], [[2.8482730e-07 5.2369388e-05] - [5.9366950e-03 2.9803949e-03] - [3.5036278e-04 7.1193378e-03]], [[4.9529279e-07 5.2183723e-05] - [1.1645140e-02 5.7325245e-03] - [9.3005699e-04 9.2536054e-03]][[1.0276552e-06 8.0660598e-04] - [1.1192721e+00 5.5462743e-01] - [1.3911834e-06 5.0699881e-01]], [[1.9629638e-06 1.0409504e-03] - [1.4059305e-01 9.8087142e-02] - [2.9492646e-05 7.8705320e-01]], [[1.5107402e-05 2.2995488e-04] - [1.3135825e-02 2.9816182e-02] - [3.2964785e-04 1.1215217e+00]], [[4.6238395e-05 8.7195730e-05] - [5.0054342e-03 1.0795027e-02] - [7.2217149e-02 4.4091666e-01]][[1.6532673e-08 1.2337935e-05] - [1.8386631e-02 9.0206165e-03] - [2.1821704e-08 6.5857974e-03]], [[1.4398993e-07 9.3322326e-05] - [1.7502917e-03 1.1333022e-03] - [1.2352929e-05 1.0990819e-02]], [[2.0866393e-07 1.5849226e-06] - [1.0531329e-04 1.2841877e-04] - [5.5780807e-06 5.5626593e-03]], [[6.2783320e-07 1.1660991e-06] - [6.5703644e-05 1.4478948e-04] - [1.1987891e-03 5.8870776e-03]][[0.2726472 0.2604765]], [[0.2826295 0.2668458]], [[0.2735803 0.2601036]], [[0.2904175 0.2750069]], [[0.0346119 0.2258269]], [[0.0357798 0.2250003]], [[0.0340719 0.2185953]], [[0.0361609 0.2167588]], [[0.0033548 0.2889666]], [[0.0034114 0.2907593]], [[0.003291 0.2797162]], [[0.0034234 0.2921258]], [[0.0192865 0.1128092]], [[0.0197139 0.1145903]], [[0.0187492 0.1104225]], [[0.0195192 0.1139769]][[0.0088278 0.0052328]], [[0.0095476 0.0057391]], [[0.0058888 0.003768 ]], [[0.0115889 0.007087 ]], [[0.0010202 0.0025054]], [[0.0003997 0.00688 ]], [[0.0007498 0.0052687]], [[0.0011406 0.0063813]], [[6.0992197e-05 2.7321486e-03]], [[3.8711312e-05 2.3727208e-03]], [[5.9558150e-05 3.2498217e-03]], [[4.8541326e-05 2.7025714e-03]], [[0.00043 0.0019911]], [[0.0005155 0.001849 ]], [[0.0003513 0.0026556]], [[0.0009313 0.0044993]][[1.9753031e-04] - [4.0779730e-01] - [1.2512884e-01]], [[2.0324106e-04] - [4.2264184e-01] - [1.2663026e-01]], [[1.9762520e-04] - [4.0915145e-01] - [1.2433487e-01]], [[2.0923706e-04] - [4.3430891e-01] - [1.3090624e-01]], [[0.0002534] - [0.0589558] - [0.2012296]], [[0.0002515] - [0.0604991] - [0.2000296]], [[0.0003047] - [0.0579695] - [0.1943931]], [[0.0002334] - [0.0612558] - [0.1914305]], [[6.0316499e-05] - [1.0710899e-02] - [2.8155013e-01]], [[6.3374666e-05] - [1.0915514e-02] - [2.8319179e-01]], [[5.9392082e-05] - [1.0461186e-02] - [2.7248670e-01]], [[6.1979034e-05] - [1.0864408e-02] - [2.8462272e-01]], [[3.3331895e-05] - [3.9469598e-03] - [1.2811544e-01]], [[3.3851214e-05] - [4.0088932e-03] - [1.3026145e-01]], [[3.2587500e-05] - [3.8583992e-03] - [1.2528067e-01]], [[3.3663516e-05] - [3.9862087e-03] - [1.2947625e-01]][[5.9589021e-06] - [9.8268994e-03] - [2.9572748e-03]], [[6.3833088e-06] - [1.0635294e-02] - [3.3142463e-03]], [[4.0655901e-06] - [6.5668548e-03] - [2.3984140e-03]], [[7.7097818e-06] - [1.2908862e-02] - [4.2297322e-03]], [[4.1105542e-05] - [1.1418202e-03] - [2.4520182e-03]], [[4.0406959e-05] - [4.9407982e-04] - [6.8737413e-03]], [[5.2191221e-05] - [9.9374825e-04] - [5.2279051e-03]], [[5.1601995e-05] - [1.3462879e-03] - [6.3408638e-03]], [[5.9462092e-07] - [9.1472877e-05] - [2.7312979e-03]], [[4.5487504e-07] - [5.5343645e-05] - [2.3723911e-03]], [[1.3568221e-06] - [1.0496033e-04] - [3.2486719e-03]], [[3.9251575e-07] - [7.1661652e-05] - [2.7020572e-03]], [[4.5230086e-07] - [5.4399064e-05] - [2.0363096e-03]], [[4.2836596e-07] - [5.0791493e-05] - [1.9188091e-03]], [[5.8337236e-07] - [7.0477126e-05] - [2.6777741e-03]], [[1.0127017e-06] - [1.2155206e-04] - [4.5930293e-03]] \ No newline at end of file +[[1.6242805e-05 6.2367673e-04] + [3.2895972e-01 1.7786452e-01] + [1.8044266e-02 7.0451122e-01]], [[1.6113947e-05 5.3572864e-04] + [3.1517504e-01 1.7132833e-01] + [1.8305682e-02 7.0072832e-01]], [[1.6472052e-05 5.5758006e-04] + [3.2362364e-01 1.7597163e-01] + [1.9107080e-02 7.2600440e-01]], [[1.6693277e-05 4.9204218e-04] + [3.2429262e-01 1.7600573e-01] + [1.9042489e-02 7.3053854e-01]][[2.9719061e-07 8.0925438e-05] + [8.9432259e-03 4.4057583e-03] + [5.2078407e-04 6.4387688e-03]], [[1.8572081e-07 2.5667235e-05] + [1.1641603e-02 5.7444311e-03] + [2.6983611e-04 6.9783224e-03]], [[2.4994113e-07 5.4993390e-05] + [6.0464041e-03 3.0764811e-03] + [2.6559118e-04 7.7279780e-03]], [[2.5965232e-07 3.9618775e-05] + [1.0409564e-02 5.1850104e-03] + [2.7831231e-04 7.4480576e-03]][[1.0329435e-06 8.0861229e-04] + [1.1265142e+00 5.5796079e-01] + [1.3965860e-06 5.0338828e-01]], [[2.2264172e-06 1.0828687e-03] + [1.4724979e-01 1.0226107e-01] + [2.2656045e-05 7.7618945e-01]], [[1.4789782e-05 2.2786630e-04] + [1.3149968e-02 2.9847169e-02] + [3.2638111e-04 1.1287326e+00]], [[4.7472938e-05 8.9680338e-05] + [5.1371041e-03 1.1101179e-02] + [7.4149084e-02 4.5347217e-01]][[1.7024771e-08 1.2878244e-05] + [1.8795566e-02 9.2705008e-03] + [2.2676749e-08 7.0878353e-03]], [[2.2018925e-07 1.0785388e-04] + [2.6715019e-03 1.6657928e-03] + [1.2479083e-05 8.6814018e-03]], [[2.0928485e-07 1.6600875e-06] + [1.1859535e-04 1.7969083e-04] + [1.5981387e-05 8.0489308e-03]], [[4.0016381e-07 7.6799157e-07] + [4.4157583e-05 9.4462925e-05] + [7.0115110e-04 3.8678806e-03]][[0.287433 0.2698042]], [[0.2751228 0.2598525]], [[0.280516 0.2648274]], [[0.2834448 0.2676736]], [[0.0370558 0.2148509]], [[0.0355217 0.2130189]], [[0.0385317 0.2292804]], [[0.0361655 0.2223832]], [[0.0033268 0.2855078]], [[0.0033498 0.2849403]], [[0.003359 0.2901157]], [[0.0034555 0.2982438]], [[0.0192046 0.1128364]], [[0.0195026 0.1147807]], [[0.0203405 0.1183102]], [[0.0202861 0.1187358]][[0.00889 0.0051934]], [[0.0115249 0.0072767]], [[0.0058841 0.0040289]], [[0.0103341 0.0063266]], [[0.0009734 0.0048865]], [[0.0016425 0.0038133]], [[0.0013902 0.0048875]], [[0.0012487 0.0039808]], [[1.8994140e-05 2.0410145e-03]], [[6.8277082e-05 3.4865782e-03]], [[6.1643568e-05 4.9220817e-03]], [[7.4147413e-05 4.9263295e-03]], [[0.0005213 0.0024208]], [[0.0002702 0.0014313]], [[0.0002665 0.0022006]], [[0.0002788 0.0014892]][[2.0601081e-04] + [4.2976789e-01] + [1.2726338e-01]], [[1.9802070e-04] + [4.1138025e-01] + [1.2339705e-01]], [[2.0174854e-04] + [4.1947542e-01] + [1.2566615e-01]], [[2.0386518e-04] + [4.2385140e-01] + [1.2706310e-01]], [[0.0003403] + [0.0624955] + [0.189071 ]], [[0.0002601] + [0.060449 ] + [0.1878314]], [[0.0002765] + [0.0652412] + [0.2022944]], [[0.0002082] + [0.0613252] + [0.1970153]], [[6.0336874e-05] + [1.0617692e-02] + [2.7815664e-01]], [[5.9823997e-05] + [1.0664059e-02] + [2.7756620e-01]], [[6.0873689e-05] + [1.0744209e-02] + [2.8266963e-01]], [[6.1621521e-05] + [1.0971177e-02] + [2.9066650e-01]], [[3.3297391e-05] + [3.9432037e-03] + [1.2806450e-01]], [[3.3863464e-05] + [4.0100180e-03] + [1.3023932e-01]], [[3.4930617e-05] + [4.1344613e-03] + [1.3448129e-01]], [[3.5061805e-05] + [4.1505999e-03] + [1.3483614e-01]][[5.8259835e-06] + [9.9096571e-03] + [2.7933778e-03]], [[8.0858909e-06] + [1.2843377e-02] + [4.5631248e-03]], [[4.3418585e-06] + [6.5638646e-03] + [2.7874612e-03]], [[6.9046376e-06] + [1.1531588e-02] + [3.7205377e-03]], [[8.0710599e-05] + [1.0889301e-03] + [4.8613465e-03]], [[2.4337121e-05] + [1.8861117e-03] + [3.6987509e-03]], [[5.4812439e-05] + [1.7085353e-03] + [4.7852139e-03]], [[3.9007791e-05] + [1.4998363e-03] + [3.8929646e-03]], [[7.5757577e-07] + [2.5845755e-05] + [2.0409391e-03]], [[1.0302346e-06] + [1.1907557e-04] + [3.4852130e-03]], [[9.1936614e-07] + [1.2974020e-04] + [4.9207575e-03]], [[5.6493555e-07] + [1.2113685e-04] + [4.9253980e-03]], [[5.4602210e-07] + [6.5233025e-05] + [2.4754550e-03]], [[3.2091909e-07] + [3.8472906e-05] + [1.4560974e-03]], [[4.8475651e-07] + [5.9108262e-05] + [2.2158781e-03]], [[3.3737822e-07] + [4.0544269e-05] + [1.5145628e-03]] \ No newline at end of file diff --git a/tests/regression_tests/tally_aggregation/test.py b/tests/regression_tests/tally_aggregation/test.py index a29839a6f8..08d9116606 100644 --- a/tests/regression_tests/tally_aggregation/test.py +++ b/tests/regression_tests/tally_aggregation/test.py @@ -33,8 +33,8 @@ def model(): [pin, pin], [pin, pin], ] - box = openmc.model.rectangular_prism(2*d, 2*d, boundary_type='reflective') - main_cell = openmc.Cell(fill=lattice, region=box) + box = openmc.model.RectangularPrism(2*d, 2*d, boundary_type='reflective') + main_cell = openmc.Cell(fill=lattice, region=-box) model.geometry = openmc.Geometry([main_cell]) model.settings.batches = 10 diff --git a/tests/regression_tests/tally_arithmetic/inputs_true.dat b/tests/regression_tests/tally_arithmetic/inputs_true.dat index fbbf94fa90..2b5234c34a 100644 --- a/tests/regression_tests/tally_arithmetic/inputs_true.dat +++ b/tests/regression_tests/tally_arithmetic/inputs_true.dat @@ -1,56 +1,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 5 - 0 - - - - - 2 2 - -10.0 -10.0 - 10.0 10.0 - - - 1 2 - - - 0.0 10.0 20000000.0 - - - 1 - - - 2 1 - U234 U235 - nu-fission total - - - 1 3 - U238 U235 - total fission - - + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 5 + 0 + + + + 2 2 + -10.0 -10.0 + 10.0 10.0 + + + 1 2 + + + 0.0 10.0 20000000.0 + + + 1 + + + 2 1 + U234 U235 + nu-fission total + + + 1 3 + U238 U235 + total fission + + + diff --git a/tests/regression_tests/tally_arithmetic/results_true.dat b/tests/regression_tests/tally_arithmetic/results_true.dat index 473dd5ee7f..baa3805871 100644 --- a/tests/regression_tests/tally_arithmetic/results_true.dat +++ b/tests/regression_tests/tally_arithmetic/results_true.dat @@ -1,49 +1,49 @@ -[2.18485e-07 1.40714e-13 1.90835e-04 1.22906e-10 2.14194e-07 1.70920e-07 - 1.87087e-04 1.49289e-04 5.77324e-03 3.71823e-09 2.98131e-03 1.92010e-09 - 5.65986e-03 4.51638e-03 2.92276e-03 2.33227e-03 1.77087e-07 1.04308e-13 - 1.54676e-04 9.11076e-11 1.53337e-07 1.20737e-07 1.33931e-04 1.05457e-04 - 4.67935e-03 2.75624e-09 2.41642e-03 1.42333e-09 4.05176e-03 3.19035e-03 - 2.09234e-03 1.64750e-03 2.32566e-07 1.53743e-13 2.03133e-04 1.34286e-10 - 2.42559e-07 1.94114e-07 2.11862e-04 1.69548e-04 6.14530e-03 4.06249e-09 - 3.17345e-03 2.09788e-09 6.40937e-03 5.12926e-03 3.30981e-03 2.64876e-03 - 2.15237e-07 1.23240e-13 1.87998e-04 1.07643e-10 1.85758e-07 1.46699e-07 - 1.62249e-04 1.28134e-04 5.68742e-03 3.25649e-09 2.93699e-03 1.68166e-09 - 4.90845e-03 3.87638e-03 2.53473e-03 2.00177e-03 2.47477e-03 4.87068e-05 - 1.06189e-02 2.08993e-04 1.26298e-04 3.02426e-05 5.41924e-04 1.29766e-04 - 2.77770e-02 5.46688e-04 4.83214e-02 9.51031e-04 1.41757e-03 3.39445e-04 - 2.46604e-03 5.90506e-04 2.31220e-03 4.16752e-05 9.92129e-03 1.78822e-04 - 1.15514e-04 2.68262e-05 4.95652e-04 1.15107e-04 2.59523e-02 4.67766e-04 - 4.51471e-02 8.13735e-04 1.29654e-03 3.01100e-04 2.25548e-03 5.23800e-04 - 2.30792e-03 4.67592e-05 9.90296e-03 2.00637e-04 1.15169e-04 2.57221e-05 - 4.94175e-04 1.10370e-04 2.59043e-02 5.24828e-04 4.50637e-02 9.13003e-04 - 1.29267e-03 2.88707e-04 2.24876e-03 5.02241e-04 2.35337e-03 4.59723e-05 - 1.00979e-02 1.97260e-04 1.17182e-04 2.59928e-05 5.02809e-04 1.11531e-04 - 2.64144e-02 5.15997e-04 4.59510e-02 8.97639e-04 1.31526e-03 2.91745e-04 - 2.28805e-03 5.07527e-04][2.18485e-07 1.40714e-13 1.90835e-04 1.22906e-10 2.14194e-07 1.70920e-07 - 1.87087e-04 1.49289e-04 5.77324e-03 3.71823e-09 2.98131e-03 1.92010e-09 - 5.65986e-03 4.51638e-03 2.92276e-03 2.33227e-03 1.77087e-07 1.04308e-13 - 1.54676e-04 9.11076e-11 1.53337e-07 1.20737e-07 1.33931e-04 1.05457e-04 - 4.67935e-03 2.75624e-09 2.41642e-03 1.42333e-09 4.05176e-03 3.19035e-03 - 2.09234e-03 1.64750e-03 2.32566e-07 1.53743e-13 2.03133e-04 1.34286e-10 - 2.42559e-07 1.94114e-07 2.11862e-04 1.69548e-04 6.14530e-03 4.06249e-09 - 3.17345e-03 2.09788e-09 6.40937e-03 5.12926e-03 3.30981e-03 2.64876e-03 - 2.15237e-07 1.23240e-13 1.87998e-04 1.07643e-10 1.85758e-07 1.46699e-07 - 1.62249e-04 1.28134e-04 5.68742e-03 3.25649e-09 2.93699e-03 1.68166e-09 - 4.90845e-03 3.87638e-03 2.53473e-03 2.00177e-03 2.47477e-03 4.87068e-05 - 1.06189e-02 2.08993e-04 1.26298e-04 3.02426e-05 5.41924e-04 1.29766e-04 - 2.77770e-02 5.46688e-04 4.83214e-02 9.51031e-04 1.41757e-03 3.39445e-04 - 2.46604e-03 5.90506e-04 2.31220e-03 4.16752e-05 9.92129e-03 1.78822e-04 - 1.15514e-04 2.68262e-05 4.95652e-04 1.15107e-04 2.59523e-02 4.67766e-04 - 4.51471e-02 8.13735e-04 1.29654e-03 3.01100e-04 2.25548e-03 5.23800e-04 - 2.30792e-03 4.67592e-05 9.90296e-03 2.00637e-04 1.15169e-04 2.57221e-05 - 4.94175e-04 1.10370e-04 2.59043e-02 5.24828e-04 4.50637e-02 9.13003e-04 - 1.29267e-03 2.88707e-04 2.24876e-03 5.02241e-04 2.35337e-03 4.59723e-05 - 1.00979e-02 1.97260e-04 1.17182e-04 2.59928e-05 5.02809e-04 1.11531e-04 - 2.64144e-02 5.15997e-04 4.59510e-02 8.97639e-04 1.31526e-03 2.91745e-04 - 2.28805e-03 5.07527e-04][0.00566 0.00452 0.00292 0.00233 0.00405 0.00319 0.00209 0.00165 0.00641 - 0.00513 0.00331 0.00265 0.00491 0.00388 0.00253 0.002 0.00142 0.00034 - 0.00247 0.00059 0.0013 0.0003 0.00226 0.00052 0.00129 0.00029 0.00225 - 0.0005 0.00132 0.00029 0.00229 0.00051][0.00019 0.00019 0.00298 0.00292 0.00015 0.00013 0.00242 0.00209 0.0002 - 0.00021 0.00317 0.00331 0.00019 0.00016 0.00294 0.00253 0.01062 0.00054 - 0.04832 0.00247 0.00992 0.0005 0.04515 0.00226 0.0099 0.00049 0.04506 - 0.00225 0.0101 0.0005 0.04595 0.00229][0.00292 0.00209 0.00331 0.00253 0.00247 0.00226 0.00225 0.00229] \ No newline at end of file +[2.29467e-07 1.47622e-13 2.02646e-04 1.30367e-10 2.20704e-07 1.76624e-07 + 1.94907e-04 1.55980e-04 6.32267e-03 4.06754e-09 3.26873e-03 2.10286e-09 + 6.08121e-03 4.86666e-03 3.14390e-03 2.51599e-03 1.89223e-07 1.06256e-13 + 1.67106e-04 9.38364e-11 1.53300e-07 1.20220e-07 1.35382e-04 1.06168e-04 + 5.21380e-03 2.92775e-09 2.69546e-03 1.51361e-09 4.22399e-03 3.31252e-03 + 2.18374e-03 1.71253e-03 2.21146e-07 1.41349e-13 1.95297e-04 1.24828e-10 + 2.11844e-07 1.69398e-07 1.87083e-04 1.49598e-04 6.09339e-03 3.89470e-09 + 3.15020e-03 2.01351e-09 5.83710e-03 4.66754e-03 3.01770e-03 2.41305e-03 + 1.92872e-07 1.05908e-13 1.70328e-04 9.35293e-11 1.59466e-07 1.25399e-07 + 1.40827e-04 1.10741e-04 5.31433e-03 2.91817e-09 2.74744e-03 1.50865e-09 + 4.39388e-03 3.45520e-03 2.27158e-03 1.78629e-03 2.44601e-03 4.71663e-05 + 1.09430e-02 2.11013e-04 1.23573e-04 2.76181e-05 5.52841e-04 1.23558e-04 + 2.71755e-02 5.24023e-04 4.76200e-02 9.18253e-04 1.37291e-03 3.06841e-04 + 2.40577e-03 5.37681e-04 2.29014e-03 4.44674e-05 1.02456e-02 1.98938e-04 + 1.12524e-04 2.48126e-05 5.03411e-04 1.11007e-04 2.54438e-02 4.94038e-04 + 4.45855e-02 8.65710e-04 1.25016e-03 2.75671e-04 2.19067e-03 4.83061e-04 + 2.38500e-03 4.58053e-05 1.06700e-02 2.04924e-04 1.22899e-04 2.95267e-05 + 5.49826e-04 1.32097e-04 2.64977e-02 5.08903e-04 4.64323e-02 8.91758e-04 + 1.36542e-03 3.28045e-04 2.39265e-03 5.74839e-04 2.17094e-03 4.21772e-05 + 9.71236e-03 1.88693e-04 1.09358e-04 2.46984e-05 4.89245e-04 1.10496e-04 + 2.41194e-02 4.68594e-04 4.22648e-02 8.21124e-04 1.21498e-03 2.74402e-04 + 2.12902e-03 4.80839e-04][2.29467e-07 1.47622e-13 2.02646e-04 1.30367e-10 2.20704e-07 1.76624e-07 + 1.94907e-04 1.55980e-04 6.32267e-03 4.06754e-09 3.26873e-03 2.10286e-09 + 6.08121e-03 4.86666e-03 3.14390e-03 2.51599e-03 1.89223e-07 1.06256e-13 + 1.67106e-04 9.38364e-11 1.53300e-07 1.20220e-07 1.35382e-04 1.06168e-04 + 5.21380e-03 2.92775e-09 2.69546e-03 1.51361e-09 4.22399e-03 3.31252e-03 + 2.18374e-03 1.71253e-03 2.21146e-07 1.41349e-13 1.95297e-04 1.24828e-10 + 2.11844e-07 1.69398e-07 1.87083e-04 1.49598e-04 6.09339e-03 3.89470e-09 + 3.15020e-03 2.01351e-09 5.83710e-03 4.66754e-03 3.01770e-03 2.41305e-03 + 1.92872e-07 1.05908e-13 1.70328e-04 9.35293e-11 1.59466e-07 1.25399e-07 + 1.40827e-04 1.10741e-04 5.31433e-03 2.91817e-09 2.74744e-03 1.50865e-09 + 4.39388e-03 3.45520e-03 2.27158e-03 1.78629e-03 2.44601e-03 4.71663e-05 + 1.09430e-02 2.11013e-04 1.23573e-04 2.76181e-05 5.52841e-04 1.23558e-04 + 2.71755e-02 5.24023e-04 4.76200e-02 9.18253e-04 1.37291e-03 3.06841e-04 + 2.40577e-03 5.37681e-04 2.29014e-03 4.44674e-05 1.02456e-02 1.98938e-04 + 1.12524e-04 2.48126e-05 5.03411e-04 1.11007e-04 2.54438e-02 4.94038e-04 + 4.45855e-02 8.65710e-04 1.25016e-03 2.75671e-04 2.19067e-03 4.83061e-04 + 2.38500e-03 4.58053e-05 1.06700e-02 2.04924e-04 1.22899e-04 2.95267e-05 + 5.49826e-04 1.32097e-04 2.64977e-02 5.08903e-04 4.64323e-02 8.91758e-04 + 1.36542e-03 3.28045e-04 2.39265e-03 5.74839e-04 2.17094e-03 4.21772e-05 + 9.71236e-03 1.88693e-04 1.09358e-04 2.46984e-05 4.89245e-04 1.10496e-04 + 2.41194e-02 4.68594e-04 4.22648e-02 8.21124e-04 1.21498e-03 2.74402e-04 + 2.12902e-03 4.80839e-04][0.00608 0.00487 0.00314 0.00252 0.00422 0.00331 0.00218 0.00171 0.00584 + 0.00467 0.00302 0.00241 0.00439 0.00346 0.00227 0.00179 0.00137 0.00031 + 0.00241 0.00054 0.00125 0.00028 0.00219 0.00048 0.00137 0.00033 0.00239 + 0.00057 0.00121 0.00027 0.00213 0.00048][0.0002 0.00019 0.00327 0.00314 0.00017 0.00014 0.0027 0.00218 0.0002 + 0.00019 0.00315 0.00302 0.00017 0.00014 0.00275 0.00227 0.01094 0.00055 + 0.04762 0.00241 0.01025 0.0005 0.04459 0.00219 0.01067 0.00055 0.04643 + 0.00239 0.00971 0.00049 0.04226 0.00213][0.00314 0.00218 0.00302 0.00227 0.00241 0.00219 0.00239 0.00213] \ No newline at end of file diff --git a/tests/regression_tests/tally_assumesep/results_true.dat b/tests/regression_tests/tally_assumesep/results_true.dat index 5728cfc041..c9ccf09282 100644 --- a/tests/regression_tests/tally_assumesep/results_true.dat +++ b/tests/regression_tests/tally_assumesep/results_true.dat @@ -1,11 +1,11 @@ k-combined: -6.353587E-01 1.925016E-02 +6.268465E-01 1.154810E-02 tally 1: -7.878503E+00 -1.242768E+01 +7.828708E+00 +1.230478E+01 tally 2: -2.687214E-01 -1.487262E-02 +2.582239E-01 +1.360117E-02 tally 3: -1.344654E+01 -3.630145E+01 +1.339335E+01 +3.663458E+01 diff --git a/tests/regression_tests/tally_nuclides/results_true.dat b/tests/regression_tests/tally_nuclides/results_true.dat index ad385b82a0..93a0e03fb5 100644 --- a/tests/regression_tests/tally_nuclides/results_true.dat +++ b/tests/regression_tests/tally_nuclides/results_true.dat @@ -1,28 +1,28 @@ k-combined: -9.655280E-01 1.463557E-02 +9.732610E-01 1.400780E-02 tally 1: -6.932814E+00 -9.650528E+00 -1.591338E+00 -5.075076E-01 -1.541515E+00 -4.761914E-01 -5.341475E+00 -5.732892E+00 -6.932814E+00 -9.650528E+00 -1.591338E+00 -5.075076E-01 -1.541515E+00 -4.761914E-01 -5.341475E+00 -5.732892E+00 +7.123025E+00 +1.021136E+01 +1.619905E+00 +5.258592E-01 +1.561322E+00 +4.881841E-01 +5.503120E+00 +6.105683E+00 +7.123025E+00 +1.021136E+01 +1.619905E+00 +5.258592E-01 +1.561322E+00 +4.881841E-01 +5.503120E+00 +6.105683E+00 tally 2: -6.932814E+00 -9.650528E+00 -1.591338E+00 -5.075076E-01 -1.541515E+00 -4.761914E-01 -5.341475E+00 -5.732892E+00 +7.123025E+00 +1.021136E+01 +1.619905E+00 +5.258592E-01 +1.561322E+00 +4.881841E-01 +5.503120E+00 +6.105683E+00 diff --git a/tests/regression_tests/tally_nuclides/tallies.xml b/tests/regression_tests/tally_nuclides/tallies.xml index 3440dbf217..ea25596a4f 100644 --- a/tests/regression_tests/tally_nuclides/tallies.xml +++ b/tests/regression_tests/tally_nuclides/tallies.xml @@ -2,7 +2,7 @@ - all + Pu239 total total absorption fission scatter diff --git a/tests/regression_tests/tally_slice_merge/inputs_true.dat b/tests/regression_tests/tally_slice_merge/inputs_true.dat index 6f421b1d95..7a992cc39b 100644 --- a/tests/regression_tests/tally_slice_merge/inputs_true.dat +++ b/tests/regression_tests/tally_slice_merge/inputs_true.dat @@ -1,38 +1,187 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 @@ -50,12 +199,12 @@ 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - - - 1.26 1.26 - 17 17 - -10.71 -10.71 - + + + 1.26 1.26 + 17 17 + -10.71 -10.71 + 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 @@ -73,12 +222,12 @@ 3 3 3 3 3 4 3 3 4 3 3 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 @@ -100,12 +249,12 @@ 5 5 5 5 5 5 5 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 - - - 21.42 21.42 - 21 21 - -224.91 -224.91 - + + + 21.42 21.42 + 21 21 + -224.91 -224.91 + 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 @@ -127,222 +276,72 @@ 7 7 7 7 7 7 7 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 10 - 5 - - - -160 -160 -183 160 160 183 - - - - - - - - 2 2 - -50.0 -50.0 - 50.0 50.0 - - - 21 27 - - - 0.0 0.625 20000000.0 - - - 21 - - - 1 - - - 16 8 - U235 U238 - fission nu-fission - tracklength - - - 6 8 - U235 U238 - fission nu-fission - tracklength - - - 7 8 - U235 U238 - fission nu-fission - tracklength - - + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 10 + 5 + + + -160 -160 -183 160 160 183 + + + + + + + 2 2 + -50.0 -50.0 + 50.0 50.0 + + + 21 27 + + + 0.0 0.625 20000000.0 + + + 21 + + + 1 + + + 16 8 + U235 U238 + fission nu-fission + tracklength + + + 6 8 + U235 U238 + fission nu-fission + tracklength + + + 7 8 + U235 U238 + fission nu-fission + tracklength + + + diff --git a/tests/regression_tests/tally_slice_merge/results_true.dat b/tests/regression_tests/tally_slice_merge/results_true.dat index 84b296d64c..4b2cbdf1ad 100644 --- a/tests/regression_tests/tally_slice_merge/results_true.dat +++ b/tests/regression_tests/tally_slice_merge/results_true.dat @@ -1,36 +1,36 @@ cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-01 U235 fission 1.77e-01 1.81e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-01 U235 nu-fission 4.32e-01 4.42e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-01 U238 fission 2.43e-07 2.40e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-01 U238 nu-fission 6.07e-07 5.97e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 6.25e-01 2.00e+07 U235 fission 3.03e-02 1.93e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 6.25e-01 2.00e+07 U235 nu-fission 7.41e-02 4.67e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 6.25e-01 2.00e+07 U238 fission 1.65e-02 1.61e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 6.25e-01 2.00e+07 U238 nu-fission 4.61e-02 4.87e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-01 U235 fission 1.31e-01 2.54e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-01 U235 nu-fission 3.18e-01 6.19e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-01 U238 fission 1.80e-07 3.46e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 0.00e+00 6.25e-01 U238 nu-fission 4.49e-07 8.62e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 6.25e-01 2.00e+07 U235 fission 2.34e-02 1.23e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 6.25e-01 2.00e+07 U235 nu-fission 5.72e-02 2.99e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 6.25e-01 2.00e+07 U238 fission 1.24e-02 9.35e-04 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 27 6.25e-01 2.00e+07 U238 nu-fission 3.44e-02 2.73e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. -0 21 0.00e+00 6.25e-01 U235 fission 1.77e-01 1.81e-02 -1 21 0.00e+00 6.25e-01 U235 nu-fission 4.32e-01 4.42e-02 -2 21 0.00e+00 6.25e-01 U238 fission 2.43e-07 2.40e-08 -3 21 0.00e+00 6.25e-01 U238 nu-fission 6.07e-07 5.97e-08 -4 21 6.25e-01 2.00e+07 U235 fission 3.03e-02 1.93e-03 -5 21 6.25e-01 2.00e+07 U235 nu-fission 7.41e-02 4.67e-03 -6 21 6.25e-01 2.00e+07 U238 fission 1.65e-02 1.61e-03 -7 21 6.25e-01 2.00e+07 U238 nu-fission 4.61e-02 4.87e-03 -8 27 0.00e+00 6.25e-01 U235 fission 1.31e-01 2.54e-02 -9 27 0.00e+00 6.25e-01 U235 nu-fission 3.18e-01 6.19e-02 -10 27 0.00e+00 6.25e-01 U238 fission 1.80e-07 3.46e-08 -11 27 0.00e+00 6.25e-01 U238 nu-fission 4.49e-07 8.62e-08 -12 27 6.25e-01 2.00e+07 U235 fission 2.34e-02 1.23e-03 -13 27 6.25e-01 2.00e+07 U235 nu-fission 5.72e-02 2.99e-03 -14 27 6.25e-01 2.00e+07 U238 fission 1.24e-02 9.35e-04 -15 27 6.25e-01 2.00e+07 U238 nu-fission 3.44e-02 2.73e-03 +0 21 0.00e+00 6.25e-01 U235 fission 1.93e-01 1.92e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-01 U235 nu-fission 4.70e-01 4.67e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-01 U238 fission 2.65e-07 2.60e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-01 U238 nu-fission 6.60e-07 6.47e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 6.25e-01 2.00e+07 U235 fission 3.39e-02 1.53e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 6.25e-01 2.00e+07 U235 nu-fission 8.30e-02 3.75e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 6.25e-01 2.00e+07 U238 fission 1.70e-02 2.01e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 6.25e-01 2.00e+07 U238 nu-fission 4.76e-02 6.12e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-01 U235 fission 7.61e-02 5.84e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-01 U235 nu-fission 1.85e-01 1.42e-02 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-01 U238 fission 1.06e-07 7.62e-09 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 0.00e+00 6.25e-01 U238 nu-fission 2.64e-07 1.90e-08 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 6.25e-01 2.00e+07 U235 fission 1.69e-02 1.34e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 6.25e-01 2.00e+07 U235 nu-fission 4.15e-02 3.29e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 6.25e-01 2.00e+07 U238 fission 1.10e-02 1.69e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 27 6.25e-01 2.00e+07 U238 nu-fission 3.13e-02 5.35e-03 cell energy low [eV] energy high [eV] nuclide score mean std. dev. +0 21 0.00e+00 6.25e-01 U235 fission 1.93e-01 1.92e-02 +1 21 0.00e+00 6.25e-01 U235 nu-fission 4.70e-01 4.67e-02 +2 21 0.00e+00 6.25e-01 U238 fission 2.65e-07 2.60e-08 +3 21 0.00e+00 6.25e-01 U238 nu-fission 6.60e-07 6.47e-08 +4 21 6.25e-01 2.00e+07 U235 fission 3.39e-02 1.53e-03 +5 21 6.25e-01 2.00e+07 U235 nu-fission 8.30e-02 3.75e-03 +6 21 6.25e-01 2.00e+07 U238 fission 1.70e-02 2.01e-03 +7 21 6.25e-01 2.00e+07 U238 nu-fission 4.76e-02 6.12e-03 +8 27 0.00e+00 6.25e-01 U235 fission 7.61e-02 5.84e-03 +9 27 0.00e+00 6.25e-01 U235 nu-fission 1.85e-01 1.42e-02 +10 27 0.00e+00 6.25e-01 U238 fission 1.06e-07 7.62e-09 +11 27 0.00e+00 6.25e-01 U238 nu-fission 2.64e-07 1.90e-08 +12 27 6.25e-01 2.00e+07 U235 fission 1.69e-02 1.34e-03 +13 27 6.25e-01 2.00e+07 U235 nu-fission 4.15e-02 3.29e-03 +14 27 6.25e-01 2.00e+07 U238 fission 1.10e-02 1.69e-03 +15 27 6.25e-01 2.00e+07 U238 nu-fission 3.13e-02 5.35e-03 sum(distribcell) energy low [eV] energy high [eV] nuclide score mean std. dev. 0 (0, 100, 2000, 30000) 0.00e+00 6.25e-01 U235 fission 0.00e+00 0.00e+00 1 (0, 100, 2000, 30000) 0.00e+00 6.25e-01 U235 nu-fission 0.00e+00 0.00e+00 @@ -49,19 +49,19 @@ 14 (500, 5000, 50000) 6.25e-01 2.00e+07 U238 fission 0.00e+00 0.00e+00 15 (500, 5000, 50000) 6.25e-01 2.00e+07 U238 nu-fission 0.00e+00 0.00e+00 sum(mesh) energy low [eV] energy high [eV] nuclide score mean std. dev. -0 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 fission 2.75e-02 4.32e-03 -1 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 nu-fission 6.71e-02 1.05e-02 -2 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 fission 3.78e-08 5.83e-09 -3 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 nu-fission 9.42e-08 1.45e-08 -4 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 fission 3.96e-03 7.84e-04 -5 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 nu-fission 9.71e-03 1.91e-03 -6 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 fission 3.92e-03 8.53e-04 -7 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 nu-fission 1.08e-02 2.25e-03 -8 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 fission 5.42e-02 2.50e-02 -9 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 nu-fission 1.32e-01 6.10e-02 -10 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 fission 7.47e-08 3.42e-08 -11 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 nu-fission 1.86e-07 8.53e-08 -12 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 fission 6.58e-03 2.03e-03 -13 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 nu-fission 1.61e-02 4.95e-03 -14 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 fission 3.74e-03 1.12e-03 -15 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 nu-fission 1.05e-02 3.20e-03 +0 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 fission 1.94e-03 1.03e-03 +1 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U235 nu-fission 4.74e-03 2.50e-03 +2 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 fission 2.74e-09 1.42e-09 +3 ((1, 1), (1, 2)) 0.00e+00 6.25e-01 U238 nu-fission 6.83e-09 3.53e-09 +4 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 fission 9.02e-04 3.69e-04 +5 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U235 nu-fission 2.24e-03 9.12e-04 +6 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 fission 1.44e-03 8.42e-04 +7 ((1, 1), (1, 2)) 6.25e-01 2.00e+07 U238 nu-fission 4.37e-03 2.64e-03 +8 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 fission 1.27e-02 2.76e-03 +9 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U235 nu-fission 3.09e-02 6.72e-03 +10 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 fission 1.70e-08 3.57e-09 +11 ((2, 1), (2, 2)) 0.00e+00 6.25e-01 U238 nu-fission 4.25e-08 8.90e-09 +12 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 fission 1.43e-03 1.69e-04 +13 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U235 nu-fission 3.52e-03 4.20e-04 +14 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 fission 1.37e-03 2.98e-04 +15 ((2, 1), (2, 2)) 6.25e-01 2.00e+07 U238 nu-fission 4.16e-03 1.08e-03 diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index 090e314483..aec73979b9 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -149,6 +149,9 @@ class TallySliceMergeTestHarness(PyAPITestHarness): sum2 = mesh_tally.summation(filter_type=openmc.MeshFilter, filter_bins=[(2, 1), (2, 2)]) + mesh = mesh_tally.find_filter(openmc.MeshFilter).mesh + assert mesh.name == 'mesh' + # Merge the mesh tally slices merge_tally = sum1.merge(sum2) diff --git a/tests/regression_tests/time_cutoff/__init__.py b/tests/regression_tests/time_cutoff/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/time_cutoff/inputs_true.dat b/tests/regression_tests/time_cutoff/inputs_true.dat new file mode 100644 index 0000000000..e1102d4756 --- /dev/null +++ b/tests/regression_tests/time_cutoff/inputs_true.dat @@ -0,0 +1,34 @@ + + + + + + + + + + fixed source + 100 + 10 + + + 0.0 0.0 0.0 + + + 10000.0 1.0 + + + + 1e-07 + + + + + 0.0 1e-07 2e-07 + + + 1 + flux + + + diff --git a/tests/regression_tests/time_cutoff/results_true.dat b/tests/regression_tests/time_cutoff/results_true.dat new file mode 100644 index 0000000000..d3d5e1b3cd --- /dev/null +++ b/tests/regression_tests/time_cutoff/results_true.dat @@ -0,0 +1,5 @@ +tally 1: +1.383148E+02 +1.913099E+03 +0.000000E+00 +0.000000E+00 diff --git a/tests/regression_tests/time_cutoff/test.py b/tests/regression_tests/time_cutoff/test.py new file mode 100755 index 0000000000..9554a6e2a0 --- /dev/null +++ b/tests/regression_tests/time_cutoff/test.py @@ -0,0 +1,42 @@ +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def time_model(): + model = openmc.Model() + time_cutoff = 1e-7 + + # A single sphere + s1 = openmc.Sphere(r=200, boundary_type='vacuum') + sphere = openmc.Cell() + sphere.region = -s1 + model.geometry = openmc.Geometry([sphere]) + + # Set the running parameters + settings_file = openmc.Settings() + settings_file.run_mode = 'fixed source' + settings_file.batches = 10 + settings_file.particles = 100 + settings_file.cutoff = {'time_neutron': time_cutoff} + settings_file.source = openmc.IndependentSource( + space=openmc.stats.Point(), energy=openmc.stats.Discrete([1e4], [1])) + model.settings = settings_file + + # Tally flux under time cutoff + tallies = openmc.Tallies() + tally = openmc.Tally() + tally.scores = ['flux'] + time_filter = openmc.TimeFilter([0, time_cutoff, 2*time_cutoff]) + tally.filters = [time_filter] + tallies.append(tally) + model.tallies = tallies + + return model + + +def test_time_cutoff(time_model): + harness = PyAPITestHarness('statepoint.10.h5', time_model) + harness.main() diff --git a/tests/regression_tests/torus/inputs_true.dat b/tests/regression_tests/torus/inputs_true.dat index 0045d7e466..df4af14438 100644 --- a/tests/regression_tests/torus/inputs_true.dat +++ b/tests/regression_tests/torus/inputs_true.dat @@ -1,34 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 1000 - 10 - 5 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + diff --git a/tests/regression_tests/torus/large_major/__init__.py b/tests/regression_tests/torus/large_major/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/torus/large_major/inputs_true.dat b/tests/regression_tests/torus/large_major/inputs_true.dat new file mode 100644 index 0000000000..513a8c67e7 --- /dev/null +++ b/tests/regression_tests/torus/large_major/inputs_true.dat @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + -1000.0 0 0 + + + + + + flux + + + diff --git a/tests/regression_tests/torus/large_major/results_true.dat b/tests/regression_tests/torus/large_major/results_true.dat new file mode 100644 index 0000000000..cb9f54970c --- /dev/null +++ b/tests/regression_tests/torus/large_major/results_true.dat @@ -0,0 +1,3 @@ +tally 1: +9.675396E+02 +9.363406E+04 diff --git a/tests/regression_tests/torus/large_major/test.py b/tests/regression_tests/torus/large_major/test.py new file mode 100644 index 0000000000..1ce1ab2cac --- /dev/null +++ b/tests/regression_tests/torus/large_major/test.py @@ -0,0 +1,41 @@ +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.Model() + tungsten = openmc.Material() + tungsten.set_density('g/cm3', 1.0) + tungsten.add_nuclide('W184', 1.0) + ss = openmc.Material() + ss.set_density('g/cm3', 5.0) + ss.add_nuclide('Fe56', 1.0) + model.materials.extend([tungsten, ss]) + + # Create nested torii with very large major radii + R = 1000.0 + vacuum = openmc.ZTorus(a=R, b=30.0, c=30.0) + first_wall = openmc.ZTorus(a=R, b=35.0, c=35.0) + vessel = openmc.ZTorus(a=R, b=40.0, c=40.0, boundary_type='vacuum') + cell1 = openmc.Cell(region=-vacuum) + cell2 = openmc.Cell(fill=tungsten, region=+vacuum & -first_wall) + cell3 = openmc.Cell(fill=ss, region=+first_wall & -vessel) + model.geometry = openmc.Geometry([cell1, cell2, cell3]) + + model.settings.run_mode ='fixed source' + model.settings.particles = 1000 + model.settings.batches = 10 + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point((-R, 0, 0,))) + + tally = openmc.Tally() + tally.scores = ['flux'] + model.tallies.append(tally) + return model + + +def test_torus_large_major(model): + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/torus/results_true.dat b/tests/regression_tests/torus/results_true.dat index 42444ad09a..84cd3c7a44 100644 --- a/tests/regression_tests/torus/results_true.dat +++ b/tests/regression_tests/torus/results_true.dat @@ -1,2 +1,2 @@ k-combined: -7.628424E-01 2.557300E-02 +7.666453E-01 1.478848E-02 diff --git a/tests/regression_tests/trace/results_true.dat b/tests/regression_tests/trace/results_true.dat index fe46748c80..97b997ae6b 100644 --- a/tests/regression_tests/trace/results_true.dat +++ b/tests/regression_tests/trace/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 diff --git a/tests/regression_tests/track_output/geometry.xml b/tests/regression_tests/track_output/geometry.xml deleted file mode 100644 index 5b16fe26cd..0000000000 --- a/tests/regression_tests/track_output/geometry.xml +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 3 2 2 2 2 2 2 2 1 2 2 2 - 2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - - - - - - - - - - -12.2682 -12.2682 - 1.63576 1.63576 - - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 - - - - - - - - - - - - - - - - - - - -85.8774 -85.8774 - 24.5364 24.5364 - - 999 999 130 140 150 999 999 - 999 220 230 240 250 260 999 - 130 320 777 222 333 360 150 - 410 240 444 111 666 240 470 - 510 520 888 555 998 560 570 - 999 620 630 240 650 660 999 - 999 999 510 740 570 999 999 - - - - - - - - diff --git a/tests/regression_tests/track_output/materials.xml b/tests/regression_tests/track_output/materials.xml deleted file mode 100644 index 5dc9a64755..0000000000 --- a/tests/regression_tests/track_output/materials.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/regression_tests/track_output/model.xml b/tests/regression_tests/track_output/model.xml new file mode 100644 index 0000000000..36ccb2b468 --- /dev/null +++ b/tests/regression_tests/track_output/model.xml @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 3 2 2 2 2 2 2 2 3 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 1 2 2 1 2 2 2 1 2 2 1 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 3 2 2 2 2 2 2 2 1 2 2 2 +2 2 2 2 2 1 2 2 2 1 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 1.63576 1.63576 + 15 15 + -12.2682 -12.2682 + +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 +2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + + 24.5364 24.5364 + 7 7 + -85.8774 -85.8774 + +999 999 130 140 150 999 999 +999 220 230 240 250 260 999 +130 320 777 222 333 360 150 +410 240 444 111 666 240 470 +510 520 888 555 998 560 570 +999 620 630 240 650 660 999 +999 999 510 740 570 999 999 + + + + + + + + + + + + eigenvalue + 100 + 2 + 0 + + + -1.0 -1.0 -1.0 1.0 1.0 1.0 + + + 1 1 1 1 1 30 2 1 60 + + diff --git a/tests/regression_tests/track_output/results_true.dat b/tests/regression_tests/track_output/results_true.dat index 1d0ca80399..205ac3eecb 100644 --- a/tests/regression_tests/track_output/results_true.dat +++ b/tests/regression_tests/track_output/results_true.dat @@ -1,9 +1,216 @@ - - - - - - - - - +neutron [((-9.663085e-01, -6.616522e-01, -9.863690e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 0.000000e+00, 1.000000e+00, 23, 2403, 1) + ((-1.281491e+00, -4.879529e-01, -7.708709e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 1.323629e-10, 1.000000e+00, 22, 2403, 3) + ((-1.368452e+00, -4.400285e-01, -7.114141e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 1.688824e-10, 1.000000e+00, 21, 2403, 2) + ((-2.150539e+00, -9.015151e-03, -1.766823e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 4.973246e-10, 1.000000e+00, 22, 2403, 3) + ((-2.237499e+00, 3.890922e-02, -1.172255e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 5.338441e-10, 1.000000e+00, 23, 2403, 1) + ((-2.453640e+00, 1.580257e-01, 3.055504e-02), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 6.246136e-10, 1.000000e+00, 23, 2402, 1) + ((-2.767485e+00, 3.309877e-01, 2.451383e-01), (-7.513921e-01, 4.140970e-01, 5.137447e-01), 5.293873e+06, 7.564146e-10, 1.000000e+00, 22, 2402, 3) + ((-2.825099e+00, 3.627392e-01, 2.845305e-01), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 7.806100e-10, 1.000000e+00, 22, 2402, 3) + ((-3.274427e+00, 6.029890e-01, 6.987901e-01), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 9.878481e-10, 1.000000e+00, 23, 2402, 1) + ((-3.676327e+00, 8.178800e-01, 1.069324e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.173212e-09, 1.000000e+00, 23, 2416, 1) + ((-4.089400e+00, 1.038745e+00, 1.450158e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.363728e-09, 1.000000e+00, 23, 2415, 1) + ((-4.456639e+00, 1.235102e+00, 1.788735e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.533105e-09, 1.000000e+00, 22, 2415, 3) + ((-4.536974e+00, 1.278056e+00, 1.862800e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.570157e-09, 1.000000e+00, 21, 2415, 2) + ((-5.410401e+00, 1.745067e+00, 2.668060e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 1.972998e-09, 1.000000e+00, 22, 2415, 3) + ((-5.490736e+00, 1.788021e+00, 2.742125e+00), (-6.842434e-01, 3.658561e-01, 6.308409e-01), 5.292729e+06, 2.010050e-09, 1.000000e+00, 23, 2415, 1) + ((-5.576301e+00, 1.833771e+00, 2.821012e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.049514e-09, 1.000000e+00, 23, 2415, 1) + ((-5.725160e+00, 1.896661e+00, 2.330311e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.227030e-09, 1.000000e+00, 23, 2414, 1) + ((-6.116514e+00, 2.061999e+00, 1.040248e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 2.693724e-09, 1.000000e+00, 22, 2414, 3) + ((-6.534762e+00, 2.238699e+00, -3.384686e-01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 3.192489e-09, 1.000000e+00, 23, 2414, 1) + ((-7.043525e+00, 2.453640e+00, -2.015560e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 3.799195e-09, 1.000000e+00, 23, 2428, 1) + ((-7.360920e+00, 2.587732e+00, -3.061825e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 4.177692e-09, 1.000000e+00, 11, 1757, 1) + ((-8.996680e+00, 3.278803e+00, -8.453960e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.128354e-09, 1.000000e+00, 23, 2427, 1) + ((-9.220205e+00, 3.373238e+00, -9.190791e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.394910e-09, 1.000000e+00, 22, 2427, 3) + ((-9.320244e+00, 3.415502e+00, -9.520561e+00), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 6.514208e-09, 1.000000e+00, 21, 2427, 2) + ((-1.005591e+01, 3.726304e+00, -1.194562e+01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 7.391498e-09, 1.000000e+00, 22, 2427, 3) + ((-1.015595e+01, 3.768569e+00, -1.227539e+01), (-2.881377e-01, 1.217316e-01, -9.498200e-01), 4.458753e+06, 7.510796e-09, 1.000000e+00, 23, 2427, 1) + ((-1.062054e+01, 3.964848e+00, -1.380687e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.064826e-09, 1.000000e+00, 23, 2427, 1) + ((-1.063244e+01, 3.969501e+00, -1.382484e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.072756e-09, 1.000000e+00, 23, 2426, 1) + ((-1.093907e+01, 4.089400e+00, -1.428802e+01), (-5.395885e-01, 2.109921e-01, -8.150623e-01), 4.068754e+06, 8.277097e-09, 1.000000e+00, 23, 2437, 1) + ((-1.108410e+01, 4.146112e+00, -1.450710e+01), (4.855193e-01, 3.316053e-01, -8.088937e-01), 8.856868e+05, 8.373750e-09, 1.000000e+00, 23, 2437, 1) + ((-1.080173e+01, 4.338973e+00, -1.497755e+01), (2.818553e-01, 5.419536e-02, -9.579251e-01), 7.653670e+05, 8.820862e-09, 1.000000e+00, 23, 2437, 1) + ((-1.063244e+01, 4.371524e+00, -1.555290e+01), (2.818553e-01, 5.419536e-02, -9.579251e-01), 7.653670e+05, 9.317522e-09, 1.000000e+00, 23, 2438, 1) + ((-1.041266e+01, 4.413783e+00, -1.629984e+01), (2.686183e-01, -3.269476e-01, -9.060626e-01), 6.562001e+05, 9.962308e-09, 1.000000e+00, 23, 2438, 1) + ((-1.016754e+01, 4.115428e+00, -1.712667e+01), (-6.340850e-01, -7.142137e-01, -2.963697e-01), 7.155058e+04, 1.077718e-08, 1.000000e+00, 23, 2438, 1) + ((-1.019064e+01, 4.089400e+00, -1.713747e+01), (-6.340850e-01, -7.142137e-01, -2.963697e-01), 7.155058e+04, 1.087569e-08, 1.000000e+00, 23, 2427, 1) + ((-1.024915e+01, 4.023496e+00, -1.716481e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.112511e-08, 1.000000e+00, 23, 2427, 1) + ((-1.018199e+01, 3.749638e+00, -1.740047e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.276389e-08, 1.000000e+00, 22, 2427, 3) + ((-1.015866e+01, 3.654498e+00, -1.748234e+01), (1.827568e-01, -7.452255e-01, -6.412791e-01), 2.628504e+04, 1.333321e-08, 1.000000e+00, 21, 2427, 2) + ((-1.009978e+01, 3.414404e+00, -1.768895e+01), (1.735861e-01, -9.678731e-01, 1.819053e-01), 2.620416e+04, 1.476994e-08, 1.000000e+00, 21, 2427, 2) + ((-9.994809e+00, 2.829099e+00, -1.757894e+01), (4.635724e-01, 5.558106e-01, 6.900545e-01), 2.214649e+04, 1.747088e-08, 1.000000e+00, 21, 2427, 2) + ((-9.553365e+00, 3.358379e+00, -1.692183e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.209726e-08, 1.000000e+00, 21, 2427, 2) + ((-1.011854e+01, 3.687059e+00, -1.760205e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.716243e-08, 1.000000e+00, 22, 2427, 3) + ((-1.020058e+01, 3.734765e+00, -1.770077e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 2.789760e-08, 1.000000e+00, 23, 2427, 1) + ((-1.063244e+01, 3.985917e+00, -1.822054e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 3.176800e-08, 1.000000e+00, 23, 2426, 1) + ((-1.081038e+01, 4.089400e+00, -1.843470e+01), (-5.990363e-01, 3.483710e-01, -7.209668e-01), 1.813617e+04, 3.336273e-08, 1.000000e+00, 23, 2437, 1) + ((-1.081564e+01, 4.092455e+00, -1.844103e+01), (-6.690877e-01, 6.718700e-01, -3.176671e-01), 1.355236e+04, 3.340982e-08, 1.000000e+00, 23, 2437, 1) + ((-1.096190e+01, 4.239327e+00, -1.851047e+01), (-5.904492e-01, 8.058629e-01, 4.421237e-02), 1.153343e+04, 3.476743e-08, 1.000000e+00, 23, 2437, 1) + ((-1.109457e+01, 4.420404e+00, -1.850054e+01), (-5.904492e-01, 8.058629e-01, 4.421237e-02), 1.153343e+04, 3.628014e-08, 1.000000e+00, 22, 2437, 3) + ((-1.113484e+01, 4.475368e+00, -1.849752e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.673931e-08, 1.000000e+00, 22, 2437, 3) + ((-1.117187e+01, 4.372425e+00, -1.850142e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.748914e-08, 1.000000e+00, 23, 2437, 1) + ((-1.127367e+01, 4.089400e+00, -1.851213e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 3.955064e-08, 1.000000e+00, 23, 2426, 1) + ((-1.135375e+01, 3.866733e+00, -1.852056e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.117251e-08, 1.000000e+00, 22, 2426, 3) + ((-1.138419e+01, 3.782113e+00, -1.852377e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.178887e-08, 1.000000e+00, 21, 2426, 2) + ((-1.172456e+01, 2.835777e+00, -1.855959e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.868184e-08, 1.000000e+00, 22, 2426, 3) + ((-1.175499e+01, 2.751157e+00, -1.856280e+01), (-3.382310e-01, -9.403895e-01, -3.560132e-02), 1.114105e+04, 4.929820e-08, 1.000000e+00, 23, 2426, 1) + ((-1.179525e+01, 2.639224e+00, -1.856703e+01), (-4.738733e-01, -7.934600e-01, 3.819231e-01), 8.873828e+03, 5.011351e-08, 1.000000e+00, 23, 2426, 1) + ((-1.190609e+01, 2.453640e+00, -1.847770e+01), (-4.738733e-01, -7.934600e-01, 3.819231e-01), 8.873828e+03, 5.190861e-08, 1.000000e+00, 23, 2411, 1) + ((-1.222017e+01, 1.927741e+00, -1.822457e+01), (-5.716267e-01, 2.823908e-01, 7.703884e-01), 1.028345e+03, 5.699551e-08, 1.000000e+00, 23, 2411, 1) + ((-1.226820e+01, 1.951470e+00, -1.815983e+01), (-5.716267e-01, 2.823908e-01, 7.703884e-01), 1.028345e+03, 5.888995e-08, 1.000000e+00, 23, 2100, 1) + ((-1.236181e+01, 1.997712e+00, -1.803368e+01), (1.748787e-01, 6.613493e-01, 7.294070e-01), 4.250133e+02, 6.258186e-08, 1.000000e+00, 23, 2100, 1) + ((-1.234968e+01, 2.043587e+00, -1.798308e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 6.501444e-08, 1.000000e+00, 23, 2100, 1) + ((-1.276513e+01, 2.146245e+00, -1.744506e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 8.979603e-08, 1.000000e+00, 22, 2100, 3) + ((-1.313233e+01, 2.236980e+00, -1.696953e+01), (-6.043263e-01, 1.493281e-01, 7.826180e-01), 4.022525e+02, 1.116994e-07, 1.000000e+00, 23, 2100, 1) + ((-1.318468e+01, 2.249916e+00, -1.690173e+01), (-8.862976e-01, -6.402442e-02, 4.586692e-01), 3.113244e+02, 1.148223e-07, 1.000000e+00, 23, 2100, 1) + ((-1.363685e+01, 2.217253e+00, -1.666773e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.357269e-07, 1.000000e+00, 23, 2100, 1) + ((-1.334593e+01, 2.453640e+00, -1.709033e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.617034e-07, 1.000000e+00, 23, 2101, 1) + ((-1.308145e+01, 2.668542e+00, -1.747452e+01), (5.149966e-01, 4.184610e-01, -7.481102e-01), 2.471907e+02, 1.853189e-07, 1.000000e+00, 22, 2101, 3) + ((-1.304150e+01, 2.701005e+00, -1.753256e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 1.888862e-07, 1.000000e+00, 22, 2101, 3) + ((-1.304665e+01, 2.669815e+00, -1.753058e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 1.903682e-07, 1.000000e+00, 23, 2101, 1) + ((-1.308231e+01, 2.453640e+00, -1.751686e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.006392e-07, 1.000000e+00, 23, 2100, 1) + ((-1.311790e+01, 2.237916e+00, -1.750317e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.108888e-07, 1.000000e+00, 22, 2100, 3) + ((-1.313266e+01, 2.148507e+00, -1.749750e+01), (-1.624676e-01, -9.847331e-01, 6.248920e-02), 2.387811e+02, 2.151369e-07, 1.000000e+00, 21, 2100, 2) + ((-1.320190e+01, 1.728791e+00, -1.747087e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.350787e-07, 1.000000e+00, 21, 2100, 2) + ((-1.309582e+01, 2.150526e+00, -1.834143e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.808996e-07, 1.000000e+00, 22, 2100, 3) + ((-1.307365e+01, 2.238628e+00, -1.852329e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 2.904717e-07, 1.000000e+00, 23, 2100, 1) + ((-1.301957e+01, 2.453640e+00, -1.896713e+01), (1.090151e-01, 4.333767e-01, -8.945950e-01), 2.357642e+02, 3.138324e-07, 1.000000e+00, 23, 2101, 1) + ((-1.297913e+01, 2.614390e+00, -1.929896e+01), (6.012134e-01, 7.985735e-01, -2.868317e-02), 4.041011e+01, 3.312976e-07, 1.000000e+00, 23, 2101, 1) + ((-1.295975e+01, 2.640133e+00, -1.929988e+01), (6.762805e-01, -5.875192e-01, 4.443713e-01), 3.527833e+01, 3.349640e-07, 1.000000e+00, 23, 2101, 1) + ((-1.282907e+01, 2.526604e+00, -1.921402e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 3.584852e-07, 1.000000e+00, 23, 2101, 1) + ((-1.284217e+01, 2.453640e+00, -1.921346e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 3.799102e-07, 1.000000e+00, 23, 2100, 1) + ((-1.288683e+01, 2.204886e+00, -1.921158e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 4.529534e-07, 1.000000e+00, 22, 2100, 3) + ((-1.290264e+01, 2.116831e+00, -1.921091e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 4.788097e-07, 1.000000e+00, 21, 2100, 2) + ((-1.308145e+01, 1.120923e+00, -1.920338e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 7.712448e-07, 1.000000e+00, 22, 2100, 3) + ((-1.309726e+01, 1.032868e+00, -1.920271e+01), (-1.767101e-01, -9.842348e-01, 7.448995e-03), 6.258032e+00, 7.971010e-07, 1.000000e+00, 23, 2100, 1) + ((-1.312690e+01, 8.677798e-01, -1.920146e+01), (-6.009065e-01, -7.617185e-01, 2.422732e-01), 3.971935e+00, 8.455768e-07, 1.000000e+00, 23, 2100, 1) + ((-1.316626e+01, 8.178800e-01, -1.918559e+01), (-6.009065e-01, -7.617185e-01, 2.422732e-01), 3.971935e+00, 8.693415e-07, 1.000000e+00, 23, 2099, 1) + ((-1.324019e+01, 7.241633e-01, -1.915578e+01), (-9.367321e-01, -1.471894e-01, -3.175976e-01), 2.365289e+00, 9.139738e-07, 1.000000e+00, 23, 2099, 1) + ((-1.348497e+01, 6.857011e-01, -1.923877e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.036815e-06, 1.000000e+00, 23, 2099, 1) + ((-1.390396e+01, 7.859275e-01, -1.924890e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.269071e-06, 1.000000e+00, 23, 2114, 1) + ((-1.403754e+01, 8.178800e-01, -1.925212e+01), (-9.722929e-01, 2.325825e-01, -2.349230e-02), 1.799413e+00, 1.343115e-06, 1.000000e+00, 23, 2115, 1) + ((-1.504223e+01, 1.058212e+00, -1.927640e+01), (-4.085804e-01, -2.719942e-01, -8.712526e-01), 1.628266e+00, 1.900041e-06, 1.000000e+00, 23, 2115, 1) + ((-1.523744e+01, 9.282558e-01, -1.969268e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 2.170751e-06, 1.000000e+00, 23, 2115, 1) + ((-1.553972e+01, 1.106680e+00, -1.997974e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 2.636878e-06, 1.000000e+00, 23, 2129, 1) + ((-1.585973e+01, 1.295571e+00, -2.028364e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 3.130348e-06, 1.000000e+00, 22, 2129, 3) + ((-1.593583e+01, 1.340489e+00, -2.035590e+01), (-6.666265e-01, 3.934879e-01, -6.330690e-01), 4.946445e-01, 3.247693e-06, 1.000000e+00, 21, 2129, 2) + ((-1.647184e+01, 1.656882e+00, -2.086494e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 4.074258e-06, 1.000000e+00, 21, 2129, 2) + ((-1.585080e+01, 1.545037e+00, -2.159072e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.061748e-06, 1.000000e+00, 22, 2129, 3) + ((-1.576406e+01, 1.529415e+00, -2.169209e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.199673e-06, 1.000000e+00, 23, 2129, 1) + ((-1.553972e+01, 1.489015e+00, -2.195425e+01), (6.457458e-01, -1.162934e-01, -7.546444e-01), 4.958073e-01, 5.556377e-06, 1.000000e+00, 23, 2115, 1) + ((-1.546081e+01, 1.474803e+00, -2.204648e+01), (3.257038e-01, -9.420596e-01, -8.025439e-02), 3.076319e-01, 5.681853e-06, 1.000000e+00, 23, 2115, 1) + ((-1.543563e+01, 1.401988e+00, -2.205268e+01), (-3.677195e-01, -8.784218e-01, -3.052172e-01), 8.422973e-02, 5.782605e-06, 1.000000e+00, 23, 2115, 1) + ((-1.553972e+01, 1.153338e+00, -2.213907e+01), (-3.677195e-01, -8.784218e-01, -3.052172e-01), 8.422973e-02, 6.487752e-06, 1.000000e+00, 23, 2129, 1) + ((-1.565955e+01, 8.670807e-01, -2.223854e+01), (1.611814e-01, 2.692337e-01, -9.494913e-01), 8.559448e-02, 7.299552e-06, 1.000000e+00, 23, 2129, 1) + ((-1.563263e+01, 9.120446e-01, -2.239711e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 7.712256e-06, 1.000000e+00, 23, 2129, 1) + ((-1.592604e+01, 1.214620e+00, -2.249695e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 9.406032e-06, 1.000000e+00, 22, 2129, 3) + ((-1.598742e+01, 1.277922e+00, -2.251784e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 9.760391e-06, 1.000000e+00, 21, 2129, 2) + ((-1.670388e+01, 2.016770e+00, -2.276163e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 1.389636e-05, 1.000000e+00, 22, 2129, 3) + ((-1.676526e+01, 2.080073e+00, -2.278252e+01), (-6.773989e-01, 6.985688e-01, -2.305048e-01), 3.418159e-02, 1.425072e-05, 1.000000e+00, 23, 2129, 1) + ((-1.681575e+01, 2.132139e+00, -2.279970e+01), (4.286076e-02, -9.409799e-01, -3.357375e-01), 1.813618e-02, 1.454218e-05, 1.000000e+00, 23, 2129, 1) + ((-1.681374e+01, 2.087933e+00, -2.281547e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.479439e-05, 1.000000e+00, 23, 2129, 1) + ((-1.682083e+01, 2.021795e+00, -2.287841e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.532970e-05, 1.000000e+00, 22, 2129, 3) + ((-1.684417e+01, 1.804084e+00, -2.308558e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.709181e-05, 1.000000e+00, 21, 2129, 2) + ((-1.686879e+01, 1.574384e+00, -2.330417e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 1.895097e-05, 1.000000e+00, 22, 2129, 3) + ((-1.689212e+01, 1.356673e+00, -2.351134e+01), (-7.741702e-02, -7.222450e-01, -6.872909e-01), 1.529603e-02, 2.071308e-05, 1.000000e+00, 23, 2129, 1) + ((-1.689233e+01, 1.354754e+00, -2.351317e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.072861e-05, 1.000000e+00, 23, 2129, 1) + ((-1.689148e+01, 1.355453e+00, -2.351229e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.073958e-05, 1.000000e+00, 22, 2129, 3) + ((-1.682182e+01, 1.413107e+00, -2.343954e+01), (6.002458e-01, 4.967948e-01, 6.268172e-01), 8.602342e-03, 2.164421e-05, 1.000000e+00, 21, 2129, 2) + ((-1.636364e+01, 1.792326e+00, -2.296107e+01), (9.641593e-01, -1.162836e-01, 2.384846e-01), 7.837617e-03, 2.759442e-05, 1.000000e+00, 21, 2129, 2) + ((-1.598564e+01, 1.746738e+00, -2.286758e+01), (9.641593e-01, -1.162836e-01, 2.384846e-01), 7.837617e-03, 3.079609e-05, 0.000000e+00, 21, 2129, 2)] +neutron [((-9.469716e-01, -2.580266e-01, 1.414357e-01), (1.438873e-01, 2.365819e-01, 9.608983e-01), 9.724461e+05, 0.000000e+00, 1.000000e+00, 23, 2403, 1) + ((-9.064818e-01, -1.914524e-01, 4.118326e-01), (9.231638e-01, 3.100833e-01, 2.271937e-01), 1.746594e+05, 2.064696e-10, 1.000000e+00, 23, 2403, 1) + ((-8.178800e-01, -1.616918e-01, 4.336377e-01), (9.231638e-01, 3.100833e-01, 2.271937e-01), 1.746594e+05, 3.725262e-10, 1.000000e+00, 11, 1756, 1) + ((-3.834875e-01, -1.578285e-02, 5.405432e-01), (-5.547498e-01, -6.358598e-02, 8.295839e-01), 1.474011e+05, 1.186660e-09, 1.000000e+00, 11, 1756, 1) + ((-7.999329e-01, -6.351625e-02, 1.163304e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 2.600465e-09, 1.000000e+00, 11, 1756, 1) + ((-8.178800e-01, -6.341842e-02, 1.190721e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 2.662321e-09, 1.000000e+00, 23, 2403, 1) + ((-1.035984e+00, -6.222961e-02, 1.523906e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 3.414026e-09, 1.000000e+00, 22, 2403, 3) + ((-1.124618e+00, -6.174649e-02, 1.659308e+00), (-5.476898e-01, 2.985279e-03, 8.366761e-01), 1.467299e+05, 3.719509e-09, 1.000000e+00, 21, 2403, 2) + ((-1.705404e+00, -5.858082e-02, 2.546543e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 5.721217e-09, 1.000000e+00, 21, 2403, 2) + ((-1.851116e+00, -4.676544e-01, 2.685103e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 6.583734e-09, 1.000000e+00, 22, 2403, 3) + ((-1.880791e+00, -5.509662e-01, 2.713322e+00), (-3.196694e-01, -8.974451e-01, 3.039799e-01), 1.460188e+05, 6.759394e-09, 1.000000e+00, 23, 2403, 1) + ((-1.948026e+00, -7.397227e-01, 2.777257e+00), (-8.068708e-01, -5.218555e-01, -2.768147e-01), 6.012975e+04, 7.157380e-09, 1.000000e+00, 23, 2403, 1) + ((-2.068870e+00, -8.178800e-01, 2.735799e+00), (-8.068708e-01, -5.218555e-01, -2.768147e-01), 6.012975e+04, 7.598974e-09, 1.000000e+00, 23, 2388, 1) + ((-2.111345e+00, -8.453511e-01, 2.721228e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 7.754188e-09, 1.000000e+00, 23, 2388, 1) + ((-2.453640e+00, -1.161216e+00, 3.335542e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 1.421339e-08, 1.000000e+00, 23, 2387, 1) + ((-2.715369e+00, -1.402736e+00, 3.805265e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 1.915229e-08, 1.000000e+00, 22, 2387, 3) + ((-2.785083e+00, -1.467067e+00, 3.930380e+00), (-4.440083e-01, -4.097242e-01, 7.968580e-01), 7.445994e+03, 2.046780e-08, 1.000000e+00, 21, 2387, 2) + ((-2.940196e+00, -1.610203e+00, 4.208760e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 2.339483e-08, 1.000000e+00, 21, 2387, 2) + ((-3.600602e+00, -1.239800e+00, 3.812386e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.112139e-08, 1.000000e+00, 22, 2387, 3) + ((-3.682067e+00, -1.194109e+00, 3.763490e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.207450e-08, 1.000000e+00, 23, 2387, 1) + ((-4.089400e+00, -9.656478e-01, 3.519009e+00), (-7.727102e-01, 4.333905e-01, -4.637797e-01), 6.395532e+03, 3.684019e-08, 1.000000e+00, 23, 2386, 1) + ((-4.185927e+00, -9.115084e-01, 3.461074e+00), (4.334152e-01, -1.576729e-02, -9.010564e-01), 2.532713e+01, 3.796953e-08, 1.000000e+00, 23, 2386, 1) + ((-4.089400e+00, -9.150200e-01, 3.260397e+00), (4.334152e-01, -1.576729e-02, -9.010564e-01), 2.532713e+01, 6.996444e-08, 1.000000e+00, 23, 2387, 1) + ((-3.920090e+00, -9.211794e-01, 2.908406e+00), (9.094673e-01, -3.659267e-01, -1.974002e-01), 9.547085e+00, 1.260840e-07, 1.000000e+00, 23, 2387, 1) + ((-3.729948e+00, -9.976834e-01, 2.867135e+00), (-2.916959e-01, -6.494657e-01, -7.022164e-01), 8.590591e+00, 1.750036e-07, 1.000000e+00, 23, 2387, 1) + ((-3.744933e+00, -1.031047e+00, 2.831062e+00), (-2.916959e-01, -6.494657e-01, -7.022164e-01), 8.590591e+00, 1.876753e-07, 0.000000e+00, 23, 2387, 1)] +neutron [((6.474155e+00, -5.192870e+00, 5.413003e+00), (-9.597810e-01, -1.081529e-01, -2.590820e-01), 9.900333e+05, 4.450859e-05, 1.000000e+00, 21, 2367, 2) + ((6.142109e+00, -5.230286e+00, 5.323371e+00), (-9.597810e-01, -1.081529e-01, -2.590820e-01), 9.900333e+05, 4.450884e-05, 1.000000e+00, 22, 2367, 3) + ((6.041244e+00, -5.241652e+00, 5.296144e+00), (-9.597810e-01, -1.081529e-01, -2.590820e-01), 9.900333e+05, 4.450892e-05, 1.000000e+00, 23, 2367, 1) + ((6.004037e+00, -5.245845e+00, 5.286100e+00), (-6.195407e-01, -7.332723e-01, -2.801446e-01), 5.512347e+05, 4.450895e-05, 1.000000e+00, 23, 2367, 1) + ((5.725160e+00, -5.575916e+00, 5.159997e+00), (-6.195407e-01, -7.332723e-01, -2.801446e-01), 5.512347e+05, 4.450939e-05, 1.000000e+00, 23, 2366, 1) + ((5.599064e+00, -5.725160e+00, 5.102979e+00), (-6.195407e-01, -7.332723e-01, -2.801446e-01), 5.512347e+05, 4.450958e-05, 1.000000e+00, 23, 2352, 1) + ((5.296886e+00, -6.082810e+00, 4.966340e+00), (-6.195407e-01, -7.332723e-01, -2.801446e-01), 5.512347e+05, 4.451006e-05, 1.000000e+00, 22, 2352, 3) + ((5.240003e+00, -6.150135e+00, 4.940619e+00), (-6.195407e-01, -7.332723e-01, -2.801446e-01), 5.512347e+05, 4.451015e-05, 1.000000e+00, 21, 2352, 2) + ((4.752072e+00, -6.727638e+00, 4.719986e+00), (3.721510e-02, -9.647240e-01, 2.606197e-01), 5.045933e+05, 4.451092e-05, 1.000000e+00, 21, 2352, 2) + ((4.764028e+00, -7.037568e+00, 4.803713e+00), (3.721510e-02, -9.647240e-01, 2.606197e-01), 5.045933e+05, 4.451124e-05, 1.000000e+00, 22, 2352, 3) + ((4.767580e+00, -7.129630e+00, 4.828584e+00), (3.721510e-02, -9.647240e-01, 2.606197e-01), 5.045933e+05, 4.451134e-05, 1.000000e+00, 23, 2352, 1) + ((4.776502e+00, -7.360920e+00, 4.891066e+00), (3.721510e-02, -9.647240e-01, 2.606197e-01), 5.045933e+05, 4.451158e-05, 1.000000e+00, 23, 2338, 1) + ((4.778286e+00, -7.407162e+00, 4.903559e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451163e-05, 1.000000e+00, 23, 2338, 1) + ((4.981926e+00, -7.580442e+00, 5.259893e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451239e-05, 1.000000e+00, 22, 2338, 3) + ((5.154144e+00, -7.726985e+00, 5.561246e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451303e-05, 1.000000e+00, 21, 2338, 2) + ((5.313757e+00, -7.862801e+00, 5.840540e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451362e-05, 1.000000e+00, 22, 2338, 3) + ((5.485976e+00, -8.009344e+00, 6.141893e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451426e-05, 1.000000e+00, 23, 2338, 1) + ((5.725160e+00, -8.212869e+00, 6.560424e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451515e-05, 1.000000e+00, 23, 2339, 1) + ((6.004950e+00, -8.450945e+00, 7.050007e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451618e-05, 1.000000e+00, 22, 2339, 3) + ((6.360530e+00, -8.753512e+00, 7.672210e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451750e-05, 1.000000e+00, 23, 2339, 1) + ((6.646303e+00, -8.996680e+00, 8.172263e+00), (4.571054e-01, -3.889563e-01, 7.998548e-01), 1.818775e+05, 4.451856e-05, 1.000000e+00, 23, 2326, 1) + ((6.702490e+00, -9.044491e+00, 8.270582e+00), (2.028534e-01, -9.607965e-01, -1.889990e-01), 1.805295e+04, 4.451877e-05, 1.000000e+00, 23, 2326, 1) + ((6.745127e+00, -9.246436e+00, 8.230857e+00), (2.028534e-01, -9.607965e-01, -1.889990e-01), 1.805295e+04, 4.451990e-05, 1.000000e+00, 22, 2326, 3) + ((6.767219e+00, -9.351070e+00, 8.210274e+00), (2.028534e-01, -9.607965e-01, -1.889990e-01), 1.805295e+04, 4.452049e-05, 1.000000e+00, 21, 2326, 2) + ((6.885344e+00, -9.910562e+00, 8.100216e+00), (-3.857824e-01, -3.006310e-01, -8.722344e-01), 1.795765e+04, 4.452362e-05, 1.000000e+00, 21, 2326, 2) + ((6.803615e+00, -9.974251e+00, 7.915431e+00), (-7.096135e-01, 6.778415e-01, 1.923009e-01), 1.779137e+04, 4.452476e-05, 1.000000e+00, 21, 2326, 2) + ((6.334815e+00, -9.526441e+00, 8.042473e+00), (-1.748210e-01, 9.821705e-01, -6.912779e-02), 1.729669e+04, 4.452835e-05, 1.000000e+00, 21, 2326, 2) + ((6.304853e+00, -9.358111e+00, 8.030625e+00), (-1.748210e-01, 9.821705e-01, -6.912779e-02), 1.729669e+04, 4.452929e-05, 1.000000e+00, 22, 2326, 3) + ((6.288777e+00, -9.267793e+00, 8.024268e+00), (-1.748210e-01, 9.821705e-01, -6.912779e-02), 1.729669e+04, 4.452979e-05, 1.000000e+00, 23, 2326, 1) + ((6.275105e+00, -9.190979e+00, 8.018862e+00), (-7.174952e-01, 5.897531e-01, -3.706642e-01), 9.207900e+03, 4.453022e-05, 1.000000e+00, 23, 2326, 1) + ((6.038719e+00, -8.996680e+00, 7.896743e+00), (-7.174952e-01, 5.897531e-01, -3.706642e-01), 9.207900e+03, 4.453271e-05, 1.000000e+00, 23, 2339, 1) + ((6.028805e+00, -8.988531e+00, 7.891621e+00), (-2.989803e-01, 4.300779e-01, -8.518473e-01), 5.653774e+03, 4.453281e-05, 1.000000e+00, 23, 2339, 1) + ((5.725160e+00, -8.551743e+00, 7.026484e+00), (-2.989803e-01, 4.300779e-01, -8.518473e-01), 5.653774e+03, 4.454257e-05, 1.000000e+00, 23, 2338, 1) + ((5.507324e+00, -8.238390e+00, 6.405832e+00), (-2.989803e-01, 4.300779e-01, -8.518473e-01), 5.653774e+03, 4.454958e-05, 1.000000e+00, 22, 2338, 3) + ((5.417387e+00, -8.109017e+00, 6.149584e+00), (-2.989803e-01, 4.300779e-01, -8.518473e-01), 5.653774e+03, 4.455247e-05, 1.000000e+00, 21, 2338, 2) + ((5.162017e+00, -7.741671e+00, 5.421991e+00), (7.522612e-01, -1.036919e-01, -6.506543e-01), 5.619608e+03, 4.456069e-05, 1.000000e+00, 21, 2338, 2) + ((5.184146e+00, -7.744722e+00, 5.402851e+00), (7.522612e-01, -1.036919e-01, -6.506543e-01), 5.619608e+03, 4.456097e-05, 1.000000e+00, 22, 2338, 3) + ((5.348057e+00, -7.767315e+00, 5.261079e+00), (7.522612e-01, -1.036919e-01, -6.506543e-01), 5.619608e+03, 4.456307e-05, 1.000000e+00, 23, 2338, 1) + ((5.581568e+00, -7.799502e+00, 5.059108e+00), (2.029690e-01, -4.131185e-01, -8.877706e-01), 3.359662e+03, 4.456606e-05, 1.000000e+00, 23, 2338, 1) + ((5.707007e+00, -8.054818e+00, 4.510447e+00), (3.894183e-01, -7.459178e-01, -5.403333e-01), 3.303714e+03, 4.457377e-05, 1.000000e+00, 23, 2338, 1) + ((5.725160e+00, -8.089590e+00, 4.485259e+00), (3.894183e-01, -7.459178e-01, -5.403333e-01), 3.303714e+03, 4.457436e-05, 1.000000e+00, 23, 2339, 1) + ((5.750893e+00, -8.138881e+00, 4.449554e+00), (-2.879829e-01, -5.545287e-01, -7.807456e-01), 1.744626e+03, 4.457519e-05, 1.000000e+00, 23, 2339, 1) + ((5.725160e+00, -8.188431e+00, 4.379790e+00), (-2.879829e-01, -5.545287e-01, -7.807456e-01), 1.744626e+03, 4.457674e-05, 1.000000e+00, 23, 2338, 1) + ((5.532143e+00, -8.560097e+00, 3.856504e+00), (-6.185172e-01, -7.766669e-01, -1.192687e-01), 8.559363e+02, 4.458834e-05, 1.000000e+00, 23, 2338, 1) + ((5.522333e+00, -8.572415e+00, 3.854613e+00), (-3.452053e-01, -2.657159e-02, -9.381510e-01), 9.272133e+01, 4.458873e-05, 1.000000e+00, 23, 2338, 1) + ((5.366948e+00, -8.584375e+00, 3.432328e+00), (4.761677e-01, -8.187551e-01, -3.207871e-01), 6.522002e+00, 4.462253e-05, 1.000000e+00, 23, 2338, 1) + ((5.516634e+00, -8.841755e+00, 3.331487e+00), (4.237115e-01, -6.022578e-01, 6.765752e-01), 9.968491e-01, 4.471152e-05, 1.000000e+00, 23, 2338, 1) + ((5.625629e+00, -8.996680e+00, 3.505530e+00), (4.237115e-01, -6.022578e-01, 6.765752e-01), 9.968491e-01, 4.489779e-05, 1.000000e+00, 23, 2325, 1) + ((5.723424e+00, -9.135684e+00, 3.661687e+00), (7.706137e-01, -6.358415e-01, 4.312932e-02), 5.695813e-01, 4.506493e-05, 1.000000e+00, 23, 2325, 1) + ((5.725160e+00, -9.137116e+00, 3.661784e+00), (7.706137e-01, -6.358415e-01, 4.312932e-02), 5.695813e-01, 4.506708e-05, 1.000000e+00, 23, 2326, 1) + ((6.079210e+00, -9.429247e+00, 3.681599e+00), (7.706137e-01, -6.358415e-01, 4.312932e-02), 5.695813e-01, 4.550721e-05, 1.000000e+00, 22, 2326, 3) + ((6.147194e+00, -9.485341e+00, 3.685404e+00), (7.706137e-01, -6.358415e-01, 4.312932e-02), 5.695813e-01, 4.559172e-05, 1.000000e+00, 21, 2326, 2) + ((6.864744e+00, -1.007740e+01, 3.725564e+00), (3.888690e-01, -8.895503e-01, -2.397522e-01), 5.691123e-01, 4.648372e-05, 1.000000e+00, 21, 2326, 2) + ((6.908415e+00, -1.017730e+01, 3.698639e+00), (3.888690e-01, -8.895503e-01, -2.397522e-01), 5.691123e-01, 4.659135e-05, 1.000000e+00, 22, 2326, 3) + ((6.945959e+00, -1.026318e+01, 3.675492e+00), (3.888690e-01, -8.895503e-01, -2.397522e-01), 5.691123e-01, 4.668388e-05, 1.000000e+00, 23, 2326, 1) + ((6.966122e+00, -1.030930e+01, 3.663060e+00), (7.493672e-01, -6.303754e-01, 2.026713e-01), 2.849108e-01, 4.673357e-05, 1.000000e+00, 23, 2326, 1) + ((7.350254e+00, -1.063244e+01, 3.766951e+00), (7.493672e-01, -6.303754e-01, 2.026713e-01), 2.849108e-01, 4.742789e-05, 1.000000e+00, 23, 2311, 1) + ((7.360920e+00, -1.064141e+01, 3.769836e+00), (7.493672e-01, -6.303754e-01, 2.026713e-01), 2.849108e-01, 4.744717e-05, 1.000000e+00, 23, 2312, 1) + ((7.449168e+00, -1.071565e+01, 3.793703e+00), (1.626881e-01, -1.480918e-01, 9.755006e-01), 2.025207e-01, 4.760667e-05, 1.000000e+00, 23, 2312, 1) + ((7.495754e+00, -1.075805e+01, 4.073039e+00), (-1.185723e-01, -6.787603e-01, 7.247241e-01), 2.121260e-01, 4.806671e-05, 1.000000e+00, 23, 2312, 1) + ((7.451135e+00, -1.101347e+01, 4.345753e+00), (-4.978877e-01, -3.750598e-02, 8.664301e-01), 2.609768e-02, 4.865741e-05, 1.000000e+00, 23, 2312, 1) + ((7.426401e+00, -1.101533e+01, 4.388795e+00), (9.221835e-01, -6.407530e-02, 3.814078e-01), 2.031765e-02, 4.887973e-05, 1.000000e+00, 23, 2312, 1) + ((7.438938e+00, -1.101621e+01, 4.393980e+00), (8.832139e-01, 3.563144e-01, 3.049151e-01), 2.038019e-02, 4.894869e-05, 1.000000e+00, 23, 2312, 1) + ((7.578355e+00, -1.095996e+01, 4.442112e+00), (5.564936e-01, 8.135970e-01, 1.684482e-01), 4.869241e-02, 4.974811e-05, 1.000000e+00, 23, 2312, 1) + ((7.802376e+00, -1.063244e+01, 4.509922e+00), (5.564936e-01, 8.135970e-01, 1.684482e-01), 4.869241e-02, 5.106705e-05, 1.000000e+00, 23, 2327, 1) + ((7.826035e+00, -1.059785e+01, 4.517083e+00), (-8.227572e-01, -5.662268e-01, 4.957651e-02), 8.202195e-02, 5.120634e-05, 1.000000e+00, 23, 2327, 1) + ((7.775776e+00, -1.063244e+01, 4.520112e+00), (-8.227572e-01, -5.662268e-01, 4.957651e-02), 8.202195e-02, 5.136055e-05, 1.000000e+00, 23, 2312, 1) + ((7.360920e+00, -1.091795e+01, 4.545109e+00), (-8.227572e-01, -5.662268e-01, 4.957651e-02), 8.202195e-02, 5.263343e-05, 1.000000e+00, 23, 2311, 1) + ((7.180267e+00, -1.104227e+01, 4.555995e+00), (5.568446e-01, -6.706436e-01, -4.900625e-01), 5.560680e-02, 5.318772e-05, 1.000000e+00, 23, 2311, 1) + ((7.207392e+00, -1.107494e+01, 4.532123e+00), (9.936512e-01, 4.529367e-02, -1.029847e-01), 8.142074e-02, 5.333706e-05, 1.000000e+00, 23, 2311, 1) + ((7.252245e+00, -1.107290e+01, 4.527474e+00), (9.936512e-01, 4.529367e-02, -1.029847e-01), 8.142074e-02, 5.345144e-05, 0.000000e+00, 23, 2311, 1)] diff --git a/tests/regression_tests/track_output/settings.xml b/tests/regression_tests/track_output/settings.xml deleted file mode 100644 index 299ee72c53..0000000000 --- a/tests/regression_tests/track_output/settings.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - eigenvalue - 2 - 0 - 100 - - - - - -1 -1 -1 1 1 1 - - - - - 1 1 1 - 1 1 2 - - - diff --git a/tests/regression_tests/track_output/test.py b/tests/regression_tests/track_output/test.py index a5300a4aed..299e1aa34a 100644 --- a/tests/regression_tests/track_output/test.py +++ b/tests/regression_tests/track_output/test.py @@ -1,11 +1,12 @@ import glob import os -from subprocess import call -import shutil +from pathlib import Path +import numpy as np +import openmc import pytest -from tests.testing_harness import TestHarness +from tests.testing_harness import TestHarness, config class TrackTestHarness(TestHarness): @@ -13,26 +14,33 @@ class TrackTestHarness(TestHarness): """Make sure statepoint.* and track* have been created.""" TestHarness._test_output_created(self) - outputs = glob.glob('track_1_1_*.h5') - assert len(outputs) == 2, 'Expected two track files.' + if config['mpi'] and int(config['mpi_np']) > 1: + outputs = Path.cwd().glob('tracks_p*.h5') + assert len(list(outputs)) == int(config['mpi_np']) + else: + assert Path('tracks.h5').is_file() def _get_results(self): - """Digest info in the statepoint and return as a string.""" - # Run the track-to-vtk conversion script. - call(['../../../scripts/openmc-track-to-vtk', '-o', 'poly'] + - glob.glob('track_1_1_*.h5')) + """Get data from track file and return as a string.""" - # Make sure the vtk file was created then return it's contents. - assert os.path.isfile('poly.pvtp'), 'poly.pvtp file not found.' + # For MPI mode, combine track files + if config['mpi']: + track_files = list(glob.glob('tracks_p*.h5')) + openmc.Tracks.combine(track_files, 'tracks.h5') - with open('poly.pvtp', 'r') as fin: - outstr = fin.read() + # Get string of track file information + outstr = '' + tracks = openmc.Tracks('tracks.h5') + for track in tracks: + with np.printoptions(formatter={'float_kind': '{:.6e}'.format}): + for ptrack in track: + outstr += f"{ptrack.particle} {ptrack.states}\n" return outstr def _cleanup(self): TestHarness._cleanup(self) - output = glob.glob('track*') + glob.glob('poly*') + output = glob.glob('tracks*') + glob.glob('poly*') for f in output: if os.path.exists(f): os.remove(f) diff --git a/tests/regression_tests/translation/results_true.dat b/tests/regression_tests/translation/results_true.dat index 32897b8e75..6e03d2224f 100644 --- a/tests/regression_tests/translation/results_true.dat +++ b/tests/regression_tests/translation/results_true.dat @@ -1,2 +1,2 @@ k-combined: -4.003951E-01 7.739871E-03 +4.076610E-01 6.454244E-03 diff --git a/tests/regression_tests/trigger_batch_interval/results_true.dat b/tests/regression_tests/trigger_batch_interval/results_true.dat index 2fcfaa10a7..92fa99d87a 100644 --- a/tests/regression_tests/trigger_batch_interval/results_true.dat +++ b/tests/regression_tests/trigger_batch_interval/results_true.dat @@ -1,28 +1,28 @@ k-combined: -9.764624E-01 8.747085E-03 +9.863217E-01 6.499354E-03 tally 1: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 tally 2: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 diff --git a/tests/regression_tests/trigger_batch_interval/tallies.xml b/tests/regression_tests/trigger_batch_interval/tallies.xml index 3440dbf217..ea25596a4f 100644 --- a/tests/regression_tests/trigger_batch_interval/tallies.xml +++ b/tests/regression_tests/trigger_batch_interval/tallies.xml @@ -2,7 +2,7 @@ - all + Pu239 total total absorption fission scatter diff --git a/tests/regression_tests/trigger_no_batch_interval/results_true.dat b/tests/regression_tests/trigger_no_batch_interval/results_true.dat index 2fcfaa10a7..92fa99d87a 100644 --- a/tests/regression_tests/trigger_no_batch_interval/results_true.dat +++ b/tests/regression_tests/trigger_no_batch_interval/results_true.dat @@ -1,28 +1,28 @@ k-combined: -9.764624E-01 8.747085E-03 +9.863217E-01 6.499354E-03 tally 1: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 tally 2: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 diff --git a/tests/regression_tests/trigger_no_batch_interval/tallies.xml b/tests/regression_tests/trigger_no_batch_interval/tallies.xml index 3440dbf217..ea25596a4f 100644 --- a/tests/regression_tests/trigger_no_batch_interval/tallies.xml +++ b/tests/regression_tests/trigger_no_batch_interval/tallies.xml @@ -2,7 +2,7 @@ - all + Pu239 total total absorption fission scatter diff --git a/tests/regression_tests/trigger_no_status/results_true.dat b/tests/regression_tests/trigger_no_status/results_true.dat index e95f05ccb6..a62e1b3fe5 100644 --- a/tests/regression_tests/trigger_no_status/results_true.dat +++ b/tests/regression_tests/trigger_no_status/results_true.dat @@ -1,28 +1,28 @@ k-combined: -9.688702E-01 2.263104E-02 +9.858966E-01 1.500542E-02 tally 1: -7.069810E+00 -1.000691E+01 -1.597648E+00 -5.109653E-01 -1.547262E+00 -4.792189E-01 -5.472161E+00 -5.995488E+00 -7.069810E+00 -1.000691E+01 -1.597648E+00 -5.109653E-01 -1.547262E+00 -4.792189E-01 -5.472161E+00 -5.995488E+00 +7.081828E+00 +1.003709E+01 +1.607382E+00 +5.171386E-01 +1.559242E+00 +4.866413E-01 +5.474447E+00 +5.997737E+00 +7.081828E+00 +1.003709E+01 +1.607382E+00 +5.171386E-01 +1.559242E+00 +4.866413E-01 +5.474447E+00 +5.997737E+00 tally 2: -7.069810E+00 -1.000691E+01 -1.597648E+00 -5.109653E-01 -1.547262E+00 -4.792189E-01 -5.472161E+00 -5.995488E+00 +7.081828E+00 +1.003709E+01 +1.607382E+00 +5.171386E-01 +1.559242E+00 +4.866413E-01 +5.474447E+00 +5.997737E+00 diff --git a/tests/regression_tests/trigger_no_status/tallies.xml b/tests/regression_tests/trigger_no_status/tallies.xml index 3440dbf217..ea25596a4f 100644 --- a/tests/regression_tests/trigger_no_status/tallies.xml +++ b/tests/regression_tests/trigger_no_status/tallies.xml @@ -2,7 +2,7 @@ - all + Pu239 total total absorption fission scatter diff --git a/tests/regression_tests/trigger_statepoint_restart/inputs_true.dat b/tests/regression_tests/trigger_statepoint_restart/inputs_true.dat index 2c422697b9..59a8297009 100644 --- a/tests/regression_tests/trigger_statepoint_restart/inputs_true.dat +++ b/tests/regression_tests/trigger_statepoint_restart/inputs_true.dat @@ -1,35 +1,34 @@ - - - - - - - - - - - - - - eigenvalue - 400 - 15 - 10 - - 0.003 - std_dev - - - true - 1000 - 1 - - 1 - - - - - flux - - + + + + + + + + + + + + + eigenvalue + 400 + 15 + 10 + + 0.002 + std_dev + + + true + 1000 + 1 + + 1 + + + + flux + + + diff --git a/tests/regression_tests/trigger_statepoint_restart/results_true.dat b/tests/regression_tests/trigger_statepoint_restart/results_true.dat index 3ab555d734..3cb1e230dd 100644 --- a/tests/regression_tests/trigger_statepoint_restart/results_true.dat +++ b/tests/regression_tests/trigger_statepoint_restart/results_true.dat @@ -1,5 +1,5 @@ k-combined: -2.963805E-01 2.986351E-03 +2.948661E-01 1.949846E-03 tally 1: -8.017826E+01 -6.436316E+02 +5.515170E+01 +4.349007E+02 diff --git a/tests/regression_tests/trigger_statepoint_restart/test.py b/tests/regression_tests/trigger_statepoint_restart/test.py index 8d4d561926..b242f7f1cf 100644 --- a/tests/regression_tests/trigger_statepoint_restart/test.py +++ b/tests/regression_tests/trigger_statepoint_restart/test.py @@ -29,7 +29,7 @@ def model(): settings.inactive = 10 settings.particles = 400 # Choose a sufficiently low threshold to enable use of trigger - settings.keff_trigger = {'type': 'std_dev', 'threshold': 0.003} + settings.keff_trigger = {'type': 'std_dev', 'threshold': 0.002} settings.trigger_max_batches = 1000 settings.trigger_batch_interval = 1 settings.trigger_active = True @@ -41,10 +41,10 @@ def model(): tallies = openmc.Tallies([t]) # Put it all together - model = openmc.model.Model(materials=materials, - geometry=geometry, - settings=settings, - tallies=tallies) + model = openmc.Model(materials=materials, + geometry=geometry, + settings=settings, + tallies=tallies) return model @@ -90,7 +90,7 @@ class TriggerStatepointRestartTestHarness(PyAPITestHarness): assert spfile with openmc.StatePoint(spfile) as sp: sp_batchno_1 = sp.current_batch - k_combined_1 = sp.k_combined + keff_1 = sp.keff assert sp_batchno_1 > 5 print('Last batch no = %d' % sp_batchno_1) self._write_inputs(self._get_inputs()) @@ -108,13 +108,13 @@ class TriggerStatepointRestartTestHarness(PyAPITestHarness): assert spfile with openmc.StatePoint(spfile) as sp: sp_batchno_2 = sp.current_batch - k_combined_2 = sp.k_combined + keff_2 = sp.keff assert sp_batchno_2 > 5 assert sp_batchno_1 == sp_batchno_2, \ 'Different final batch number after restart' # need str() here as uncertainties.ufloat instances are always different - assert str(k_combined_1) == str(k_combined_2), \ - 'Different final k_combined after restart' + assert str(keff_1) == str(keff_2), \ + 'Different final keff after restart' self._write_inputs(self._get_inputs()) self._compare_inputs() self._test_output_created() diff --git a/tests/regression_tests/trigger_tallies/results_true.dat b/tests/regression_tests/trigger_tallies/results_true.dat index 2fcfaa10a7..92fa99d87a 100644 --- a/tests/regression_tests/trigger_tallies/results_true.dat +++ b/tests/regression_tests/trigger_tallies/results_true.dat @@ -1,28 +1,28 @@ k-combined: -9.764624E-01 8.747085E-03 +9.863217E-01 6.499354E-03 tally 1: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 tally 2: -1.408588E+01 -1.985937E+01 -3.194837E+00 -1.021391E+00 -3.096170E+00 -9.592197E-01 -1.089104E+01 -1.187349E+01 +1.423436E+01 +2.027258E+01 +3.223740E+00 +1.039872E+00 +3.125016E+00 +9.771709E-01 +1.101062E+01 +1.212977E+01 diff --git a/tests/regression_tests/trigger_tallies/tallies.xml b/tests/regression_tests/trigger_tallies/tallies.xml index 0d66e1538e..ffe8a1a9e3 100644 --- a/tests/regression_tests/trigger_tallies/tallies.xml +++ b/tests/regression_tests/trigger_tallies/tallies.xml @@ -2,7 +2,7 @@ - all + Pu239 total total absorption fission scatter diff --git a/tests/regression_tests/triso/inputs_true.dat b/tests/regression_tests/triso/inputs_true.dat index 2ea049465c..c966845685 100644 --- a/tests/regression_tests/triso/inputs_true.dat +++ b/tests/regression_tests/triso/inputs_true.dat @@ -1,442 +1,442 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.3333333333333333 0.3333333333333333 0.3333333333333333 - 38 - 3 3 3 - -0.5 -0.5 -0.5 - -17 18 19 -14 15 16 -11 12 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.3333333333333333 0.3333333333333333 0.3333333333333333 + 30 + 3 3 3 + -0.5 -0.5 -0.5 + +9 10 11 +6 7 8 +3 4 5 -26 27 28 -23 24 25 -20 21 22 +18 19 20 +15 16 17 +12 13 14 -35 36 37 -32 33 34 -29 30 31 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 4 - 0 - - - 0.0 0.0 0.0 - - - +27 28 29 +24 25 26 +21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 4 + 0 + + + 0.0 0.0 0.0 + + + + diff --git a/tests/regression_tests/triso/results_true.dat b/tests/regression_tests/triso/results_true.dat index 279094ab3d..d9850e410f 100644 --- a/tests/regression_tests/triso/results_true.dat +++ b/tests/regression_tests/triso/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.406055E+00 9.581396E-02 +1.604832E+00 2.031393E-03 diff --git a/tests/regression_tests/triso/test.py b/tests/regression_tests/triso/test.py index c447156913..3fa5e3c60d 100644 --- a/tests/regression_tests/triso/test.py +++ b/tests/regression_tests/triso/test.py @@ -1,7 +1,3 @@ -import random -from math import sqrt - -import numpy as np import openmc import openmc.model @@ -9,7 +5,8 @@ from tests.testing_harness import PyAPITestHarness class TRISOTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Define TRISO matrials fuel = openmc.Material() fuel.set_density('g/cm3', 10.5) @@ -64,8 +61,8 @@ class TRISOTestHarness(PyAPITestHarness): box = openmc.Cell(region=box_region) outer_radius = 422.5*1e-4 - centers = openmc.model.pack_spheres(radius=outer_radius, - region=box_region, num_spheres=100) + centers = openmc.model.pack_spheres( + radius=outer_radius, region=box_region, num_spheres=100, seed=1) trisos = [openmc.model.TRISO(outer_radius, inner_univ, c) for c in centers] @@ -78,20 +75,19 @@ class TRISOTestHarness(PyAPITestHarness): box.fill = lattice root = openmc.Universe(0, cells=[box]) - geom = openmc.Geometry(root) - geom.export_to_xml() + self._model.geometry = openmc.Geometry(root) settings = openmc.Settings() settings.batches = 4 settings.inactive = 0 settings.particles = 100 - settings.source = openmc.Source(space=openmc.stats.Point()) - settings.export_to_xml() + settings.source = openmc.IndependentSource(space=openmc.stats.Point()) + self._model.settings = settings - mats = openmc.Materials([fuel, porous_carbon, ipyc, sic, opyc, graphite]) - mats.export_to_xml() + self._model.materials = openmc.Materials([fuel, porous_carbon, ipyc, + sic, opyc, graphite]) def test_triso(): - harness = TRISOTestHarness('statepoint.4.h5') + harness = TRISOTestHarness('statepoint.4.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/triso_virtual_lattice/inputs_true.dat b/tests/regression_tests/triso_virtual_lattice/inputs_true.dat index aac4fde5d5..cdf1574633 100644 --- a/tests/regression_tests/triso_virtual_lattice/inputs_true.dat +++ b/tests/regression_tests/triso_virtual_lattice/inputs_true.dat @@ -1,278 +1,278 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.0 1.0 1.0 - 12 - 1 1 1 - -0.5 -0.5 -0.5 - -11 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eigenvalue - 100 - 4 - 0 - - - 0.0 0.0 0.0 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0 1.0 1.0 + 4 + 1 1 1 + -0.5 -0.5 -0.5 + +3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 100 + 4 + 0 + + + 0.0 0.0 0.0 + + + + diff --git a/tests/regression_tests/triso_virtual_lattice/results_true.dat b/tests/regression_tests/triso_virtual_lattice/results_true.dat index 3e9ca126f8..b576675599 100644 --- a/tests/regression_tests/triso_virtual_lattice/results_true.dat +++ b/tests/regression_tests/triso_virtual_lattice/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.719897E+00 3.608153E-02 +1.514485E+00 2.297724E-02 diff --git a/tests/regression_tests/triso_virtual_lattice/test.py b/tests/regression_tests/triso_virtual_lattice/test.py index 481866a944..dbde24bfcc 100644 --- a/tests/regression_tests/triso_virtual_lattice/test.py +++ b/tests/regression_tests/triso_virtual_lattice/test.py @@ -9,7 +9,8 @@ from tests.testing_harness import PyAPITestHarness class TRISOVirtualLatticeTestHarness(PyAPITestHarness): - def _build_inputs(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) # Define TRISO matrials fuel = openmc.Material() fuel.set_density('g/cm3', 10.5) @@ -65,7 +66,7 @@ class TRISOVirtualLatticeTestHarness(PyAPITestHarness): outer_radius = 422.5*1e-4 centers = openmc.model.pack_spheres(radius=outer_radius, - region=box_region, num_spheres=100) + region=box_region, num_spheres=100, seed=1) trisos = [openmc.model.TRISO(outer_radius, inner_univ, c) for c in centers] @@ -78,20 +79,19 @@ class TRISOVirtualLatticeTestHarness(PyAPITestHarness): box.fill = lattice root = openmc.Universe(0, cells=[box]) - geom = openmc.Geometry(root) - geom.export_to_xml() + self._model.geometry = openmc.Geometry(root) settings = openmc.Settings() settings.batches = 4 settings.inactive = 0 settings.particles = 100 - settings.source = openmc.Source(space=openmc.stats.Point()) - settings.export_to_xml() + settings.source = openmc.IndependentSource(space=openmc.stats.Point()) + self._model.settings = settings - mats = openmc.Materials([fuel, porous_carbon, ipyc, sic, opyc, graphite]) - mats.export_to_xml() + self._model.materials = openmc.Materials([fuel, porous_carbon, ipyc, + sic, opyc, graphite]) def test_triso_virtual_lattice(): - harness = TRISOVirtualLatticeTestHarness('statepoint.4.h5') + harness = TRISOVirtualLatticeTestHarness('statepoint.4.h5', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/uniform_fs/results_true.dat b/tests/regression_tests/uniform_fs/results_true.dat index ebf06a8d3e..f7ceecfb73 100644 --- a/tests/regression_tests/uniform_fs/results_true.dat +++ b/tests/regression_tests/uniform_fs/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.643255E-01 1.041799E-02 +3.675645E-01 4.342970E-03 diff --git a/tests/regression_tests/universe/results_true.dat b/tests/regression_tests/universe/results_true.dat index fe46748c80..97b997ae6b 100644 --- a/tests/regression_tests/universe/results_true.dat +++ b/tests/regression_tests/universe/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.987050E-01 1.827430E-03 +2.940336E-01 7.338463E-04 diff --git a/tests/regression_tests/unstructured_mesh/inputs_true.dat b/tests/regression_tests/unstructured_mesh/inputs_true.dat new file mode 100644 index 0000000000..e7a485b4d1 --- /dev/null +++ b/tests/regression_tests/unstructured_mesh/inputs_true.dat @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_hexes.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true0.dat b/tests/regression_tests/unstructured_mesh/inputs_true0.dat index 7411e9b940..2508c25b3c 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true0.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true0.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true1.dat b/tests/regression_tests/unstructured_mesh/inputs_true1.dat index e85aab1193..04bbdb4b27 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true1.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true1.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true10.dat b/tests/regression_tests/unstructured_mesh/inputs_true10.dat index 628e5e7bcc..169f0a6b4e 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true10.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true10.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true11.dat b/tests/regression_tests/unstructured_mesh/inputs_true11.dat index 1c0a579e54..8163750caf 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true11.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true11.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true12.dat b/tests/regression_tests/unstructured_mesh/inputs_true12.dat index a75344435f..be0655e98d 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true12.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true12.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - tracklength - - - 2 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true13.dat b/tests/regression_tests/unstructured_mesh/inputs_true13.dat index c62c3c9113..42c0cc1226 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true13.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true13.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - tracklength - - - 2 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true14.dat b/tests/regression_tests/unstructured_mesh/inputs_true14.dat index b7ac4c048c..a27c7445dc 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true14.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true14.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - tracklength - - - 2 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true15.dat b/tests/regression_tests/unstructured_mesh/inputs_true15.dat index 1d96efc7d3..c69bd15d0d 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true15.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true15.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - tracklength - - - 2 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true2.dat b/tests/regression_tests/unstructured_mesh/inputs_true2.dat index a2a2d4a27a..ba9d477b55 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true2.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true2.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true3.dat b/tests/regression_tests/unstructured_mesh/inputs_true3.dat index c96f902016..d840bfa095 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true3.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true3.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true4.dat b/tests/regression_tests/unstructured_mesh/inputs_true4.dat index a39452da7c..db8e371771 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true4.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true4.dat @@ -1,92 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.exo - - - 9 - - - 10 - - - 9 - flux - tracklength - - - 10 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true5.dat b/tests/regression_tests/unstructured_mesh/inputs_true5.dat index 288b7d1fcb..b2bbc7e165 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true5.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true5.dat @@ -1,92 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.exo - - - 11 - - - 12 - - - 11 - flux - tracklength - - - 12 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true6.dat b/tests/regression_tests/unstructured_mesh/inputs_true6.dat index 883e6cbf48..d6ad0d98d8 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true6.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true6.dat @@ -1,92 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.exo - - - 13 - - - 14 - - - 13 - flux - tracklength - - - 14 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true7.dat b/tests/regression_tests/unstructured_mesh/inputs_true7.dat index 267966e3ca..924790a8c9 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true7.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true7.dat @@ -1,92 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 100 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.exo - - - 15 - - - 16 - - - 15 - flux - tracklength - - - 16 - flux - tracklength - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + tracklength + + + 2 + flux + tracklength + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true8.dat b/tests/regression_tests/unstructured_mesh/inputs_true8.dat index 9a7ad57113..c04beb2112 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true8.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true8.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets_w_holes.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets_w_holes.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/inputs_true9.dat b/tests/regression_tests/unstructured_mesh/inputs_true9.dat index 8aedad2377..111898ff24 100644 --- a/tests/regression_tests/unstructured_mesh/inputs_true9.dat +++ b/tests/regression_tests/unstructured_mesh/inputs_true9.dat @@ -1,91 +1,90 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 10 - - - - - 0.0 1.0 - - - 0.0 1.0 - - - - 15000000.0 1.0 - - - - - - - 10 10 10 - -10.0 -10.0 -10.0 - 10.0 10.0 10.0 - - - test_mesh_tets.e - - - 1 - - - 2 - - - 1 - flux - collision - - - 2 - flux - collision - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 10 + + + + + 1.0 1.0 + + + 0.0 1.0 + + + + 15000000.0 1.0 + + + + + + 10 10 10 + -10.0 -10.0 -10.0 + 10.0 10.0 10.0 + + + test_mesh_tets.e + + + 1 + + + 2 + + + 1 + flux + collision + + + 2 + flux + collision + + + diff --git a/tests/regression_tests/unstructured_mesh/results_true.dat b/tests/regression_tests/unstructured_mesh/results_true.dat deleted file mode 100644 index c93eadfdba..0000000000 --- a/tests/regression_tests/unstructured_mesh/results_true.dat +++ /dev/null @@ -1,26002 +0,0 @@ -tally 1: -4.873713E-02 -2.881006E-04 -7.388548E-02 -6.421281E-04 -8.309847E-02 -8.079791E-04 -8.802030E-02 -8.590396E-04 -6.935977E-02 -5.711723E-04 -7.203243E-02 -5.847086E-04 -8.146742E-02 -8.422730E-04 -7.112287E-02 -5.331461E-04 -6.944025E-02 -6.220640E-04 -4.605581E-02 -3.052058E-04 -7.341445E-02 -6.319466E-04 -9.013420E-02 -1.083246E-03 -1.221831E-01 -1.761591E-03 -1.244445E-01 -1.714565E-03 -1.324446E-01 -2.041161E-03 -1.110879E-01 -1.399393E-03 -1.484356E-01 -2.412924E-03 -1.243711E-01 -1.740278E-03 -1.011503E-01 -1.167740E-03 -6.361036E-02 -4.887610E-04 -6.774704E-02 -6.414084E-04 -8.554708E-02 -9.422426E-04 -1.400424E-01 -2.238846E-03 -1.573053E-01 -2.687261E-03 -1.640744E-01 -2.864310E-03 -1.305526E-01 -1.848808E-03 -1.688498E-01 -3.171592E-03 -1.886270E-01 -3.972143E-03 -1.198794E-01 -1.610468E-03 -7.781359E-02 -8.133302E-04 -8.003923E-02 -7.488868E-04 -1.533578E-01 -2.662538E-03 -2.008234E-01 -4.137147E-03 -2.073409E-01 -4.520687E-03 -1.975748E-01 -4.160887E-03 -2.267844E-01 -5.491338E-03 -1.881489E-01 -3.776524E-03 -1.546827E-01 -2.713647E-03 -1.390602E-01 -2.088805E-03 -9.507737E-02 -1.035204E-03 -9.443902E-02 -9.783367E-04 -1.528603E-01 -2.521963E-03 -2.009995E-01 -4.414632E-03 -2.303894E-01 -5.751985E-03 -2.273601E-01 -5.379286E-03 -2.086601E-01 -4.747259E-03 -1.587188E-01 -2.863534E-03 -1.656423E-01 -2.826265E-03 -1.358638E-01 -2.045306E-03 -9.729054E-02 -1.078500E-03 -1.007280E-01 -1.185702E-03 -1.587021E-01 -2.754745E-03 -1.900019E-01 -3.874093E-03 -1.874138E-01 -4.177562E-03 -2.233840E-01 -5.748107E-03 -2.044120E-01 -4.806449E-03 -1.840480E-01 -3.872224E-03 -2.000442E-01 -4.610770E-03 -1.260142E-01 -1.809723E-03 -8.400216E-02 -8.434010E-04 -5.900093E-02 -3.850176E-04 -1.411568E-01 -2.471608E-03 -1.744586E-01 -3.239802E-03 -1.834956E-01 -3.972256E-03 -2.120173E-01 -4.965877E-03 -1.896203E-01 -3.727876E-03 -1.768841E-01 -3.517246E-03 -1.329247E-01 -1.981664E-03 -9.206320E-02 -1.061169E-03 -8.660878E-02 -8.546437E-04 -7.697726E-02 -7.926443E-04 -1.161555E-01 -1.548200E-03 -1.386967E-01 -2.047818E-03 -1.482373E-01 -2.537385E-03 -1.929067E-01 -4.259826E-03 -1.371938E-01 -1.949236E-03 -1.308202E-01 -2.027747E-03 -1.135428E-01 -1.560189E-03 -1.215934E-01 -1.672414E-03 -4.697342E-02 -3.377074E-04 -4.963529E-02 -3.397352E-04 -8.476039E-02 -8.774056E-04 -1.108440E-01 -1.411371E-03 -1.054914E-01 -1.252891E-03 -1.453762E-01 -2.358977E-03 -9.293716E-02 -9.683367E-04 -1.224872E-01 -1.612325E-03 -8.334352E-02 -7.907688E-04 -7.546069E-02 -7.025010E-04 -5.962134E-02 -6.951755E-04 -3.107769E-02 -1.723123E-04 -8.479467E-02 -9.620488E-04 -8.295926E-02 -7.769636E-04 -8.805824E-02 -8.500302E-04 -8.616294E-02 -8.661213E-04 -7.861089E-02 -6.835617E-04 -7.593919E-02 -7.633339E-04 -7.303400E-02 -6.464593E-04 -7.512486E-02 -6.007484E-04 -5.595863E-02 -4.403603E-04 -6.015811E-02 -4.297440E-04 -8.867588E-02 -8.521129E-04 -1.103542E-01 -1.469796E-03 -1.153731E-01 -1.606650E-03 -1.280835E-01 -1.791883E-03 -1.434578E-01 -2.215626E-03 -1.104920E-01 -1.470286E-03 -9.385805E-02 -1.114787E-03 -1.169307E-01 -1.600595E-03 -6.083594E-02 -4.303510E-04 -8.816849E-02 -8.791686E-04 -1.350912E-01 -2.127521E-03 -2.026574E-01 -4.456203E-03 -2.082171E-01 -4.587768E-03 -2.291899E-01 -5.560114E-03 -2.040384E-01 -4.355166E-03 -2.081784E-01 -5.172938E-03 -1.958644E-01 -4.308708E-03 -1.535028E-01 -2.754667E-03 -6.265531E-02 -4.662647E-04 -1.239416E-01 -1.608329E-03 -1.655136E-01 -3.098331E-03 -2.152185E-01 -5.116031E-03 -2.663061E-01 -7.502107E-03 -3.072160E-01 -9.873652E-03 -2.453306E-01 -6.585229E-03 -2.577460E-01 -6.982524E-03 -2.470375E-01 -6.761678E-03 -1.492132E-01 -2.465269E-03 -1.115903E-01 -1.444496E-03 -8.901752E-02 -9.826260E-04 -1.849459E-01 -3.824332E-03 -3.139129E-01 -1.040826E-02 -3.209843E-01 -1.072304E-02 -3.074029E-01 -9.805249E-03 -3.289037E-01 -1.137280E-02 -3.333668E-01 -1.140301E-02 -2.305636E-01 -5.572002E-03 -1.911618E-01 -3.897249E-03 -1.239342E-01 -1.745528E-03 -1.463546E-01 -2.897280E-03 -2.041194E-01 -4.264966E-03 -2.781505E-01 -8.222869E-03 -3.535732E-01 -1.290897E-02 -3.736359E-01 -1.424372E-02 -3.507573E-01 -1.268186E-02 -3.537878E-01 -1.317025E-02 -2.797764E-01 -8.141471E-03 -2.257660E-01 -5.307299E-03 -1.248875E-01 -1.867307E-03 -1.334027E-01 -1.952356E-03 -2.190936E-01 -5.220949E-03 -2.880627E-01 -9.292540E-03 -3.683567E-01 -1.398170E-02 -3.451428E-01 -1.245893E-02 -3.602954E-01 -1.357760E-02 -3.363047E-01 -1.177859E-02 -2.658735E-01 -8.042327E-03 -1.720687E-01 -3.616340E-03 -9.348145E-02 -9.216558E-04 -1.303415E-01 -2.064085E-03 -1.757103E-01 -3.440908E-03 -2.442374E-01 -6.810297E-03 -2.858542E-01 -8.698701E-03 -3.349866E-01 -1.174757E-02 -2.840415E-01 -8.265549E-03 -2.763234E-01 -8.045873E-03 -2.478361E-01 -6.589169E-03 -1.766169E-01 -3.338895E-03 -1.236805E-01 -1.649583E-03 -1.307918E-01 -1.796440E-03 -1.756810E-01 -3.938136E-03 -2.071625E-01 -4.889011E-03 -2.332393E-01 -5.650908E-03 -2.524899E-01 -7.236764E-03 -2.393364E-01 -5.985752E-03 -2.142983E-01 -4.994425E-03 -2.132056E-01 -4.981318E-03 -1.437349E-01 -2.274574E-03 -1.142901E-01 -1.631508E-03 -7.809219E-02 -7.844820E-04 -1.076778E-01 -1.505283E-03 -1.787646E-01 -3.437697E-03 -1.664344E-01 -2.938475E-03 -1.868305E-01 -3.942399E-03 -2.065932E-01 -4.503697E-03 -1.644873E-01 -3.015461E-03 -1.553155E-01 -2.637052E-03 -1.523517E-01 -2.641268E-03 -8.646767E-02 -8.586764E-04 -4.575304E-02 -2.880831E-04 -8.659752E-02 -1.007122E-03 -1.086638E-01 -1.418859E-03 -1.206844E-01 -1.631006E-03 -1.553521E-01 -2.699733E-03 -1.023448E-01 -1.229544E-03 -1.236892E-01 -1.719554E-03 -1.059603E-01 -1.474144E-03 -9.843445E-02 -1.147384E-03 -6.598215E-02 -6.724344E-04 -8.999660E-02 -1.129810E-03 -1.016655E-01 -1.128618E-03 -1.115822E-01 -1.437827E-03 -1.649326E-01 -3.015499E-03 -1.937529E-01 -4.048175E-03 -1.852317E-01 -3.668098E-03 -1.620688E-01 -2.937845E-03 -1.290063E-01 -1.922280E-03 -1.208701E-01 -1.669032E-03 -4.421440E-02 -2.483126E-04 -1.159755E-01 -1.426808E-03 -1.459714E-01 -2.613102E-03 -1.778589E-01 -3.427022E-03 -2.346134E-01 -5.940704E-03 -3.063520E-01 -1.009182E-02 -2.950770E-01 -9.132939E-03 -2.583166E-01 -7.253540E-03 -2.101808E-01 -5.272950E-03 -1.850368E-01 -3.730831E-03 -7.244837E-02 -5.806488E-04 -1.441504E-01 -2.203647E-03 -2.206693E-01 -5.103470E-03 -3.047901E-01 -9.751684E-03 -3.531926E-01 -1.351362E-02 -3.945357E-01 -1.624582E-02 -4.621245E-01 -2.215536E-02 -4.200516E-01 -1.926381E-02 -3.397496E-01 -1.264644E-02 -2.045791E-01 -4.443989E-03 -1.132779E-01 -1.665634E-03 -1.168744E-01 -1.466979E-03 -2.665743E-01 -7.799994E-03 -4.108479E-01 -1.764683E-02 -5.099245E-01 -2.656607E-02 -5.375292E-01 -2.986206E-02 -5.477580E-01 -3.184925E-02 -5.060566E-01 -2.644067E-02 -3.917491E-01 -1.649179E-02 -2.631839E-01 -7.172646E-03 -1.393797E-01 -2.179333E-03 -1.415608E-01 -2.582853E-03 -2.695963E-01 -8.100051E-03 -4.591315E-01 -2.150436E-02 -5.838830E-01 -3.565296E-02 -6.933462E-01 -4.933765E-02 -6.498992E-01 -4.329824E-02 -5.274087E-01 -2.893644E-02 -4.644714E-01 -2.208542E-02 -2.784825E-01 -8.228619E-03 -1.756971E-01 -3.435838E-03 -1.841108E-01 -3.753742E-03 -2.556216E-01 -6.993979E-03 -4.539882E-01 -2.105903E-02 -5.644697E-01 -3.353269E-02 -6.477708E-01 -4.262528E-02 -6.274586E-01 -4.094566E-02 -4.871803E-01 -2.460107E-02 -3.651460E-01 -1.537598E-02 -2.377974E-01 -6.036841E-03 -1.492687E-01 -2.421537E-03 -1.449703E-01 -2.190902E-03 -2.414255E-01 -6.387083E-03 -4.115719E-01 -1.783179E-02 -4.694990E-01 -2.282330E-02 -5.311416E-01 -2.970750E-02 -5.218655E-01 -2.795428E-02 -4.993270E-01 -2.574782E-02 -3.564483E-01 -1.422766E-02 -2.266457E-01 -5.534879E-03 -1.952576E-01 -4.177312E-03 -1.042590E-01 -1.176129E-03 -1.697991E-01 -3.286179E-03 -2.717221E-01 -8.156353E-03 -3.954295E-01 -1.626070E-02 -3.609317E-01 -1.316825E-02 -4.028309E-01 -1.737150E-02 -3.598743E-01 -1.378511E-02 -2.960368E-01 -9.045648E-03 -1.916245E-01 -4.182938E-03 -1.400504E-01 -2.146240E-03 -7.940300E-02 -7.986853E-04 -1.200054E-01 -1.726710E-03 -2.148774E-01 -4.835765E-03 -2.268851E-01 -5.713301E-03 -3.111988E-01 -1.041879E-02 -2.623195E-01 -7.661642E-03 -2.331769E-01 -5.928043E-03 -2.156752E-01 -5.033537E-03 -1.882320E-01 -3.956427E-03 -9.864956E-02 -1.084355E-03 -6.087770E-02 -5.179666E-04 -1.086978E-01 -1.313619E-03 -1.344587E-01 -1.970449E-03 -1.740701E-01 -3.313694E-03 -1.804922E-01 -3.525957E-03 -1.724149E-01 -3.543590E-03 -1.399028E-01 -2.273390E-03 -1.456847E-01 -2.524033E-03 -1.194497E-01 -1.524909E-03 -6.585025E-02 -6.225934E-04 -8.228757E-02 -8.696950E-04 -9.653270E-02 -9.897964E-04 -1.628236E-01 -3.163363E-03 -2.294784E-01 -5.585206E-03 -2.013744E-01 -4.481916E-03 -1.904412E-01 -4.042826E-03 -1.614976E-01 -2.818448E-03 -1.577143E-01 -2.732044E-03 -1.102866E-01 -1.529048E-03 -7.446456E-02 -6.633761E-04 -1.435986E-01 -2.237722E-03 -1.897248E-01 -4.132042E-03 -2.294338E-01 -5.931694E-03 -3.421437E-01 -1.221982E-02 -3.839244E-01 -1.553491E-02 -3.266968E-01 -1.088442E-02 -3.198477E-01 -1.080985E-02 -2.628059E-01 -7.789175E-03 -1.734962E-01 -3.443215E-03 -1.286647E-01 -1.955856E-03 -1.691938E-01 -3.460989E-03 -2.825603E-01 -8.930867E-03 -3.393298E-01 -1.245987E-02 -4.719879E-01 -2.299342E-02 -5.453030E-01 -3.116166E-02 -5.283745E-01 -2.875357E-02 -4.717010E-01 -2.274570E-02 -3.543923E-01 -1.356917E-02 -2.286992E-01 -5.495781E-03 -1.503321E-01 -2.412737E-03 -1.914802E-01 -4.228512E-03 -2.663347E-01 -7.478291E-03 -4.945846E-01 -2.581416E-02 -7.132283E-01 -5.214747E-02 -8.772549E-01 -8.070787E-02 -9.215230E-01 -8.643514E-02 -7.136543E-01 -5.309880E-02 -4.683027E-01 -2.335474E-02 -2.848724E-01 -8.819567E-03 -1.962746E-01 -4.034279E-03 -1.963942E-01 -4.150202E-03 -3.081918E-01 -1.036112E-02 -5.313924E-01 -2.943772E-02 -8.980200E-01 -8.439371E-02 -1.404070E+00 -1.986543E-01 -1.242684E+00 -1.571634E-01 -8.663320E-01 -7.587086E-02 -5.654541E-01 -3.252262E-02 -3.281152E-01 -1.090032E-02 -2.044034E-01 -4.517108E-03 -2.006195E-01 -4.176414E-03 -3.100848E-01 -1.042288E-02 -5.422115E-01 -3.004711E-02 -9.330393E-01 -8.964243E-02 -1.277607E+00 -1.657398E-01 -1.173275E+00 -1.409364E-01 -8.049595E-01 -6.791702E-02 -4.739057E-01 -2.461174E-02 -3.052609E-01 -9.872506E-03 -2.088298E-01 -4.793522E-03 -2.133326E-01 -4.862481E-03 -2.680060E-01 -7.625628E-03 -4.610074E-01 -2.155754E-02 -6.717074E-01 -4.626196E-02 -8.563743E-01 -7.430382E-02 -8.419238E-01 -7.436332E-02 -6.475334E-01 -4.244410E-02 -4.496406E-01 -2.101233E-02 -3.081348E-01 -1.046704E-02 -1.619249E-01 -3.062437E-03 -1.415369E-01 -2.255371E-03 -2.064850E-01 -4.712501E-03 -3.464136E-01 -1.293262E-02 -4.719265E-01 -2.272450E-02 -6.461406E-01 -4.354686E-02 -5.185955E-01 -2.909923E-02 -4.946850E-01 -2.606769E-02 -3.812863E-01 -1.500761E-02 -2.715616E-01 -7.844887E-03 -1.440914E-01 -2.297969E-03 -1.220110E-01 -1.727820E-03 -1.988195E-01 -4.845577E-03 -2.623807E-01 -7.651539E-03 -3.140727E-01 -1.024276E-02 -3.576120E-01 -1.349203E-02 -3.350431E-01 -1.214172E-02 -3.623127E-01 -1.392899E-02 -2.727478E-01 -8.086348E-03 -2.149275E-01 -5.361115E-03 -1.314584E-01 -1.963133E-03 -9.162433E-02 -9.983184E-04 -1.361470E-01 -2.027658E-03 -1.761731E-01 -3.389541E-03 -1.989963E-01 -4.594834E-03 -1.950142E-01 -4.102131E-03 -2.134629E-01 -4.672913E-03 -1.986280E-01 -4.090736E-03 -1.587464E-01 -2.850886E-03 -1.139834E-01 -1.491308E-03 -9.993253E-02 -1.150993E-03 -7.801279E-02 -7.470217E-04 -1.374998E-01 -1.969932E-03 -1.804494E-01 -3.568829E-03 -2.466413E-01 -6.594134E-03 -2.341345E-01 -5.848688E-03 -1.995756E-01 -4.362592E-03 -1.602288E-01 -3.066421E-03 -1.562435E-01 -2.552603E-03 -1.104688E-01 -1.359308E-03 -1.007842E-01 -1.139355E-03 -1.261199E-01 -1.847741E-03 -1.775287E-01 -3.680466E-03 -2.767100E-01 -8.008082E-03 -3.400792E-01 -1.195930E-02 -3.935631E-01 -1.640831E-02 -3.523748E-01 -1.302259E-02 -2.906020E-01 -9.059548E-03 -2.537932E-01 -6.876862E-03 -1.861336E-01 -3.787655E-03 -1.313071E-01 -2.065327E-03 -1.591139E-01 -2.819971E-03 -2.386577E-01 -6.457612E-03 -3.911616E-01 -1.594274E-02 -5.394472E-01 -3.061236E-02 -6.866890E-01 -4.817139E-02 -6.283365E-01 -4.055448E-02 -5.748601E-01 -3.459526E-02 -4.435554E-01 -2.009979E-02 -3.296444E-01 -1.154923E-02 -1.967361E-01 -4.169679E-03 -1.739968E-01 -3.208983E-03 -3.082977E-01 -9.811520E-03 -5.382503E-01 -3.058501E-02 -8.367621E-01 -7.291096E-02 -1.211062E+00 -1.496807E-01 -1.258748E+00 -1.608596E-01 -9.427524E-01 -9.038319E-02 -6.071099E-01 -3.854873E-02 -3.709298E-01 -1.478349E-02 -2.098170E-01 -4.835442E-03 -2.119024E-01 -4.712157E-03 -3.552855E-01 -1.323431E-02 -6.321647E-01 -4.077718E-02 -1.246141E+00 -1.565725E-01 -4.380951E+00 -1.942648E+00 -4.247605E+00 -1.810085E+00 -1.281712E+00 -1.683930E-01 -6.221859E-01 -3.919458E-02 -3.675010E-01 -1.432994E-02 -1.841643E-01 -3.510340E-03 -2.146914E-01 -5.048810E-03 -3.256950E-01 -1.139212E-02 -6.810729E-01 -4.813808E-02 -1.247489E+00 -1.574970E-01 -4.442262E+00 -1.985510E+00 -4.257415E+00 -1.821021E+00 -1.235711E+00 -1.535743E-01 -6.900244E-01 -4.832224E-02 -3.697406E-01 -1.425051E-02 -2.012083E-01 -4.510582E-03 -1.912047E-01 -4.273539E-03 -3.451744E-01 -1.225531E-02 -5.600376E-01 -3.307491E-02 -8.544790E-01 -7.409781E-02 -1.325242E+00 -1.782579E-01 -1.346957E+00 -1.841487E-01 -9.214797E-01 -8.688521E-02 -4.797898E-01 -2.328018E-02 -3.426011E-01 -1.225085E-02 -1.774389E-01 -3.518562E-03 -1.554220E-01 -2.654272E-03 -2.668481E-01 -7.631552E-03 -4.104944E-01 -1.755636E-02 -5.095732E-01 -2.660987E-02 -6.919120E-01 -4.918083E-02 -7.159855E-01 -5.218821E-02 -6.006622E-01 -3.710972E-02 -4.366095E-01 -2.004334E-02 -2.694762E-01 -7.978724E-03 -1.656914E-01 -3.089952E-03 -1.756674E-01 -3.399731E-03 -2.115494E-01 -4.962007E-03 -2.931609E-01 -9.634091E-03 -3.427335E-01 -1.230723E-02 -3.926075E-01 -1.686560E-02 -4.266016E-01 -1.861013E-02 -3.380544E-01 -1.177601E-02 -3.203345E-01 -1.042122E-02 -2.107818E-01 -4.975737E-03 -1.190587E-01 -1.667581E-03 -1.043469E-01 -1.388459E-03 -1.409254E-01 -2.165664E-03 -1.158111E-01 -1.584653E-03 -2.204672E-01 -5.077689E-03 -2.548870E-01 -6.790565E-03 -2.560290E-01 -6.703128E-03 -2.273470E-01 -5.548064E-03 -1.544622E-01 -2.859247E-03 -1.514176E-01 -2.580868E-03 -9.401839E-02 -9.957536E-04 -8.183741E-02 -1.018095E-03 -1.494302E-01 -2.610469E-03 -1.724667E-01 -3.163766E-03 -1.906518E-01 -3.886725E-03 -2.093021E-01 -4.547285E-03 -2.192608E-01 -5.381555E-03 -2.142120E-01 -5.229554E-03 -1.818979E-01 -3.730168E-03 -1.593537E-01 -2.930097E-03 -1.006301E-01 -1.243388E-03 -1.363959E-01 -2.201651E-03 -1.921074E-01 -4.287698E-03 -2.807569E-01 -8.586780E-03 -3.032112E-01 -9.281779E-03 -3.488735E-01 -1.290376E-02 -3.912428E-01 -1.651860E-02 -4.032276E-01 -1.745809E-02 -2.909627E-01 -9.146241E-03 -1.775633E-01 -3.593723E-03 -1.331733E-01 -1.922430E-03 -1.524251E-01 -2.694091E-03 -2.697127E-01 -7.768392E-03 -4.166062E-01 -1.776536E-02 -5.663715E-01 -3.298425E-02 -6.244007E-01 -3.998959E-02 -6.599368E-01 -4.570158E-02 -5.685603E-01 -3.382373E-02 -3.800938E-01 -1.547625E-02 -2.603056E-01 -7.053095E-03 -1.706694E-01 -3.148614E-03 -1.731386E-01 -3.384991E-03 -3.109075E-01 -1.033479E-02 -5.771187E-01 -3.413152E-02 -8.606415E-01 -7.510241E-02 -1.294303E+00 -1.706906E-01 -1.313399E+00 -1.767631E-01 -7.817706E-01 -6.342406E-02 -5.927279E-01 -3.756882E-02 -3.708410E-01 -1.458571E-02 -1.723505E-01 -3.356435E-03 -2.304233E-01 -5.998040E-03 -3.750987E-01 -1.501277E-02 -6.734152E-01 -4.746505E-02 -1.310995E+00 -1.732589E-01 -4.421199E+00 -1.964531E+00 -4.214183E+00 -1.786748E+00 -1.223434E+00 -1.530503E-01 -5.926626E-01 -3.641847E-02 -3.153714E-01 -1.019858E-02 -2.037244E-01 -4.381188E-03 -1.863505E-01 -3.684332E-03 -3.648623E-01 -1.378365E-02 -6.164882E-01 -3.893164E-02 -1.178969E+00 -1.417606E-01 -4.221345E+00 -1.789744E+00 -4.317433E+00 -1.872379E+00 -1.243011E+00 -1.581634E-01 -6.866745E-01 -4.754022E-02 -3.586774E-01 -1.301597E-02 -2.322214E-01 -5.492154E-03 -1.791966E-01 -3.463117E-03 -3.076717E-01 -9.716616E-03 -4.906144E-01 -2.470769E-02 -8.471037E-01 -7.406279E-02 -1.363587E+00 -1.889087E-01 -1.335963E+00 -1.803764E-01 -8.600227E-01 -7.530944E-02 -5.437258E-01 -3.114706E-02 -3.713934E-01 -1.456554E-02 -1.995931E-01 -4.422520E-03 -1.343031E-01 -2.012284E-03 -2.572828E-01 -6.840867E-03 -4.435747E-01 -2.050887E-02 -5.766007E-01 -3.437038E-02 -7.346272E-01 -5.540755E-02 -7.053071E-01 -5.111709E-02 -6.063267E-01 -3.981091E-02 -4.094521E-01 -1.801684E-02 -2.832627E-01 -8.238241E-03 -1.618924E-01 -3.067045E-03 -1.257079E-01 -1.802188E-03 -2.209365E-01 -5.343615E-03 -2.538278E-01 -7.109201E-03 -3.620261E-01 -1.396948E-02 -4.303042E-01 -1.872503E-02 -4.036624E-01 -1.774648E-02 -3.185915E-01 -1.192555E-02 -2.959360E-01 -9.265211E-03 -2.254861E-01 -5.671747E-03 -1.339005E-01 -2.034506E-03 -1.251282E-01 -1.754100E-03 -1.155042E-01 -1.606166E-03 -1.603827E-01 -2.694604E-03 -2.425425E-01 -6.218648E-03 -2.827714E-01 -8.247692E-03 -2.354428E-01 -5.778596E-03 -1.964531E-01 -4.365869E-03 -1.620035E-01 -3.000138E-03 -1.402609E-01 -2.252442E-03 -7.770439E-02 -7.584947E-04 -8.904883E-02 -9.832411E-04 -1.364994E-01 -2.060968E-03 -1.556202E-01 -3.013917E-03 -1.794759E-01 -3.609302E-03 -2.027676E-01 -4.271217E-03 -1.910134E-01 -3.830997E-03 -2.102956E-01 -4.746179E-03 -2.069806E-01 -4.732181E-03 -1.280198E-01 -1.803469E-03 -8.851884E-02 -1.050264E-03 -1.411243E-01 -2.309051E-03 -2.058752E-01 -4.840657E-03 -2.589000E-01 -7.354104E-03 -2.346176E-01 -5.819008E-03 -3.064791E-01 -9.995356E-03 -3.537650E-01 -1.318436E-02 -3.442248E-01 -1.225618E-02 -2.856280E-01 -9.047308E-03 -1.556316E-01 -2.976492E-03 -9.305931E-02 -9.568946E-04 -1.918345E-01 -3.871787E-03 -3.083654E-01 -1.109476E-02 -4.076303E-01 -1.807424E-02 -4.595063E-01 -2.220396E-02 -5.684770E-01 -3.345901E-02 -5.734059E-01 -3.325457E-02 -4.719375E-01 -2.393976E-02 -3.033874E-01 -1.029056E-02 -2.394489E-01 -6.540160E-03 -1.431584E-01 -2.691530E-03 -1.857128E-01 -3.720812E-03 -3.401652E-01 -1.213267E-02 -4.998027E-01 -2.627877E-02 -6.660538E-01 -4.542647E-02 -8.594730E-01 -7.625343E-02 -8.531451E-01 -7.416582E-02 -6.787742E-01 -4.744554E-02 -4.724654E-01 -2.328201E-02 -3.030469E-01 -9.464288E-03 -1.840238E-01 -3.680486E-03 -2.172436E-01 -4.932506E-03 -3.724089E-01 -1.512940E-02 -5.834908E-01 -3.595138E-02 -8.787419E-01 -7.754496E-02 -1.235501E+00 -1.556040E-01 -1.186802E+00 -1.419219E-01 -8.626663E-01 -7.644446E-02 -5.366263E-01 -2.954661E-02 -3.177208E-01 -1.051791E-02 -1.844215E-01 -3.676916E-03 -1.932190E-01 -4.023337E-03 -3.693850E-01 -1.452218E-02 -5.443615E-01 -3.020527E-02 -8.889578E-01 -8.111822E-02 -1.273207E+00 -1.655818E-01 -1.247526E+00 -1.585006E-01 -8.324980E-01 -7.213823E-02 -4.899464E-01 -2.450485E-02 -3.370823E-01 -1.208260E-02 -1.809043E-01 -3.714864E-03 -1.615415E-01 -2.906239E-03 -2.937828E-01 -9.651929E-03 -4.913048E-01 -2.639344E-02 -6.850176E-01 -4.797579E-02 -8.755910E-01 -7.958162E-02 -8.703001E-01 -7.730840E-02 -6.973913E-01 -5.089360E-02 -4.203999E-01 -1.844697E-02 -3.140340E-01 -1.039993E-02 -1.441709E-01 -2.488125E-03 -1.350197E-01 -2.064659E-03 -2.543930E-01 -7.177543E-03 -3.348117E-01 -1.259149E-02 -4.679892E-01 -2.273369E-02 -6.434887E-01 -4.331187E-02 -5.381700E-01 -2.974074E-02 -4.555945E-01 -2.167655E-02 -3.401588E-01 -1.191694E-02 -2.681734E-01 -7.550804E-03 -1.704519E-01 -3.365024E-03 -9.745304E-02 -1.232312E-03 -1.678994E-01 -3.221936E-03 -2.605430E-01 -7.393204E-03 -3.128187E-01 -1.138967E-02 -3.712111E-01 -1.460808E-02 -3.609117E-01 -1.331179E-02 -3.321706E-01 -1.170549E-02 -2.480901E-01 -6.266952E-03 -2.002359E-01 -4.338795E-03 -1.162468E-01 -1.536532E-03 -7.239001E-02 -5.815144E-04 -1.163546E-01 -1.453456E-03 -1.636165E-01 -3.201377E-03 -1.973025E-01 -4.660155E-03 -2.100585E-01 -5.039148E-03 -2.171398E-01 -5.070524E-03 -1.995065E-01 -4.193481E-03 -1.813545E-01 -3.549108E-03 -1.365406E-01 -2.182883E-03 -8.101165E-02 -7.758114E-04 -7.133170E-02 -5.967128E-04 -1.319234E-01 -1.933536E-03 -1.445023E-01 -2.409485E-03 -1.495938E-01 -2.481635E-03 -1.801306E-01 -3.481001E-03 -1.733442E-01 -3.171024E-03 -1.725688E-01 -3.165238E-03 -1.518925E-01 -2.585745E-03 -1.077414E-01 -1.395636E-03 -7.934204E-02 -7.147327E-04 -1.062269E-01 -1.354440E-03 -1.751122E-01 -3.623982E-03 -2.203107E-01 -5.413498E-03 -2.341057E-01 -5.605200E-03 -2.669322E-01 -7.526239E-03 -2.746743E-01 -7.646941E-03 -2.389916E-01 -6.235257E-03 -1.587546E-01 -2.827472E-03 -1.498724E-01 -2.673613E-03 -1.051763E-01 -1.391507E-03 -1.424360E-01 -2.468775E-03 -2.608608E-01 -7.055429E-03 -3.154162E-01 -1.024928E-02 -3.674104E-01 -1.418521E-02 -3.976162E-01 -1.732640E-02 -4.746502E-01 -2.349709E-02 -3.662445E-01 -1.447515E-02 -2.506236E-01 -6.593606E-03 -2.161204E-01 -4.801367E-03 -1.306963E-01 -1.975560E-03 -1.770351E-01 -3.932818E-03 -2.635109E-01 -7.772472E-03 -4.024536E-01 -1.731960E-02 -4.701442E-01 -2.265515E-02 -5.561475E-01 -3.235124E-02 -5.523785E-01 -3.142962E-02 -5.071292E-01 -2.592728E-02 -3.746613E-01 -1.518899E-02 -2.620776E-01 -7.439402E-03 -1.561701E-01 -2.901626E-03 -1.722037E-01 -3.249117E-03 -2.729386E-01 -8.041848E-03 -4.157578E-01 -1.761081E-02 -5.608817E-01 -3.221445E-02 -7.027459E-01 -5.154772E-02 -6.902150E-01 -4.817212E-02 -5.174043E-01 -2.771951E-02 -4.297342E-01 -1.970980E-02 -2.927718E-01 -9.421566E-03 -1.860039E-01 -3.719139E-03 -1.510489E-01 -2.673423E-03 -2.992366E-01 -9.425407E-03 -4.569637E-01 -2.187717E-02 -5.810709E-01 -3.569290E-02 -6.664391E-01 -4.607395E-02 -6.898814E-01 -4.853841E-02 -5.306547E-01 -2.907467E-02 -4.138514E-01 -1.844067E-02 -2.836181E-01 -9.090950E-03 -1.827952E-01 -3.670505E-03 -1.339105E-01 -1.976730E-03 -2.247962E-01 -5.455936E-03 -3.775577E-01 -1.529613E-02 -4.605234E-01 -2.228303E-02 -5.731045E-01 -3.426291E-02 -5.698592E-01 -3.415653E-02 -4.700203E-01 -2.298571E-02 -3.419079E-01 -1.254302E-02 -2.594048E-01 -7.486049E-03 -1.403745E-01 -2.288996E-03 -1.307053E-01 -2.306039E-03 -2.305399E-01 -5.746901E-03 -2.866663E-01 -8.437990E-03 -3.547902E-01 -1.415343E-02 -3.903305E-01 -1.721878E-02 -3.995569E-01 -1.663856E-02 -3.597994E-01 -1.369907E-02 -3.084707E-01 -1.005451E-02 -1.929703E-01 -4.111212E-03 -1.226805E-01 -1.616264E-03 -9.707080E-02 -1.277801E-03 -1.349714E-01 -2.412097E-03 -1.706592E-01 -3.112630E-03 -2.431900E-01 -6.662780E-03 -2.719484E-01 -7.972869E-03 -2.782047E-01 -8.195264E-03 -2.198394E-01 -5.159833E-03 -1.847153E-01 -3.606258E-03 -1.655937E-01 -3.412468E-03 -1.132640E-01 -1.342051E-03 -6.977329E-02 -6.061071E-04 -8.063628E-02 -7.877860E-04 -1.369291E-01 -2.216310E-03 -1.527061E-01 -2.661389E-03 -1.808807E-01 -3.626051E-03 -1.723708E-01 -3.227953E-03 -1.570487E-01 -2.824199E-03 -1.402935E-01 -2.196525E-03 -1.046978E-01 -1.394771E-03 -7.765529E-02 -6.990073E-04 -5.309912E-02 -4.179704E-04 -8.113995E-02 -8.093210E-04 -1.004879E-01 -1.274755E-03 -1.474015E-01 -2.957775E-03 -1.227188E-01 -1.661445E-03 -1.367411E-01 -2.049836E-03 -1.257025E-01 -1.739859E-03 -9.595806E-02 -1.143520E-03 -5.932828E-02 -4.443026E-04 -6.214003E-02 -4.698384E-04 -1.047908E-01 -1.265030E-03 -1.389236E-01 -2.066555E-03 -1.441238E-01 -2.187643E-03 -1.954745E-01 -4.024829E-03 -2.018960E-01 -4.436820E-03 -2.304400E-01 -5.702269E-03 -1.889259E-01 -3.911134E-03 -1.651129E-01 -2.907211E-03 -1.266147E-01 -1.798632E-03 -9.726395E-02 -1.038410E-03 -1.239366E-01 -2.020884E-03 -1.692392E-01 -3.039249E-03 -2.262593E-01 -5.810761E-03 -2.371783E-01 -6.148422E-03 -2.724207E-01 -7.965467E-03 -2.754620E-01 -8.212118E-03 -2.616121E-01 -7.738784E-03 -2.416846E-01 -6.508644E-03 -1.823329E-01 -4.244589E-03 -1.153662E-01 -1.541320E-03 -1.498687E-01 -2.427094E-03 -2.294988E-01 -5.678440E-03 -2.337590E-01 -6.042902E-03 -3.165064E-01 -1.047831E-02 -3.372589E-01 -1.176332E-02 -3.369130E-01 -1.214308E-02 -3.077673E-01 -9.854791E-03 -2.727368E-01 -8.153505E-03 -2.291204E-01 -5.762201E-03 -1.449108E-01 -2.631818E-03 -1.222645E-01 -1.652033E-03 -2.342919E-01 -6.080976E-03 -2.867658E-01 -9.143890E-03 -3.284926E-01 -1.110468E-02 -3.721608E-01 -1.458209E-02 -3.943281E-01 -1.624417E-02 -3.127115E-01 -1.090060E-02 -2.181120E-01 -5.081861E-03 -2.038850E-01 -4.541180E-03 -1.471756E-01 -2.612669E-03 -1.356517E-01 -2.315498E-03 -2.039418E-01 -4.569034E-03 -2.379673E-01 -6.028728E-03 -2.912334E-01 -8.794159E-03 -3.675708E-01 -1.388797E-02 -3.830007E-01 -1.493415E-02 -3.497252E-01 -1.323190E-02 -2.129266E-01 -5.366850E-03 -2.051774E-01 -5.235913E-03 -1.495687E-01 -2.578269E-03 -1.007903E-01 -1.213029E-03 -1.942201E-01 -4.152027E-03 -2.691558E-01 -7.844599E-03 -2.833552E-01 -8.562033E-03 -3.539964E-01 -1.296456E-02 -3.313330E-01 -1.152405E-02 -2.882665E-01 -9.275609E-03 -2.606018E-01 -7.326373E-03 -2.170792E-01 -5.088581E-03 -1.175397E-01 -1.425662E-03 -9.291662E-02 -1.030694E-03 -1.502293E-01 -2.605564E-03 -1.683973E-01 -3.164858E-03 -2.300104E-01 -5.597737E-03 -2.921841E-01 -9.137452E-03 -3.105624E-01 -1.077940E-02 -2.457323E-01 -6.748662E-03 -1.935480E-01 -4.153509E-03 -1.781340E-01 -3.735545E-03 -8.549182E-02 -7.932774E-04 -7.995611E-02 -6.914994E-04 -1.120022E-01 -1.608931E-03 -1.666393E-01 -3.351392E-03 -1.763892E-01 -3.781795E-03 -2.215879E-01 -5.446848E-03 -1.973224E-01 -4.088057E-03 -1.807076E-01 -3.807788E-03 -1.497413E-01 -2.510679E-03 -1.258135E-01 -1.824079E-03 -7.256548E-02 -6.032346E-04 -5.802787E-02 -4.068274E-04 -8.087439E-02 -8.125859E-04 -9.445601E-02 -1.220157E-03 -1.278262E-01 -1.976480E-03 -1.447187E-01 -2.496284E-03 -1.492610E-01 -2.453885E-03 -1.294170E-01 -1.822523E-03 -8.046777E-02 -7.104920E-04 -8.863832E-02 -9.206404E-04 -4.044133E-02 -2.016136E-04 -3.647497E-02 -1.736207E-04 -7.726524E-02 -7.189629E-04 -6.850678E-02 -5.399094E-04 -7.120540E-02 -6.049792E-04 -9.914070E-02 -1.178916E-03 -9.212489E-02 -1.014142E-03 -8.081211E-02 -7.572737E-04 -6.454690E-02 -5.539457E-04 -4.993214E-02 -3.161361E-04 -3.464406E-02 -1.743401E-04 -5.195266E-02 -3.077352E-04 -1.032248E-01 -1.231336E-03 -1.140722E-01 -1.517037E-03 -9.270288E-02 -1.194042E-03 -1.199421E-01 -1.713870E-03 -1.129313E-01 -1.491867E-03 -1.104339E-01 -1.526039E-03 -1.080665E-01 -1.372021E-03 -8.160995E-02 -6.843931E-04 -6.342734E-02 -5.271807E-04 -9.331087E-02 -1.097048E-03 -1.185250E-01 -1.534560E-03 -1.349132E-01 -2.447610E-03 -1.304714E-01 -2.217664E-03 -1.581891E-01 -2.862519E-03 -1.584546E-01 -2.780212E-03 -1.538480E-01 -2.634437E-03 -1.637997E-01 -2.992391E-03 -1.181898E-01 -1.765232E-03 -8.541970E-02 -8.382310E-04 -9.823634E-02 -1.116285E-03 -1.486333E-01 -2.343453E-03 -1.631008E-01 -2.995222E-03 -1.755338E-01 -3.448102E-03 -1.754730E-01 -3.227041E-03 -1.842652E-01 -3.637768E-03 -1.577573E-01 -2.654428E-03 -1.401177E-01 -2.280343E-03 -1.341096E-01 -1.900302E-03 -8.035023E-02 -8.312873E-04 -1.063904E-01 -1.375093E-03 -1.216848E-01 -1.721657E-03 -2.004753E-01 -4.510521E-03 -1.889712E-01 -3.847826E-03 -2.081610E-01 -4.756630E-03 -2.301263E-01 -5.717347E-03 -2.048600E-01 -4.561616E-03 -1.542754E-01 -2.639748E-03 -1.321829E-01 -1.993402E-03 -9.014640E-02 -9.359911E-04 -7.974780E-02 -8.078227E-04 -1.252435E-01 -1.711334E-03 -1.570584E-01 -2.959866E-03 -2.039016E-01 -4.591866E-03 -2.189983E-01 -5.090456E-03 -2.191556E-01 -5.055427E-03 -2.006583E-01 -4.702936E-03 -1.427473E-01 -2.173216E-03 -1.024856E-01 -1.223983E-03 -8.494077E-02 -9.050889E-04 -1.043652E-01 -1.259550E-03 -1.135027E-01 -1.445575E-03 -1.600726E-01 -2.607556E-03 -1.831531E-01 -4.043413E-03 -2.007142E-01 -4.477475E-03 -1.767643E-01 -3.298936E-03 -1.522826E-01 -2.841107E-03 -1.288167E-01 -1.944141E-03 -1.129177E-01 -1.466746E-03 -9.166114E-02 -1.059245E-03 -7.278205E-02 -7.174163E-04 -1.368554E-01 -2.140442E-03 -1.576875E-01 -2.802493E-03 -1.490721E-01 -2.494952E-03 -2.062740E-01 -4.926381E-03 -1.707643E-01 -3.221954E-03 -1.793503E-01 -3.661578E-03 -1.255583E-01 -1.994441E-03 -1.011375E-01 -1.127338E-03 -5.944771E-02 -5.079367E-04 -6.143141E-02 -4.850205E-04 -8.747215E-02 -9.029610E-04 -1.135806E-01 -1.572457E-03 -1.258766E-01 -1.776739E-03 -1.460430E-01 -2.470050E-03 -1.449479E-01 -2.231440E-03 -1.295714E-01 -1.929423E-03 -1.137994E-01 -1.612784E-03 -8.730775E-02 -8.683726E-04 -6.186674E-02 -4.521535E-04 -4.404648E-02 -3.596329E-04 -6.609800E-02 -5.725272E-04 -8.204018E-02 -7.710094E-04 -9.063221E-02 -1.005713E-03 -9.594152E-02 -1.071929E-03 -8.648906E-02 -9.624176E-04 -6.818770E-02 -5.453348E-04 -5.887333E-02 -4.783382E-04 -6.201530E-02 -4.304358E-04 -4.838730E-02 -3.281581E-04 -tally 2: -2.393627E-03 -2.636290E-06 -2.222156E-03 -1.516148E-06 -2.867439E-03 -2.178580E-06 -7.923009E-03 -1.131939E-05 -3.986634E-03 -4.120137E-06 -7.826419E-03 -1.381669E-05 -6.138176E-03 -7.147428E-06 -3.876880E-03 -2.697208E-06 -2.980562E-03 -3.407321E-06 -1.774473E-03 -8.936196E-07 -2.805366E-03 -2.770194E-06 -3.942385E-03 -3.541964E-06 -4.287920E-03 -3.508749E-06 -7.477034E-03 -1.023581E-05 -1.829256E-03 -9.746709E-07 -5.062973E-03 -5.540060E-06 -4.594275E-03 -5.704022E-06 -3.405515E-03 -3.312460E-06 -9.423220E-03 -1.189576E-05 -1.066888E-02 -2.047020E-05 -7.596120E-03 -1.097485E-05 -5.727684E-03 -9.708804E-06 -4.299383E-03 -3.708395E-06 -9.513211E-03 -1.464618E-05 -5.378401E-03 -5.129879E-06 -5.096252E-03 -3.430859E-06 -7.146855E-03 -1.124612E-05 -6.572738E-03 -7.596525E-06 -7.380131E-03 -9.633384E-06 -6.219137E-03 -8.163623E-06 -6.908868E-03 -8.615668E-06 -7.350963E-03 -1.116403E-05 -5.438361E-03 -6.022683E-06 -4.233493E-03 -5.102016E-06 -9.383252E-03 -1.536263E-05 -1.199002E-02 -2.034238E-05 -7.934135E-03 -8.959239E-06 -8.511914E-03 -1.022394E-05 -6.162682E-03 -4.504202E-06 -6.648149E-03 -5.723838E-06 -7.264317E-03 -8.981506E-06 -1.037779E-02 -1.703942E-05 -4.759363E-03 -3.403173E-06 -9.522407E-03 -1.428157E-05 -7.387030E-03 -1.550312E-05 -4.844392E-03 -8.366311E-06 -6.843631E-03 -8.685895E-06 -7.764489E-03 -8.325268E-06 -5.854513E-03 -5.702341E-06 -7.087671E-03 -1.025951E-05 -2.415844E-03 -2.739481E-06 -3.086207E-03 -1.914733E-06 -3.654957E-03 -3.442162E-06 -4.643653E-03 -7.197439E-06 -1.156734E-02 -1.759634E-05 -7.348653E-03 -9.450024E-06 -7.852217E-03 -9.889996E-06 -4.188787E-03 -2.746400E-06 -4.526053E-03 -3.421811E-06 -7.133876E-03 -5.514540E-06 -7.642743E-03 -1.296144E-05 -3.436567E-03 -5.211995E-06 -2.358938E-03 -1.100467E-06 -4.225043E-03 -3.484328E-06 -6.975938E-03 -1.402542E-05 -2.948552E-03 -2.413285E-06 -7.430097E-03 -9.911242E-06 -9.344383E-03 -1.730038E-05 -5.155130E-03 -4.067305E-06 -9.223392E-03 -1.460017E-05 -6.706254E-03 -1.105767E-05 -6.585388E-03 -9.369522E-06 -5.779745E-03 -9.335364E-06 -8.502853E-03 -1.535431E-05 -3.196086E-03 -2.721454E-06 -4.062636E-03 -4.530161E-06 -4.949408E-03 -4.754719E-06 -8.697214E-03 -1.269695E-05 -7.720575E-03 -1.111572E-05 -9.096811E-03 -1.174536E-05 -8.395332E-03 -1.344990E-05 -4.327467E-03 -4.178301E-06 -8.098133E-03 -2.129072E-05 -8.641159E-03 -1.993637E-05 -4.910339E-03 -4.798040E-06 -9.222729E-03 -1.731644E-05 -2.362293E-03 -1.920887E-06 -6.474215E-03 -7.989801E-06 -5.093903E-03 -4.638782E-06 -5.190870E-03 -4.064962E-06 -9.156062E-03 -1.062935E-05 -8.084833E-03 -1.278518E-05 -6.006837E-03 -5.037849E-06 -2.770574E-03 -1.880169E-06 -8.452309E-03 -1.160057E-05 -3.397900E-03 -2.727237E-06 -2.289237E-03 -1.800828E-06 -4.695731E-03 -3.765699E-06 -4.924545E-03 -5.339278E-06 -3.924491E-03 -3.534032E-06 -5.847744E-03 -5.832921E-06 -5.289218E-03 -4.574845E-06 -6.846305E-03 -7.758157E-06 -1.047199E-02 -2.215876E-05 -4.108674E-03 -4.661482E-06 -3.645438E-03 -3.482466E-06 -7.403892E-03 -9.076272E-06 -9.992980E-03 -1.850054E-05 -5.443023E-03 -6.695689E-06 -3.847833E-03 -4.206098E-06 -2.277807E-03 -2.917797E-06 -3.224646E-03 -2.590833E-06 -2.712146E-03 -1.564363E-06 -2.511703E-03 -2.073210E-06 -2.851624E-03 -2.406321E-06 -6.419364E-03 -8.208048E-06 -3.583974E-03 -2.129485E-06 -4.071264E-03 -3.984855E-06 -4.533579E-03 -3.713304E-06 -4.578848E-03 -5.079242E-06 -5.611098E-03 -6.817450E-06 -5.466353E-03 -4.988832E-06 -6.854470E-03 -7.459449E-06 -1.157268E-02 -2.134366E-05 -7.273077E-03 -9.732982E-06 -8.349702E-03 -1.250149E-05 -5.050822E-03 -6.018695E-06 -2.514025E-03 -1.130420E-06 -3.427587E-03 -3.225038E-06 -3.873162E-03 -2.996903E-06 -7.323374E-03 -8.254236E-06 -6.098103E-03 -7.253404E-06 -6.739636E-03 -8.349945E-06 -9.485072E-03 -1.713563E-05 -5.984992E-03 -9.163531E-06 -8.834991E-03 -1.335810E-05 -4.066934E-03 -3.579803E-06 -1.652658E-02 -4.834880E-05 -7.000637E-03 -1.069231E-05 -7.937972E-03 -1.290117E-05 -3.766475E-03 -2.867345E-06 -2.461271E-03 -1.788656E-06 -7.788544E-03 -1.121555E-05 -9.541095E-03 -2.231145E-05 -7.679962E-03 -9.840343E-06 -5.742139E-03 -4.940156E-06 -5.062263E-03 -4.779159E-06 -1.289208E-02 -2.955604E-05 -1.544740E-02 -3.866639E-05 -1.954402E-02 -4.721734E-05 -9.149250E-03 -1.133509E-05 -1.090073E-02 -2.153530E-05 -4.758115E-03 -3.459784E-06 -6.236554E-03 -7.454691E-06 -1.032788E-02 -1.596371E-05 -1.444275E-02 -3.574908E-05 -6.464816E-03 -6.384836E-06 -8.737589E-03 -1.139821E-05 -1.522241E-02 -3.688054E-05 -1.106278E-02 -2.001640E-05 -1.657751E-03 -7.896422E-07 -9.985083E-03 -2.005574E-05 -1.652296E-02 -4.365058E-05 -1.302377E-02 -2.178742E-05 -9.435666E-03 -1.687352E-05 -6.711506E-03 -6.885294E-06 -1.142166E-02 -1.609449E-05 -1.419856E-02 -2.849773E-05 -8.442366E-03 -1.011801E-05 -1.071637E-02 -1.610076E-05 -4.476911E-03 -3.961715E-06 -7.809454E-03 -1.419144E-05 -1.181657E-02 -2.104822E-05 -1.373873E-02 -2.251824E-05 -8.797909E-03 -9.748724E-06 -1.504789E-02 -4.195617E-05 -7.006137E-03 -6.669697E-06 -9.537389E-03 -1.849189E-05 -1.780900E-02 -4.302041E-05 -1.724591E-02 -3.993250E-05 -9.881865E-03 -1.435848E-05 -1.212976E-02 -2.281306E-05 -1.155598E-02 -2.437179E-05 -1.323280E-02 -2.884432E-05 -8.626223E-03 -1.632385E-05 -8.459408E-03 -1.577691E-05 -5.796477E-03 -8.302775E-06 -7.329870E-03 -9.181815E-06 -5.899442E-03 -9.585863E-06 -3.270135E-03 -2.283602E-06 -1.256169E-02 -2.107461E-05 -1.234423E-02 -1.770300E-05 -7.478528E-03 -9.995232E-06 -9.809248E-03 -1.705587E-05 -1.508701E-02 -4.933892E-05 -9.484511E-03 -1.383716E-05 -9.906783E-03 -1.637085E-05 -1.682365E-02 -3.925758E-05 -1.258622E-02 -2.571505E-05 -1.501752E-02 -3.834255E-05 -8.820664E-03 -1.146570E-05 -9.142316E-03 -1.367354E-05 -1.607994E-02 -3.440957E-05 -1.819921E-02 -4.213322E-05 -1.049376E-02 -1.508696E-05 -1.125487E-02 -1.829361E-05 -1.255946E-02 -2.400912E-05 -1.494753E-02 -2.880665E-05 -9.906163E-03 -1.714469E-05 -9.801065E-03 -1.600484E-05 -9.144471E-03 -1.267827E-05 -9.284445E-03 -1.370415E-05 -7.423687E-03 -7.432654E-06 -5.839529E-03 -7.346711E-06 -1.203215E-02 -1.827698E-05 -1.168395E-02 -2.279351E-05 -8.086186E-03 -1.615505E-05 -7.899387E-03 -1.190799E-05 -1.297481E-02 -2.625448E-05 -1.012733E-02 -1.604797E-05 -7.655395E-03 -8.264047E-06 -6.482761E-03 -7.546581E-06 -9.071892E-03 -1.344566E-05 -1.012199E-02 -1.652283E-05 -9.504897E-03 -1.476924E-05 -5.662581E-03 -5.323904E-06 -7.382282E-03 -1.000786E-05 -6.180783E-03 -7.844120E-06 -2.197420E-03 -1.391522E-06 -6.007425E-03 -7.959122E-06 -7.715644E-03 -1.608319E-05 -5.247749E-03 -7.416762E-06 -1.694277E-03 -6.578326E-07 -5.339576E-03 -5.097832E-06 -6.019786E-03 -5.618274E-06 -4.786336E-03 -7.186037E-06 -6.122950E-03 -5.610674E-06 -8.057029E-03 -1.204366E-05 -6.301473E-03 -6.965375E-06 -4.120695E-03 -3.695383E-06 -5.874479E-03 -6.599197E-06 -6.691277E-03 -7.819406E-06 -5.109849E-03 -6.563614E-06 -8.391296E-03 -9.940452E-06 -5.839507E-03 -5.521558E-06 -4.596793E-03 -4.130860E-06 -5.374245E-03 -1.030905E-05 -1.632711E-03 -8.301397E-07 -3.126223E-03 -2.656593E-06 -3.937711E-03 -3.000478E-06 -1.156184E-02 -2.627381E-05 -5.611109E-03 -6.000471E-06 -6.252306E-03 -8.525688E-06 -9.821256E-03 -2.080867E-05 -6.202478E-03 -9.705719E-06 -9.744072E-03 -1.419879E-05 -4.563774E-03 -5.256907E-06 -8.587477E-03 -1.097640E-05 -7.671018E-03 -1.047414E-05 -7.311535E-03 -8.520205E-06 -5.701036E-03 -7.538301E-06 -2.627195E-03 -1.584039E-06 -9.969461E-03 -1.489942E-05 -7.095469E-03 -8.576195E-06 -1.168498E-02 -2.716853E-05 -1.070319E-02 -2.148367E-05 -1.016441E-02 -1.786630E-05 -1.292596E-02 -2.389182E-05 -1.217737E-02 -2.769412E-05 -1.181009E-02 -2.340802E-05 -1.574105E-02 -3.124980E-05 -1.232805E-02 -2.754960E-05 -1.188839E-02 -2.281613E-05 -6.603428E-03 -7.994872E-06 -9.606012E-03 -1.590439E-05 -1.440947E-02 -3.393244E-05 -1.302185E-02 -2.781063E-05 -1.299900E-02 -2.077190E-05 -9.475720E-03 -1.345778E-05 -1.146355E-02 -2.243784E-05 -1.349840E-02 -2.573754E-05 -7.739444E-03 -8.409802E-06 -1.466716E-02 -2.433264E-05 -1.806287E-02 -4.053139E-05 -8.810509E-03 -1.132204E-05 -1.024195E-02 -1.779935E-05 -2.025420E-02 -5.383414E-05 -1.707065E-02 -3.895805E-05 -1.038874E-02 -1.447558E-05 -1.204454E-02 -2.594328E-05 -1.354337E-02 -2.168067E-05 -1.483635E-02 -2.755558E-05 -1.192445E-02 -3.329659E-05 -2.158708E-02 -6.668840E-05 -1.234119E-02 -2.669020E-05 -1.410581E-02 -3.302957E-05 -8.354805E-03 -9.152126E-06 -9.135582E-03 -1.272798E-05 -1.639890E-02 -3.260235E-05 -1.941356E-02 -4.768472E-05 -9.432927E-03 -1.518984E-05 -8.177517E-03 -1.165366E-05 -9.416129E-03 -1.296249E-05 -8.128386E-03 -8.608672E-06 -1.105831E-02 -1.814734E-05 -1.401062E-02 -3.340952E-05 -1.142672E-02 -2.129460E-05 -9.161476E-03 -1.306319E-05 -9.794446E-03 -1.416201E-05 -9.135097E-03 -1.706236E-05 -1.853399E-02 -3.920131E-05 -1.227701E-02 -2.106422E-05 -1.056602E-02 -2.102108E-05 -8.267866E-03 -1.184427E-05 -1.303652E-02 -3.007847E-05 -1.109107E-02 -1.508434E-05 -1.255273E-02 -3.715445E-05 -1.568071E-02 -3.587957E-05 -1.697298E-02 -4.299097E-05 -2.038316E-02 -6.095075E-05 -1.285306E-02 -2.307142E-05 -1.201762E-02 -3.129200E-05 -1.499546E-02 -3.208959E-05 -2.043263E-02 -5.689094E-05 -1.189038E-02 -2.042718E-05 -1.950482E-02 -5.269872E-05 -1.134487E-02 -2.521302E-05 -1.878263E-02 -4.929885E-05 -1.873654E-02 -5.934321E-05 -1.419859E-02 -2.646755E-05 -1.651980E-02 -4.148598E-05 -1.314908E-02 -2.182046E-05 -1.021903E-02 -1.717431E-05 -1.208532E-02 -2.496385E-05 -1.982124E-02 -4.519007E-05 -2.237475E-02 -7.384327E-05 -1.168783E-02 -2.197925E-05 -1.121917E-02 -1.848105E-05 -1.176886E-02 -1.989856E-05 -1.028612E-02 -2.002048E-05 -6.441121E-03 -1.208678E-05 -9.334858E-03 -1.685718E-05 -1.221526E-02 -2.734816E-05 -8.109247E-03 -1.518336E-05 -8.368313E-03 -1.063729E-05 -8.855930E-03 -1.170749E-05 -1.172890E-02 -1.956774E-05 -9.863812E-03 -1.315788E-05 -5.592525E-03 -6.344568E-06 -8.987555E-03 -2.234382E-05 -3.266822E-03 -2.479505E-06 -6.189326E-03 -8.824564E-06 -5.844977E-03 -5.169490E-06 -7.046630E-03 -1.052376E-05 -6.579857E-03 -8.415446E-06 -1.093757E-02 -1.570418E-05 -1.534199E-03 -4.843955E-07 -3.972367E-03 -3.930212E-06 -9.061215E-03 -1.261741E-05 -8.800538E-03 -1.921110E-05 -3.225443E-03 -3.493463E-06 -5.742921E-03 -7.524467E-06 -8.894549E-03 -9.527470E-06 -1.014672E-02 -2.120305E-05 -5.364672E-03 -6.378322E-06 -1.066153E-02 -1.712472E-05 -6.130226E-03 -6.243703E-06 -4.018022E-03 -2.930167E-06 -4.956188E-03 -4.356249E-06 -6.252583E-03 -6.885470E-06 -8.007253E-03 -8.821722E-06 -6.639119E-03 -8.933356E-06 -1.463344E-02 -3.442332E-05 -1.038024E-02 -1.880148E-05 -1.206043E-02 -2.558498E-05 -4.570005E-03 -3.726957E-06 -1.242332E-02 -2.230997E-05 -1.977578E-02 -4.435986E-05 -1.061292E-02 -2.445701E-05 -1.424423E-02 -3.432092E-05 -9.396166E-03 -1.401917E-05 -1.392713E-02 -2.180224E-05 -1.424206E-02 -2.481086E-05 -1.709208E-02 -4.686642E-05 -1.878028E-02 -4.097819E-05 -1.586170E-02 -3.899306E-05 -1.186469E-02 -2.666019E-05 -1.771802E-02 -4.244451E-05 -2.012793E-02 -4.893656E-05 -1.650347E-02 -3.934790E-05 -1.148512E-02 -1.454575E-05 -1.749383E-02 -5.245021E-05 -1.225617E-02 -3.059339E-05 -1.346221E-02 -2.189743E-05 -2.304004E-02 -5.757458E-05 -2.222990E-02 -5.924120E-05 -1.952055E-02 -4.455112E-05 -2.382543E-02 -8.732633E-05 -1.530886E-02 -4.091608E-05 -1.339459E-02 -2.841021E-05 -1.625542E-02 -3.786269E-05 -2.338447E-02 -6.870429E-05 -1.490538E-02 -3.792400E-05 -1.790848E-02 -4.535348E-05 -1.122685E-02 -2.253480E-05 -1.330235E-02 -2.292380E-05 -2.135308E-02 -5.586248E-05 -1.695542E-02 -3.629907E-05 -1.155426E-02 -1.914452E-05 -1.571691E-02 -3.489736E-05 -1.572803E-02 -4.611373E-05 -1.284552E-02 -2.456383E-05 -1.901269E-02 -4.808607E-05 -2.485036E-02 -8.880235E-05 -1.465112E-02 -3.185430E-05 -2.020800E-02 -4.881856E-05 -1.401589E-02 -2.430294E-05 -9.002706E-03 -1.229314E-05 -1.992235E-02 -4.240852E-05 -2.006701E-02 -4.842321E-05 -1.641971E-02 -4.817406E-05 -1.595751E-02 -4.338042E-05 -1.853727E-02 -4.393578E-05 -2.192942E-02 -6.739913E-05 -1.867919E-02 -5.139172E-05 -2.177355E-02 -5.751645E-05 -1.184805E-02 -2.240654E-05 -2.099274E-02 -5.999346E-05 -1.322643E-02 -2.131002E-05 -1.656305E-02 -4.169252E-05 -2.381731E-02 -9.074804E-05 -2.704019E-02 -1.044920E-04 -1.801171E-02 -4.035920E-05 -1.967092E-02 -5.363932E-05 -1.168953E-02 -1.754128E-05 -1.832249E-02 -3.962019E-05 -1.704595E-02 -3.468261E-05 -1.265896E-02 -2.389931E-05 -1.393723E-02 -3.209713E-05 -1.201908E-02 -2.196652E-05 -1.121588E-02 -1.970440E-05 -1.427491E-02 -2.765834E-05 -2.153384E-02 -5.860763E-05 -1.776842E-02 -3.609550E-05 -7.672472E-03 -1.269710E-05 -1.265904E-02 -2.534380E-05 -1.278354E-02 -2.508897E-05 -1.391779E-02 -4.666810E-05 -2.101476E-02 -6.314023E-05 -1.153450E-02 -1.634642E-05 -7.257174E-03 -7.518882E-06 -1.927707E-02 -5.477085E-05 -9.781524E-03 -2.152387E-05 -9.945925E-03 -2.025565E-05 -1.389600E-02 -2.298270E-05 -1.494296E-02 -3.352071E-05 -1.367151E-02 -2.896436E-05 -1.028864E-02 -1.588654E-05 -1.473101E-02 -2.707095E-05 -1.455944E-02 -2.781560E-05 -9.476864E-03 -1.561347E-05 -1.102794E-02 -1.772767E-05 -9.572498E-03 -1.064449E-05 -1.064989E-02 -1.750406E-05 -6.954816E-03 -6.367916E-06 -7.557762E-03 -9.032801E-06 -1.211805E-02 -1.920718E-05 -1.845178E-02 -5.873218E-05 -7.793790E-03 -1.112683E-05 -5.371627E-03 -5.960823E-06 -1.235046E-02 -2.103259E-05 -9.750089E-03 -1.543672E-05 -8.995748E-03 -1.080595E-05 -5.259436E-03 -1.260414E-05 -4.013446E-03 -6.247425E-06 -8.493091E-03 -1.154191E-05 -8.658736E-03 -1.173196E-05 -5.707568E-03 -4.449143E-06 -6.897118E-03 -1.114446E-05 -1.178625E-02 -1.974305E-05 -9.315402E-03 -1.290580E-05 -8.406166E-03 -1.025434E-05 -9.901055E-03 -1.667960E-05 -1.146925E-02 -2.215717E-05 -5.969641E-03 -6.447141E-06 -5.949865E-03 -8.960001E-06 -6.092588E-03 -5.866966E-06 -5.286804E-03 -4.484612E-06 -4.962451E-03 -8.039594E-06 -9.068920E-03 -1.020218E-05 -8.393955E-03 -1.320876E-05 -9.622928E-03 -1.423650E-05 -8.529527E-03 -9.768501E-06 -1.200903E-02 -2.384159E-05 -1.259390E-02 -2.566463E-05 -1.039659E-02 -1.406860E-05 -1.729423E-02 -3.541882E-05 -1.557253E-02 -3.014897E-05 -1.194539E-02 -2.560525E-05 -9.677750E-03 -1.383701E-05 -1.167374E-02 -1.893076E-05 -1.273338E-02 -2.292756E-05 -1.535190E-02 -3.729267E-05 -1.508233E-02 -2.812628E-05 -1.684483E-02 -3.431291E-05 -2.196642E-02 -6.517979E-05 -1.835655E-02 -4.270904E-05 -1.427403E-02 -2.810685E-05 -1.296891E-02 -3.425248E-05 -1.678523E-02 -5.402550E-05 -1.824967E-02 -5.634714E-05 -1.959838E-02 -4.878209E-05 -1.406357E-02 -2.175952E-05 -1.401524E-02 -2.275436E-05 -1.788740E-02 -3.919852E-05 -1.598923E-02 -2.974737E-05 -1.525101E-02 -3.383079E-05 -2.077298E-02 -5.733559E-05 -1.623945E-02 -5.546589E-05 -1.233338E-02 -2.483742E-05 -2.022717E-02 -5.714772E-05 -2.102155E-02 -5.635658E-05 -2.171802E-02 -5.725283E-05 -2.133014E-02 -5.962681E-05 -1.179614E-02 -2.066294E-05 -1.970629E-02 -4.561315E-05 -2.645275E-02 -9.399736E-05 -2.354050E-02 -6.803919E-05 -1.525552E-02 -2.858970E-05 -2.070256E-02 -5.101418E-05 -1.947053E-02 -4.695688E-05 -2.302697E-02 -7.365548E-05 -1.913354E-02 -4.496174E-05 -2.105937E-02 -5.760147E-05 -1.965231E-02 -4.417453E-05 -1.995841E-02 -5.444455E-05 -1.084572E-02 -1.610270E-05 -1.332954E-02 -2.257737E-05 -2.183162E-02 -5.722081E-05 -2.309396E-02 -6.057606E-05 -9.499018E-03 -1.386623E-05 -1.726231E-02 -3.538973E-05 -1.918548E-02 -5.089001E-05 -1.973649E-02 -5.391374E-05 -1.451999E-02 -3.154997E-05 -1.853969E-02 -4.789122E-05 -1.931149E-02 -4.543759E-05 -1.524463E-02 -3.367646E-05 -1.366351E-02 -2.193771E-05 -1.277601E-02 -2.556554E-05 -2.419813E-02 -6.652057E-05 -2.472332E-02 -7.514840E-05 -1.216343E-02 -1.994778E-05 -1.684175E-02 -4.956582E-05 -1.339179E-02 -2.283730E-05 -1.780869E-02 -4.714950E-05 -1.281070E-02 -3.243018E-05 -1.377308E-02 -3.117101E-05 -5.215885E-03 -4.980057E-06 -8.389167E-03 -9.063809E-06 -9.223056E-03 -1.174158E-05 -1.034179E-02 -1.248975E-05 -1.925913E-02 -6.207101E-05 -1.950035E-02 -4.864558E-05 -7.340878E-03 -8.419462E-06 -1.607559E-02 -3.378180E-05 -1.350363E-02 -2.299601E-05 -1.775362E-02 -4.047509E-05 -9.317817E-03 -1.104227E-05 -1.531705E-02 -4.269706E-05 -1.565342E-02 -3.305222E-05 -1.391485E-02 -2.938257E-05 -1.313236E-02 -2.426377E-05 -1.070285E-02 -1.778239E-05 -1.570769E-02 -2.778648E-05 -1.722250E-02 -3.454670E-05 -1.045335E-02 -1.595923E-05 -9.750797E-03 -1.489157E-05 -8.756572E-03 -1.522794E-05 -1.314662E-02 -2.457130E-05 -1.370416E-02 -3.418084E-05 -1.098248E-02 -2.064375E-05 -9.200090E-03 -1.182484E-05 -1.567874E-02 -3.315221E-05 -6.763240E-03 -8.877038E-06 -8.410342E-03 -8.599497E-06 -1.335894E-02 -2.549323E-05 -1.565848E-02 -3.152726E-05 -4.913273E-03 -5.986876E-06 -9.624242E-03 -1.314215E-05 -7.547191E-03 -9.843029E-06 -9.081341E-03 -1.527721E-05 -5.698591E-03 -6.688189E-06 -9.964477E-03 -1.260472E-05 -9.144787E-03 -1.380216E-05 -2.922066E-03 -3.175247E-06 -6.106620E-03 -6.365824E-06 -4.141014E-03 -5.277155E-06 -1.500883E-02 -2.794044E-05 -1.313811E-02 -2.817445E-05 -5.537395E-03 -6.121500E-06 -6.214355E-03 -7.107250E-06 -9.970886E-03 -1.328943E-05 -1.158117E-02 -2.364707E-05 -8.759169E-03 -1.211573E-05 -1.307338E-02 -2.576745E-05 -6.194517E-03 -9.257741E-06 -8.087970E-03 -1.366458E-05 -8.727779E-03 -1.125895E-05 -7.481614E-03 -9.784879E-06 -7.811272E-03 -1.168412E-05 -7.288542E-03 -8.415727E-06 -1.395237E-02 -3.211289E-05 -1.345304E-02 -2.404425E-05 -1.075854E-02 -1.527000E-05 -9.524531E-03 -1.516407E-05 -1.703097E-02 -3.823359E-05 -1.787871E-02 -5.611642E-05 -1.180236E-02 -1.719343E-05 -1.146997E-02 -2.095613E-05 -1.152446E-02 -1.653464E-05 -1.398187E-02 -2.396242E-05 -1.526339E-02 -3.893414E-05 -1.206190E-02 -2.128747E-05 -1.578188E-02 -3.704679E-05 -1.775426E-02 -3.663474E-05 -1.962916E-02 -5.493244E-05 -1.511991E-02 -3.404611E-05 -1.133202E-02 -2.452193E-05 -1.623663E-02 -3.459453E-05 -1.586922E-02 -3.841963E-05 -1.786175E-02 -4.289114E-05 -1.326353E-02 -2.595816E-05 -1.445145E-02 -2.990583E-05 -1.509105E-02 -2.887493E-05 -1.761108E-02 -3.799916E-05 -1.671644E-02 -3.638132E-05 -1.692226E-02 -3.667379E-05 -1.123264E-02 -2.662808E-05 -1.119900E-02 -1.598286E-05 -1.930512E-02 -4.893797E-05 -2.291023E-02 -7.500981E-05 -1.093046E-02 -2.227226E-05 -2.121672E-02 -7.861749E-05 -9.618950E-03 -1.801199E-05 -1.213716E-02 -2.201469E-05 -1.856853E-02 -4.365656E-05 -1.665627E-02 -3.787320E-05 -1.301603E-02 -3.128189E-05 -1.971201E-02 -6.183556E-05 -2.240750E-02 -7.011853E-05 -2.216291E-02 -6.809313E-05 -1.153008E-02 -2.676316E-05 -1.676451E-02 -4.351892E-05 -2.591571E-02 -8.037903E-05 -2.037389E-02 -4.959375E-05 -1.495140E-02 -2.829832E-05 -1.051363E-02 -2.065167E-05 -1.979307E-02 -5.604015E-05 -2.624329E-02 -8.019430E-05 -1.335982E-02 -2.844614E-05 -1.612983E-02 -6.010017E-05 -1.728825E-02 -4.711910E-05 -2.429057E-02 -7.415431E-05 -1.616100E-02 -3.084388E-05 -1.338558E-02 -3.228322E-05 -1.340928E-02 -3.387569E-05 -1.708924E-02 -4.087477E-05 -1.336049E-02 -2.477778E-05 -1.587999E-02 -3.626669E-05 -1.935273E-02 -4.723278E-05 -2.470520E-02 -7.859676E-05 -1.475079E-02 -3.640394E-05 -1.679996E-02 -3.695619E-05 -1.042709E-02 -1.967458E-05 -1.968690E-02 -5.949834E-05 -1.327946E-02 -2.828789E-05 -2.199912E-02 -6.466489E-05 -1.849493E-02 -3.808605E-05 -1.601814E-02 -3.868414E-05 -8.360208E-03 -8.722194E-06 -1.048244E-02 -1.553938E-05 -1.355191E-02 -3.191766E-05 -2.019710E-02 -5.689596E-05 -1.515848E-02 -3.149152E-05 -2.234350E-02 -6.679195E-05 -2.213825E-02 -7.381934E-05 -1.917888E-02 -4.731955E-05 -1.084756E-02 -1.986509E-05 -1.609220E-02 -3.078859E-05 -1.473280E-02 -3.933279E-05 -1.529880E-02 -3.127194E-05 -1.639564E-02 -4.529775E-05 -1.443123E-02 -3.075459E-05 -1.882187E-02 -5.273612E-05 -1.460498E-02 -3.301670E-05 -1.236974E-02 -2.302425E-05 -1.547979E-02 -5.040930E-05 -8.943072E-03 -1.146125E-05 -1.038577E-02 -2.359524E-05 -9.957494E-03 -1.246302E-05 -8.165810E-03 -1.286712E-05 -9.730475E-03 -1.696051E-05 -1.144342E-02 -1.696256E-05 -1.018757E-02 -1.502940E-05 -6.455273E-03 -6.892868E-06 -1.173241E-02 -1.925350E-05 -1.116334E-02 -1.482619E-05 -5.399877E-03 -4.405606E-06 -3.959202E-03 -4.471062E-06 -9.736729E-03 -1.616042E-05 -4.414460E-03 -5.308331E-06 -5.659090E-03 -4.225962E-06 -4.260578E-03 -3.519428E-06 -1.063057E-02 -2.671737E-05 -9.252412E-03 -1.335948E-05 -7.509662E-03 -1.033233E-05 -5.631650E-03 -4.603917E-06 -7.374915E-03 -9.613999E-06 -1.017301E-02 -1.890382E-05 -4.657208E-03 -4.110749E-06 -8.263045E-03 -9.144029E-06 -5.083557E-03 -3.617645E-06 -5.753770E-03 -6.399330E-06 -3.157208E-03 -2.023717E-06 -2.929392E-03 -1.415512E-06 -3.306531E-03 -3.326325E-06 -7.308585E-03 -7.051788E-06 -2.131441E-03 -3.159885E-06 -6.056253E-03 -6.146480E-06 -3.197345E-03 -1.906328E-06 -7.156591E-03 -8.158604E-06 -1.008394E-02 -1.612226E-05 -1.360120E-02 -3.007127E-05 -1.087460E-02 -1.978085E-05 -8.751267E-03 -1.202395E-05 -1.327913E-02 -2.254308E-05 -1.549315E-02 -3.493369E-05 -1.276663E-02 -2.254426E-05 -1.422943E-02 -3.266120E-05 -7.543284E-03 -8.425611E-06 -1.148895E-02 -2.316672E-05 -1.075957E-02 -1.733122E-05 -1.228568E-02 -2.274310E-05 -1.298949E-02 -3.103013E-05 -1.687551E-02 -3.373288E-05 -1.174671E-02 -1.478816E-05 -1.298113E-02 -2.280689E-05 -1.375075E-02 -3.564635E-05 -1.865407E-02 -4.650386E-05 -1.316565E-02 -3.034949E-05 -1.644195E-02 -3.039939E-05 -1.167304E-02 -1.910205E-05 -1.119646E-02 -2.041134E-05 -2.051982E-02 -5.295597E-05 -1.446398E-02 -2.483661E-05 -1.663221E-02 -4.310298E-05 -1.774865E-02 -3.613160E-05 -1.551828E-02 -3.922463E-05 -1.987068E-02 -5.088653E-05 -1.111420E-02 -2.006407E-05 -1.756540E-02 -4.468660E-05 -1.151848E-02 -1.953188E-05 -1.430522E-02 -3.322674E-05 -8.678157E-03 -1.516243E-05 -1.189921E-02 -1.954174E-05 -1.667314E-02 -5.548567E-05 -2.197194E-02 -6.943126E-05 -2.047457E-02 -4.730016E-05 -2.145796E-02 -5.503501E-05 -1.433861E-02 -3.407856E-05 -1.789249E-02 -4.132722E-05 -1.497597E-02 -3.790603E-05 -1.784518E-02 -5.989398E-05 -1.348223E-02 -2.451114E-05 -1.850761E-02 -4.354345E-05 -9.460668E-03 -1.235477E-05 -1.608153E-02 -3.627464E-05 -2.158938E-02 -7.192126E-05 -2.591114E-02 -7.881538E-05 -1.615323E-02 -3.735540E-05 -1.456875E-02 -2.959443E-05 -1.713879E-02 -3.971707E-05 -2.011125E-02 -5.426015E-05 -1.600843E-02 -3.860238E-05 -2.349989E-02 -6.721393E-05 -9.675390E-03 -1.720621E-05 -1.321534E-02 -2.201319E-05 -1.388877E-02 -3.752647E-05 -9.338560E-03 -1.222660E-05 -1.872993E-02 -4.179060E-05 -1.729197E-02 -4.763909E-05 -1.433923E-02 -3.648874E-05 -1.982945E-02 -5.208569E-05 -9.529878E-03 -1.355652E-05 -1.902053E-02 -4.097824E-05 -9.167842E-03 -1.041942E-05 -1.571952E-02 -4.188405E-05 -9.716564E-03 -2.414735E-05 -1.423573E-02 -3.524484E-05 -1.148788E-02 -2.604718E-05 -1.545038E-02 -3.289072E-05 -1.724229E-02 -4.091912E-05 -2.114484E-02 -5.448881E-05 -1.201008E-02 -1.761314E-05 -1.049205E-02 -1.851795E-05 -1.007470E-02 -1.508850E-05 -1.216162E-02 -1.779226E-05 -9.172443E-03 -1.542767E-05 -1.158129E-02 -2.700118E-05 -8.634939E-03 -1.235312E-05 -9.968731E-03 -1.636632E-05 -8.806160E-03 -1.312756E-05 -7.282738E-03 -7.675380E-06 -1.501313E-02 -3.403794E-05 -1.772683E-02 -3.946856E-05 -6.797082E-03 -6.826066E-06 -6.203754E-03 -6.576663E-06 -1.255269E-02 -2.273841E-05 -9.325891E-03 -1.968887E-05 -5.665941E-03 -7.672343E-06 -9.482040E-03 -1.321433E-05 -5.895438E-03 -7.290209E-06 -4.028986E-03 -3.562047E-06 -9.179212E-03 -1.479180E-05 -5.065510E-03 -7.876080E-06 -9.623638E-03 -1.331451E-05 -8.243023E-03 -1.155595E-05 -6.785547E-03 -8.869686E-06 -6.830346E-03 -6.869962E-06 -5.952624E-03 -7.591589E-06 -1.262730E-02 -2.795731E-05 -7.222793E-03 -7.969191E-06 -7.364474E-03 -9.912407E-06 -4.073203E-03 -3.221529E-06 -6.307497E-03 -8.387738E-06 -6.770719E-03 -9.965921E-06 -7.935928E-03 -9.247514E-06 -8.991810E-03 -1.001059E-05 -5.746536E-03 -5.204627E-06 -4.632325E-03 -3.055561E-06 -7.410656E-03 -9.676112E-06 -7.030579E-03 -9.078592E-06 -9.940893E-03 -2.033444E-05 -4.593423E-03 -3.918985E-06 -3.958315E-03 -2.344423E-06 -4.956734E-03 -6.309001E-06 -7.499601E-03 -8.403664E-06 -4.748645E-03 -4.199540E-06 -5.390021E-03 -5.915551E-06 -8.712210E-03 -1.738886E-05 -8.103852E-03 -9.180564E-06 -1.025459E-02 -1.310828E-05 -1.184291E-02 -1.833442E-05 -5.755536E-03 -5.404194E-06 -8.221201E-03 -9.335727E-06 -9.828395E-03 -1.545245E-05 -1.152575E-02 -1.759013E-05 -5.829495E-03 -7.232703E-06 -1.218356E-02 -2.664254E-05 -6.170466E-03 -7.308485E-06 -7.313197E-03 -7.829261E-06 -1.367693E-02 -3.155396E-05 -1.355349E-02 -2.093247E-05 -1.169703E-02 -1.533618E-05 -1.225998E-02 -3.530234E-05 -1.125463E-02 -1.795265E-05 -9.696736E-03 -1.412411E-05 -1.093214E-02 -1.673039E-05 -1.278387E-02 -2.130505E-05 -8.880561E-03 -1.364130E-05 -1.181523E-02 -1.870304E-05 -9.189492E-03 -1.453491E-05 -9.455271E-03 -1.423365E-05 -1.776658E-02 -4.257355E-05 -1.296514E-02 -2.121667E-05 -1.003865E-02 -1.487795E-05 -1.359312E-02 -2.765495E-05 -1.162264E-02 -2.140342E-05 -1.033131E-02 -1.304998E-05 -1.542205E-02 -3.650649E-05 -1.535565E-02 -3.683101E-05 -9.763595E-03 -1.309061E-05 -1.726548E-02 -4.632789E-05 -1.043992E-02 -1.651951E-05 -6.110039E-03 -6.694762E-06 -1.985635E-02 -6.510789E-05 -8.438493E-03 -9.248252E-06 -1.851818E-02 -4.904640E-05 -1.995254E-02 -5.020531E-05 -1.335907E-02 -2.466345E-05 -1.649899E-02 -3.318908E-05 -1.452837E-02 -3.635670E-05 -1.935287E-02 -5.575704E-05 -1.319131E-02 -2.791021E-05 -1.492064E-02 -3.073511E-05 -1.131605E-02 -2.493004E-05 -1.462680E-02 -2.849063E-05 -1.962759E-02 -5.083607E-05 -1.701425E-02 -4.167266E-05 -7.965972E-03 -1.229258E-05 -1.458100E-02 -2.718206E-05 -1.026138E-02 -2.113714E-05 -1.308429E-02 -2.003268E-05 -9.038340E-03 -9.340773E-06 -1.586616E-02 -3.218048E-05 -6.515114E-03 -9.139171E-06 -1.003265E-02 -1.250538E-05 -7.477346E-03 -9.170786E-06 -8.376990E-03 -1.161182E-05 -1.829624E-02 -3.702430E-05 -1.569831E-02 -2.881524E-05 -1.105450E-02 -2.103495E-05 -1.042629E-02 -1.723138E-05 -9.998085E-03 -1.227071E-05 -1.516738E-02 -3.324549E-05 -1.090125E-02 -1.985519E-05 -1.432278E-02 -3.386798E-05 -8.418229E-03 -1.050672E-05 -1.108353E-02 -1.739127E-05 -7.787352E-03 -1.084467E-05 -6.757477E-03 -8.314301E-06 -1.266965E-02 -2.014700E-05 -1.223368E-02 -1.816810E-05 -1.000405E-02 -2.132755E-05 -1.260170E-02 -2.392494E-05 -9.344763E-03 -1.208588E-05 -1.243003E-02 -2.215004E-05 -8.842577E-03 -1.468191E-05 -1.171609E-02 -2.634512E-05 -7.361574E-03 -1.005220E-05 -4.937175E-03 -3.421412E-06 -8.801345E-03 -1.258402E-05 -8.660082E-03 -1.317890E-05 -1.066110E-02 -2.285362E-05 -8.182265E-03 -1.352232E-05 -1.194368E-02 -2.542532E-05 -5.247990E-03 -7.113477E-06 -1.329855E-02 -2.805232E-05 -1.667204E-02 -4.715717E-05 -5.516192E-03 -4.861602E-06 -8.032885E-03 -9.526962E-06 -8.923177E-03 -1.221885E-05 -7.273785E-03 -7.646285E-06 -9.997748E-03 -1.389196E-05 -1.049460E-02 -1.325998E-05 -1.231591E-02 -1.779583E-05 -1.187687E-02 -1.777669E-05 -4.027367E-03 -5.095043E-06 -5.883273E-03 -1.010000E-05 -5.881059E-03 -5.701541E-06 -6.065490E-03 -8.604636E-06 -1.853273E-03 -1.189336E-06 -4.775189E-03 -6.494172E-06 -4.633358E-03 -4.183400E-06 -4.180683E-03 -5.129026E-06 -4.541541E-03 -5.646520E-06 -7.074233E-04 -2.834238E-07 -2.724695E-03 -2.217855E-06 -1.700068E-03 -8.357081E-07 -3.833078E-03 -2.656428E-06 -6.572586E-03 -8.866295E-06 -5.292952E-03 -3.867444E-06 -3.517788E-03 -2.530088E-06 -2.740599E-03 -2.004521E-06 -5.438345E-03 -8.165786E-06 -3.641025E-03 -3.113878E-06 -4.966383E-03 -7.623277E-06 -3.292270E-03 -2.722033E-06 -4.468651E-03 -5.017784E-06 -2.625164E-03 -1.543702E-06 -3.246447E-03 -2.141940E-06 -6.765655E-03 -9.017921E-06 -6.472797E-03 -5.971218E-06 -5.166553E-03 -3.916196E-06 -4.715011E-03 -4.421483E-06 -8.868545E-03 -1.033407E-05 -9.454655E-03 -1.287272E-05 -7.378309E-03 -9.793455E-06 -8.294983E-03 -1.242029E-05 -9.030433E-03 -1.660734E-05 -5.691832E-03 -4.629989E-06 -6.844709E-03 -1.039079E-05 -6.076908E-03 -6.364980E-06 -1.018307E-02 -1.478904E-05 -8.398207E-03 -1.353213E-05 -1.038657E-02 -1.650576E-05 -1.318384E-02 -2.408888E-05 -8.015055E-03 -1.126916E-05 -1.030145E-02 -1.582573E-05 -6.890267E-03 -8.319222E-06 -7.150937E-03 -7.850470E-06 -4.889144E-03 -3.720968E-06 -8.323465E-03 -1.115383E-05 -9.054557E-03 -1.439423E-05 -1.406749E-02 -2.792472E-05 -1.188336E-02 -2.280525E-05 -1.029778E-02 -1.535135E-05 -6.586615E-03 -8.689373E-06 -6.989279E-03 -6.602524E-06 -8.696264E-03 -1.699910E-05 -1.215054E-02 -1.809626E-05 -9.660496E-03 -1.576109E-05 -6.998390E-03 -6.984012E-06 -4.566410E-03 -4.700686E-06 -7.914312E-03 -8.046281E-06 -1.119024E-02 -1.682956E-05 -8.557711E-03 -1.169180E-05 -1.116108E-02 -2.002096E-05 -1.596243E-02 -3.319159E-05 -1.417800E-02 -2.698476E-05 -1.370714E-02 -3.345511E-05 -6.571905E-03 -1.170061E-05 -9.528663E-03 -1.906413E-05 -1.285473E-02 -1.987588E-05 -1.051645E-02 -2.095546E-05 -6.496841E-03 -6.085712E-06 -1.535496E-02 -3.318420E-05 -1.422914E-02 -2.578886E-05 -1.481483E-02 -2.681040E-05 -3.973095E-03 -3.122718E-06 -1.132021E-02 -1.735904E-05 -3.004322E-03 -4.387582E-06 -7.360833E-03 -1.310893E-05 -1.047617E-02 -1.750773E-05 -1.122781E-02 -2.131611E-05 -5.694910E-03 -7.234627E-06 -7.668968E-03 -8.067750E-06 -4.449311E-03 -4.768775E-06 -3.787286E-03 -2.624668E-06 -1.002060E-02 -1.749285E-05 -1.395365E-02 -2.967710E-05 -8.499007E-03 -1.449296E-05 -9.187712E-03 -1.502460E-05 -9.842313E-03 -1.218596E-05 -1.196667E-02 -1.597317E-05 -9.851299E-03 -1.503088E-05 -7.735045E-03 -9.286037E-06 -1.079491E-02 -1.766380E-05 -1.091930E-02 -1.648255E-05 -1.108268E-02 -1.578770E-05 -8.120340E-03 -1.291411E-05 -1.246096E-02 -2.528446E-05 -1.202701E-02 -1.816947E-05 -8.319659E-03 -1.340429E-05 -6.275166E-03 -6.982397E-06 -9.030250E-03 -1.174300E-05 -8.187094E-03 -1.235622E-05 -5.549210E-03 -5.420156E-06 -6.789734E-03 -7.904257E-06 -4.425697E-03 -3.919582E-06 -6.512926E-03 -7.118649E-06 -6.535547E-03 -1.403886E-05 -7.543231E-03 -1.205568E-05 -7.377749E-03 -1.142071E-05 -6.797258E-03 -7.523771E-06 -7.181352E-03 -8.290755E-06 -7.045510E-03 -1.013770E-05 -8.236912E-03 -1.265044E-05 -7.857392E-03 -1.143180E-05 -2.604005E-03 -2.317099E-06 -6.968220E-03 -1.347326E-05 -6.200344E-03 -7.619475E-06 -6.667622E-03 -1.088132E-05 -2.645846E-03 -1.854304E-06 -5.207931E-03 -4.574122E-06 -5.239891E-03 -5.012226E-06 -9.605662E-03 -1.784435E-05 -4.891233E-03 -6.117222E-06 -6.504651E-03 -1.229520E-05 -4.837232E-03 -6.126627E-06 -5.744494E-03 -9.825890E-06 -4.153989E-03 -5.541638E-06 -2.163062E-03 -2.727406E-06 -7.562241E-03 -1.683819E-05 -4.906111E-03 -7.696070E-06 -4.582205E-03 -7.559853E-06 -4.360889E-03 -6.753941E-06 -2.573412E-03 -2.074263E-06 -7.341817E-03 -1.510923E-05 -1.348516E-04 -1.818494E-08 -2.140265E-03 -2.186708E-06 -5.132621E-03 -6.802003E-06 -3.951538E-03 -5.966124E-06 -3.677707E-03 -4.225767E-06 -1.976184E-03 -9.056492E-07 -4.382040E-04 -1.920227E-07 -1.818577E-03 -1.643089E-06 -1.584983E-03 -1.096049E-06 -3.083453E-03 -3.167381E-06 -2.957748E-03 -3.417374E-06 -4.181554E-03 -3.122268E-06 -8.205844E-03 -1.339216E-05 -8.373927E-03 -1.673225E-05 -4.491527E-03 -6.082578E-06 -6.731608E-03 -1.117129E-05 -4.850830E-03 -4.259080E-06 -7.231407E-03 -8.438356E-06 -5.959383E-03 -1.199394E-05 -8.812537E-03 -1.137742E-05 -5.064971E-03 -5.265762E-06 -6.088263E-03 -5.366214E-06 -1.231967E-02 -2.529922E-05 -6.664708E-03 -6.756574E-06 -7.146824E-03 -6.992158E-06 -7.597362E-03 -8.315013E-06 -8.302133E-03 -1.025085E-05 -7.908279E-03 -8.283137E-06 -4.910695E-03 -4.757721E-06 -7.465751E-03 -1.337654E-05 -6.657558E-03 -7.888053E-06 -5.401779E-03 -5.483988E-06 -3.267001E-03 -2.915272E-06 -6.045569E-03 -6.294647E-06 -1.070370E-02 -1.951887E-05 -7.552609E-03 -7.432950E-06 -8.877234E-03 -1.068410E-05 -1.075436E-02 -1.493748E-05 -5.659998E-03 -5.370515E-06 -8.655450E-03 -1.139402E-05 -6.300286E-03 -8.630445E-06 -8.360752E-03 -1.357159E-05 -5.594374E-03 -5.201663E-06 -5.933381E-03 -5.223719E-06 -5.436755E-03 -5.337268E-06 -5.498344E-03 -4.605588E-06 -1.006944E-02 -1.688863E-05 -6.917860E-03 -8.432437E-06 -1.165526E-02 -1.802342E-05 -1.077532E-02 -2.444899E-05 -4.239569E-03 -2.886382E-06 -6.796744E-03 -5.792939E-06 -6.662910E-03 -5.729331E-06 -7.255977E-03 -1.065759E-05 -2.894086E-03 -1.903304E-06 -6.311066E-03 -4.912946E-06 -1.099018E-02 -2.054283E-05 -7.241143E-03 -7.442478E-06 -4.567091E-03 -3.071464E-06 -6.773593E-03 -5.905000E-06 -8.327525E-03 -9.451448E-06 -1.069359E-02 -1.514720E-05 -3.670959E-03 -3.304885E-06 -9.124065E-03 -1.471763E-05 -3.861446E-03 -3.151454E-06 -6.221877E-03 -1.027786E-05 -5.254419E-03 -6.135711E-06 -7.702917E-03 -1.051860E-05 -5.115541E-03 -6.357416E-06 -5.321924E-03 -4.501815E-06 -7.639895E-03 -1.792277E-05 -5.676732E-03 -4.933198E-06 -7.917058E-03 -1.103359E-05 -6.987672E-03 -8.124731E-06 -7.605775E-03 -1.154995E-05 -1.077244E-02 -2.439257E-05 -5.513601E-03 -7.937817E-06 -8.315505E-03 -9.181773E-06 -5.719492E-03 -6.912423E-06 -4.809607E-03 -6.393160E-06 -5.490138E-03 -6.557157E-06 -2.585724E-03 -1.337147E-06 -3.578006E-03 -2.074822E-06 -6.644166E-03 -9.849481E-06 -5.073345E-03 -6.101950E-06 -8.583604E-03 -1.928242E-05 -6.278182E-03 -8.352587E-06 -7.476949E-03 -1.201282E-05 -4.477112E-03 -4.894391E-06 -9.059255E-03 -1.429652E-05 -3.030949E-03 -1.590060E-06 -5.686827E-03 -5.262592E-06 -4.734421E-03 -6.896077E-06 -2.912353E-03 -1.875480E-06 -6.517175E-03 -6.330268E-06 -9.203829E-03 -1.181214E-05 -5.012765E-03 -5.129300E-06 -7.823830E-03 -1.330136E-05 -5.110174E-03 -4.376566E-06 -7.379964E-03 -8.333357E-06 -5.468869E-03 -6.083600E-06 -7.519458E-03 -1.048700E-05 -4.611391E-03 -3.814525E-06 -7.062996E-03 -1.181993E-05 -3.837864E-03 -3.789477E-06 -6.759816E-03 -8.026354E-06 -5.672758E-03 -4.924437E-06 -8.864970E-03 -1.097704E-05 -5.305515E-03 -6.789690E-06 -4.264513E-03 -5.260234E-06 -5.512145E-03 -8.119123E-06 -5.053780E-03 -5.404719E-06 -7.029594E-03 -9.741504E-06 -2.374002E-03 -1.449069E-06 -2.256464E-03 -1.391425E-06 -4.548643E-03 -7.978885E-06 -2.810872E-03 -2.456200E-06 -4.084882E-03 -6.257623E-06 -6.517591E-03 -9.674764E-06 -6.200630E-03 -9.438817E-06 -2.745907E-03 -1.809511E-06 -3.972065E-03 -3.245352E-06 -5.942817E-03 -7.352165E-06 -3.263414E-03 -3.353913E-06 -8.153438E-03 -1.079966E-05 -7.165596E-03 -1.017019E-05 -4.251216E-03 -2.817736E-06 -8.011851E-03 -1.068301E-05 -9.752890E-04 -7.589177E-07 -3.469946E-03 -2.493177E-06 -6.811986E-03 -8.722919E-06 -5.394586E-03 -6.424939E-06 -5.872081E-03 -8.499852E-06 -8.818832E-03 -1.189657E-05 -7.866284E-03 -8.632785E-06 -8.116334E-03 -1.347044E-05 -2.423674E-03 -2.219673E-06 -7.457717E-03 -9.186735E-06 -6.773921E-03 -1.764821E-05 -6.930098E-03 -1.011624E-05 -8.618704E-03 -1.022187E-05 -1.184609E-02 -2.438262E-05 -8.878289E-03 -1.326793E-05 -5.073855E-03 -4.038299E-06 -9.760737E-03 -1.548967E-05 -7.965991E-03 -1.151518E-05 -5.975862E-03 -1.006202E-05 -7.122347E-03 -8.079230E-06 -7.180618E-03 -7.951754E-06 -5.027224E-03 -6.787710E-06 -1.109603E-02 -1.848734E-05 -1.295024E-02 -3.036716E-05 -8.023705E-03 -8.286741E-06 -1.144577E-02 -2.665312E-05 -1.019084E-02 -1.881473E-05 -1.361484E-02 -2.576942E-05 -9.013737E-03 -1.466391E-05 -1.147532E-02 -2.462687E-05 -1.225257E-02 -2.181790E-05 -8.159645E-03 -1.259135E-05 -4.127139E-03 -2.557351E-06 -5.325669E-03 -6.365360E-06 -1.347774E-02 -2.661460E-05 -7.265261E-03 -1.252096E-05 -1.373140E-02 -2.385999E-05 -1.262611E-02 -2.245356E-05 -9.465554E-03 -2.262286E-05 -8.452943E-03 -1.155915E-05 -4.998547E-03 -7.068774E-06 -7.648621E-03 -9.105026E-06 -9.203837E-03 -1.311626E-05 -1.107019E-02 -1.950577E-05 -1.315936E-02 -2.410764E-05 -7.844275E-03 -9.659512E-06 -9.825100E-03 -1.724414E-05 -1.047811E-02 -1.262306E-05 -1.207238E-02 -2.752816E-05 -1.085843E-02 -1.470096E-05 -1.594894E-02 -4.046810E-05 -1.497569E-02 -2.863883E-05 -1.030144E-02 -2.348225E-05 -6.125370E-03 -7.226790E-06 -1.405421E-02 -4.764046E-05 -1.625035E-02 -3.353624E-05 -9.577818E-03 -1.794568E-05 -1.448263E-02 -2.770743E-05 -1.029916E-02 -1.861537E-05 -9.183850E-03 -1.591847E-05 -9.237708E-03 -1.576843E-05 -1.079813E-02 -1.581077E-05 -1.260676E-02 -3.554547E-05 -2.054039E-02 -5.879657E-05 -7.345172E-03 -8.644617E-06 -9.151035E-03 -1.651766E-05 -1.271524E-02 -2.302768E-05 -1.487615E-02 -3.960752E-05 -7.259983E-03 -1.107807E-05 -5.855371E-03 -4.857225E-06 -7.570428E-03 -9.406879E-06 -5.643592E-03 -7.265176E-06 -1.113252E-02 -1.652636E-05 -1.101056E-02 -1.732233E-05 -7.781992E-03 -7.475792E-06 -1.014997E-02 -1.698223E-05 -7.547933E-03 -1.219150E-05 -4.922153E-03 -4.916764E-06 -6.063790E-03 -7.031316E-06 -8.119766E-03 -1.484039E-05 -5.084706E-03 -4.983059E-06 -5.431730E-03 -5.048229E-06 -8.720640E-03 -1.061184E-05 -8.480299E-03 -1.325453E-05 -1.209019E-02 -2.053697E-05 -1.028370E-02 -2.394843E-05 -8.037695E-03 -1.000534E-05 -9.075446E-03 -1.720138E-05 -5.348851E-03 -5.241839E-06 -6.061215E-03 -7.799859E-06 -9.501216E-03 -1.498369E-05 -1.649742E-02 -3.974159E-05 -8.337027E-03 -1.677761E-05 -1.156020E-02 -1.673947E-05 -1.046774E-02 -2.653737E-05 -7.675581E-03 -8.456541E-06 -9.617224E-03 -1.794211E-05 -1.268842E-02 -2.897864E-05 -9.844835E-03 -1.818849E-05 -9.330947E-03 -1.549578E-05 -5.838065E-03 -7.948493E-06 -8.798788E-03 -1.342636E-05 -9.306629E-03 -1.318598E-05 -4.920602E-03 -4.823960E-06 -1.710686E-03 -7.729255E-07 -2.859179E-03 -1.745176E-06 -4.456373E-03 -4.366517E-06 -2.395798E-03 -1.736746E-06 -7.027745E-03 -1.682294E-05 -4.030298E-03 -4.530935E-06 -2.803361E-03 -2.792072E-06 -6.688412E-03 -1.091671E-05 -7.703157E-03 -8.772855E-06 -4.949493E-03 -3.982137E-06 -7.365244E-03 -1.073387E-05 -9.386372E-03 -1.134513E-05 -9.003757E-03 -1.332391E-05 -7.974051E-03 -1.354602E-05 -1.137434E-02 -1.857838E-05 -1.143520E-02 -2.117358E-05 -3.030499E-03 -2.165815E-06 -4.867784E-03 -3.911154E-06 -5.246175E-03 -6.161091E-06 -5.832426E-03 -5.525543E-06 -7.382520E-03 -9.067048E-06 -1.188676E-02 -1.778783E-05 -1.199313E-02 -2.082141E-05 -1.266957E-02 -1.962424E-05 -1.533876E-02 -3.337162E-05 -7.389726E-03 -1.341338E-05 -7.529503E-03 -1.564696E-05 -1.491955E-02 -2.945644E-05 -1.074068E-02 -1.373117E-05 -1.472706E-02 -3.572507E-05 -1.199986E-02 -3.422317E-05 -8.514133E-03 -1.135618E-05 -1.634996E-02 -4.087320E-05 -1.955740E-02 -5.492009E-05 -1.126909E-02 -1.541148E-05 -1.586435E-02 -3.446303E-05 -1.700178E-02 -4.898376E-05 -1.703540E-02 -3.720395E-05 -1.967755E-02 -4.525544E-05 -1.330271E-02 -2.702604E-05 -1.888903E-02 -4.979443E-05 -2.004837E-02 -5.083025E-05 -1.435878E-02 -3.285995E-05 -1.930301E-02 -5.509308E-05 -1.628120E-02 -3.580789E-05 -1.727344E-02 -4.342335E-05 -1.216615E-02 -1.983233E-05 -1.700189E-02 -4.670523E-05 -1.269439E-02 -2.172602E-05 -2.010215E-02 -5.553642E-05 -2.319103E-02 -6.565091E-05 -1.414047E-02 -3.346494E-05 -2.027618E-02 -5.626720E-05 -1.646954E-02 -3.312557E-05 -2.251086E-02 -5.995831E-05 -1.610985E-02 -3.156849E-05 -1.377020E-02 -2.853634E-05 -1.669701E-02 -3.307612E-05 -1.159986E-02 -1.816147E-05 -1.261975E-02 -1.769023E-05 -2.134097E-02 -5.752884E-05 -1.841902E-02 -4.295167E-05 -2.422207E-02 -7.083744E-05 -1.543392E-02 -4.221173E-05 -2.157215E-02 -6.548409E-05 -2.644384E-02 -8.410907E-05 -2.311629E-02 -6.681868E-05 -2.395484E-02 -6.437514E-05 -1.922089E-02 -4.174544E-05 -1.663494E-02 -3.328109E-05 -9.964643E-03 -1.255907E-05 -1.150794E-02 -1.961340E-05 -1.939308E-02 -4.712734E-05 -1.787616E-02 -4.675712E-05 -2.319516E-02 -6.969066E-05 -1.741640E-02 -5.935363E-05 -1.789596E-02 -4.474293E-05 -2.512717E-02 -7.989830E-05 -1.220933E-02 -1.607761E-05 -1.359674E-02 -2.403775E-05 -1.590621E-02 -4.874402E-05 -1.664365E-02 -3.902009E-05 -1.028716E-02 -1.812738E-05 -1.543365E-02 -3.451753E-05 -1.719718E-02 -5.881162E-05 -1.666983E-02 -4.833689E-05 -1.586040E-02 -3.290082E-05 -1.592280E-02 -2.852261E-05 -2.560930E-02 -8.694469E-05 -2.095050E-02 -5.412080E-05 -1.939382E-02 -4.569593E-05 -1.830391E-02 -4.667520E-05 -1.549161E-02 -4.356136E-05 -1.284248E-02 -2.163546E-05 -1.082843E-02 -1.936706E-05 -1.108300E-02 -2.032165E-05 -1.963227E-02 -5.789246E-05 -1.883825E-02 -5.273957E-05 -1.630332E-02 -3.434978E-05 -1.574688E-02 -3.564693E-05 -1.794394E-02 -3.918955E-05 -2.060232E-02 -5.819679E-05 -1.732204E-02 -4.346625E-05 -1.922985E-02 -4.636825E-05 -1.116057E-02 -1.992936E-05 -1.216817E-02 -1.947069E-05 -8.811885E-03 -1.441275E-05 -1.603072E-02 -3.965729E-05 -1.459394E-02 -2.864759E-05 -1.150127E-02 -2.431776E-05 -1.146459E-02 -2.593052E-05 -1.120930E-02 -2.027939E-05 -1.077685E-02 -1.909421E-05 -1.564605E-02 -5.177856E-05 -1.438847E-02 -2.596269E-05 -1.575101E-02 -2.945546E-05 -5.164090E-03 -4.380072E-06 -5.313984E-03 -4.984595E-06 -3.270078E-03 -3.714857E-06 -4.763025E-03 -5.471147E-06 -5.854797E-03 -4.013420E-06 -7.497916E-03 -9.530633E-06 -4.307122E-03 -4.511481E-06 -2.673595E-03 -1.496751E-06 -7.668976E-03 -1.208726E-05 -6.666645E-03 -9.107345E-06 -4.278473E-03 -3.613729E-06 -5.196607E-03 -6.165915E-06 -9.012663E-03 -1.072065E-05 -7.125950E-03 -1.021884E-05 -1.110525E-02 -1.609758E-05 -6.452393E-03 -6.522408E-06 -1.614916E-02 -2.933964E-05 -1.252957E-02 -2.095707E-05 -9.931672E-03 -1.358529E-05 -1.307449E-02 -2.442977E-05 -5.859693E-03 -6.187229E-06 -6.811267E-03 -6.011900E-06 -1.636036E-02 -4.227062E-05 -9.529154E-03 -1.673527E-05 -9.884570E-03 -1.607810E-05 -1.162560E-02 -2.293323E-05 -6.582499E-03 -8.254458E-06 -1.846807E-02 -3.840513E-05 -9.921319E-03 -1.528378E-05 -1.451278E-02 -3.842835E-05 -1.536970E-02 -4.453795E-05 -1.597714E-02 -3.763051E-05 -1.416499E-02 -2.488034E-05 -1.579940E-02 -3.618366E-05 -1.729494E-02 -4.587947E-05 -1.591255E-02 -3.608980E-05 -1.155321E-02 -2.053226E-05 -1.599423E-02 -3.545225E-05 -1.125966E-02 -2.267289E-05 -2.172447E-02 -5.857701E-05 -1.372075E-02 -3.321713E-05 -1.816373E-02 -4.485596E-05 -1.857039E-02 -4.872304E-05 -1.742011E-02 -3.730490E-05 -1.568977E-02 -3.029088E-05 -2.829373E-02 -1.168965E-04 -2.157357E-02 -7.579178E-05 -2.125484E-02 -6.473902E-05 -2.196378E-02 -7.339057E-05 -1.819373E-02 -4.494748E-05 -2.152003E-02 -5.928285E-05 -1.856070E-02 -4.410267E-05 -2.207371E-02 -6.449028E-05 -2.386394E-02 -6.969242E-05 -2.457216E-02 -1.102599E-04 -2.058268E-02 -5.017858E-05 -1.907854E-02 -4.286270E-05 -2.340706E-02 -8.278475E-05 -2.388190E-02 -6.533857E-05 -2.860789E-02 -9.992731E-05 -1.998933E-02 -5.361464E-05 -2.707938E-02 -9.662902E-05 -2.019340E-02 -5.415561E-05 -3.109482E-02 -1.022004E-04 -2.253924E-02 -7.055172E-05 -2.374030E-02 -6.831307E-05 -2.529035E-02 -8.295294E-05 -2.739289E-02 -8.546620E-05 -2.273185E-02 -6.442166E-05 -2.785353E-02 -8.812366E-05 -3.191849E-02 -1.234987E-04 -2.739246E-02 -9.446447E-05 -1.372157E-02 -2.877717E-05 -1.714263E-02 -4.531432E-05 -1.958677E-02 -4.920099E-05 -1.932218E-02 -4.756040E-05 -1.938252E-02 -5.565524E-05 -2.128472E-02 -6.581062E-05 -1.818983E-02 -4.440473E-05 -2.522476E-02 -9.504815E-05 -2.108060E-02 -6.460391E-05 -2.301325E-02 -6.325908E-05 -2.425505E-02 -7.889282E-05 -2.312674E-02 -8.098059E-05 -1.450662E-02 -3.120847E-05 -1.518342E-02 -2.854939E-05 -2.375891E-02 -6.170759E-05 -2.191567E-02 -5.656277E-05 -1.717224E-02 -4.219605E-05 -1.584923E-02 -2.801671E-05 -2.292044E-02 -6.761182E-05 -2.565035E-02 -9.677221E-05 -1.979804E-02 -4.912546E-05 -2.534051E-02 -8.007940E-05 -3.425993E-02 -1.487128E-04 -2.139063E-02 -5.051176E-05 -1.422774E-02 -2.343122E-05 -2.541660E-02 -8.204787E-05 -1.717071E-02 -3.842694E-05 -1.456931E-02 -2.599937E-05 -2.407397E-02 -7.826348E-05 -2.982739E-02 -1.099192E-04 -1.812330E-02 -4.520557E-05 -1.506065E-02 -3.120742E-05 -1.957887E-02 -5.130992E-05 -2.577824E-02 -8.648590E-05 -1.959134E-02 -6.907548E-05 -2.361937E-02 -6.849830E-05 -1.277061E-02 -2.340598E-05 -1.244142E-02 -2.267140E-05 -9.333247E-03 -1.041879E-05 -7.908182E-03 -9.293981E-06 -1.463279E-02 -3.362978E-05 -1.366375E-02 -2.733021E-05 -1.213165E-02 -1.941620E-05 -1.142127E-02 -1.911366E-05 -1.101989E-02 -1.602290E-05 -1.634784E-02 -3.444363E-05 -1.399007E-02 -2.474094E-05 -1.355244E-02 -2.255801E-05 -8.699738E-03 -1.229441E-05 -6.242338E-03 -7.967030E-06 -1.101667E-02 -2.565058E-05 -8.204914E-03 -1.826875E-05 -1.073981E-02 -1.693632E-05 -1.272259E-02 -2.077325E-05 -3.495009E-03 -2.520678E-06 -8.225887E-03 -1.254359E-05 -1.009530E-02 -1.562680E-05 -1.178865E-02 -2.042891E-05 -8.663517E-03 -1.609834E-05 -1.169582E-02 -2.164100E-05 -7.396443E-03 -1.192095E-05 -3.652278E-03 -3.129723E-06 -1.045202E-02 -2.112608E-05 -1.037936E-02 -1.718392E-05 -4.516079E-03 -3.575143E-06 -1.095270E-02 -1.774093E-05 -4.883767E-03 -5.927447E-06 -6.503683E-03 -7.716956E-06 -4.663924E-03 -5.713440E-06 -6.283619E-03 -4.627844E-06 -1.015268E-02 -1.309267E-05 -9.180971E-03 -1.527190E-05 -1.951120E-02 -4.198333E-05 -1.915689E-02 -5.707669E-05 -1.373893E-02 -2.451846E-05 -1.700799E-02 -4.037549E-05 -8.089066E-03 -1.170312E-05 -1.428411E-02 -2.720105E-05 -1.592720E-02 -3.708704E-05 -1.631763E-02 -3.412617E-05 -1.129445E-02 -1.941873E-05 -1.915612E-02 -4.735245E-05 -1.422466E-02 -3.019642E-05 -1.623769E-02 -4.050190E-05 -3.000266E-02 -1.011980E-04 -2.431859E-02 -7.075474E-05 -1.997843E-02 -5.249597E-05 -2.330404E-02 -6.963217E-05 -2.446833E-02 -7.157726E-05 -2.086381E-02 -6.071597E-05 -2.692405E-02 -8.142503E-05 -3.334489E-02 -1.331416E-04 -2.459016E-02 -7.539450E-05 -2.490317E-02 -6.829414E-05 -3.465734E-02 -1.336723E-04 -2.655747E-02 -8.402121E-05 -2.461979E-02 -7.593460E-05 -2.071273E-02 -5.109741E-05 -1.963799E-02 -4.721624E-05 -2.830417E-02 -1.108014E-04 -2.011415E-02 -4.210370E-05 -2.710260E-02 -9.007571E-05 -2.284700E-02 -6.898714E-05 -3.401467E-02 -1.508203E-04 -3.114460E-02 -1.181041E-04 -3.240556E-02 -1.301155E-04 -2.879980E-02 -1.090167E-04 -3.128129E-02 -1.046425E-04 -1.710639E-02 -3.063073E-05 -2.380055E-02 -6.611900E-05 -2.461530E-02 -6.805558E-05 -2.334066E-02 -6.791023E-05 -2.377977E-02 -7.167847E-05 -3.617503E-02 -1.573371E-04 -1.872815E-02 -5.621939E-05 -1.775203E-02 -4.642358E-05 -2.771547E-02 -8.299647E-05 -3.061731E-02 -1.020731E-04 -2.507147E-02 -7.334317E-05 -3.870082E-02 -1.721376E-04 -2.186224E-02 -6.325183E-05 -2.007397E-02 -5.062141E-05 -1.912434E-02 -4.450748E-05 -3.143924E-02 -1.217065E-04 -2.928610E-02 -1.369102E-04 -2.498862E-02 -9.234430E-05 -3.196385E-02 -1.380535E-04 -2.888275E-02 -9.066905E-05 -2.680537E-02 -9.933885E-05 -3.510043E-02 -1.418994E-04 -2.849205E-02 -9.541143E-05 -3.088469E-02 -1.124620E-04 -2.410541E-02 -7.456599E-05 -2.652028E-02 -7.737605E-05 -3.100674E-02 -1.204077E-04 -2.627656E-02 -7.414914E-05 -3.172017E-02 -1.179298E-04 -3.654420E-02 -1.501200E-04 -2.437387E-02 -8.465163E-05 -2.329587E-02 -8.609453E-05 -2.851454E-02 -9.764789E-05 -2.727184E-02 -1.020548E-04 -2.887669E-02 -9.379474E-05 -2.486065E-02 -6.980906E-05 -1.336139E-02 -3.237058E-05 -1.783830E-02 -4.308671E-05 -1.984536E-02 -4.793066E-05 -1.663979E-02 -3.245507E-05 -1.629628E-02 -3.780856E-05 -2.783691E-02 -9.185991E-05 -1.901340E-02 -4.331876E-05 -1.589227E-02 -3.023585E-05 -1.511908E-02 -3.924720E-05 -2.103836E-02 -5.685631E-05 -1.846361E-02 -5.596947E-05 -2.921881E-02 -1.027479E-04 -1.582650E-02 -2.965346E-05 -1.468540E-02 -2.895302E-05 -9.775043E-03 -1.581144E-05 -1.531973E-02 -3.646605E-05 -1.629584E-02 -4.077722E-05 -1.301406E-02 -2.211695E-05 -1.400095E-02 -2.464499E-05 -1.812501E-02 -5.136800E-05 -1.883997E-02 -4.846725E-05 -1.310098E-02 -2.076611E-05 -1.641887E-02 -3.214577E-05 -2.575947E-02 -7.664609E-05 -1.393085E-02 -2.533855E-05 -1.176890E-02 -1.965214E-05 -1.001319E-02 -1.752840E-05 -5.440474E-03 -5.776297E-06 -1.602659E-02 -3.187961E-05 -1.220713E-02 -2.605586E-05 -7.910828E-03 -1.235157E-05 -6.370403E-03 -7.314863E-06 -1.200010E-02 -2.093134E-05 -1.051384E-02 -2.114633E-05 -8.728632E-03 -1.217334E-05 -9.023232E-03 -1.429000E-05 -9.324890E-03 -1.238002E-05 -9.724819E-03 -1.340545E-05 -7.999095E-03 -1.507108E-05 -1.196325E-02 -2.492866E-05 -1.708992E-02 -4.187497E-05 -1.252870E-02 -2.576528E-05 -1.245286E-02 -2.205925E-05 -1.710226E-02 -4.820652E-05 -1.077670E-02 -2.779337E-05 -8.954749E-03 -1.459088E-05 -1.587508E-02 -5.891538E-05 -1.256231E-02 -3.626849E-05 -1.456161E-02 -2.677457E-05 -1.442492E-02 -2.873208E-05 -1.513933E-02 -2.781837E-05 -1.983944E-02 -4.742520E-05 -1.796161E-02 -5.099223E-05 -1.372378E-02 -2.143333E-05 -2.054995E-02 -5.149337E-05 -1.901284E-02 -4.203203E-05 -1.545044E-02 -3.240467E-05 -1.613542E-02 -3.093719E-05 -1.748080E-02 -3.676025E-05 -1.983930E-02 -5.826899E-05 -2.077484E-02 -5.311470E-05 -2.156771E-02 -7.349191E-05 -1.910656E-02 -5.646548E-05 -2.216856E-02 -5.896630E-05 -2.363877E-02 -6.273768E-05 -2.287745E-02 -6.142836E-05 -2.078760E-02 -5.965409E-05 -2.096805E-02 -6.106318E-05 -2.748905E-02 -8.547689E-05 -3.051782E-02 -1.062831E-04 -2.439885E-02 -6.963272E-05 -2.385521E-02 -7.024650E-05 -2.722803E-02 -8.442159E-05 -2.779212E-02 -9.251750E-05 -3.008247E-02 -1.087302E-04 -2.911535E-02 -9.162499E-05 -2.377217E-02 -6.480525E-05 -2.478405E-02 -8.301183E-05 -2.916643E-02 -1.151040E-04 -3.299041E-02 -1.251473E-04 -3.387553E-02 -1.275181E-04 -3.185326E-02 -1.151563E-04 -2.738559E-02 -8.510575E-05 -3.552781E-02 -1.467678E-04 -2.050637E-02 -4.879306E-05 -2.837699E-02 -8.517767E-05 -2.623559E-02 -7.322670E-05 -3.299486E-02 -1.475369E-04 -2.852314E-02 -1.058243E-04 -3.897511E-02 -1.782398E-04 -3.453237E-02 -1.467871E-04 -2.695630E-02 -9.399806E-05 -2.993587E-02 -1.069727E-04 -3.357367E-02 -1.220837E-04 -3.533685E-02 -1.307797E-04 -3.768880E-02 -1.706168E-04 -2.378269E-02 -8.193354E-05 -2.476389E-02 -6.667890E-05 -1.835306E-02 -3.678306E-05 -2.760875E-02 -1.044222E-04 -3.364777E-02 -1.294893E-04 -2.689602E-02 -8.234805E-05 -2.994600E-02 -1.038678E-04 -3.354397E-02 -1.250106E-04 -2.806016E-02 -1.032022E-04 -3.628839E-02 -1.512597E-04 -3.254567E-02 -1.229982E-04 -3.532092E-02 -1.401948E-04 -2.079706E-02 -5.214734E-05 -2.220721E-02 -6.771925E-05 -2.792774E-02 -9.954429E-05 -2.945320E-02 -1.043290E-04 -3.367757E-02 -1.278353E-04 -3.498071E-02 -1.439058E-04 -3.069969E-02 -1.094894E-04 -2.802931E-02 -9.732849E-05 -3.379486E-02 -1.251030E-04 -3.112018E-02 -1.419631E-04 -2.905240E-02 -9.457676E-05 -3.204785E-02 -1.232863E-04 -2.256002E-02 -6.449945E-05 -2.013216E-02 -4.579187E-05 -2.378756E-02 -7.502656E-05 -1.850227E-02 -4.559569E-05 -2.577098E-02 -8.077705E-05 -2.622626E-02 -8.900068E-05 -2.011102E-02 -5.119938E-05 -1.881459E-02 -5.303212E-05 -2.333357E-02 -8.135744E-05 -2.169922E-02 -5.260477E-05 -2.865567E-02 -1.072530E-04 -3.018313E-02 -1.180560E-04 -1.619341E-02 -3.631507E-05 -2.048742E-02 -6.014736E-05 -1.648517E-02 -3.461260E-05 -2.016257E-02 -5.377223E-05 -1.821956E-02 -4.021256E-05 -2.158168E-02 -6.922336E-05 -1.520759E-02 -3.087500E-05 -1.460640E-02 -2.484693E-05 -2.423578E-02 -9.468824E-05 -1.422992E-02 -2.626671E-05 -2.233711E-02 -6.453963E-05 -2.201935E-02 -6.607908E-05 -9.865297E-03 -1.253449E-05 -1.407603E-02 -2.374027E-05 -1.554488E-02 -3.357059E-05 -1.104207E-02 -1.800945E-05 -1.152633E-02 -1.752515E-05 -1.628689E-02 -3.825834E-05 -1.961306E-03 -1.509555E-06 -7.317970E-03 -1.170313E-05 -8.045927E-03 -1.128636E-05 -7.690351E-03 -9.455253E-06 -1.305182E-02 -2.924924E-05 -8.478606E-03 -1.183524E-05 -8.368030E-03 -9.397112E-06 -6.954808E-03 -5.988757E-06 -1.478825E-02 -2.903770E-05 -1.329221E-02 -2.352890E-05 -1.153738E-02 -2.279952E-05 -1.243207E-02 -2.027409E-05 -9.794691E-03 -2.519096E-05 -1.301443E-02 -2.673139E-05 -7.744527E-03 -8.512395E-06 -1.094360E-02 -1.898404E-05 -1.322530E-02 -2.416304E-05 -1.130737E-02 -2.067371E-05 -1.475002E-02 -2.641761E-05 -1.707041E-02 -4.061788E-05 -1.873188E-02 -3.858649E-05 -2.599435E-02 -7.840083E-05 -1.356899E-02 -3.303086E-05 -1.640959E-02 -4.231153E-05 -1.712330E-02 -4.025543E-05 -2.408636E-02 -6.859578E-05 -1.647314E-02 -3.975609E-05 -2.116131E-02 -5.516930E-05 -1.843826E-02 -4.250156E-05 -1.528596E-02 -3.432942E-05 -1.872595E-02 -4.482976E-05 -2.278412E-02 -7.039773E-05 -1.646460E-02 -3.454420E-05 -2.813905E-02 -1.098347E-04 -1.918135E-02 -4.628739E-05 -2.880134E-02 -1.294280E-04 -2.929725E-02 -1.035423E-04 -2.521557E-02 -8.873832E-05 -1.598874E-02 -3.488466E-05 -2.763380E-02 -9.554358E-05 -2.869939E-02 -9.202942E-05 -2.713151E-02 -8.978531E-05 -1.987710E-02 -4.592695E-05 -2.437199E-02 -8.143180E-05 -2.321959E-02 -6.492177E-05 -3.341995E-02 -1.498991E-04 -3.725498E-02 -1.896851E-04 -2.546980E-02 -7.669037E-05 -3.217017E-02 -1.220964E-04 -3.340259E-02 -1.408478E-04 -2.814856E-02 -9.960802E-05 -3.512673E-02 -1.528623E-04 -3.192684E-02 -1.177255E-04 -4.396846E-02 -2.179530E-04 -2.532908E-02 -9.266623E-05 -2.547875E-02 -7.493150E-05 -2.198311E-02 -5.696022E-05 -2.752789E-02 -8.630574E-05 -2.765173E-02 -8.761218E-05 -2.779760E-02 -1.213200E-04 -3.155525E-02 -1.104115E-04 -3.217963E-02 -1.396421E-04 -2.812793E-02 -1.067509E-04 -3.145703E-02 -1.323921E-04 -3.473095E-02 -1.271199E-04 -3.132386E-02 -1.050335E-04 -2.550248E-02 -7.173055E-05 -2.519509E-02 -8.292973E-05 -2.719586E-02 -9.875000E-05 -3.129909E-02 -1.364716E-04 -2.405862E-02 -6.392928E-05 -3.794317E-02 -1.796683E-04 -2.411060E-02 -7.563523E-05 -3.925533E-02 -1.941205E-04 -2.464678E-02 -8.141818E-05 -2.681802E-02 -8.985410E-05 -4.257422E-02 -2.011050E-04 -3.169616E-02 -1.228099E-04 -2.070267E-02 -8.295562E-05 -1.875182E-02 -4.519138E-05 -2.746689E-02 -8.527112E-05 -3.193689E-02 -1.260160E-04 -3.274412E-02 -1.277208E-04 -3.159605E-02 -1.244039E-04 -2.739115E-02 -9.148770E-05 -2.496372E-02 -7.319379E-05 -2.543741E-02 -8.751995E-05 -3.092377E-02 -1.322133E-04 -3.099099E-02 -1.159996E-04 -3.339925E-02 -1.244687E-04 -1.992237E-02 -5.132256E-05 -2.014907E-02 -4.750167E-05 -2.144112E-02 -6.724888E-05 -3.234934E-02 -1.819994E-04 -2.676069E-02 -9.670530E-05 -2.163370E-02 -5.007717E-05 -1.255610E-02 -2.744980E-05 -1.686689E-02 -4.406712E-05 -2.491027E-02 -8.641625E-05 -2.180394E-02 -5.962243E-05 -2.074450E-02 -7.196276E-05 -2.673557E-02 -9.943740E-05 -1.141559E-02 -2.064868E-05 -1.086445E-02 -1.772569E-05 -1.994739E-02 -6.095357E-05 -1.294529E-02 -2.949692E-05 -1.279449E-02 -2.013615E-05 -1.695923E-02 -5.267358E-05 -1.003362E-02 -1.892494E-05 -1.177407E-02 -3.074621E-05 -1.427717E-02 -3.062012E-05 -1.772566E-02 -4.566137E-05 -1.448022E-02 -2.786842E-05 -1.885150E-02 -4.869026E-05 -6.234737E-03 -6.766915E-06 -8.586280E-03 -1.484755E-05 -6.177392E-03 -1.214223E-05 -5.323791E-03 -4.680794E-06 -1.176080E-02 -2.484279E-05 -1.114881E-02 -1.645519E-05 -6.273419E-03 -6.143891E-06 -5.166701E-03 -3.867691E-06 -8.069436E-03 -1.040414E-05 -4.713136E-03 -5.175457E-06 -1.199780E-02 -1.859984E-05 -8.029148E-03 -8.699198E-06 -5.632949E-03 -5.837510E-06 -9.206218E-03 -1.575125E-05 -9.680753E-03 -1.846461E-05 -1.155184E-02 -2.188509E-05 -1.074904E-02 -1.891184E-05 -1.181458E-02 -2.456277E-05 -1.127047E-02 -2.593399E-05 -1.356860E-02 -2.814265E-05 -5.544832E-03 -6.946307E-06 -9.942970E-03 -1.675490E-05 -1.631004E-02 -3.536433E-05 -1.506925E-02 -2.482344E-05 -1.182795E-02 -1.897537E-05 -1.482385E-02 -3.899302E-05 -1.209032E-02 -1.754837E-05 -1.195251E-02 -2.049614E-05 -1.038664E-02 -1.424835E-05 -1.422392E-02 -2.932993E-05 -1.541884E-02 -3.247431E-05 -1.631665E-02 -4.604922E-05 -1.384486E-02 -3.663157E-05 -1.878261E-02 -4.189792E-05 -1.586209E-02 -3.733994E-05 -2.018001E-02 -5.531326E-05 -1.737200E-02 -4.051342E-05 -1.867979E-02 -4.835331E-05 -1.873274E-02 -7.026344E-05 -3.092919E-02 -1.440764E-04 -1.756290E-02 -4.330757E-05 -1.677922E-02 -3.960529E-05 -1.917939E-02 -5.218395E-05 -2.058760E-02 -5.104735E-05 -1.950686E-02 -4.503102E-05 -1.898111E-02 -5.123138E-05 -2.095898E-02 -6.978821E-05 -2.496761E-02 -7.344351E-05 -2.216966E-02 -6.981877E-05 -1.539258E-02 -3.355552E-05 -2.090036E-02 -5.893204E-05 -2.830594E-02 -9.908163E-05 -2.079711E-02 -5.700234E-05 -2.438388E-02 -7.315541E-05 -2.071811E-02 -5.920090E-05 -3.238056E-02 -1.338963E-04 -1.998547E-02 -4.982584E-05 -2.456523E-02 -7.090598E-05 -3.010218E-02 -9.569115E-05 -2.615311E-02 -8.034083E-05 -2.193093E-02 -6.501139E-05 -2.230235E-02 -6.810904E-05 -2.787441E-02 -9.364266E-05 -3.363819E-02 -1.294292E-04 -2.455137E-02 -7.174173E-05 -2.515678E-02 -6.839428E-05 -2.184998E-02 -5.948214E-05 -3.240910E-02 -1.297175E-04 -2.930001E-02 -9.691693E-05 -2.531952E-02 -7.468705E-05 -3.926456E-02 -1.746326E-04 -3.138940E-02 -1.141098E-04 -2.053774E-02 -4.616986E-05 -1.863850E-02 -5.063978E-05 -2.488227E-02 -8.135984E-05 -2.946035E-02 -9.621222E-05 -1.816353E-02 -4.227005E-05 -2.739033E-02 -9.425769E-05 -2.235176E-02 -7.322516E-05 -2.512226E-02 -7.712444E-05 -2.093974E-02 -5.655060E-05 -1.951079E-02 -4.907052E-05 -3.008712E-02 -9.648471E-05 -2.695713E-02 -8.322148E-05 -2.032086E-02 -4.478975E-05 -1.588968E-02 -3.284638E-05 -2.795370E-02 -9.478738E-05 -3.242775E-02 -1.467751E-04 -1.851414E-02 -5.030367E-05 -1.699040E-02 -4.048405E-05 -2.381562E-02 -8.005819E-05 -2.396088E-02 -7.106899E-05 -1.334683E-02 -2.033683E-05 -2.683684E-02 -9.730671E-05 -3.212945E-02 -1.268600E-04 -2.413721E-02 -7.785437E-05 -1.278877E-02 -2.285665E-05 -2.017408E-02 -5.101405E-05 -2.425974E-02 -8.238864E-05 -1.559733E-02 -4.134474E-05 -2.323747E-02 -5.912862E-05 -3.185441E-02 -1.214009E-04 -1.297541E-02 -3.542303E-05 -1.620965E-02 -3.785927E-05 -2.077217E-02 -5.048348E-05 -2.084714E-02 -5.308615E-05 -2.198650E-02 -6.677202E-05 -2.713339E-02 -7.996952E-05 -1.008900E-02 -1.493544E-05 -7.515252E-03 -6.938048E-06 -1.725580E-02 -4.511463E-05 -1.861578E-02 -4.706579E-05 -1.192562E-02 -1.706496E-05 -1.539067E-02 -3.704392E-05 -1.563574E-02 -3.149768E-05 -1.617709E-02 -3.434601E-05 -1.421795E-02 -2.693384E-05 -9.544761E-03 -1.420198E-05 -1.920816E-02 -4.315771E-05 -2.104112E-02 -6.953280E-05 -8.386327E-03 -1.191493E-05 -8.722802E-03 -1.610480E-05 -1.338316E-02 -2.795188E-05 -1.139681E-02 -2.426741E-05 -1.023902E-02 -1.793399E-05 -1.031055E-02 -1.727351E-05 -7.202274E-03 -8.248223E-06 -5.930048E-03 -5.651618E-06 -7.496873E-03 -1.049976E-05 -1.159952E-02 -1.637372E-05 -1.149358E-02 -2.129416E-05 -1.751957E-02 -3.876503E-05 -1.304134E-02 -2.268145E-05 -6.423719E-03 -1.030715E-05 -1.229584E-02 -1.921824E-05 -1.597047E-02 -2.887519E-05 -7.728575E-03 -8.694018E-06 -1.102992E-02 -1.663289E-05 -9.958745E-03 -1.518594E-05 -9.321102E-03 -1.260439E-05 -9.226395E-03 -1.473519E-05 -1.071198E-02 -1.716096E-05 -1.209027E-02 -2.012204E-05 -1.299345E-02 -2.535948E-05 -1.377877E-02 -2.712220E-05 -1.505661E-02 -3.492299E-05 -1.496534E-02 -3.555281E-05 -1.674256E-02 -3.935472E-05 -9.494973E-03 -1.562850E-05 -9.878116E-03 -1.686202E-05 -1.512689E-02 -2.756935E-05 -2.271723E-02 -7.190876E-05 -1.204699E-02 -2.702913E-05 -1.339825E-02 -2.817955E-05 -1.767135E-02 -4.942115E-05 -1.480386E-02 -6.975977E-05 -1.636386E-02 -3.439959E-05 -1.220265E-02 -2.153744E-05 -1.347844E-02 -2.910724E-05 -1.359228E-02 -3.101663E-05 -9.728835E-03 -1.507092E-05 -1.528103E-02 -3.443965E-05 -2.096063E-02 -6.754181E-05 -2.094364E-02 -6.023053E-05 -1.266226E-02 -2.412451E-05 -2.535680E-02 -7.344640E-05 -2.598994E-02 -9.953050E-05 -2.060213E-02 -7.034543E-05 -1.788607E-02 -3.674071E-05 -1.148154E-02 -1.909188E-05 -2.245671E-02 -6.003250E-05 -2.763268E-02 -9.551846E-05 -2.267967E-02 -6.032826E-05 -1.916473E-02 -4.604843E-05 -1.294451E-02 -1.970613E-05 -1.934618E-02 -4.918252E-05 -2.293529E-02 -7.197108E-05 -1.496065E-02 -2.444659E-05 -2.226558E-02 -5.644756E-05 -1.948569E-02 -4.450894E-05 -2.081785E-02 -5.246988E-05 -1.522522E-02 -3.455714E-05 -2.719218E-02 -8.650624E-05 -3.018717E-02 -1.279696E-04 -1.871704E-02 -6.935361E-05 -2.067645E-02 -5.026583E-05 -2.040699E-02 -4.591994E-05 -2.662203E-02 -8.511265E-05 -1.360877E-02 -2.453053E-05 -2.047026E-02 -5.992658E-05 -2.099358E-02 -6.877382E-05 -1.757234E-02 -4.380517E-05 -2.023155E-02 -5.570688E-05 -1.620245E-02 -3.127153E-05 -2.718760E-02 -1.061924E-04 -1.747980E-02 -4.190787E-05 -1.548489E-02 -3.376610E-05 -2.645660E-02 -9.074843E-05 -1.786629E-02 -4.215277E-05 -1.474131E-02 -2.581817E-05 -1.388717E-02 -2.989188E-05 -2.441704E-02 -7.084896E-05 -2.104368E-02 -5.681421E-05 -2.433800E-02 -7.895153E-05 -1.597437E-02 -3.483039E-05 -1.412656E-02 -2.566245E-05 -1.687717E-02 -3.771639E-05 -2.190482E-02 -8.253917E-05 -1.386107E-02 -3.378072E-05 -1.256494E-02 -2.033235E-05 -1.458675E-02 -2.616735E-05 -1.633493E-02 -3.166210E-05 -2.288089E-02 -6.714973E-05 -2.398060E-02 -7.693950E-05 -1.834264E-02 -4.444484E-05 -2.286357E-02 -5.661515E-05 -1.648013E-02 -4.331697E-05 -1.300614E-02 -2.342646E-05 -1.426626E-02 -2.966175E-05 -2.218616E-02 -7.311082E-05 -1.244578E-02 -2.216996E-05 -2.287835E-02 -6.924841E-05 -1.752439E-02 -4.165478E-05 -2.103286E-02 -5.842027E-05 -1.533084E-02 -3.870604E-05 -1.829172E-02 -5.193773E-05 -1.756896E-02 -4.997547E-05 -2.219401E-02 -5.804587E-05 -1.083994E-02 -1.562087E-05 -1.144466E-02 -2.086121E-05 -1.743653E-02 -3.615221E-05 -9.676259E-03 -1.226729E-05 -1.296315E-02 -2.293653E-05 -1.353945E-02 -3.069949E-05 -7.066700E-03 -7.777520E-06 -8.915397E-03 -1.286545E-05 -1.337770E-02 -2.968336E-05 -1.210357E-02 -2.373080E-05 -1.021357E-02 -1.742336E-05 -1.615799E-02 -4.003564E-05 -9.037428E-03 -1.958275E-05 -3.183370E-03 -2.393571E-06 -8.515537E-03 -1.434148E-05 -8.594763E-03 -1.144929E-05 -6.557583E-03 -6.809462E-06 -9.324712E-03 -1.284671E-05 -6.075741E-03 -8.217443E-06 -7.732948E-03 -1.254995E-05 -9.364155E-03 -1.391050E-05 -1.033625E-02 -1.710810E-05 -1.643522E-02 -5.092731E-05 -1.913240E-02 -5.188056E-05 -4.699889E-03 -4.253342E-06 -3.667145E-03 -3.051204E-06 -6.956946E-03 -7.413590E-06 -9.143993E-03 -1.510114E-05 -5.630486E-03 -7.481628E-06 -8.090881E-03 -1.407277E-05 -6.451248E-03 -9.019754E-06 -8.432893E-03 -1.236291E-05 -7.535742E-03 -9.713557E-06 -4.055813E-03 -2.764876E-06 -6.361136E-03 -7.935395E-06 -7.066013E-03 -9.687252E-06 -8.347614E-03 -1.322030E-05 -5.583607E-03 -6.318991E-06 -9.734817E-03 -1.915868E-05 -9.117770E-03 -1.299951E-05 -5.466583E-03 -5.814486E-06 -6.377661E-03 -5.982386E-06 -1.066356E-02 -1.915872E-05 -1.205945E-02 -1.894758E-05 -9.934821E-03 -1.678379E-05 -1.138156E-02 -2.321476E-05 -1.063324E-02 -1.739239E-05 -8.377153E-03 -1.072894E-05 -1.600249E-02 -3.173041E-05 -1.442818E-02 -3.650694E-05 -1.643458E-02 -2.935759E-05 -1.937762E-02 -5.539763E-05 -8.400592E-03 -1.059830E-05 -1.497133E-02 -3.316378E-05 -1.469275E-02 -3.001660E-05 -1.502203E-02 -2.852008E-05 -9.750216E-03 -1.395013E-05 -1.497859E-02 -2.421550E-05 -2.111839E-02 -6.023324E-05 -1.358782E-02 -2.820256E-05 -1.250078E-02 -2.294909E-05 -1.058650E-02 -1.688760E-05 -1.488385E-02 -2.666627E-05 -1.927781E-02 -5.429379E-05 -1.208022E-02 -2.250992E-05 -1.497153E-02 -2.676884E-05 -1.244590E-02 -2.756211E-05 -9.989045E-03 -1.303571E-05 -8.894574E-03 -1.174122E-05 -1.938283E-02 -4.239857E-05 -1.804394E-02 -3.515511E-05 -1.337739E-02 -2.482975E-05 -1.487943E-02 -3.347708E-05 -1.110033E-02 -2.136237E-05 -1.333452E-02 -2.308294E-05 -1.635319E-02 -3.229248E-05 -8.693802E-03 -1.542452E-05 -1.031414E-02 -2.112585E-05 -1.838966E-02 -4.539908E-05 -2.354073E-02 -7.698141E-05 -1.080321E-02 -1.831625E-05 -2.112481E-02 -6.253585E-05 -2.050287E-02 -5.388829E-05 -1.779381E-02 -3.937477E-05 -1.477468E-02 -2.411115E-05 -1.293051E-02 -2.250585E-05 -1.609738E-02 -3.396791E-05 -2.681377E-02 -8.749523E-05 -1.670204E-02 -4.359534E-05 -2.206205E-02 -6.377754E-05 -1.873639E-02 -5.703093E-05 -1.889950E-02 -5.052682E-05 -1.824219E-02 -6.072963E-05 -9.270805E-03 -9.959479E-06 -1.684688E-02 -3.370730E-05 -1.521705E-02 -2.961236E-05 -1.701708E-02 -4.241891E-05 -1.338529E-02 -2.440114E-05 -2.037312E-02 -5.462555E-05 -1.229143E-02 -1.878363E-05 -1.429618E-02 -3.043241E-05 -1.452304E-02 -3.335612E-05 -1.081202E-02 -1.892516E-05 -1.166461E-02 -2.163556E-05 -9.237578E-03 -1.212749E-05 -1.315603E-02 -2.637907E-05 -1.701531E-02 -4.938074E-05 -1.071566E-02 -1.400306E-05 -6.882737E-03 -1.095801E-05 -9.693556E-03 -1.516152E-05 -1.473076E-02 -4.380304E-05 -1.172802E-02 -2.661846E-05 -1.044100E-02 -1.616180E-05 -1.321485E-02 -2.517015E-05 -1.034434E-02 -2.118831E-05 -1.604723E-02 -3.547683E-05 -1.309313E-02 -2.716364E-05 -1.410482E-02 -2.701123E-05 -1.932682E-02 -4.094043E-05 -1.570824E-02 -3.228547E-05 -1.071500E-02 -2.525484E-05 -9.230951E-03 -1.398163E-05 -1.889558E-02 -4.381257E-05 -1.401925E-02 -2.692560E-05 -9.335801E-03 -1.384367E-05 -1.635939E-02 -3.876884E-05 -9.920998E-03 -1.367019E-05 -1.029500E-02 -1.637783E-05 -8.294680E-03 -1.093734E-05 -1.266183E-02 -2.587245E-05 -1.407777E-02 -2.856187E-05 -1.854542E-02 -4.825382E-05 -5.748894E-03 -8.790611E-06 -6.718806E-03 -1.183924E-05 -8.894787E-03 -1.306300E-05 -9.405707E-03 -1.288762E-05 -8.792692E-03 -1.122248E-05 -1.103181E-02 -1.580934E-05 -2.659005E-03 -1.548241E-06 -7.016434E-03 -7.516622E-06 -7.803753E-03 -1.013698E-05 -5.086094E-03 -7.063507E-06 -6.912737E-03 -6.574511E-06 -6.396947E-03 -6.066584E-06 -3.808735E-03 -2.749937E-06 -1.756683E-03 -8.375727E-07 -5.851836E-03 -1.028488E-05 -4.947448E-03 -5.127717E-06 -2.630989E-03 -1.428143E-06 -2.910935E-03 -1.681876E-06 -1.838842E-03 -8.992911E-07 -1.293684E-03 -6.685764E-07 -3.339457E-03 -3.028553E-06 -4.580626E-03 -4.624997E-06 -8.307235E-03 -1.674431E-05 -4.486573E-03 -3.705825E-06 -1.144529E-02 -2.352240E-05 -4.880878E-03 -3.852879E-06 -1.005985E-02 -1.391072E-05 -5.741160E-03 -7.136284E-06 -3.844734E-03 -3.016161E-06 -4.765015E-03 -4.225164E-06 -9.903891E-03 -1.243668E-05 -6.186072E-03 -7.288467E-06 -5.025861E-03 -4.672189E-06 -9.762213E-03 -1.357133E-05 -7.207433E-03 -1.732068E-05 -7.775127E-03 -1.904290E-05 -8.549835E-03 -9.367660E-06 -7.175698E-03 -7.187069E-06 -9.296385E-03 -1.322855E-05 -1.130874E-02 -1.956313E-05 -6.989995E-03 -7.487382E-06 -9.825026E-03 -1.567524E-05 -8.647918E-03 -1.652352E-05 -1.087355E-02 -1.533263E-05 -8.616514E-03 -1.142788E-05 -7.352790E-03 -1.365573E-05 -1.138586E-02 -2.265717E-05 -8.641472E-03 -1.322858E-05 -1.041352E-02 -1.315105E-05 -7.829927E-03 -1.004051E-05 -1.425328E-02 -2.580893E-05 -8.892806E-03 -1.300336E-05 -7.287566E-03 -1.259700E-05 -1.443032E-02 -3.334244E-05 -1.017849E-02 -1.483161E-05 -1.467996E-02 -3.045887E-05 -5.224154E-03 -5.244222E-06 -9.725108E-03 -1.589220E-05 -7.665836E-03 -1.588331E-05 -1.010342E-02 -1.784078E-05 -9.419018E-03 -1.369817E-05 -8.670373E-03 -1.032775E-05 -1.138937E-02 -1.811885E-05 -1.798735E-02 -4.793175E-05 -1.065784E-02 -1.799829E-05 -1.308082E-02 -2.484335E-05 -1.240918E-02 -2.570768E-05 -1.745339E-02 -5.770116E-05 -9.113925E-03 -1.349884E-05 -1.402932E-02 -2.482802E-05 -1.888352E-02 -4.563824E-05 -1.225796E-02 -2.638006E-05 -6.431220E-03 -8.855282E-06 -4.770081E-03 -4.250052E-06 -1.089652E-02 -1.611332E-05 -8.433574E-03 -1.309921E-05 -8.737584E-03 -1.411637E-05 -1.265065E-02 -2.849634E-05 -6.191713E-03 -9.561153E-06 -1.006364E-02 -1.497675E-05 -5.975885E-03 -7.218506E-06 -8.088755E-03 -1.058200E-05 -1.148407E-02 -1.800324E-05 -8.621138E-03 -1.060417E-05 -6.466931E-03 -5.559033E-06 -5.921299E-03 -7.337435E-06 -1.106199E-02 -1.985630E-05 -1.358816E-02 -2.266472E-05 -9.249095E-03 -1.326487E-05 -1.231097E-02 -2.413051E-05 -1.262777E-02 -2.135896E-05 -1.340058E-02 -2.838546E-05 -5.482441E-03 -5.699830E-06 -9.399780E-03 -1.217503E-05 -1.326355E-02 -3.084524E-05 -1.091665E-02 -1.571125E-05 -9.066945E-03 -1.253097E-05 -1.007715E-02 -1.495997E-05 -1.420187E-02 -3.247816E-05 -8.867918E-03 -1.783720E-05 -1.035797E-02 -1.618714E-05 -1.051439E-02 -1.905852E-05 -9.663711E-03 -1.733227E-05 -6.083326E-03 -8.227985E-06 -8.498983E-03 -1.203506E-05 -6.305066E-03 -7.288313E-06 -7.022938E-03 -1.842350E-05 -5.300040E-03 -6.674218E-06 -1.192288E-02 -2.251146E-05 -1.268268E-02 -2.376464E-05 -1.105252E-02 -1.907200E-05 -1.141409E-02 -2.122429E-05 -6.863367E-03 -9.680619E-06 -9.454583E-03 -1.410784E-05 -6.678539E-03 -7.419299E-06 -5.435679E-03 -5.223001E-06 -5.839983E-03 -7.472675E-06 -4.126792E-03 -4.743950E-06 -6.770400E-03 -9.680068E-06 -6.192936E-03 -7.071171E-06 -5.115501E-03 -7.222293E-06 -9.291347E-03 -2.204886E-05 -9.263101E-03 -2.093887E-05 -4.871234E-03 -4.945479E-06 -7.362790E-03 -1.225470E-05 -4.662289E-03 -6.432463E-06 -3.023591E-03 -2.882907E-06 -3.141104E-03 -2.308920E-06 -4.357737E-03 -7.258711E-06 -6.160322E-03 -8.985831E-06 -2.171491E-03 -1.749207E-06 -6.561643E-03 -8.803272E-06 -7.571781E-03 -1.203184E-05 -7.440169E-03 -1.110403E-05 -5.648646E-03 -5.494924E-06 -5.357281E-03 -6.204706E-06 -1.090071E-02 -2.058466E-05 -8.383241E-03 -1.206302E-05 -5.676127E-03 -7.724753E-06 -7.425364E-03 -1.054573E-05 -5.486371E-03 -8.048247E-06 -8.635014E-03 -1.394706E-05 -9.041685E-03 -1.478222E-05 -8.430211E-03 -1.309974E-05 -9.019663E-03 -1.451788E-05 -1.031929E-02 -1.746481E-05 -8.473885E-03 -1.174896E-05 -7.773439E-03 -1.036634E-05 -6.249343E-03 -6.064341E-06 -7.095661E-03 -1.120516E-05 -8.128270E-03 -7.992625E-06 -8.497285E-03 -1.303653E-05 -9.777637E-03 -1.235924E-05 -9.741495E-03 -1.557086E-05 -7.393292E-03 -1.285346E-05 -9.196281E-03 -1.458607E-05 -8.847877E-03 -1.117609E-05 -9.688267E-03 -1.592845E-05 -8.380207E-03 -9.989138E-06 -6.280642E-03 -9.051451E-06 -5.915732E-03 -6.877346E-06 -7.178298E-03 -7.637799E-06 -1.393362E-02 -3.213127E-05 -1.124851E-02 -1.946971E-05 -7.778231E-03 -8.216708E-06 -1.100977E-02 -1.616695E-05 -9.914677E-03 -1.920656E-05 -1.140639E-02 -1.803697E-05 -9.289048E-03 -2.453446E-05 -1.333524E-02 -2.981179E-05 -1.511863E-02 -3.813471E-05 -1.309031E-02 -3.606896E-05 -9.646063E-03 -1.776204E-05 -1.508239E-02 -3.051447E-05 -1.260645E-02 -2.587693E-05 -1.462482E-02 -3.353264E-05 -1.521268E-02 -3.209409E-05 -1.468435E-02 -2.541208E-05 -1.071159E-02 -1.974334E-05 -2.153100E-02 -5.966820E-05 -1.732644E-02 -4.011287E-05 -1.667843E-02 -3.822452E-05 -1.463217E-02 -3.010523E-05 -1.656645E-02 -5.475637E-05 -1.067989E-02 -1.830023E-05 -8.824628E-03 -1.045690E-05 -2.286417E-02 -6.960907E-05 -1.839696E-02 -4.509087E-05 -1.925060E-02 -3.941153E-05 -2.258451E-02 -6.336171E-05 -1.439116E-02 -3.414902E-05 -1.155750E-02 -1.864626E-05 -1.259396E-02 -2.399348E-05 -1.558812E-02 -4.045055E-05 -1.722120E-02 -4.143410E-05 -1.762002E-02 -3.632759E-05 -7.874101E-03 -8.254184E-06 -1.768575E-02 -3.461006E-05 -1.146683E-02 -1.794910E-05 -9.688500E-03 -1.346720E-05 -2.241492E-02 -6.339022E-05 -1.934312E-02 -4.482690E-05 -1.794148E-02 -3.870190E-05 -1.579372E-02 -3.004235E-05 -1.087259E-02 -1.529755E-05 -1.257568E-02 -2.258086E-05 -1.388017E-02 -3.225403E-05 -1.427934E-02 -2.832685E-05 -1.576751E-02 -4.828020E-05 -9.333555E-03 -1.323683E-05 -1.295220E-02 -2.221784E-05 -1.292944E-02 -2.650558E-05 -1.321910E-02 -2.608639E-05 -1.867794E-02 -5.342650E-05 -1.187778E-02 -2.004699E-05 -1.570348E-02 -3.839194E-05 -7.349983E-03 -1.280817E-05 -1.048335E-02 -1.591520E-05 -1.281254E-02 -2.311657E-05 -9.441415E-03 -1.884897E-05 -7.817420E-03 -1.075829E-05 -7.050894E-03 -8.992796E-06 -1.439696E-02 -2.646431E-05 -9.666048E-03 -1.691859E-05 -1.384058E-02 -2.171361E-05 -1.483968E-02 -2.360714E-05 -6.696329E-03 -8.201920E-06 -1.461109E-02 -3.484250E-05 -1.152968E-02 -2.487151E-05 -1.235571E-02 -1.863282E-05 -1.071843E-02 -2.007060E-05 -1.192423E-02 -2.405436E-05 -5.175335E-03 -5.686680E-06 -6.167308E-03 -6.085033E-06 -6.512706E-03 -7.423219E-06 -1.084502E-02 -2.042988E-05 -1.426237E-02 -3.199412E-05 -9.833550E-03 -1.401763E-05 -1.066371E-02 -2.034673E-05 -1.088205E-02 -1.432050E-05 -1.322884E-03 -6.133009E-07 -3.131323E-03 -2.085873E-06 -7.577039E-03 -1.169415E-05 -3.697020E-03 -2.265535E-06 -8.463691E-04 -7.163407E-07 -4.936343E-03 -4.470816E-06 -2.785210E-03 -2.057770E-06 -4.647148E-03 -1.334765E-05 -4.739139E-03 -4.910528E-06 -2.212737E-03 -9.823784E-07 -3.179311E-03 -2.106597E-06 -5.139877E-03 -6.883127E-06 -6.756613E-03 -1.192459E-05 -9.019603E-03 -1.454516E-05 -7.170921E-03 -1.179166E-05 -1.332204E-02 -2.272315E-05 -1.045555E-02 -1.579302E-05 -1.471762E-02 -2.957336E-05 -6.940743E-03 -6.167781E-06 -9.285093E-03 -1.234474E-05 -5.274768E-03 -9.733764E-06 -9.363326E-03 -1.064273E-05 -1.124025E-02 -1.812349E-05 -1.242897E-02 -2.335802E-05 -1.381362E-02 -3.426304E-05 -9.040314E-03 -1.768352E-05 -8.800291E-03 -1.233677E-05 -6.219799E-03 -6.238154E-06 -9.482593E-03 -1.409322E-05 -1.380779E-02 -2.529043E-05 -1.200427E-02 -2.953271E-05 -1.419132E-02 -3.098266E-05 -1.353906E-02 -2.607804E-05 -1.431241E-02 -2.799048E-05 -1.223915E-02 -2.748804E-05 -1.852083E-02 -5.311250E-05 -1.887810E-02 -5.774443E-05 -1.277722E-02 -2.643979E-05 -8.077414E-03 -1.128725E-05 -1.339142E-02 -2.294610E-05 -1.301300E-02 -2.073634E-05 -1.397830E-02 -3.029223E-05 -1.892762E-02 -4.920518E-05 -1.535394E-02 -4.149745E-05 -1.298011E-02 -2.187906E-05 -1.905806E-02 -4.036737E-05 -1.706919E-02 -3.990119E-05 -1.435454E-02 -2.543584E-05 -1.937779E-02 -5.201021E-05 -2.323117E-02 -6.155812E-05 -1.994685E-02 -5.377319E-05 -1.320573E-02 -2.175679E-05 -1.591133E-02 -3.535892E-05 -2.108852E-02 -6.407539E-05 -1.839423E-02 -4.410414E-05 -2.011863E-02 -4.580140E-05 -1.883453E-02 -4.495207E-05 -2.134485E-02 -5.276746E-05 -1.725640E-02 -4.465566E-05 -2.590341E-02 -8.438375E-05 -2.198626E-02 -5.942456E-05 -1.721832E-02 -3.710932E-05 -1.867389E-02 -4.165207E-05 -2.535786E-02 -8.231711E-05 -2.435788E-02 -7.524302E-05 -2.417986E-02 -7.690249E-05 -3.527942E-02 -1.503529E-04 -2.545610E-02 -7.903560E-05 -2.898461E-02 -1.038511E-04 -2.650130E-02 -7.765544E-05 -3.220034E-02 -1.339104E-04 -2.615617E-02 -8.054223E-05 -1.575566E-02 -3.464485E-05 -1.721242E-02 -3.933144E-05 -2.529147E-02 -7.224719E-05 -2.161550E-02 -5.729706E-05 -3.254276E-02 -1.237101E-04 -2.601524E-02 -7.880812E-05 -2.030934E-02 -5.449343E-05 -2.529817E-02 -7.746587E-05 -2.459558E-02 -7.049178E-05 -2.743013E-02 -1.051976E-04 -2.818835E-02 -9.687328E-05 -3.082240E-02 -1.199245E-04 -1.534318E-02 -3.153431E-05 -1.798131E-02 -4.183178E-05 -1.329898E-02 -2.483181E-05 -1.519124E-02 -3.090643E-05 -2.253493E-02 -6.110325E-05 -1.932903E-02 -5.403216E-05 -2.343651E-02 -6.986521E-05 -1.851194E-02 -4.830851E-05 -2.854734E-02 -1.149020E-04 -3.554508E-02 -1.594168E-04 -2.420896E-02 -8.063001E-05 -2.438815E-02 -6.949914E-05 -1.424242E-02 -3.042725E-05 -1.883095E-02 -4.453941E-05 -1.803807E-02 -5.353218E-05 -1.472433E-02 -2.708169E-05 -2.645673E-02 -1.277462E-04 -1.659156E-02 -4.460790E-05 -1.884914E-02 -5.255122E-05 -1.323181E-02 -3.012118E-05 -2.078655E-02 -7.250952E-05 -1.929529E-02 -4.896056E-05 -1.365072E-02 -2.296122E-05 -1.548320E-02 -3.361702E-05 -1.963984E-02 -5.182163E-05 -1.938728E-02 -4.745111E-05 -1.852813E-02 -4.964750E-05 -1.254203E-02 -2.416303E-05 -1.598636E-02 -3.867130E-05 -1.557994E-02 -3.560554E-05 -8.834017E-03 -1.069678E-05 -9.484032E-03 -1.565529E-05 -1.303143E-02 -2.234676E-05 -2.200867E-02 -5.927220E-05 -1.598709E-02 -2.961814E-05 -1.402800E-02 -2.779038E-05 -4.378255E-03 -4.055161E-06 -6.657558E-03 -8.689849E-06 -5.742424E-03 -6.994090E-06 -1.690917E-03 -9.618234E-07 -6.422205E-03 -6.220083E-06 -1.098008E-02 -1.552449E-05 -4.852648E-03 -6.005700E-06 -4.131316E-03 -2.940200E-06 -5.377384E-03 -9.958808E-06 -7.493944E-03 -1.136740E-05 -5.679683E-03 -5.334788E-06 -9.041948E-03 -1.233892E-05 -1.621598E-02 -3.896922E-05 -9.707536E-03 -1.580953E-05 -9.796285E-03 -1.170913E-05 -1.038632E-02 -1.400251E-05 -1.854616E-02 -3.938948E-05 -1.637195E-02 -3.459637E-05 -1.090541E-02 -1.920540E-05 -1.554833E-02 -3.590212E-05 -4.272073E-03 -3.682734E-06 -9.074265E-03 -9.996509E-06 -1.177873E-02 -2.562780E-05 -1.154733E-02 -1.708677E-05 -2.122244E-02 -5.095551E-05 -1.703089E-02 -4.517001E-05 -1.005035E-02 -1.266093E-05 -1.780384E-02 -3.747413E-05 -1.376515E-02 -3.253018E-05 -1.530894E-02 -2.892052E-05 -2.606504E-02 -7.859853E-05 -2.255541E-02 -6.286365E-05 -1.918774E-02 -4.676979E-05 -2.519231E-02 -7.640422E-05 -1.664088E-02 -4.401309E-05 -1.584636E-02 -3.452858E-05 -2.616309E-02 -8.630269E-05 -2.255699E-02 -5.940173E-05 -2.236967E-02 -6.994996E-05 -2.248979E-02 -5.377855E-05 -2.341480E-02 -6.489990E-05 -2.206893E-02 -5.500117E-05 -3.339387E-02 -1.332827E-04 -2.791990E-02 -9.540192E-05 -2.717893E-02 -9.827563E-05 -2.822648E-02 -9.247065E-05 -2.172258E-02 -6.122060E-05 -2.728508E-02 -9.267319E-05 -2.826253E-02 -1.031269E-04 -2.187117E-02 -5.283388E-05 -2.020422E-02 -6.258085E-05 -2.760119E-02 -8.854689E-05 -2.549801E-02 -7.868954E-05 -3.220341E-02 -1.492987E-04 -3.173533E-02 -1.221411E-04 -3.585220E-02 -1.462868E-04 -2.903224E-02 -9.451319E-05 -3.829574E-02 -1.873248E-04 -3.086935E-02 -1.256218E-04 -3.176725E-02 -1.374912E-04 -2.621060E-02 -7.855573E-05 -3.291329E-02 -1.248943E-04 -2.777869E-02 -9.696220E-05 -2.695184E-02 -8.169195E-05 -3.242055E-02 -1.203522E-04 -3.779282E-02 -1.712919E-04 -3.692252E-02 -1.722327E-04 -2.748845E-02 -9.639756E-05 -3.175151E-02 -1.162038E-04 -3.286221E-02 -1.232662E-04 -3.712727E-02 -1.594838E-04 -4.431598E-02 -2.588766E-04 -2.816960E-02 -9.502113E-05 -2.976286E-02 -1.142861E-04 -2.180841E-02 -6.585707E-05 -2.719208E-02 -8.542344E-05 -3.801560E-02 -1.668348E-04 -3.413937E-02 -1.429112E-04 -3.175514E-02 -1.235214E-04 -4.203652E-02 -2.192502E-04 -4.877746E-02 -2.744258E-04 -5.692679E-02 -3.692246E-04 -4.818489E-02 -2.687441E-04 -5.535578E-02 -3.290118E-04 -3.156612E-02 -1.123567E-04 -2.679829E-02 -7.787807E-05 -2.954264E-02 -1.270123E-04 -2.476226E-02 -8.030343E-05 -3.814973E-02 -1.578272E-04 -4.016143E-02 -2.884730E-04 -4.229195E-02 -2.061337E-04 -2.947005E-02 -1.041482E-04 -3.357567E-02 -1.360735E-04 -4.664871E-02 -2.552612E-04 -4.112621E-02 -2.176987E-04 -3.595856E-02 -1.956542E-04 -1.913431E-02 -5.063581E-05 -2.330523E-02 -6.230393E-05 -2.045993E-02 -5.592751E-05 -2.473756E-02 -7.625570E-05 -3.674309E-02 -1.672365E-04 -3.204595E-02 -1.157121E-04 -2.629494E-02 -8.519728E-05 -3.002442E-02 -1.205283E-04 -3.236353E-02 -1.309526E-04 -3.239381E-02 -1.479481E-04 -2.659107E-02 -1.086535E-04 -3.565570E-02 -1.622239E-04 -1.031682E-02 -1.492807E-05 -1.580618E-02 -2.893579E-05 -1.666675E-02 -4.393186E-05 -2.024456E-02 -4.734254E-05 -1.651767E-02 -4.977631E-05 -1.887559E-02 -4.409518E-05 -1.508773E-02 -4.644738E-05 -1.388725E-02 -2.554677E-05 -2.369485E-02 -7.387938E-05 -1.868167E-02 -4.664973E-05 -1.516685E-02 -3.321346E-05 -1.963316E-02 -4.662537E-05 -7.052241E-03 -1.080582E-05 -1.204334E-02 -2.356212E-05 -7.618798E-03 -1.308303E-05 -1.218334E-02 -2.230559E-05 -1.192185E-02 -2.127660E-05 -1.044557E-02 -1.520178E-05 -4.348002E-03 -4.867274E-06 -9.181379E-03 -1.456359E-05 -8.481267E-03 -1.556887E-05 -5.867984E-03 -5.323949E-06 -1.523208E-02 -3.002171E-05 -8.902081E-03 -1.728737E-05 -1.212351E-02 -2.267329E-05 -1.158769E-02 -1.826918E-05 -5.557893E-03 -6.873951E-06 -1.030077E-02 -1.814127E-05 -1.216476E-02 -2.168097E-05 -1.280341E-02 -2.462766E-05 -7.553244E-03 -7.549456E-06 -9.275611E-03 -9.489655E-06 -7.606773E-03 -8.958010E-06 -9.160622E-03 -1.424787E-05 -1.133109E-02 -1.611610E-05 -7.409008E-03 -1.208115E-05 -2.084102E-02 -5.449907E-05 -2.465743E-02 -1.037751E-04 -2.310657E-02 -6.292924E-05 -2.801769E-02 -8.512445E-05 -2.049117E-02 -6.630728E-05 -1.395829E-02 -3.118581E-05 -2.735901E-02 -9.218170E-05 -2.177632E-02 -5.817883E-05 -1.932991E-02 -6.388604E-05 -2.326591E-02 -8.330336E-05 -2.546379E-02 -9.605909E-05 -1.830716E-02 -5.090147E-05 -3.960142E-02 -1.859660E-04 -2.348343E-02 -6.600054E-05 -2.909393E-02 -1.040812E-04 -3.586926E-02 -1.451039E-04 -2.269127E-02 -6.430762E-05 -2.691696E-02 -8.449728E-05 -4.229624E-02 -2.362055E-04 -4.471419E-02 -2.231402E-04 -3.407449E-02 -1.370223E-04 -3.586700E-02 -1.483496E-04 -3.803403E-02 -1.652852E-04 -3.820565E-02 -1.619415E-04 -2.921534E-02 -1.128913E-04 -4.098638E-02 -1.981151E-04 -3.611801E-02 -1.596274E-04 -4.036543E-02 -1.787815E-04 -3.018584E-02 -1.080246E-04 -4.026732E-02 -1.932383E-04 -4.637639E-02 -3.369042E-04 -4.901208E-02 -2.912517E-04 -5.221514E-02 -2.936532E-04 -5.298382E-02 -3.167761E-04 -4.141488E-02 -1.913012E-04 -5.078383E-02 -2.957356E-04 -3.104830E-02 -1.114729E-04 -4.570836E-02 -2.306958E-04 -2.697314E-02 -8.551540E-05 -3.590745E-02 -1.369825E-04 -4.416767E-02 -2.348955E-04 -3.878026E-02 -1.683331E-04 -3.635274E-02 -1.469492E-04 -5.095760E-02 -3.033151E-04 -5.419363E-02 -3.231656E-04 -6.111133E-02 -4.092896E-04 -5.080100E-02 -2.813520E-04 -6.152776E-02 -4.073105E-04 -3.751766E-02 -1.468483E-04 -3.277827E-02 -1.186335E-04 -3.710633E-02 -1.751177E-04 -5.323020E-02 -3.420073E-04 -3.201431E-02 -1.550803E-04 -5.882138E-02 -4.307388E-04 -4.562728E-02 -2.741025E-04 -4.436946E-02 -2.582142E-04 -4.128151E-02 -1.889531E-04 -5.761773E-02 -4.256437E-04 -4.986009E-02 -2.652036E-04 -5.753376E-02 -3.668636E-04 -3.464308E-02 -1.308318E-04 -3.597366E-02 -1.880711E-04 -3.783475E-02 -1.677422E-04 -4.507477E-02 -2.257889E-04 -3.512550E-02 -1.623708E-04 -4.440361E-02 -2.237977E-04 -4.763862E-02 -2.507592E-04 -4.245582E-02 -2.037194E-04 -3.865696E-02 -1.809637E-04 -4.216341E-02 -1.962900E-04 -5.099828E-02 -2.952246E-04 -5.108812E-02 -2.920996E-04 -2.655713E-02 -8.104377E-05 -3.189652E-02 -1.226231E-04 -3.163248E-02 -1.587616E-04 -2.509589E-02 -9.570491E-05 -4.297892E-02 -2.277676E-04 -3.642828E-02 -1.647058E-04 -3.007066E-02 -1.231652E-04 -3.132243E-02 -1.486680E-04 -3.188231E-02 -1.196363E-04 -4.753178E-02 -2.731076E-04 -2.908004E-02 -1.035304E-04 -2.727262E-02 -9.600226E-05 -1.855747E-02 -5.837677E-05 -2.435306E-02 -7.097335E-05 -2.204917E-02 -6.488149E-05 -2.268670E-02 -6.926881E-05 -2.470896E-02 -8.507493E-05 -2.736268E-02 -9.366711E-05 -1.453619E-02 -2.821293E-05 -1.989468E-02 -5.291966E-05 -2.569258E-02 -8.578609E-05 -2.226465E-02 -6.565828E-05 -2.252517E-02 -7.081452E-05 -1.855256E-02 -4.738776E-05 -1.203698E-02 -2.579555E-05 -1.218962E-02 -2.270495E-05 -1.171765E-02 -2.354905E-05 -8.779187E-03 -1.212571E-05 -1.425888E-02 -2.553189E-05 -1.169957E-02 -2.644501E-05 -1.052860E-02 -1.949914E-05 -5.907471E-03 -5.068540E-06 -1.275403E-02 -2.624760E-05 -1.148436E-02 -1.794213E-05 -1.018673E-02 -1.501340E-05 -1.783660E-02 -4.785080E-05 -1.016197E-02 -2.293166E-05 -8.941927E-03 -1.192991E-05 -6.602581E-03 -1.272161E-05 -1.174062E-02 -2.147543E-05 -1.526300E-02 -3.260204E-05 -1.686817E-02 -4.102027E-05 -1.467939E-02 -2.968059E-05 -1.601430E-02 -4.562808E-05 -9.165391E-03 -2.688624E-05 -9.663957E-03 -1.372277E-05 -1.287992E-02 -2.365195E-05 -9.579531E-03 -1.375180E-05 -2.469585E-02 -8.049487E-05 -2.845287E-02 -1.112732E-04 -1.767051E-02 -4.658157E-05 -2.485448E-02 -9.432058E-05 -1.238872E-02 -2.846828E-05 -1.889711E-02 -4.468197E-05 -3.099403E-02 -1.059405E-04 -2.259916E-02 -7.077471E-05 -2.695314E-02 -9.575430E-05 -2.211342E-02 -5.413822E-05 -2.224612E-02 -7.249655E-05 -1.773084E-02 -4.592694E-05 -3.194210E-02 -1.064003E-04 -2.971760E-02 -9.736956E-05 -3.499652E-02 -1.826230E-04 -4.850239E-02 -2.738683E-04 -3.305773E-02 -1.168139E-04 -3.506155E-02 -1.587282E-04 -3.676758E-02 -1.484870E-04 -4.676291E-02 -2.525569E-04 -3.590431E-02 -1.478484E-04 -3.835973E-02 -1.689943E-04 -4.592471E-02 -2.353405E-04 -4.213441E-02 -2.024568E-04 -4.148586E-02 -1.780338E-04 -4.501161E-02 -2.189422E-04 -4.546973E-02 -2.294416E-04 -5.445339E-02 -3.211478E-04 -4.091696E-02 -1.919828E-04 -4.547941E-02 -2.523329E-04 -5.386399E-02 -3.097517E-04 -5.805028E-02 -3.886410E-04 -4.323647E-02 -2.210891E-04 -5.481511E-02 -3.380385E-04 -5.588950E-02 -3.728116E-04 -4.521068E-02 -2.655202E-04 -4.323965E-02 -1.976109E-04 -4.853174E-02 -2.734839E-04 -5.925107E-02 -4.092840E-04 -6.126965E-02 -3.889545E-04 -5.496039E-02 -3.536070E-04 -5.043703E-02 -3.116190E-04 -5.642187E-02 -3.765290E-04 -6.063856E-02 -4.379295E-04 -5.195487E-02 -3.214080E-04 -6.938655E-02 -5.412456E-04 -6.384563E-02 -4.345441E-04 -7.340916E-02 -5.829419E-04 -4.160832E-02 -1.915303E-04 -4.400244E-02 -2.364017E-04 -5.387088E-02 -3.278923E-04 -5.794972E-02 -3.768932E-04 -4.823866E-02 -2.869653E-04 -5.581812E-02 -3.628216E-04 -5.507172E-02 -3.230172E-04 -6.302356E-02 -4.527401E-04 -5.707918E-02 -3.888513E-04 -4.754358E-02 -2.594617E-04 -6.541199E-02 -4.781326E-04 -6.028104E-02 -4.146449E-04 -3.451579E-02 -1.431485E-04 -3.241942E-02 -1.223581E-04 -4.685494E-02 -2.525308E-04 -4.111890E-02 -2.121316E-04 -4.153961E-02 -1.996497E-04 -5.816750E-02 -3.609870E-04 -3.677652E-02 -1.668470E-04 -4.696518E-02 -2.600495E-04 -3.377120E-02 -1.501347E-04 -4.376838E-02 -2.325356E-04 -5.222808E-02 -3.054620E-04 -5.928320E-02 -4.267763E-04 -2.991812E-02 -1.056949E-04 -3.616946E-02 -1.524179E-04 -3.177703E-02 -1.168362E-04 -4.705585E-02 -2.646056E-04 -3.665252E-02 -1.695105E-04 -4.766781E-02 -2.674389E-04 -3.202832E-02 -1.073493E-04 -2.751650E-02 -9.309837E-05 -4.818224E-02 -2.392320E-04 -3.403290E-02 -1.418131E-04 -4.574016E-02 -2.354785E-04 -4.773050E-02 -2.476124E-04 -1.973857E-02 -5.421533E-05 -2.110340E-02 -6.009155E-05 -2.365572E-02 -6.648519E-05 -2.131581E-02 -5.365256E-05 -2.548189E-02 -8.296044E-05 -2.738380E-02 -8.103041E-05 -1.857273E-02 -4.206503E-05 -2.430428E-02 -7.766038E-05 -1.413939E-02 -2.821225E-05 -2.111430E-02 -5.198944E-05 -2.690085E-02 -9.577166E-05 -3.477173E-02 -1.518451E-04 -1.694126E-02 -4.610916E-05 -1.802237E-02 -4.677578E-05 -1.547843E-02 -3.071140E-05 -1.258621E-02 -2.676730E-05 -1.480772E-02 -2.870448E-05 -1.612853E-02 -3.275094E-05 -1.269558E-02 -2.294471E-05 -1.222544E-02 -1.845380E-05 -1.456829E-02 -3.311358E-05 -9.857513E-03 -1.669207E-05 -1.626604E-02 -4.458605E-05 -1.611973E-02 -3.998382E-05 -1.971005E-02 -6.046116E-05 -1.431380E-02 -2.594436E-05 -1.824132E-02 -5.495026E-05 -1.868711E-02 -5.257059E-05 -1.452558E-02 -2.646510E-05 -1.767521E-02 -4.157062E-05 -1.224286E-02 -2.048681E-05 -1.303634E-02 -2.950766E-05 -1.330541E-02 -2.767230E-05 -1.068270E-02 -1.441894E-05 -1.784826E-02 -4.556976E-05 -1.384211E-02 -2.825659E-05 -1.729138E-02 -3.675164E-05 -2.243899E-02 -6.004831E-05 -2.715655E-02 -9.746046E-05 -2.570504E-02 -8.920071E-05 -1.697516E-02 -3.974905E-05 -1.660121E-02 -3.254815E-05 -2.224450E-02 -5.768880E-05 -2.530951E-02 -9.164893E-05 -1.840321E-02 -4.291921E-05 -2.017760E-02 -6.034117E-05 -2.049468E-02 -4.717679E-05 -2.282381E-02 -6.631028E-05 -2.767431E-02 -8.578573E-05 -3.024946E-02 -1.035261E-04 -3.587885E-02 -1.385980E-04 -4.511890E-02 -2.188810E-04 -2.993143E-02 -1.172685E-04 -4.054681E-02 -1.947113E-04 -3.798858E-02 -1.756458E-04 -4.431323E-02 -2.215003E-04 -3.189602E-02 -1.184446E-04 -3.858052E-02 -1.789441E-04 -4.477428E-02 -2.243938E-04 -4.703586E-02 -2.423313E-04 -3.435009E-02 -1.302766E-04 -4.912269E-02 -2.876019E-04 -3.586050E-02 -1.601671E-04 -5.202288E-02 -2.962633E-04 -4.354503E-02 -2.302813E-04 -4.484413E-02 -2.193914E-04 -4.626090E-02 -2.295773E-04 -5.272085E-02 -3.140576E-04 -4.016476E-02 -1.851520E-04 -4.698312E-02 -2.700090E-04 -7.091623E-02 -6.301431E-04 -4.767851E-02 -2.507174E-04 -4.786618E-02 -2.609399E-04 -4.038170E-02 -1.767402E-04 -4.773992E-02 -2.552553E-04 -6.553326E-02 -4.533702E-04 -4.409668E-02 -2.246932E-04 -5.516788E-02 -3.459366E-04 -5.575453E-02 -3.327095E-04 -6.234876E-02 -4.350389E-04 -4.468174E-02 -2.279110E-04 -5.066531E-02 -2.919067E-04 -7.038547E-02 -5.211120E-04 -6.314941E-02 -4.264819E-04 -4.552569E-02 -2.253455E-04 -4.441783E-02 -2.383583E-04 -5.471130E-02 -3.515655E-04 -5.570461E-02 -3.610859E-04 -4.732130E-02 -2.515536E-04 -5.830395E-02 -4.045076E-04 -4.928927E-02 -2.534507E-04 -4.443354E-02 -2.155311E-04 -4.739067E-02 -2.296684E-04 -4.805589E-02 -2.422571E-04 -6.278404E-02 -4.449155E-04 -6.952048E-02 -5.145532E-04 -2.516418E-02 -7.500451E-05 -3.118980E-02 -1.112167E-04 -4.421923E-02 -2.460515E-04 -4.297090E-02 -2.413819E-04 -3.779702E-02 -1.857046E-04 -5.299210E-02 -3.257368E-04 -4.026655E-02 -1.807099E-04 -4.206085E-02 -2.256535E-04 -4.079656E-02 -1.987081E-04 -3.610266E-02 -1.649391E-04 -4.381846E-02 -2.263935E-04 -4.980200E-02 -2.864882E-04 -2.860174E-02 -1.712915E-04 -2.445643E-02 -1.020759E-04 -3.878477E-02 -2.026049E-04 -2.480236E-02 -6.838491E-05 -2.734325E-02 -9.461308E-05 -3.693833E-02 -1.822458E-04 -2.490103E-02 -7.329360E-05 -2.677818E-02 -8.305017E-05 -3.264724E-02 -1.437491E-04 -3.715999E-02 -2.239488E-04 -2.373224E-02 -6.329401E-05 -3.900048E-02 -1.863015E-04 -1.125664E-02 -2.181811E-05 -2.161924E-02 -7.195899E-05 -1.744174E-02 -4.273755E-05 -1.238666E-02 -2.145334E-05 -2.141496E-02 -5.796334E-05 -3.027096E-02 -9.721558E-05 -2.034339E-02 -5.581306E-05 -1.730093E-02 -4.542569E-05 -2.661993E-02 -8.452374E-05 -2.161864E-02 -5.465888E-05 -1.450249E-02 -2.452960E-05 -2.302180E-02 -6.121219E-05 -6.340596E-03 -5.499932E-06 -9.140384E-03 -1.585008E-05 -1.397492E-02 -2.598945E-05 -1.189682E-02 -2.167082E-05 -1.311861E-02 -2.016097E-05 -1.750898E-02 -4.671329E-05 -9.795781E-03 -1.975542E-05 -1.094278E-02 -1.976709E-05 -1.639501E-02 -3.446322E-05 -7.320291E-03 -7.206036E-06 -1.849111E-02 -4.335473E-05 -1.434342E-02 -2.991316E-05 -1.167361E-02 -1.759595E-05 -9.859610E-03 -1.289330E-05 -1.133762E-02 -1.561793E-05 -1.865540E-02 -4.343736E-05 -1.350518E-02 -2.129333E-05 -1.358916E-02 -2.271382E-05 -9.345990E-03 -1.288554E-05 -1.065558E-02 -1.661140E-05 -8.770677E-03 -1.373474E-05 -1.321675E-02 -2.139500E-05 -1.306520E-02 -2.539346E-05 -1.129556E-02 -2.247168E-05 -2.256971E-02 -6.489740E-05 -2.030940E-02 -5.625582E-05 -2.024779E-02 -5.494975E-05 -1.906346E-02 -4.047418E-05 -2.207434E-02 -6.757473E-05 -1.730595E-02 -3.788068E-05 -2.519625E-02 -9.116269E-05 -2.094102E-02 -5.386236E-05 -2.252706E-02 -7.123740E-05 -2.249589E-02 -6.348244E-05 -1.677894E-02 -4.502916E-05 -1.191569E-02 -2.092636E-05 -3.193092E-02 -1.066907E-04 -3.474315E-02 -1.354003E-04 -3.428457E-02 -1.357392E-04 -5.095478E-02 -3.135854E-04 -2.785938E-02 -1.094102E-04 -2.524070E-02 -7.421058E-05 -3.410453E-02 -1.325498E-04 -4.419349E-02 -2.287531E-04 -3.121966E-02 -1.145504E-04 -3.172565E-02 -1.576778E-04 -3.732684E-02 -1.794004E-04 -2.798822E-02 -9.904213E-05 -3.829890E-02 -1.745254E-04 -3.669187E-02 -1.481631E-04 -3.831832E-02 -1.640348E-04 -4.717292E-02 -2.431524E-04 -3.246900E-02 -1.090875E-04 -3.181397E-02 -1.110646E-04 -3.422430E-02 -1.455143E-04 -4.595899E-02 -2.593546E-04 -2.943781E-02 -1.144304E-04 -4.083225E-02 -2.153138E-04 -4.861114E-02 -2.668189E-04 -4.566953E-02 -2.275971E-04 -4.762605E-02 -2.702276E-04 -3.885235E-02 -1.678236E-04 -4.746492E-02 -2.732016E-04 -6.187919E-02 -3.977228E-04 -3.385168E-02 -1.655806E-04 -3.779606E-02 -1.742919E-04 -4.344441E-02 -2.144777E-04 -4.983722E-02 -2.780628E-04 -3.805659E-02 -1.869986E-04 -3.881150E-02 -1.693755E-04 -5.243613E-02 -3.131126E-04 -4.108551E-02 -1.935253E-04 -3.562260E-02 -1.423362E-04 -3.787549E-02 -1.654425E-04 -4.885818E-02 -2.578691E-04 -5.332653E-02 -3.213342E-04 -4.348495E-02 -2.331286E-04 -4.663540E-02 -2.474672E-04 -3.285731E-02 -1.375774E-04 -4.576846E-02 -2.311007E-04 -3.184404E-02 -1.192332E-04 -3.998156E-02 -1.844262E-04 -5.607513E-02 -3.422109E-04 -4.953587E-02 -2.676057E-04 -3.693477E-02 -1.700381E-04 -2.948894E-02 -1.003101E-04 -5.540578E-02 -3.851845E-04 -5.314370E-02 -3.109412E-04 -4.109329E-02 -1.998923E-04 -5.093669E-02 -3.059425E-04 -4.209028E-02 -2.361378E-04 -4.465662E-02 -2.120794E-04 -2.708725E-02 -9.631361E-05 -3.635562E-02 -1.595262E-04 -4.238804E-02 -1.947726E-04 -3.974596E-02 -1.806688E-04 -2.982744E-02 -1.207685E-04 -3.283297E-02 -1.354648E-04 -3.271957E-02 -1.397951E-04 -2.724542E-02 -1.015777E-04 -3.609538E-02 -1.698517E-04 -3.335710E-02 -1.624664E-04 -2.079895E-02 -5.610101E-05 -2.603309E-02 -9.493586E-05 -2.597898E-02 -9.783976E-05 -3.135251E-02 -1.402609E-04 -2.775791E-02 -9.666104E-05 -3.244893E-02 -1.414683E-04 -1.736161E-02 -4.120267E-05 -2.103660E-02 -6.662057E-05 -2.254942E-02 -6.127994E-05 -2.617237E-02 -9.655322E-05 -1.879302E-02 -4.046759E-05 -2.239207E-02 -7.270900E-05 -1.582884E-02 -3.421542E-05 -1.701627E-02 -3.287993E-05 -1.586730E-02 -3.071759E-05 -1.637662E-02 -4.285548E-05 -1.575516E-02 -2.967636E-05 -1.749637E-02 -4.429182E-05 -1.377991E-02 -2.869990E-05 -1.666042E-02 -3.900259E-05 -1.590173E-02 -4.041443E-05 -1.533466E-02 -3.740187E-05 -1.242820E-02 -2.230617E-05 -2.005763E-02 -5.559139E-05 -1.888435E-02 -5.293569E-05 -1.437606E-02 -2.992754E-05 -1.160735E-02 -2.488650E-05 -1.903862E-02 -4.929965E-05 -1.792944E-02 -4.623131E-05 -1.925927E-02 -4.967790E-05 -1.197483E-02 -1.865556E-05 -7.572584E-03 -1.282465E-05 -1.344056E-02 -2.673348E-05 -1.049282E-02 -1.487580E-05 -1.018581E-02 -1.491092E-05 -9.650534E-03 -1.458568E-05 -7.696745E-03 -8.956471E-06 -6.117465E-03 -6.145859E-06 -7.711888E-03 -1.379326E-05 -7.178538E-03 -8.529096E-06 -7.013352E-03 -9.358596E-06 -5.223833E-03 -4.743484E-06 -1.958755E-02 -5.840140E-05 -1.514889E-02 -3.048083E-05 -1.420009E-02 -3.392782E-05 -1.779423E-02 -4.000672E-05 -9.916829E-03 -1.600897E-05 -1.084516E-02 -1.883449E-05 -1.721403E-02 -3.892250E-05 -1.697631E-02 -5.336366E-05 -1.126561E-02 -1.779695E-05 -1.183784E-02 -2.042908E-05 -1.279900E-02 -2.278884E-05 -1.221352E-02 -2.185990E-05 -2.126137E-02 -5.259427E-05 -1.948422E-02 -7.413728E-05 -2.691940E-02 -7.987754E-05 -2.366460E-02 -7.849119E-05 -1.493129E-02 -3.458121E-05 -2.528845E-02 -7.805956E-05 -1.941301E-02 -4.532019E-05 -3.315469E-02 -1.407045E-04 -1.764622E-02 -4.338977E-05 -1.952377E-02 -5.147550E-05 -2.600704E-02 -9.058738E-05 -2.442799E-02 -7.377464E-05 -2.298511E-02 -6.212241E-05 -2.519641E-02 -9.638931E-05 -3.975880E-02 -1.694798E-04 -3.835063E-02 -1.723954E-04 -2.433477E-02 -7.177972E-05 -2.711003E-02 -1.279711E-04 -3.083661E-02 -1.018837E-04 -4.207502E-02 -2.089394E-04 -2.844391E-02 -9.560578E-05 -2.835547E-02 -8.648090E-05 -5.190679E-02 -2.829647E-04 -3.607591E-02 -1.406622E-04 -2.726024E-02 -9.295552E-05 -2.334992E-02 -7.837059E-05 -2.556006E-02 -8.066549E-05 -3.157571E-02 -1.018377E-04 -2.717890E-02 -9.754656E-05 -3.812328E-02 -1.529731E-04 -3.196833E-02 -1.246738E-04 -3.749985E-02 -1.546562E-04 -1.861417E-02 -4.193655E-05 -2.827800E-02 -8.681001E-05 -3.521111E-02 -1.537273E-04 -3.631214E-02 -1.510528E-04 -3.010902E-02 -1.101278E-04 -2.898513E-02 -1.002289E-04 -3.759216E-02 -1.686797E-04 -3.924809E-02 -1.926092E-04 -3.000742E-02 -1.032469E-04 -3.063104E-02 -1.058005E-04 -3.016836E-02 -1.406736E-04 -3.811226E-02 -2.142070E-04 -3.067660E-02 -1.137129E-04 -2.919174E-02 -9.744001E-05 -4.351818E-02 -2.481411E-04 -3.459089E-02 -1.418237E-04 -2.354519E-02 -7.125933E-05 -2.341512E-02 -7.530145E-05 -3.423411E-02 -1.362388E-04 -3.807351E-02 -1.835906E-04 -3.060066E-02 -1.306426E-04 -3.887634E-02 -1.767107E-04 -2.972630E-02 -1.452173E-04 -2.761804E-02 -8.313790E-05 -2.564981E-02 -1.041595E-04 -2.302814E-02 -5.748532E-05 -3.430159E-02 -1.266003E-04 -3.080546E-02 -1.097842E-04 -1.862840E-02 -4.692046E-05 -2.212362E-02 -5.769304E-05 -3.055531E-02 -1.529521E-04 -2.289574E-02 -8.233761E-05 -2.656172E-02 -8.446802E-05 -3.583300E-02 -1.541747E-04 -2.557682E-02 -7.916517E-05 -2.097606E-02 -5.055022E-05 -2.228757E-02 -5.988269E-05 -1.816051E-02 -3.901578E-05 -2.325555E-02 -7.127170E-05 -2.918248E-02 -1.039399E-04 -1.305527E-02 -2.604090E-05 -1.318002E-02 -2.893713E-05 -1.574393E-02 -3.521171E-05 -1.414192E-02 -3.552473E-05 -1.633140E-02 -3.849962E-05 -1.643542E-02 -4.093687E-05 -1.215108E-02 -2.596067E-05 -1.663208E-02 -4.623828E-05 -1.519499E-02 -2.784484E-05 -1.392851E-02 -2.190759E-05 -2.267580E-02 -6.466320E-05 -2.215409E-02 -7.872980E-05 -1.106097E-02 -1.935578E-05 -1.318957E-02 -2.458468E-05 -1.432387E-02 -2.771689E-05 -1.302310E-02 -2.533330E-05 -1.818189E-02 -4.776340E-05 -1.401528E-02 -2.698770E-05 -3.589051E-03 -3.157955E-06 -9.717553E-03 -1.921247E-05 -8.432737E-03 -1.168760E-05 -8.867899E-03 -2.125223E-05 -1.176987E-02 -2.796774E-05 -1.387858E-02 -2.900888E-05 -7.333775E-03 -9.493537E-06 -5.494780E-03 -7.270347E-06 -8.505259E-03 -1.106187E-05 -5.349113E-03 -6.272249E-06 -7.278213E-03 -7.362414E-06 -8.521400E-03 -1.020574E-05 -8.308309E-03 -1.075116E-05 -8.167064E-03 -1.109423E-05 -5.533013E-03 -6.466675E-06 -5.112442E-03 -8.851249E-06 -5.870076E-03 -6.648936E-06 -3.929558E-03 -2.494118E-06 -1.248728E-02 -2.662149E-05 -7.689572E-03 -1.632791E-05 -1.064286E-02 -1.731638E-05 -8.865827E-03 -2.182318E-05 -1.060594E-02 -1.974250E-05 -1.107000E-02 -1.741690E-05 -1.046576E-02 -1.304525E-05 -8.923758E-03 -1.382295E-05 -6.048588E-03 -6.515013E-06 -1.140457E-02 -1.772344E-05 -1.071605E-02 -2.320839E-05 -1.108521E-02 -2.146136E-05 -1.372658E-02 -3.150815E-05 -1.185879E-02 -2.592177E-05 -1.328057E-02 -2.007755E-05 -2.366818E-02 -6.240710E-05 -1.310524E-02 -2.483124E-05 -1.756288E-02 -3.846799E-05 -1.953925E-02 -4.224682E-05 -2.714173E-02 -9.200317E-05 -2.089836E-02 -5.417932E-05 -1.880252E-02 -4.633361E-05 -1.825440E-02 -4.676871E-05 -1.703893E-02 -3.290578E-05 -1.536560E-02 -2.501324E-05 -1.723409E-02 -4.375407E-05 -2.378506E-02 -6.856927E-05 -2.116439E-02 -5.565273E-05 -1.694042E-02 -5.241605E-05 -1.860566E-02 -4.783232E-05 -1.867229E-02 -4.443429E-05 -2.347238E-02 -6.225289E-05 -1.240017E-02 -2.472321E-05 -1.834566E-02 -5.076889E-05 -2.345212E-02 -6.899204E-05 -1.744726E-02 -4.012671E-05 -2.139472E-02 -5.719986E-05 -2.881782E-02 -1.041089E-04 -2.249258E-02 -6.622191E-05 -3.099210E-02 -1.124327E-04 -2.723683E-02 -1.077003E-04 -2.961326E-02 -1.107622E-04 -2.886855E-02 -1.209198E-04 -3.359957E-02 -1.450335E-04 -1.842677E-02 -4.144114E-05 -2.067987E-02 -5.241403E-05 -2.384292E-02 -8.392423E-05 -2.523377E-02 -7.399318E-05 -1.482060E-02 -2.934033E-05 -1.685267E-02 -4.268337E-05 -2.474266E-02 -6.858025E-05 -2.464842E-02 -7.502593E-05 -1.931712E-02 -6.623063E-05 -2.571146E-02 -8.499419E-05 -1.919509E-02 -5.019677E-05 -2.194688E-02 -6.974387E-05 -2.021151E-02 -5.705824E-05 -1.939673E-02 -5.393718E-05 -2.366200E-02 -7.358565E-05 -3.181441E-02 -1.136270E-04 -1.839678E-02 -3.699144E-05 -1.988625E-02 -6.005553E-05 -2.730155E-02 -9.038604E-05 -1.977072E-02 -5.242521E-05 -1.499223E-02 -2.966075E-05 -2.040274E-02 -6.508672E-05 -1.411627E-02 -2.586129E-05 -1.706927E-02 -3.713346E-05 -1.171754E-02 -1.971987E-05 -2.013780E-02 -5.939024E-05 -2.029685E-02 -6.146855E-05 -2.908889E-02 -1.037204E-04 -1.740582E-02 -3.597993E-05 -1.564902E-02 -6.335090E-05 -1.991302E-02 -4.746765E-05 -1.756837E-02 -4.867597E-05 -2.061727E-02 -6.000022E-05 -2.574876E-02 -8.243095E-05 -1.191200E-02 -1.934272E-05 -2.009666E-02 -4.813195E-05 -1.746764E-02 -5.388627E-05 -1.239022E-02 -2.424386E-05 -2.173442E-02 -4.983400E-05 -1.517203E-02 -3.068246E-05 -1.329712E-02 -2.150332E-05 -2.354363E-02 -8.627378E-05 -1.532883E-02 -3.270327E-05 -1.472157E-02 -3.319558E-05 -1.938773E-02 -4.827508E-05 -2.165397E-02 -6.469989E-05 -1.271540E-02 -1.967861E-05 -1.665138E-02 -4.718665E-05 -9.283413E-03 -1.143878E-05 -1.046201E-02 -2.072362E-05 -1.673621E-02 -3.707353E-05 -1.445073E-02 -2.924329E-05 -7.634313E-03 -1.179099E-05 -7.070615E-03 -8.043539E-06 -1.155338E-02 -1.871310E-05 -8.618350E-03 -1.431974E-05 -9.424572E-03 -1.207411E-05 -8.545662E-03 -1.247473E-05 -3.838707E-03 -4.805334E-06 -5.882837E-03 -4.922359E-06 -8.145896E-03 -1.250575E-05 -5.962503E-03 -5.532809E-06 -1.116529E-02 -3.223331E-05 -1.080742E-02 -1.791118E-05 -4.327838E-03 -3.722361E-06 -3.132066E-03 -2.831681E-06 -5.985652E-03 -5.243244E-06 -6.402910E-03 -9.755445E-06 -5.996163E-03 -1.017758E-05 -7.516797E-03 -1.133864E-05 -1.109701E-03 -5.874266E-07 -3.145346E-03 -2.131176E-06 -3.071793E-03 -3.203751E-06 -5.112008E-03 -6.623440E-06 -1.057799E-02 -2.219886E-05 -4.499437E-03 -4.252659E-06 -8.765798E-03 -1.784490E-05 -8.921959E-03 -1.351279E-05 -7.502480E-03 -1.202219E-05 -7.196379E-03 -9.204669E-06 -5.832796E-03 -1.016451E-05 -7.038191E-03 -6.254164E-06 -9.382421E-03 -1.566041E-05 -1.345656E-02 -3.028211E-05 -6.337258E-03 -7.407982E-06 -8.543843E-03 -1.265584E-05 -1.381261E-02 -2.802183E-05 -1.190749E-02 -1.793613E-05 -9.437321E-03 -1.194730E-05 -9.732419E-03 -1.651545E-05 -1.500610E-02 -3.340884E-05 -1.678411E-02 -3.491286E-05 -1.028094E-02 -1.374135E-05 -1.485088E-02 -3.057123E-05 -8.964661E-03 -1.485585E-05 -1.226822E-02 -2.015578E-05 -1.042577E-02 -1.589161E-05 -6.708855E-03 -9.646613E-06 -1.277453E-02 -2.203458E-05 -7.224932E-03 -8.275023E-06 -1.255069E-02 -2.759249E-05 -1.234545E-02 -2.557302E-05 -1.676381E-02 -3.443801E-05 -2.225843E-02 -7.382383E-05 -9.664194E-03 -1.388455E-05 -1.384208E-02 -2.485536E-05 -1.170741E-02 -2.722875E-05 -1.298519E-02 -2.278333E-05 -1.219523E-02 -2.170852E-05 -1.455145E-02 -2.652917E-05 -1.803860E-02 -4.551654E-05 -1.716760E-02 -3.418763E-05 -1.512094E-02 -3.308210E-05 -1.040105E-02 -1.595604E-05 -1.796769E-02 -3.949154E-05 -1.872289E-02 -4.103439E-05 -9.077191E-03 -1.199908E-05 -1.631540E-02 -3.369350E-05 -1.526935E-02 -3.456559E-05 -2.236068E-02 -7.214380E-05 -8.845141E-03 -2.279004E-05 -1.352347E-02 -2.540469E-05 -1.484395E-02 -3.807417E-05 -1.804445E-02 -4.342710E-05 -1.007944E-02 -1.452963E-05 -1.467548E-02 -4.429846E-05 -2.042873E-02 -5.671423E-05 -1.758792E-02 -5.925658E-05 -1.761311E-02 -4.685220E-05 -2.004514E-02 -5.536859E-05 -1.238507E-02 -2.340619E-05 -1.046525E-02 -1.737035E-05 -1.444879E-02 -3.355825E-05 -8.277840E-03 -1.437311E-05 -1.079506E-02 -1.848135E-05 -1.561308E-02 -4.224535E-05 -1.137179E-02 -2.608300E-05 -1.280633E-02 -2.210100E-05 -1.162328E-02 -2.231053E-05 -1.296251E-02 -2.730055E-05 -1.389949E-02 -3.026765E-05 -1.411889E-02 -3.201101E-05 -8.232902E-03 -1.404323E-05 -1.242929E-02 -2.465073E-05 -9.545643E-03 -1.867285E-05 -9.740853E-03 -1.452865E-05 -1.306805E-02 -2.407394E-05 -1.010373E-02 -1.920540E-05 -8.546938E-03 -1.818086E-05 -9.873382E-03 -2.146517E-05 -1.643827E-02 -5.513889E-05 -1.117018E-02 -2.390203E-05 -9.216643E-03 -1.323340E-05 -1.434946E-02 -2.862359E-05 -1.245343E-02 -2.322243E-05 -1.212911E-02 -2.285075E-05 -9.836707E-03 -1.645734E-05 -1.201364E-02 -2.024192E-05 -1.596859E-02 -3.442108E-05 -1.368837E-02 -2.688655E-05 -1.015380E-02 -1.592459E-05 -9.892082E-03 -1.321377E-05 -1.360812E-02 -3.262388E-05 -1.003913E-02 -1.400775E-05 -1.478387E-02 -3.871602E-05 -9.205053E-03 -1.179337E-05 -9.383339E-03 -1.300416E-05 -7.080640E-03 -8.203597E-06 -6.507205E-03 -7.055020E-06 -8.843528E-03 -1.233759E-05 -1.087757E-02 -1.436248E-05 -9.075363E-03 -1.538346E-05 -4.079253E-03 -4.032406E-06 -6.146524E-03 -1.072823E-05 -7.605892E-03 -2.021674E-05 -6.737771E-03 -8.340942E-06 -7.791360E-03 -1.322504E-05 -4.367729E-03 -5.871757E-06 -5.112041E-03 -7.704614E-06 -6.685377E-03 -8.598228E-06 -5.332365E-03 -6.964174E-06 -2.025949E-03 -1.106713E-06 -7.308916E-03 -1.083527E-05 -2.657078E-03 -2.018754E-06 -3.845964E-03 -3.282949E-06 -7.906876E-03 -1.309640E-05 -3.020084E-03 -2.116323E-06 -4.987032E-03 -5.707520E-06 -1.202747E-02 -3.060476E-05 -7.093728E-03 -7.508046E-06 -7.422432E-03 -8.656078E-06 -9.077236E-03 -2.478476E-05 -6.891522E-03 -1.220166E-05 -5.076941E-03 -4.237107E-06 -8.523515E-03 -1.390735E-05 -6.414767E-03 -9.365735E-06 -9.380022E-03 -1.462751E-05 -8.456230E-03 -1.199551E-05 -9.158278E-03 -1.192449E-05 -2.298967E-03 -1.013954E-06 -7.058890E-03 -6.383597E-06 -4.876567E-03 -3.760010E-06 -1.316587E-02 -2.644816E-05 -8.359774E-03 -1.081163E-05 -1.469243E-02 -3.486053E-05 -7.460324E-03 -7.746075E-06 -7.025833E-03 -7.150545E-06 -4.599519E-03 -5.621223E-06 -1.754686E-02 -5.599449E-05 -8.496819E-03 -1.519010E-05 -1.334303E-02 -2.243087E-05 -1.273838E-02 -2.372539E-05 -1.041534E-02 -2.066977E-05 -9.140508E-03 -1.075841E-05 -1.657971E-02 -4.469598E-05 -1.247039E-02 -2.906265E-05 -1.297140E-02 -2.473045E-05 -1.664008E-02 -4.158991E-05 -1.836946E-02 -4.883353E-05 -1.411162E-02 -4.313628E-05 -1.406170E-02 -2.454496E-05 -2.270767E-02 -6.143414E-05 -1.567617E-02 -3.204306E-05 -1.611804E-02 -3.752055E-05 -1.312188E-02 -2.232058E-05 -1.726062E-02 -4.888466E-05 -2.260453E-02 -5.912727E-05 -2.295104E-02 -7.305721E-05 -2.082979E-02 -5.212405E-05 -3.139636E-02 -1.124507E-04 -1.701985E-02 -5.917730E-05 -1.573079E-02 -3.833147E-05 -1.648501E-02 -4.521319E-05 -1.295769E-02 -2.000518E-05 -1.538561E-02 -3.581006E-05 -2.113104E-02 -5.954145E-05 -1.269260E-02 -1.914463E-05 -1.597644E-02 -2.898818E-05 -1.749472E-02 -4.995488E-05 -1.170597E-02 -2.494586E-05 -1.972148E-02 -5.486069E-05 -2.930071E-02 -1.089599E-04 -1.284182E-02 -2.296682E-05 -1.568134E-02 -3.876494E-05 -1.557912E-02 -3.819947E-05 -1.760851E-02 -3.904704E-05 -1.450774E-02 -4.086604E-05 -1.375807E-02 -3.319496E-05 -1.290194E-02 -2.142172E-05 -1.402767E-02 -2.932888E-05 -1.769773E-02 -4.995015E-05 -1.419402E-02 -3.040909E-05 -1.675459E-02 -3.235864E-05 -1.976178E-02 -5.009760E-05 -1.675838E-02 -3.910658E-05 -1.689170E-02 -3.464763E-05 -1.284022E-02 -2.649261E-05 -1.316325E-02 -2.377493E-05 -1.455084E-02 -2.510139E-05 -1.102459E-02 -1.885880E-05 -8.350866E-03 -8.396386E-06 -8.651131E-03 -1.124192E-05 -1.376705E-02 -2.764081E-05 -1.428569E-02 -3.790437E-05 -1.724679E-02 -3.366818E-05 -1.584053E-02 -3.651487E-05 -1.193033E-02 -2.181873E-05 -1.984626E-02 -5.592598E-05 -1.214395E-02 -1.945894E-05 -1.365596E-02 -2.557166E-05 -1.898643E-02 -5.157604E-05 -1.455388E-02 -2.799320E-05 -1.182403E-02 -2.115918E-05 -8.626630E-03 -1.007433E-05 -1.197781E-02 -1.983881E-05 -1.005772E-02 -1.640145E-05 -1.873799E-02 -4.930639E-05 -1.693176E-02 -3.650659E-05 -8.818286E-03 -1.286259E-05 -1.139988E-02 -1.621095E-05 -1.028933E-02 -1.453198E-05 -8.404568E-03 -9.460915E-06 -1.275744E-02 -2.774096E-05 -1.168183E-02 -1.973529E-05 -8.454758E-03 -1.146522E-05 -8.323601E-03 -8.829709E-06 -7.495546E-03 -8.715799E-06 -5.296323E-03 -6.046794E-06 -9.891852E-03 -1.398584E-05 -9.955102E-03 -1.943758E-05 -8.059482E-03 -1.695389E-05 -9.676780E-03 -1.658395E-05 -4.834433E-03 -5.897640E-06 -6.636064E-03 -7.609925E-06 -7.619866E-03 -1.212597E-05 -2.894660E-03 -1.856127E-06 -7.553124E-03 -1.223718E-05 -2.300404E-03 -1.121136E-06 -7.327033E-03 -9.105655E-06 -4.846398E-03 -4.166167E-06 -8.315901E-03 -1.319751E-05 -7.221182E-03 -7.485967E-06 -5.462020E-03 -3.502442E-06 -9.453474E-03 -1.071037E-05 -1.293719E-02 -2.483879E-05 -1.321412E-02 -2.574150E-05 -1.359066E-02 -2.348428E-05 -9.882529E-03 -1.691834E-05 -1.409150E-02 -3.523994E-05 -1.070054E-02 -1.906767E-05 -1.266239E-02 -1.972906E-05 -1.432889E-02 -3.545618E-05 -1.011895E-02 -1.360275E-05 -1.025183E-02 -1.608180E-05 -5.529921E-03 -7.911985E-06 -1.629012E-02 -3.223742E-05 -1.114479E-02 -2.101499E-05 -1.215717E-02 -1.846892E-05 -1.689915E-02 -4.620696E-05 -1.139613E-02 -1.742241E-05 -1.213431E-02 -2.199183E-05 -1.846118E-02 -5.474736E-05 -1.709853E-02 -3.678232E-05 -1.735796E-02 -4.553680E-05 -1.853277E-02 -4.646147E-05 -1.763467E-02 -4.466597E-05 -1.818218E-02 -4.824198E-05 -1.872596E-02 -6.942835E-05 -2.022757E-02 -5.512053E-05 -1.699277E-02 -3.870609E-05 -1.055491E-02 -1.687463E-05 -1.377011E-02 -2.394912E-05 -1.684636E-02 -3.614976E-05 -2.147831E-02 -6.616404E-05 -1.730285E-02 -3.913079E-05 -1.957720E-02 -5.565960E-05 -1.855637E-02 -4.612437E-05 -2.805186E-02 -1.029670E-04 -2.418902E-02 -6.781295E-05 -2.188652E-02 -6.579480E-05 -1.771569E-02 -4.195257E-05 -2.256760E-02 -6.456468E-05 -1.918774E-02 -5.286254E-05 -2.960608E-02 -1.004602E-04 -2.712839E-02 -8.980797E-05 -2.794452E-02 -1.254679E-04 -3.008968E-02 -1.089661E-04 -3.649934E-02 -1.734688E-04 -2.937679E-02 -1.082854E-04 -3.282797E-02 -1.345859E-04 -4.521759E-02 -2.543431E-04 -2.398232E-02 -7.384139E-05 -3.254161E-02 -1.265125E-04 -3.001705E-02 -1.111357E-04 -2.399317E-02 -6.646668E-05 -3.503715E-02 -1.480964E-04 -3.148980E-02 -1.398976E-04 -2.604556E-02 -9.628490E-05 -3.036652E-02 -1.264006E-04 -2.939357E-02 -1.170192E-04 -3.743087E-02 -1.775840E-04 -3.085132E-02 -1.134780E-04 -4.147944E-02 -1.996575E-04 -3.527838E-02 -1.521337E-04 -2.366172E-02 -6.804925E-05 -2.713514E-02 -1.027237E-04 -2.174215E-02 -5.749073E-05 -2.675659E-02 -8.382558E-05 -2.359986E-02 -7.430600E-05 -2.367000E-02 -7.298151E-05 -3.227569E-02 -1.232973E-04 -2.733957E-02 -1.003086E-04 -3.002721E-02 -9.578465E-05 -3.707352E-02 -1.507472E-04 -2.382487E-02 -6.891582E-05 -2.959048E-02 -1.054578E-04 -1.869568E-02 -4.658988E-05 -2.597326E-02 -8.064723E-05 -2.153451E-02 -5.840127E-05 -1.628961E-02 -4.271967E-05 -2.741092E-02 -8.522384E-05 -2.733756E-02 -9.324995E-05 -2.864715E-02 -1.013597E-04 -3.455306E-02 -1.448078E-04 -3.347393E-02 -1.327937E-04 -3.527874E-02 -1.637966E-04 -2.441550E-02 -8.480735E-05 -2.623780E-02 -8.256558E-05 -1.716091E-02 -5.253723E-05 -1.324941E-02 -2.243910E-05 -2.246659E-02 -6.057114E-05 -2.192475E-02 -5.668645E-05 -2.272490E-02 -7.122633E-05 -3.254641E-02 -1.485757E-04 -1.504227E-02 -2.731302E-05 -1.654008E-02 -4.044070E-05 -2.442456E-02 -8.014611E-05 -2.260914E-02 -6.419431E-05 -1.988931E-02 -6.266967E-05 -3.422754E-02 -1.464931E-04 -1.176286E-02 -1.819119E-05 -1.090258E-02 -1.835148E-05 -1.428818E-02 -2.432816E-05 -9.647717E-03 -2.418655E-05 -1.343547E-02 -2.294350E-05 -1.672666E-02 -4.724036E-05 -1.498173E-02 -2.999368E-05 -1.209667E-02 -2.648058E-05 -1.777083E-02 -3.928551E-05 -1.906246E-02 -4.800003E-05 -1.227253E-02 -2.454598E-05 -2.054852E-02 -5.627860E-05 -1.401620E-02 -3.395841E-05 -1.618774E-02 -3.315176E-05 -9.366645E-03 -1.322093E-05 -1.271711E-02 -2.946682E-05 -1.514436E-02 -3.698271E-05 -1.357568E-02 -2.661503E-05 -6.059837E-03 -6.072503E-06 -7.510121E-03 -8.006288E-06 -1.105528E-02 -2.101618E-05 -4.887318E-03 -5.427586E-06 -8.183502E-03 -1.217955E-05 -9.960866E-03 -1.427453E-05 -1.302537E-02 -2.752410E-05 -1.432159E-02 -3.046582E-05 -1.362132E-02 -3.362721E-05 -1.728813E-02 -4.356686E-05 -1.882553E-02 -4.115052E-05 -2.022059E-02 -5.094537E-05 -9.935622E-03 -1.476612E-05 -1.423961E-02 -2.791015E-05 -1.078457E-02 -3.254092E-05 -1.010483E-02 -1.743918E-05 -1.145080E-02 -2.471476E-05 -1.537588E-02 -3.948184E-05 -3.025085E-02 -1.211946E-04 -2.522059E-02 -8.250671E-05 -2.231553E-02 -6.323615E-05 -2.348918E-02 -6.900725E-05 -2.273346E-02 -5.949276E-05 -1.817883E-02 -4.693722E-05 -2.354895E-02 -7.506897E-05 -2.667415E-02 -1.051466E-04 -1.923174E-02 -4.215181E-05 -2.251124E-02 -6.197340E-05 -2.212823E-02 -7.382044E-05 -2.627751E-02 -1.262016E-04 -2.639401E-02 -8.435288E-05 -2.355251E-02 -6.467494E-05 -1.824258E-02 -4.508497E-05 -2.658510E-02 -7.928794E-05 -2.675987E-02 -1.019694E-04 -2.294475E-02 -7.019770E-05 -2.982020E-02 -1.172018E-04 -2.650631E-02 -7.994237E-05 -3.317163E-02 -1.365190E-04 -4.086398E-02 -2.004161E-04 -2.796562E-02 -1.066212E-04 -3.652322E-02 -1.878727E-04 -3.754838E-02 -1.647330E-04 -3.743316E-02 -1.789631E-04 -2.670365E-02 -8.773372E-05 -2.985106E-02 -9.743471E-05 -3.608668E-02 -1.610685E-04 -4.205670E-02 -1.842040E-04 -3.922858E-02 -1.953706E-04 -4.448502E-02 -2.469956E-04 -4.693316E-02 -2.454100E-04 -4.848290E-02 -2.671676E-04 -4.023569E-02 -1.713754E-04 -4.294287E-02 -2.063518E-04 -4.004042E-02 -1.923766E-04 -4.357533E-02 -2.186888E-04 -3.728630E-02 -1.990696E-04 -4.083902E-02 -1.919817E-04 -3.857997E-02 -1.821259E-04 -4.901557E-02 -2.767777E-04 -5.790464E-02 -3.872461E-04 -3.900935E-02 -1.730109E-04 -4.875435E-02 -2.872844E-04 -5.468952E-02 -3.456684E-04 -4.179342E-02 -2.001437E-04 -5.381510E-02 -3.724283E-04 -4.237622E-02 -1.941664E-04 -5.150719E-02 -2.908123E-04 -2.903885E-02 -1.031896E-04 -3.589197E-02 -1.392764E-04 -6.487280E-02 -5.024965E-04 -3.912537E-02 -1.856374E-04 -3.458921E-02 -1.576917E-04 -4.382484E-02 -2.169611E-04 -5.176404E-02 -2.803853E-04 -5.379322E-02 -3.067204E-04 -4.070213E-02 -1.816834E-04 -4.088864E-02 -1.878390E-04 -3.237349E-02 -1.191717E-04 -3.712382E-02 -1.504938E-04 -3.997504E-02 -1.782786E-04 -3.267526E-02 -1.297525E-04 -4.125020E-02 -1.924418E-04 -3.958215E-02 -1.901035E-04 -4.006168E-02 -1.832588E-04 -3.769986E-02 -1.717343E-04 -5.100625E-02 -2.898618E-04 -3.840461E-02 -1.682447E-04 -3.871359E-02 -1.824587E-04 -4.283505E-02 -2.070659E-04 -2.126415E-02 -4.973731E-05 -2.889038E-02 -1.088896E-04 -2.104568E-02 -6.548850E-05 -2.848041E-02 -1.298630E-04 -3.029276E-02 -1.138285E-04 -2.862761E-02 -9.675198E-05 -2.647642E-02 -8.418245E-05 -2.861468E-02 -1.081191E-04 -3.468627E-02 -1.277418E-04 -3.349950E-02 -1.317290E-04 -3.982514E-02 -1.794172E-04 -3.268932E-02 -1.255468E-04 -1.912998E-02 -4.577703E-05 -2.085203E-02 -6.226159E-05 -1.828176E-02 -4.944577E-05 -2.381226E-02 -8.891365E-05 -2.311077E-02 -6.656845E-05 -2.473067E-02 -7.334387E-05 -1.375348E-02 -2.283618E-05 -1.804582E-02 -4.143497E-05 -2.006609E-02 -4.873691E-05 -1.884382E-02 -4.819154E-05 -1.080497E-02 -1.376297E-05 -1.726756E-02 -3.375398E-05 -1.609526E-02 -3.821013E-05 -1.411969E-02 -2.227160E-05 -9.192959E-03 -1.631434E-05 -9.217380E-03 -1.968772E-05 -1.468647E-02 -2.623946E-05 -1.488244E-02 -3.525990E-05 -1.269069E-02 -2.294212E-05 -7.765318E-03 -1.103799E-05 -1.558612E-02 -3.323429E-05 -1.740029E-02 -5.933208E-05 -8.717198E-03 -1.192243E-05 -9.978251E-03 -1.485392E-05 -8.720289E-03 -1.149015E-05 -1.020781E-02 -2.305230E-05 -1.149654E-02 -1.940965E-05 -1.788016E-02 -4.220362E-05 -1.110418E-02 -1.718658E-05 -2.482931E-02 -1.001964E-04 -1.645044E-02 -4.054796E-05 -1.894652E-02 -4.678890E-05 -1.434380E-02 -3.165754E-05 -1.258510E-02 -2.725122E-05 -2.869257E-02 -1.023938E-04 -1.622348E-02 -4.537915E-05 -2.185647E-02 -5.507321E-05 -1.954237E-02 -4.309665E-05 -2.035261E-02 -6.714277E-05 -2.087253E-02 -4.818549E-05 -1.582401E-02 -3.291377E-05 -2.671073E-02 -9.777211E-05 -2.512135E-02 -7.547644E-05 -2.506727E-02 -7.485132E-05 -1.404823E-02 -2.446571E-05 -2.280451E-02 -6.346040E-05 -2.677285E-02 -8.686426E-05 -2.736182E-02 -1.019696E-04 -4.245358E-02 -2.031516E-04 -3.781676E-02 -1.657018E-04 -3.988823E-02 -1.948658E-04 -4.639548E-02 -2.606408E-04 -2.998045E-02 -1.078111E-04 -3.116186E-02 -1.123729E-04 -5.002484E-02 -3.007452E-04 -4.018989E-02 -2.134973E-04 -3.706873E-02 -1.705493E-04 -4.841352E-02 -3.006314E-04 -4.754905E-02 -2.640563E-04 -4.364220E-02 -2.190548E-04 -5.500865E-02 -3.565984E-04 -5.532256E-02 -3.576995E-04 -5.047039E-02 -2.937072E-04 -5.060213E-02 -3.044944E-04 -6.416214E-02 -4.414515E-04 -4.420142E-02 -2.374155E-04 -7.189342E-02 -5.468649E-04 -6.014136E-02 -3.940429E-04 -6.349933E-02 -4.207906E-04 -8.074317E-02 -7.419627E-04 -5.500576E-02 -3.305937E-04 -6.217792E-02 -4.170384E-04 -5.168117E-02 -2.909656E-04 -6.458066E-02 -4.333028E-04 -5.398600E-02 -3.328720E-04 -6.745198E-02 -5.123388E-04 -7.585366E-02 -6.172453E-04 -7.667038E-02 -6.845297E-04 -8.031109E-02 -7.564824E-04 -7.073034E-02 -5.458581E-04 -7.573691E-02 -6.181860E-04 -7.610572E-02 -6.320225E-04 -9.472534E-02 -1.007312E-03 -8.942164E-02 -8.735772E-04 -6.678710E-02 -5.406900E-04 -6.813491E-02 -4.871261E-04 -6.435084E-02 -4.334073E-04 -6.618817E-02 -4.692824E-04 -8.000303E-02 -7.136374E-04 -7.211464E-02 -6.123883E-04 -9.166818E-02 -8.952483E-04 -5.925322E-02 -4.492782E-04 -9.608215E-02 -9.616517E-04 -9.591584E-02 -9.489183E-04 -6.551541E-02 -4.745820E-04 -9.550949E-02 -9.531681E-04 -5.425472E-02 -3.720953E-04 -6.247517E-02 -4.393993E-04 -5.896488E-02 -4.002445E-04 -5.123472E-02 -2.943668E-04 -7.803224E-02 -6.809156E-04 -6.717709E-02 -5.320659E-04 -4.752627E-02 -2.528912E-04 -4.731481E-02 -2.339422E-04 -6.377650E-02 -5.060539E-04 -6.343027E-02 -4.521666E-04 -5.104223E-02 -3.253849E-04 -6.842536E-02 -5.197571E-04 -3.944396E-02 -2.024706E-04 -3.774197E-02 -1.745573E-04 -3.016158E-02 -1.057402E-04 -2.940171E-02 -1.161392E-04 -5.428473E-02 -3.726778E-04 -3.466389E-02 -1.374879E-04 -2.493688E-02 -9.144503E-05 -4.409357E-02 -2.282136E-04 -3.898092E-02 -1.692947E-04 -4.161525E-02 -2.078651E-04 -4.453363E-02 -2.219554E-04 -4.844461E-02 -2.561895E-04 -1.992414E-02 -5.397706E-05 -1.350002E-02 -2.916535E-05 -2.067487E-02 -4.903837E-05 -2.325603E-02 -8.788747E-05 -2.013029E-02 -5.389647E-05 -3.203793E-02 -1.247710E-04 -1.965175E-02 -5.312061E-05 -2.463239E-02 -8.315266E-05 -2.236927E-02 -6.414213E-05 -2.615164E-02 -7.702130E-05 -3.011875E-02 -1.090074E-04 -3.242536E-02 -1.289377E-04 -1.086798E-02 -1.760814E-05 -1.502391E-02 -3.139067E-05 -2.028244E-02 -5.583147E-05 -1.735464E-02 -4.295725E-05 -1.595857E-02 -2.787063E-05 -2.051018E-02 -5.355572E-05 -1.177619E-02 -1.717906E-05 -1.084749E-02 -1.707026E-05 -1.825630E-02 -5.376844E-05 -1.670967E-02 -3.402950E-05 -1.517723E-02 -3.554202E-05 -2.351000E-02 -7.935893E-05 -1.408337E-02 -2.327322E-05 -1.414777E-02 -2.697883E-05 -1.345682E-02 -2.740309E-05 -1.807340E-02 -5.667578E-05 -1.943061E-02 -4.787161E-05 -1.781260E-02 -6.060590E-05 -1.822299E-02 -4.962172E-05 -2.071085E-02 -5.813369E-05 -1.228031E-02 -2.872901E-05 -1.195238E-02 -1.942759E-05 -2.092949E-02 -5.642842E-05 -1.529365E-02 -3.149749E-05 -2.339941E-02 -7.277783E-05 -2.913166E-02 -1.578357E-04 -1.733543E-02 -4.484055E-05 -2.332788E-02 -5.763144E-05 -2.815285E-02 -1.152065E-04 -2.037972E-02 -4.949376E-05 -3.118074E-02 -1.103084E-04 -2.652943E-02 -7.950175E-05 -2.821700E-02 -9.085185E-05 -2.985586E-02 -1.075718E-04 -2.519195E-02 -7.329635E-05 -2.548988E-02 -7.800201E-05 -4.334204E-02 -2.354327E-04 -4.370525E-02 -2.090531E-04 -4.006925E-02 -2.178363E-04 -4.954585E-02 -2.700073E-04 -4.092841E-02 -2.238665E-04 -2.776415E-02 -8.914334E-05 -4.493102E-02 -2.333231E-04 -5.357023E-02 -3.291447E-04 -3.963439E-02 -1.816002E-04 -5.209753E-02 -3.068180E-04 -4.986170E-02 -2.874548E-04 -4.594257E-02 -2.466016E-04 -7.630622E-02 -6.261797E-04 -5.881534E-02 -3.747957E-04 -6.780811E-02 -4.976784E-04 -8.984762E-02 -8.898655E-04 -5.491164E-02 -3.345133E-04 -5.735501E-02 -3.771763E-04 -7.877228E-02 -6.794994E-04 -9.906424E-02 -1.046741E-03 -5.785970E-02 -4.133043E-04 -7.848689E-02 -6.870125E-04 -1.047444E-01 -1.223298E-03 -7.404855E-02 -5.854937E-04 -9.032228E-02 -8.427419E-04 -8.401933E-02 -7.146695E-04 -8.655196E-02 -8.118612E-04 -1.130946E-01 -1.398959E-03 -9.597180E-02 -1.024696E-03 -1.173027E-01 -1.427372E-03 -1.057440E-01 -1.169551E-03 -1.236176E-01 -1.570398E-03 -1.126957E-01 -1.332155E-03 -1.492376E-01 -2.275171E-03 -1.540101E-01 -2.443893E-03 -1.715020E-01 -3.018404E-03 -7.291542E-02 -5.897697E-04 -7.660305E-02 -6.300479E-04 -9.197170E-02 -8.994110E-04 -9.982203E-02 -1.045655E-03 -1.033399E-01 -1.152382E-03 -1.139526E-01 -1.408827E-03 -8.350520E-02 -7.494750E-04 -9.839459E-02 -1.011369E-03 -1.077860E-01 -1.279212E-03 -1.228340E-01 -1.621769E-03 -1.144053E-01 -1.334188E-03 -1.571539E-01 -2.565349E-03 -6.233439E-02 -4.263330E-04 -6.563411E-02 -5.048147E-04 -6.507814E-02 -4.812141E-04 -6.362658E-02 -4.417375E-04 -7.986988E-02 -7.190156E-04 -8.970255E-02 -8.714050E-04 -5.717855E-02 -3.416545E-04 -5.668804E-02 -3.415895E-04 -7.197316E-02 -5.676520E-04 -7.136224E-02 -5.599617E-04 -8.054458E-02 -7.445675E-04 -1.023397E-01 -1.107780E-03 -4.458142E-02 -2.130924E-04 -5.439535E-02 -3.275861E-04 -5.080989E-02 -2.798307E-04 -3.946665E-02 -1.879310E-04 -6.271593E-02 -4.257844E-04 -4.965993E-02 -2.741721E-04 -4.221851E-02 -1.992549E-04 -3.585135E-02 -1.543110E-04 -4.558668E-02 -2.566453E-04 -3.888908E-02 -1.783945E-04 -4.502379E-02 -2.492156E-04 -5.625550E-02 -3.626058E-04 -2.128686E-02 -5.310350E-05 -2.848081E-02 -1.032351E-04 -3.198369E-02 -1.191780E-04 -3.220035E-02 -1.276802E-04 -3.463447E-02 -1.270326E-04 -3.109004E-02 -1.168059E-04 -2.332999E-02 -6.452643E-05 -2.117480E-02 -5.228816E-05 -2.838827E-02 -1.161483E-04 -2.329241E-02 -6.809020E-05 -2.459549E-02 -7.138168E-05 -2.765800E-02 -9.045558E-05 -1.370243E-02 -3.338519E-05 -1.582736E-02 -3.857108E-05 -1.914636E-02 -4.789559E-05 -1.905516E-02 -4.419778E-05 -1.810520E-02 -3.877471E-05 -2.115307E-02 -7.229884E-05 -1.882956E-02 -4.805493E-05 -9.281049E-03 -1.409203E-05 -1.706945E-02 -3.299567E-05 -2.172381E-02 -5.595328E-05 -1.104074E-02 -1.584618E-05 -1.946919E-02 -4.863958E-05 -1.238685E-02 -2.066688E-05 -1.378714E-02 -2.843856E-05 -1.670520E-02 -3.555438E-05 -2.766185E-02 -9.046517E-05 -1.691132E-02 -3.290270E-05 -1.927098E-02 -5.061316E-05 -1.202893E-02 -1.958457E-05 -1.922540E-02 -4.323737E-05 -1.274015E-02 -2.175309E-05 -1.524415E-02 -3.024778E-05 -1.567541E-02 -4.050037E-05 -1.898211E-02 -6.605988E-05 -2.223387E-02 -5.741257E-05 -2.452031E-02 -8.066778E-05 -2.491977E-02 -8.109453E-05 -2.610295E-02 -7.731723E-05 -1.803031E-02 -3.613025E-05 -2.184838E-02 -6.008277E-05 -2.576522E-02 -7.561914E-05 -3.038022E-02 -1.150600E-04 -2.471237E-02 -7.123333E-05 -3.173089E-02 -1.261453E-04 -3.197437E-02 -1.411443E-04 -2.786611E-02 -8.928073E-05 -4.565549E-02 -2.352807E-04 -3.535175E-02 -1.583088E-04 -4.110758E-02 -2.035220E-04 -5.345474E-02 -3.515666E-04 -3.820824E-02 -1.563980E-04 -3.254512E-02 -1.163872E-04 -4.763741E-02 -2.544110E-04 -5.216626E-02 -2.867256E-04 -4.162668E-02 -2.258042E-04 -5.349959E-02 -3.167588E-04 -5.268155E-02 -3.037455E-04 -4.827712E-02 -2.530854E-04 -7.203623E-02 -5.807994E-04 -5.256504E-02 -2.870331E-04 -7.227033E-02 -5.557721E-04 -8.778648E-02 -8.961223E-04 -5.726539E-02 -3.647823E-04 -5.856701E-02 -3.711206E-04 -8.418818E-02 -8.420171E-04 -1.110902E-01 -1.311451E-03 -6.849413E-02 -4.990580E-04 -8.795189E-02 -8.053436E-04 -1.002509E-01 -1.136773E-03 -8.057346E-02 -7.326604E-04 -8.129820E-02 -6.905078E-04 -7.847809E-02 -7.619983E-04 -9.737274E-02 -1.035174E-03 -1.154716E-01 -1.385047E-03 -9.040089E-02 -8.570313E-04 -1.211664E-01 -1.547158E-03 -1.031330E-01 -1.146280E-03 -1.249586E-01 -1.649464E-03 -7.259655E-02 -5.736210E-04 -9.951286E-02 -1.046118E-03 -1.634971E-01 -2.756448E-03 -1.297207E-01 -1.735289E-03 -6.477036E-02 -5.010788E-04 -7.479236E-02 -6.145152E-04 -9.983365E-02 -1.091064E-03 -1.057264E-01 -1.249917E-03 -8.524281E-02 -7.807948E-04 -1.159165E-01 -1.434680E-03 -7.914697E-02 -6.791950E-04 -9.369015E-02 -9.476358E-04 -9.819988E-02 -1.022095E-03 -9.686028E-02 -1.016599E-03 -1.294766E-01 -1.810274E-03 -1.296189E-01 -1.785443E-03 -5.535491E-02 -4.100726E-04 -5.475647E-02 -3.309264E-04 -7.075605E-02 -6.187772E-04 -6.387417E-02 -4.196497E-04 -7.578685E-02 -6.019889E-04 -8.903112E-02 -9.119475E-04 -6.385945E-02 -4.650950E-04 -5.390120E-02 -3.165606E-04 -5.744329E-02 -3.580388E-04 -5.920600E-02 -3.893593E-04 -7.525234E-02 -6.092966E-04 -8.573764E-02 -7.942436E-04 -2.797792E-02 -8.517754E-05 -3.790894E-02 -1.764825E-04 -4.239936E-02 -2.094748E-04 -4.003024E-02 -1.918379E-04 -5.137956E-02 -3.003587E-04 -3.883708E-02 -2.005906E-04 -2.858632E-02 -9.126637E-05 -3.012004E-02 -9.617653E-05 -4.160100E-02 -1.981616E-04 -4.247458E-02 -1.951666E-04 -3.989011E-02 -1.920698E-04 -5.270057E-02 -4.088719E-04 -1.934929E-02 -4.412789E-05 -2.359082E-02 -7.371994E-05 -2.394998E-02 -6.675994E-05 -2.637339E-02 -8.517554E-05 -2.328326E-02 -6.212789E-05 -3.651578E-02 -1.464186E-04 -2.140441E-02 -5.613878E-05 -2.241671E-02 -6.918410E-05 -2.337122E-02 -6.432183E-05 -2.144477E-02 -6.203786E-05 -3.024192E-02 -1.083505E-04 -3.331930E-02 -1.347081E-04 -1.564695E-02 -3.553327E-05 -1.812645E-02 -3.940659E-05 -2.235798E-02 -6.269701E-05 -1.419345E-02 -3.096864E-05 -2.228629E-02 -7.578567E-05 -1.860794E-02 -4.332178E-05 -1.533239E-02 -3.896858E-05 -1.580257E-02 -3.874692E-05 -2.185955E-02 -7.842950E-05 -1.195136E-02 -1.715334E-05 -1.691111E-02 -5.345113E-05 -1.575376E-02 -3.654434E-05 -1.594875E-02 -2.957220E-05 -1.223541E-02 -2.476765E-05 -1.370590E-02 -2.764042E-05 -2.661246E-02 -1.016998E-04 -2.080371E-02 -4.813282E-05 -2.468736E-02 -6.974038E-05 -1.405638E-02 -2.572385E-05 -2.459760E-02 -7.159383E-05 -1.228308E-02 -1.773752E-05 -1.017949E-02 -1.885833E-05 -2.024611E-02 -5.718430E-05 -1.797631E-02 -4.281825E-05 -1.570780E-02 -3.056781E-05 -1.271841E-02 -2.456995E-05 -2.932023E-02 -9.261980E-05 -3.237241E-02 -1.211680E-04 -2.105195E-02 -5.549955E-05 -2.483667E-02 -7.162437E-05 -2.011907E-02 -5.130375E-05 -1.983609E-02 -4.987791E-05 -1.492495E-02 -3.087968E-05 -2.870495E-02 -9.962637E-05 -2.722358E-02 -8.845542E-05 -2.118986E-02 -5.204535E-05 -4.348038E-02 -2.209404E-04 -3.712818E-02 -1.689129E-04 -4.138693E-02 -1.981821E-04 -4.470777E-02 -2.289185E-04 -2.764424E-02 -9.090079E-05 -2.655560E-02 -9.474172E-05 -4.524862E-02 -2.292538E-04 -5.164196E-02 -2.774634E-04 -3.466551E-02 -1.631603E-04 -3.563208E-02 -1.379234E-04 -4.211986E-02 -1.930478E-04 -3.079625E-02 -1.135265E-04 -5.465211E-02 -3.301887E-04 -4.860409E-02 -2.888426E-04 -7.075978E-02 -5.108370E-04 -6.867881E-02 -5.424435E-04 -3.427613E-02 -1.426574E-04 -5.955830E-02 -3.940963E-04 -6.828982E-02 -4.738886E-04 -6.525289E-02 -4.898769E-04 -3.737982E-02 -1.638396E-04 -5.286730E-02 -3.082087E-04 -6.158083E-02 -4.216724E-04 -4.980750E-02 -2.726737E-04 -5.696575E-02 -3.692634E-04 -5.058820E-02 -2.744701E-04 -6.726109E-02 -4.926664E-04 -7.839336E-02 -6.590448E-04 -7.010209E-02 -5.719905E-04 -8.384171E-02 -7.652328E-04 -6.937383E-02 -5.168671E-04 -9.468263E-02 -9.868090E-04 -5.004326E-02 -2.785169E-04 -6.606152E-02 -4.541359E-04 -9.675798E-02 -1.063286E-03 -7.230289E-02 -5.573682E-04 -6.543974E-02 -5.078440E-04 -5.267920E-02 -3.176937E-04 -8.194553E-02 -6.982175E-04 -9.613453E-02 -1.079846E-03 -6.113228E-02 -4.004864E-04 -8.522953E-02 -7.810809E-04 -5.316859E-02 -3.091340E-04 -7.827195E-02 -6.540396E-04 -5.463776E-02 -3.503148E-04 -5.071284E-02 -3.191021E-04 -8.557140E-02 -7.639230E-04 -7.700044E-02 -6.547991E-04 -5.105846E-02 -2.888427E-04 -5.004859E-02 -2.887941E-04 -5.147919E-02 -3.256217E-04 -5.538533E-02 -3.360110E-04 -6.140039E-02 -4.129538E-04 -5.936836E-02 -4.315037E-04 -4.930384E-02 -2.889606E-04 -4.619680E-02 -2.419601E-04 -4.445739E-02 -2.331544E-04 -4.824154E-02 -2.469526E-04 -6.974394E-02 -5.056104E-04 -6.084953E-02 -3.932556E-04 -3.577286E-02 -1.452599E-04 -3.166228E-02 -1.303590E-04 -3.862777E-02 -1.632780E-04 -3.916934E-02 -1.779181E-04 -3.625045E-02 -1.745839E-04 -4.231134E-02 -2.225093E-04 -3.359703E-02 -1.419870E-04 -4.006089E-02 -1.707049E-04 -3.658866E-02 -1.730857E-04 -3.341254E-02 -1.297042E-04 -4.263417E-02 -2.215026E-04 -3.955323E-02 -1.921082E-04 -2.174079E-02 -5.222197E-05 -2.442434E-02 -8.243601E-05 -2.914443E-02 -1.214208E-04 -1.824800E-02 -4.893719E-05 -3.091152E-02 -1.051207E-04 -2.972565E-02 -1.063333E-04 -3.002196E-02 -1.026816E-04 -1.967353E-02 -5.819743E-05 -2.921034E-02 -1.035681E-04 -2.329404E-02 -7.915033E-05 -2.537416E-02 -9.811049E-05 -2.636605E-02 -8.177530E-05 -9.035064E-03 -1.555651E-05 -1.655675E-02 -4.002782E-05 -1.372662E-02 -3.244904E-05 -1.311878E-02 -2.619244E-05 -1.611214E-02 -3.084313E-05 -1.574836E-02 -3.450927E-05 -7.693366E-03 -1.606361E-05 -1.135596E-02 -1.801519E-05 -1.342059E-02 -2.516888E-05 -1.160985E-02 -2.390704E-05 -1.383386E-02 -2.800471E-05 -1.971360E-02 -8.842562E-05 -1.183879E-02 -1.932907E-05 -8.105380E-03 -1.234549E-05 -1.271210E-02 -2.405656E-05 -1.537131E-02 -3.137697E-05 -1.591997E-02 -3.769775E-05 -1.088552E-02 -1.713348E-05 -1.515322E-02 -3.039160E-05 -1.275379E-02 -2.585038E-05 -8.475047E-03 -1.032301E-05 -1.017506E-02 -2.019761E-05 -1.192565E-02 -2.149550E-05 -8.221048E-03 -8.986302E-06 -1.307738E-02 -2.107929E-05 -1.086073E-02 -1.558009E-05 -2.199295E-02 -5.739512E-05 -1.994568E-02 -5.494728E-05 -1.534082E-02 -3.625194E-05 -1.764761E-02 -4.067341E-05 -1.796742E-02 -4.680639E-05 -2.032190E-02 -5.017032E-05 -1.749541E-02 -4.866856E-05 -1.732369E-02 -4.955885E-05 -1.681732E-02 -3.837118E-05 -1.769405E-02 -5.487143E-05 -2.888259E-02 -1.026998E-04 -2.098585E-02 -6.932453E-05 -3.313341E-02 -1.351006E-04 -3.207197E-02 -1.295024E-04 -2.459220E-02 -8.336446E-05 -2.450985E-02 -6.648377E-05 -3.117162E-02 -1.195002E-04 -4.003079E-02 -2.123347E-04 -2.176609E-02 -6.269817E-05 -2.994428E-02 -1.235590E-04 -3.524085E-02 -1.599016E-04 -2.408409E-02 -6.486120E-05 -4.186261E-02 -1.804413E-04 -4.461985E-02 -2.229520E-04 -3.815900E-02 -1.674626E-04 -4.651996E-02 -2.353452E-04 -4.372936E-02 -2.126895E-04 -3.471725E-02 -1.536658E-04 -3.777496E-02 -1.682954E-04 -4.542053E-02 -2.887036E-04 -3.302349E-02 -1.252640E-04 -3.007472E-02 -1.103699E-04 -4.167073E-02 -1.843542E-04 -3.435406E-02 -1.576055E-04 -5.142055E-02 -2.840339E-04 -4.459791E-02 -2.437118E-04 -5.240798E-02 -3.061931E-04 -6.184982E-02 -3.987655E-04 -4.185540E-02 -2.035740E-04 -4.274424E-02 -1.929214E-04 -4.853301E-02 -2.820023E-04 -7.063952E-02 -5.492235E-04 -4.633212E-02 -2.687210E-04 -5.821695E-02 -3.976817E-04 -6.625094E-02 -4.940422E-04 -6.129214E-02 -4.099540E-04 -3.772075E-02 -1.660719E-04 -3.714570E-02 -1.541156E-04 -4.493005E-02 -2.310471E-04 -4.557412E-02 -2.432878E-04 -4.065422E-02 -1.871281E-04 -5.417420E-02 -3.505844E-04 -3.708658E-02 -1.717372E-04 -5.130291E-02 -3.077258E-04 -3.417926E-02 -1.492890E-04 -3.377463E-02 -1.256710E-04 -5.369283E-02 -3.432927E-04 -4.836021E-02 -2.588228E-04 -3.269371E-02 -1.286374E-04 -2.896989E-02 -1.123018E-04 -4.848291E-02 -2.612160E-04 -4.333314E-02 -2.292463E-04 -3.759979E-02 -1.680559E-04 -5.061237E-02 -2.724683E-04 -3.926039E-02 -1.797325E-04 -3.938137E-02 -1.851970E-04 -3.570566E-02 -1.506562E-04 -4.159909E-02 -2.157074E-04 -5.099360E-02 -2.844970E-04 -4.605309E-02 -2.508391E-04 -2.500341E-02 -7.363724E-05 -3.039879E-02 -1.123549E-04 -3.534598E-02 -1.698480E-04 -3.702744E-02 -1.486744E-04 -3.410129E-02 -1.365716E-04 -3.991621E-02 -1.846954E-04 -3.186334E-02 -1.158884E-04 -3.268754E-02 -1.178259E-04 -2.428429E-02 -7.876563E-05 -2.495449E-02 -7.906081E-05 -3.551756E-02 -1.519603E-04 -3.018593E-02 -9.724962E-05 -2.032098E-02 -4.781656E-05 -3.017998E-02 -1.313561E-04 -2.424929E-02 -6.480258E-05 -1.950042E-02 -4.417513E-05 -2.943418E-02 -9.948664E-05 -2.707621E-02 -1.035208E-04 -1.957350E-02 -4.711947E-05 -1.583335E-02 -2.891313E-05 -2.972335E-02 -1.047751E-04 -1.739423E-02 -4.376709E-05 -1.705633E-02 -3.892027E-05 -2.121975E-02 -5.891607E-05 -1.002146E-02 -2.339078E-05 -1.405706E-02 -3.175594E-05 -1.011207E-02 -1.585291E-05 -1.573069E-02 -3.554827E-05 -1.518567E-02 -3.047833E-05 -1.617187E-02 -3.397385E-05 -7.920296E-03 -1.026935E-05 -1.007121E-02 -1.858100E-05 -1.510615E-02 -3.313591E-05 -8.793659E-03 -1.446961E-05 -1.205856E-02 -2.648093E-05 -8.862721E-03 -1.309660E-05 -1.096965E-02 -1.673476E-05 -9.547722E-03 -1.233633E-05 -1.206088E-02 -1.968307E-05 -1.373042E-02 -2.332622E-05 -7.956349E-03 -9.952553E-06 -1.135126E-02 -2.254375E-05 -8.206639E-03 -1.278801E-05 -8.323868E-03 -9.465618E-06 -5.007925E-03 -3.901625E-06 -1.175607E-02 -2.482648E-05 -1.115574E-02 -1.628158E-05 -1.194446E-02 -2.092004E-05 -1.597054E-02 -4.823093E-05 -2.068463E-02 -6.502935E-05 -1.091363E-02 -1.741992E-05 -1.977283E-02 -6.091082E-05 -1.302782E-02 -3.043481E-05 -9.410139E-03 -1.600990E-05 -2.280622E-02 -8.006568E-05 -1.891920E-02 -4.976676E-05 -1.429805E-02 -3.039093E-05 -1.718931E-02 -4.113244E-05 -1.888654E-02 -4.574971E-05 -1.694065E-02 -4.597734E-05 -2.282444E-02 -7.094719E-05 -1.988920E-02 -4.882297E-05 -2.742750E-02 -8.906026E-05 -2.101679E-02 -5.924755E-05 -1.895054E-02 -4.827704E-05 -1.764949E-02 -3.979458E-05 -2.040100E-02 -5.014728E-05 -2.987304E-02 -1.026215E-04 -1.943000E-02 -5.159278E-05 -2.438024E-02 -8.804424E-05 -1.861542E-02 -5.122973E-05 -2.192303E-02 -7.333728E-05 -2.042748E-02 -5.322568E-05 -1.860799E-02 -4.480883E-05 -2.511896E-02 -8.995213E-05 -2.543363E-02 -7.306514E-05 -2.559384E-02 -7.149326E-05 -2.813173E-02 -8.655606E-05 -1.981717E-02 -5.094262E-05 -3.244910E-02 -1.169817E-04 -2.278981E-02 -6.510336E-05 -2.089376E-02 -5.410560E-05 -4.419213E-02 -2.403809E-04 -3.061708E-02 -1.062702E-04 -2.717980E-02 -8.418003E-05 -2.722963E-02 -1.052169E-04 -3.623076E-02 -1.607841E-04 -3.979211E-02 -1.939688E-04 -2.474544E-02 -8.590714E-05 -3.372593E-02 -1.249071E-04 -2.961528E-02 -1.048999E-04 -3.445466E-02 -1.406914E-04 -2.454390E-02 -6.874037E-05 -2.368906E-02 -7.545364E-05 -3.410678E-02 -1.304264E-04 -2.229870E-02 -5.686464E-05 -2.886351E-02 -1.132080E-04 -2.149789E-02 -5.426448E-05 -3.009295E-02 -1.146713E-04 -3.196558E-02 -1.284969E-04 -2.870373E-02 -1.154291E-04 -2.864913E-02 -1.010245E-04 -2.270198E-02 -5.519665E-05 -2.760090E-02 -9.328867E-05 -2.484231E-02 -7.506549E-05 -2.750137E-02 -8.791015E-05 -3.435235E-02 -1.548738E-04 -2.827137E-02 -9.747974E-05 -2.704164E-02 -8.477548E-05 -3.021819E-02 -1.103214E-04 -3.294793E-02 -1.535995E-04 -4.645100E-02 -2.665607E-04 -1.872361E-02 -5.132776E-05 -2.760454E-02 -9.345927E-05 -3.258565E-02 -1.242178E-04 -3.666600E-02 -1.621867E-04 -2.545558E-02 -7.466531E-05 -2.185810E-02 -5.238791E-05 -3.105333E-02 -1.301601E-04 -3.170709E-02 -1.068496E-04 -1.858402E-02 -4.089794E-05 -2.136675E-02 -8.120782E-05 -2.443102E-02 -6.744651E-05 -2.220457E-02 -6.176678E-05 -2.702281E-02 -8.511652E-05 -2.446488E-02 -7.312450E-05 -2.589652E-02 -8.029643E-05 -2.337286E-02 -6.920676E-05 -2.906370E-02 -9.769604E-05 -1.361898E-02 -2.599329E-05 -1.718829E-02 -4.532911E-05 -2.553342E-02 -8.882028E-05 -1.945946E-02 -5.218395E-05 -1.097230E-02 -2.337563E-05 -2.298715E-02 -7.077749E-05 -2.181520E-02 -7.357036E-05 -1.664629E-02 -3.440840E-05 -1.886608E-02 -6.788750E-05 -1.789466E-02 -3.954208E-05 -1.813733E-02 -4.477178E-05 -1.319469E-02 -3.100847E-05 -1.940763E-02 -6.158493E-05 -1.149667E-02 -1.737857E-05 -2.405005E-02 -9.854391E-05 -1.421017E-02 -2.890108E-05 -1.029738E-02 -1.683759E-05 -1.065410E-02 -1.640087E-05 -9.811218E-03 -1.720212E-05 -1.158507E-02 -1.760286E-05 -1.432482E-02 -2.834073E-05 -3.465902E-03 -3.161524E-06 -1.038868E-02 -2.168047E-05 -1.116866E-02 -1.793978E-05 -8.524850E-03 -9.322322E-06 -1.237537E-02 -3.617368E-05 -1.465218E-02 -3.350573E-05 -7.514724E-03 -9.368017E-06 -2.560189E-03 -1.617445E-06 -8.383240E-03 -1.252593E-05 -9.446758E-03 -1.648768E-05 -7.696663E-03 -8.060991E-06 -9.311783E-03 -1.189977E-05 -6.939672E-03 -8.850888E-06 -5.252459E-03 -4.564362E-06 -7.234899E-03 -1.328951E-05 -8.665099E-03 -1.300142E-05 -9.793568E-03 -2.082520E-05 -8.825280E-03 -1.579613E-05 -1.194803E-02 -1.908193E-05 -1.074524E-02 -2.172388E-05 -1.481905E-02 -2.964893E-05 -1.294498E-02 -3.011738E-05 -8.115330E-03 -1.173017E-05 -1.093458E-02 -1.599591E-05 -1.415025E-02 -2.928045E-05 -8.999567E-03 -1.238982E-05 -7.873894E-03 -9.521432E-06 -7.526246E-03 -7.594924E-06 -1.349525E-02 -3.958103E-05 -1.459461E-02 -3.194364E-05 -1.198421E-02 -2.138597E-05 -1.015128E-02 -1.539096E-05 -1.747797E-02 -3.561184E-05 -2.401867E-02 -7.148091E-05 -1.586654E-02 -3.323231E-05 -1.800611E-02 -4.401522E-05 -1.463450E-02 -2.989044E-05 -1.683853E-02 -3.902727E-05 -1.359168E-02 -2.739250E-05 -9.625117E-03 -1.469220E-05 -1.294386E-02 -2.799895E-05 -1.103469E-02 -2.133672E-05 -1.869882E-02 -4.772681E-05 -1.503975E-02 -2.896451E-05 -1.677894E-02 -4.314601E-05 -2.258467E-02 -1.041456E-04 -1.878411E-02 -4.558049E-05 -1.310146E-02 -2.541487E-05 -1.653039E-02 -3.204738E-05 -1.927088E-02 -5.304249E-05 -8.390867E-03 -1.408823E-05 -1.750394E-02 -4.947439E-05 -1.909888E-02 -5.305029E-05 -1.321359E-02 -3.195949E-05 -1.767250E-02 -4.284184E-05 -1.766010E-02 -4.445269E-05 -1.752133E-02 -3.470706E-05 -2.024733E-02 -5.360810E-05 -1.219423E-02 -1.890812E-05 -1.433551E-02 -2.552372E-05 -1.488366E-02 -3.633706E-05 -1.309982E-02 -2.303088E-05 -1.409845E-02 -2.964092E-05 -1.364964E-02 -2.674995E-05 -2.346093E-02 -7.742790E-05 -1.619070E-02 -3.640820E-05 -1.814514E-02 -4.174971E-05 -2.088143E-02 -7.915120E-05 -2.104482E-02 -4.999110E-05 -2.108577E-02 -5.080473E-05 -1.346978E-02 -2.428984E-05 -1.812802E-02 -3.900263E-05 -1.634894E-02 -3.058475E-05 -1.823343E-02 -4.657246E-05 -1.701022E-02 -3.432397E-05 -1.436832E-02 -2.643066E-05 -1.883466E-02 -4.651260E-05 -1.591237E-02 -3.845574E-05 -1.590406E-02 -3.248073E-05 -1.054631E-02 -1.655070E-05 -2.057489E-02 -5.055329E-05 -1.697824E-02 -3.893444E-05 -1.770103E-02 -4.383559E-05 -2.134851E-02 -6.780393E-05 -1.403120E-02 -2.708815E-05 -1.449734E-02 -2.419619E-05 -1.239576E-02 -2.043627E-05 -1.415699E-02 -2.224645E-05 -1.888682E-02 -4.935465E-05 -2.160682E-02 -6.329152E-05 -2.247940E-02 -7.810124E-05 -9.606213E-03 -1.667840E-05 -2.476229E-02 -8.144159E-05 -1.725175E-02 -3.611282E-05 -1.207190E-02 -1.626596E-05 -1.353421E-02 -2.329766E-05 -1.059055E-02 -1.677463E-05 -9.737491E-03 -1.431106E-05 -6.593375E-03 -7.399377E-06 -7.039159E-03 -1.176702E-05 -1.413677E-02 -3.138472E-05 -1.094329E-02 -2.263303E-05 -6.672931E-03 -6.045366E-06 -1.032380E-02 -2.352298E-05 -8.848889E-03 -1.498309E-05 -1.268157E-02 -2.766060E-05 -7.349115E-03 -1.091693E-05 -1.228088E-02 -2.189060E-05 -1.067250E-02 -1.715732E-05 -1.212246E-02 -2.269620E-05 -4.674814E-03 -3.219220E-06 -8.219501E-03 -1.509768E-05 -1.073016E-02 -2.022852E-05 -9.406782E-03 -1.697200E-05 -7.646611E-03 -1.456233E-05 -1.080915E-02 -2.357182E-05 -6.308610E-03 -8.327166E-06 -7.590107E-03 -8.423541E-06 -1.348669E-02 -2.807690E-05 -1.095856E-02 -2.888898E-05 -5.395294E-03 -5.989965E-06 -6.079841E-03 -7.580064E-06 -7.505564E-03 -8.646729E-06 -6.201233E-03 -6.428779E-06 -1.063698E-02 -1.939413E-05 -7.313889E-03 -9.141624E-06 -9.430810E-03 -1.648005E-05 -4.842603E-03 -4.844512E-06 -5.427814E-03 -4.345647E-06 -6.437292E-03 -1.207973E-05 -8.251158E-03 -1.318350E-05 -8.809423E-03 -1.058811E-05 -5.856619E-03 -4.969559E-06 -6.991101E-03 -6.849651E-06 -5.629621E-03 -5.550323E-06 -2.585500E-03 -1.553122E-06 -1.066559E-02 -1.715979E-05 -3.085260E-03 -6.253271E-06 -9.034984E-03 -1.521387E-05 -6.190606E-03 -9.176053E-06 -9.871403E-03 -1.358973E-05 -9.739545E-03 -1.286586E-05 -7.798343E-03 -9.886920E-06 -8.149958E-03 -1.041850E-05 -2.102030E-02 -6.158217E-05 -1.435765E-02 -3.237028E-05 -7.642569E-03 -7.907376E-06 -1.123406E-02 -1.955695E-05 -1.457081E-02 -3.914562E-05 -1.788958E-02 -5.996510E-05 -1.769436E-02 -4.502383E-05 -1.240825E-02 -2.303672E-05 -1.715444E-02 -4.328672E-05 -1.566180E-02 -3.978890E-05 -1.487883E-02 -3.400964E-05 -8.561200E-03 -1.036201E-05 -2.295904E-02 -5.920445E-05 -1.388508E-02 -3.937752E-05 -1.678190E-02 -4.497456E-05 -2.154484E-02 -5.386125E-05 -6.765444E-03 -7.415266E-06 -1.215420E-02 -2.091620E-05 -2.017061E-02 -7.287759E-05 -1.909053E-02 -5.506644E-05 -1.735888E-02 -3.831995E-05 -1.991557E-02 -4.972306E-05 -1.790749E-02 -4.112227E-05 -1.407457E-02 -3.094925E-05 -2.974572E-02 -1.118172E-04 -1.708785E-02 -4.386435E-05 -2.659193E-02 -9.219939E-05 -3.035144E-02 -1.010119E-04 -1.546737E-02 -3.391982E-05 -1.887938E-02 -4.714248E-05 -1.653679E-02 -4.949250E-05 -2.110944E-02 -5.597556E-05 -2.409526E-02 -6.448595E-05 -2.230819E-02 -5.707848E-05 -1.670895E-02 -3.363955E-05 -1.501463E-02 -2.702669E-05 -1.631677E-02 -3.749166E-05 -1.693626E-02 -4.121173E-05 -2.622206E-02 -7.529216E-05 -2.377917E-02 -7.407839E-05 -1.384279E-02 -2.857021E-05 -2.126418E-02 -7.856414E-05 -1.911630E-02 -4.652883E-05 -2.184179E-02 -5.852148E-05 -1.866708E-02 -6.033622E-05 -1.172279E-02 -3.314139E-05 -1.311652E-02 -2.727134E-05 -1.670812E-02 -4.624330E-05 -1.830609E-02 -5.348783E-05 -8.513210E-03 -1.188353E-05 -1.897475E-02 -4.496529E-05 -1.598385E-02 -3.154883E-05 -1.811274E-02 -4.681726E-05 -1.851236E-02 -4.861112E-05 -1.207908E-02 -2.020737E-05 -1.762593E-02 -4.600809E-05 -1.426171E-02 -3.389465E-05 -1.390938E-02 -4.304132E-05 -1.119665E-02 -2.002203E-05 -7.985862E-03 -1.299182E-05 -1.682922E-02 -3.705605E-05 -1.031217E-02 -1.306045E-05 -1.806448E-02 -4.605140E-05 -1.608612E-02 -3.278234E-05 -7.379583E-03 -7.040180E-06 -1.449866E-02 -3.734314E-05 -1.126405E-02 -1.862160E-05 -1.434466E-02 -3.067276E-05 -1.321482E-02 -2.270946E-05 -1.175946E-02 -2.009819E-05 -8.940307E-03 -1.544249E-05 -1.025576E-02 -1.330247E-05 -1.007614E-02 -1.877197E-05 -8.436802E-03 -1.196625E-05 -2.085623E-02 -4.953759E-05 -1.670794E-02 -3.716863E-05 -1.187761E-02 -1.890192E-05 -1.850976E-02 -4.429493E-05 -4.944517E-03 -5.165715E-06 -8.128340E-03 -1.296218E-05 -1.004447E-02 -2.214422E-05 -9.963052E-03 -1.184842E-05 -7.100471E-03 -8.005623E-06 -8.470273E-03 -9.214382E-06 -1.033575E-02 -1.452750E-05 -9.624022E-03 -1.558452E-05 -8.869432E-03 -1.517840E-05 -1.644470E-02 -3.397196E-05 -7.598019E-03 -8.133919E-06 -8.945737E-03 -9.377464E-06 -5.254834E-03 -4.575273E-06 -7.542267E-03 -7.505652E-06 -9.629827E-03 -1.342434E-05 -1.060266E-02 -1.838055E-05 -3.984507E-03 -2.782462E-06 -6.823286E-03 -7.715504E-06 -8.192618E-03 -1.009785E-05 -6.740618E-03 -6.753208E-06 -1.002813E-02 -1.566585E-05 -1.402360E-02 -2.627061E-05 -9.551680E-03 -1.549743E-05 -8.410173E-03 -1.532161E-05 -9.576682E-03 -1.926642E-05 -1.644285E-02 -3.894395E-05 -9.352112E-03 -1.947845E-05 -9.522578E-03 -1.480589E-05 -1.000109E-02 -1.623473E-05 -1.271924E-02 -2.496798E-05 -1.245375E-02 -2.043780E-05 -1.519888E-02 -3.853835E-05 -6.618627E-03 -6.345424E-06 -7.617580E-03 -8.381516E-06 -7.713051E-03 -1.245806E-05 -8.903456E-03 -2.254781E-05 -1.402208E-02 -2.721848E-05 -1.177140E-02 -1.953130E-05 -1.553072E-02 -2.893580E-05 -1.482066E-02 -3.081970E-05 -9.853191E-03 -1.543282E-05 -1.186384E-02 -2.281665E-05 -1.908232E-02 -6.119132E-05 -1.778254E-02 -3.849134E-05 -1.611496E-02 -4.189813E-05 -1.421950E-02 -3.103442E-05 -2.000883E-02 -5.088716E-05 -1.245870E-02 -2.904861E-05 -2.474667E-02 -7.684411E-05 -1.941450E-02 -4.850566E-05 -1.968721E-02 -4.734318E-05 -2.432138E-02 -7.957731E-05 -1.944046E-02 -4.206030E-05 -2.088512E-02 -5.312412E-05 -2.457361E-02 -7.162438E-05 -2.803303E-02 -9.243404E-05 -2.233188E-02 -6.441041E-05 -2.574667E-02 -7.744780E-05 -2.097121E-02 -5.650314E-05 -2.655826E-02 -8.660847E-05 -3.014326E-02 -9.991619E-05 -2.918919E-02 -9.452555E-05 -2.056057E-02 -5.184372E-05 -2.990570E-02 -1.037648E-04 -2.737932E-02 -8.833139E-05 -2.356419E-02 -7.644776E-05 -3.458839E-02 -1.364027E-04 -3.248751E-02 -1.186240E-04 -3.228724E-02 -1.162923E-04 -2.982979E-02 -1.051045E-04 -2.749198E-02 -9.013699E-05 -2.265201E-02 -7.821751E-05 -2.861490E-02 -8.974738E-05 -3.877353E-02 -1.862085E-04 -2.264214E-02 -6.419386E-05 -2.565522E-02 -8.379023E-05 -3.327436E-02 -1.251213E-04 -3.365022E-02 -1.304927E-04 -3.331544E-02 -1.267184E-04 -3.940664E-02 -2.082874E-04 -3.417164E-02 -1.331489E-04 -3.695924E-02 -1.695935E-04 -3.660181E-02 -1.980269E-04 -3.049799E-02 -1.104243E-04 -3.359904E-02 -1.326383E-04 -2.972450E-02 -9.587313E-05 -2.323160E-02 -6.471218E-05 -1.839074E-02 -4.208719E-05 -2.714264E-02 -9.372657E-05 -2.872170E-02 -1.078922E-04 -3.679226E-02 -1.459261E-04 -2.377295E-02 -6.705585E-05 -3.481909E-02 -1.319533E-04 -3.638325E-02 -1.510511E-04 -2.047379E-02 -4.974814E-05 -3.932322E-02 -1.843858E-04 -1.516217E-02 -3.326445E-05 -2.722332E-02 -9.017263E-05 -1.892837E-02 -4.765562E-05 -1.649353E-02 -4.048523E-05 -2.604495E-02 -7.686911E-05 -2.994865E-02 -1.202039E-04 -1.882216E-02 -4.312602E-05 -1.766775E-02 -4.354878E-05 -2.897250E-02 -9.600078E-05 -2.918339E-02 -9.787984E-05 -2.599062E-02 -8.583368E-05 -3.616458E-02 -1.785317E-04 -1.716456E-02 -4.424230E-05 -2.976397E-02 -1.196639E-04 -2.137198E-02 -5.435882E-05 -1.498606E-02 -2.867869E-05 -2.026381E-02 -6.453877E-05 -2.108149E-02 -4.938367E-05 -1.505838E-02 -2.707808E-05 -1.913971E-02 -5.184273E-05 -2.583671E-02 -9.189443E-05 -2.089460E-02 -5.910541E-05 -2.539127E-02 -8.107037E-05 -2.284066E-02 -6.852612E-05 -9.835860E-03 -1.246182E-05 -1.766679E-02 -4.158408E-05 -1.105611E-02 -1.647013E-05 -1.530049E-02 -3.919500E-05 -1.429716E-02 -3.352166E-05 -1.357586E-02 -3.091775E-05 -1.403296E-02 -3.179172E-05 -1.978070E-02 -4.850759E-05 -2.453097E-02 -8.428665E-05 -1.939330E-02 -4.556576E-05 -1.086518E-02 -1.408645E-05 -1.579821E-02 -2.749151E-05 -6.361591E-03 -6.898179E-06 -7.702358E-03 -9.157872E-06 -1.066424E-02 -1.306101E-05 -9.634515E-03 -1.294132E-05 -1.081282E-02 -2.088218E-05 -1.644232E-02 -3.681578E-05 -9.494639E-03 -1.279154E-05 -5.636874E-03 -4.703193E-06 -1.434790E-02 -2.701384E-05 -1.369675E-02 -2.567531E-05 -1.234911E-02 -2.590620E-05 -1.416398E-02 -2.960800E-05 -1.122603E-02 -2.610403E-05 -1.184891E-02 -2.685267E-05 -1.469060E-02 -3.133618E-05 -2.298108E-02 -7.360049E-05 -1.696881E-02 -4.073271E-05 -1.829044E-02 -4.488597E-05 -8.021195E-03 -1.042375E-05 -7.483229E-03 -7.662752E-06 -9.938980E-03 -1.473587E-05 -6.186154E-03 -7.146893E-06 -1.873257E-02 -5.084856E-05 -1.274591E-02 -1.901691E-05 -1.717315E-02 -4.508495E-05 -2.587324E-02 -7.720386E-05 -1.352804E-02 -2.480610E-05 -1.759894E-02 -3.784552E-05 -1.498671E-02 -2.624849E-05 -2.471517E-02 -8.403653E-05 -2.938643E-02 -1.151484E-04 -2.091784E-02 -5.415805E-05 -1.642239E-02 -3.265718E-05 -2.253062E-02 -7.550922E-05 -1.773827E-02 -4.843752E-05 -1.778692E-02 -4.982541E-05 -3.337468E-02 -1.392002E-04 -3.682265E-02 -1.607062E-04 -2.911908E-02 -1.003985E-04 -2.664232E-02 -8.588222E-05 -2.953127E-02 -1.183748E-04 -2.406807E-02 -7.188877E-05 -3.628487E-02 -1.515959E-04 -3.071907E-02 -1.118121E-04 -3.755060E-02 -1.884817E-04 -3.845138E-02 -2.043330E-04 -4.208968E-02 -1.887229E-04 -2.650790E-02 -1.045695E-04 -4.879444E-02 -2.746335E-04 -3.995698E-02 -1.935956E-04 -3.437302E-02 -1.339692E-04 -4.424777E-02 -2.155808E-04 -4.000509E-02 -1.787367E-04 -3.942601E-02 -1.823141E-04 -5.484106E-02 -3.357163E-04 -5.027689E-02 -2.971747E-04 -5.028897E-02 -2.941870E-04 -5.290232E-02 -3.200207E-04 -3.871560E-02 -1.936856E-04 -4.561909E-02 -2.445140E-04 -5.329116E-02 -3.392022E-04 -6.411682E-02 -4.525641E-04 -4.491390E-02 -2.298662E-04 -4.543173E-02 -2.388585E-04 -4.973450E-02 -2.830273E-04 -4.837326E-02 -2.678945E-04 -5.988650E-02 -4.209610E-04 -5.451978E-02 -3.358217E-04 -7.335314E-02 -6.137308E-04 -7.801463E-02 -6.544817E-04 -5.589338E-02 -3.522166E-04 -5.916022E-02 -3.858273E-04 -4.698963E-02 -3.027433E-04 -5.895924E-02 -3.790017E-04 -4.172371E-02 -2.119338E-04 -3.894831E-02 -1.648204E-04 -5.343858E-02 -3.103206E-04 -4.977655E-02 -3.012635E-04 -6.585931E-02 -4.493935E-04 -5.729670E-02 -3.590518E-04 -5.609196E-02 -3.629946E-04 -6.500048E-02 -4.440662E-04 -4.327539E-02 -2.067690E-04 -5.097662E-02 -2.815490E-04 -4.628356E-02 -2.348648E-04 -4.666148E-02 -2.514991E-04 -3.669329E-02 -1.552102E-04 -4.034392E-02 -1.825094E-04 -5.718186E-02 -3.730299E-04 -3.997528E-02 -2.286198E-04 -5.850080E-02 -4.179988E-04 -3.979728E-02 -1.855473E-04 -6.121586E-02 -4.774824E-04 -5.116923E-02 -2.889253E-04 -4.642274E-02 -2.436217E-04 -5.061482E-02 -2.853049E-04 -3.070349E-02 -1.198974E-04 -4.050110E-02 -1.836951E-04 -2.876275E-02 -1.060796E-04 -3.061263E-02 -1.137168E-04 -4.146381E-02 -1.869894E-04 -4.129877E-02 -1.932088E-04 -3.448209E-02 -1.434523E-04 -3.610442E-02 -1.605801E-04 -4.093512E-02 -1.814782E-04 -4.568118E-02 -2.299954E-04 -3.364878E-02 -1.376802E-04 -3.936126E-02 -1.675197E-04 -1.952985E-02 -4.623048E-05 -2.129951E-02 -6.166111E-05 -2.459526E-02 -8.426675E-05 -1.884092E-02 -4.164051E-05 -3.428763E-02 -1.417068E-04 -3.407514E-02 -1.241486E-04 -3.064802E-02 -1.103630E-04 -2.019877E-02 -7.059498E-05 -3.794436E-02 -1.883194E-04 -3.266721E-02 -1.280477E-04 -2.490808E-02 -7.281690E-05 -3.064961E-02 -1.125362E-04 -1.514239E-02 -2.910925E-05 -1.569321E-02 -3.950962E-05 -1.817240E-02 -4.154077E-05 -1.593815E-02 -3.459272E-05 -2.573968E-02 -8.029098E-05 -2.193871E-02 -6.210741E-05 -1.234720E-02 -1.843394E-05 -1.130766E-02 -2.245547E-05 -1.418762E-02 -3.357410E-05 -1.391485E-02 -2.559415E-05 -1.331696E-02 -2.115069E-05 -1.903725E-02 -4.411480E-05 -2.293870E-02 -6.557987E-05 -2.241944E-02 -7.768129E-05 -8.399478E-03 -1.343754E-05 -1.217880E-02 -2.018185E-05 -1.416256E-02 -2.227003E-05 -1.520064E-02 -2.978679E-05 -1.664846E-02 -4.183492E-05 -1.339491E-02 -2.674827E-05 -9.628607E-03 -1.279454E-05 -1.158026E-02 -1.554728E-05 -1.367791E-02 -2.379966E-05 -1.376699E-02 -2.264472E-05 -1.833088E-02 -4.495911E-05 -2.473484E-02 -1.122059E-04 -1.980130E-02 -4.468138E-05 -2.937836E-02 -1.114018E-04 -2.021222E-02 -4.559590E-05 -2.115889E-02 -5.272398E-05 -2.751444E-02 -8.716393E-05 -3.118255E-02 -1.201980E-04 -3.295258E-02 -1.221249E-04 -3.264934E-02 -1.357065E-04 -2.388443E-02 -6.703731E-05 -2.649789E-02 -7.940354E-05 -4.696656E-02 -2.514244E-04 -4.553686E-02 -2.438972E-04 -3.908818E-02 -1.840355E-04 -4.402566E-02 -2.377775E-04 -3.875910E-02 -1.778735E-04 -3.543152E-02 -1.527725E-04 -6.193982E-02 -4.578553E-04 -4.402817E-02 -2.274373E-04 -3.677275E-02 -1.884320E-04 -5.902021E-02 -3.951715E-04 -4.943375E-02 -2.711632E-04 -3.724773E-02 -1.641879E-04 -4.993443E-02 -2.771281E-04 -7.563062E-02 -6.734531E-04 -4.553601E-02 -2.283737E-04 -5.336189E-02 -3.085766E-04 -6.930581E-02 -5.723133E-04 -4.674295E-02 -2.785390E-04 -8.412725E-02 -7.467027E-04 -6.681883E-02 -4.824696E-04 -8.430398E-02 -7.823907E-04 -9.943266E-02 -1.106796E-03 -8.916803E-02 -8.767378E-04 -7.239961E-02 -6.787255E-04 -8.722424E-02 -9.332353E-04 -1.031401E-01 -1.218166E-03 -7.299280E-02 -5.585065E-04 -8.196460E-02 -7.464830E-04 -7.514349E-02 -6.162898E-04 -7.409781E-02 -6.092201E-04 -1.155025E-01 -1.496467E-03 -1.069759E-01 -1.259983E-03 -1.226295E-01 -1.569654E-03 -1.503421E-01 -2.355624E-03 -1.080703E-01 -1.309087E-03 -1.129782E-01 -1.371142E-03 -8.048452E-02 -7.348043E-04 -1.058381E-01 -1.164709E-03 -6.615819E-02 -4.794240E-04 -7.980754E-02 -6.987820E-04 -1.120426E-01 -1.285452E-03 -1.016962E-01 -1.116490E-03 -1.045603E-01 -1.203503E-03 -9.692551E-02 -1.029307E-03 -1.510121E-01 -2.326069E-03 -1.459994E-01 -2.277384E-03 -8.473854E-02 -7.832629E-04 -1.294853E-01 -1.741522E-03 -6.032354E-02 -3.886121E-04 -6.987142E-02 -5.446194E-04 -7.739481E-02 -6.779391E-04 -6.202592E-02 -4.288928E-04 -1.105924E-01 -1.283621E-03 -9.239492E-02 -9.223732E-04 -6.604268E-02 -4.595692E-04 -7.012590E-02 -5.276378E-04 -9.125615E-02 -8.968665E-04 -8.440076E-02 -7.298457E-04 -6.290364E-02 -4.350725E-04 -9.542023E-02 -1.023159E-03 -3.785503E-02 -1.773114E-04 -5.510138E-02 -3.186315E-04 -4.425845E-02 -2.296502E-04 -5.216949E-02 -3.102279E-04 -5.711604E-02 -3.708531E-04 -6.348926E-02 -4.437737E-04 -3.531423E-02 -1.381941E-04 -4.676905E-02 -2.710798E-04 -5.132004E-02 -2.836163E-04 -4.379959E-02 -2.484735E-04 -5.314318E-02 -3.059228E-04 -6.677411E-02 -5.104497E-04 -2.953361E-02 -1.221048E-04 -3.348688E-02 -1.218024E-04 -3.189405E-02 -1.283561E-04 -3.066128E-02 -1.074943E-04 -2.627008E-02 -7.434735E-05 -4.099188E-02 -1.844425E-04 -2.657946E-02 -8.006763E-05 -2.677024E-02 -9.837536E-05 -2.297001E-02 -6.348807E-05 -2.847736E-02 -1.016726E-04 -3.650898E-02 -1.905274E-04 -3.678596E-02 -1.737202E-04 -9.741068E-03 -1.427221E-05 -2.112559E-02 -7.299641E-05 -1.626433E-02 -3.325133E-05 -1.556047E-02 -3.623423E-05 -2.005845E-02 -4.817075E-05 -2.235018E-02 -6.309213E-05 -1.555635E-02 -3.399168E-05 -8.473936E-03 -1.206918E-05 -2.003025E-02 -5.771667E-05 -1.619274E-02 -3.694213E-05 -2.398087E-02 -8.309002E-05 -2.048279E-02 -5.775057E-05 -1.959187E-02 -4.755352E-05 -1.817346E-02 -4.751135E-05 -1.533477E-02 -4.769982E-05 -1.889382E-02 -4.706002E-05 -1.855621E-02 -4.644130E-05 -3.121451E-02 -1.173056E-04 -1.135235E-02 -2.022523E-05 -1.457214E-02 -3.250403E-05 -1.330384E-02 -2.287686E-05 -1.426049E-02 -2.827899E-05 -2.255732E-02 -6.597520E-05 -1.409157E-02 -2.947510E-05 -2.387117E-02 -7.502115E-05 -3.498075E-02 -1.588586E-04 -2.661630E-02 -9.051962E-05 -3.141346E-02 -1.130104E-04 -2.363639E-02 -7.247261E-05 -2.949581E-02 -1.015620E-04 -3.405344E-02 -1.368730E-04 -3.067699E-02 -1.186942E-04 -2.860325E-02 -1.027432E-04 -3.136948E-02 -1.261067E-04 -2.884168E-02 -9.121556E-05 -3.172681E-02 -1.271781E-04 -6.082138E-02 -3.967354E-04 -4.665628E-02 -2.447254E-04 -3.873489E-02 -1.666752E-04 -5.112906E-02 -2.817112E-04 -3.867668E-02 -1.713578E-04 -4.211970E-02 -1.934977E-04 -6.886477E-02 -4.937257E-04 -7.019363E-02 -5.620739E-04 -5.569741E-02 -3.372249E-04 -5.856502E-02 -3.766765E-04 -5.390171E-02 -3.381837E-04 -4.680414E-02 -2.500022E-04 -1.026456E-01 -1.147075E-03 -8.535063E-02 -7.808065E-04 -8.630449E-02 -8.031445E-04 -1.052149E-01 -1.184292E-03 -7.701701E-02 -6.253512E-04 -7.834187E-02 -6.672354E-04 -1.359360E-01 -1.907855E-03 -1.460384E-01 -2.190423E-03 -1.080390E-01 -1.234268E-03 -1.317673E-01 -1.856930E-03 -9.690172E-02 -1.006627E-03 -9.258404E-02 -9.457422E-04 -1.832246E-01 -3.427961E-03 -2.054091E-01 -4.398902E-03 -1.593682E-01 -2.662133E-03 -1.880778E-01 -3.629787E-03 -2.013148E-01 -4.265877E-03 -1.990549E-01 -4.200017E-03 -5.469090E-01 -3.082757E-02 -5.552625E-01 -3.233826E-02 -2.571401E-01 -6.723483E-03 -8.548854E-01 -7.413280E-02 -5.209393E-01 -2.787584E-02 -5.093657E-01 -2.715480E-02 -1.566145E-01 -2.600405E-03 -1.993120E-01 -4.010479E-03 -1.707332E-01 -2.996975E-03 -1.840203E-01 -3.525950E-03 -5.686602E-01 -3.269532E-02 -5.046470E-01 -2.580015E-02 -1.740098E-01 -3.137069E-03 -1.748447E-01 -3.287313E-03 -5.281969E-01 -2.835860E-02 -5.218758E-01 -2.764449E-02 -2.811590E-01 -8.077168E-03 -7.835313E-01 -6.174409E-02 -8.916676E-02 -8.704144E-04 -1.147415E-01 -1.428848E-03 -1.059630E-01 -1.142821E-03 -9.640793E-02 -9.919169E-04 -1.380046E-01 -2.013093E-03 -1.453872E-01 -2.230703E-03 -8.067716E-02 -7.607075E-04 -7.414787E-02 -5.831873E-04 -1.108173E-01 -1.334816E-03 -9.716310E-02 -1.050788E-03 -1.058275E-01 -1.176253E-03 -1.234077E-01 -1.611857E-03 -4.645374E-02 -2.489769E-04 -5.612855E-02 -3.674944E-04 -5.305509E-02 -3.127379E-04 -4.929601E-02 -2.809620E-04 -6.549144E-02 -4.537754E-04 -6.303494E-02 -4.236715E-04 -3.880272E-02 -1.704460E-04 -3.861758E-02 -1.663017E-04 -5.862395E-02 -3.858310E-04 -4.737923E-02 -2.823280E-04 -5.280787E-02 -3.043443E-04 -5.249474E-02 -3.019614E-04 -2.485904E-02 -6.701075E-05 -2.926990E-02 -9.417758E-05 -3.125676E-02 -1.037311E-04 -3.289406E-02 -1.368340E-04 -4.071134E-02 -1.830700E-04 -4.109741E-02 -2.038834E-04 -2.347652E-02 -7.157581E-05 -2.072441E-02 -5.807283E-05 -4.102695E-02 -2.107954E-04 -2.166639E-02 -5.788402E-05 -3.219359E-02 -1.250457E-04 -2.832465E-02 -9.610853E-05 -1.624643E-02 -3.730962E-05 -1.725120E-02 -4.481115E-05 -1.578099E-02 -3.024585E-05 -1.571746E-02 -3.035996E-05 -1.731202E-02 -3.502473E-05 -1.643420E-02 -3.632173E-05 -1.040551E-02 -1.294862E-05 -1.127727E-02 -2.039819E-05 -1.438022E-02 -2.857038E-05 -1.260556E-02 -2.928756E-05 -2.034502E-02 -6.038560E-05 -1.640843E-02 -3.872409E-05 -2.122181E-02 -6.703870E-05 -1.545958E-02 -2.933443E-05 -1.550763E-02 -3.859489E-05 -1.928201E-02 -4.243101E-05 -2.196809E-02 -5.810938E-05 -2.193973E-02 -5.702613E-05 -1.517387E-02 -3.300117E-05 -1.732347E-02 -3.857976E-05 -1.415891E-02 -2.906573E-05 -1.407775E-02 -3.402531E-05 -2.277203E-02 -7.764043E-05 -1.580648E-02 -4.707367E-05 -2.763176E-02 -9.424628E-05 -2.372205E-02 -6.537717E-05 -2.585656E-02 -7.555138E-05 -3.438842E-02 -1.498651E-04 -1.953849E-02 -4.797528E-05 -2.039273E-02 -6.513710E-05 -3.691903E-02 -1.454538E-04 -3.217932E-02 -1.398798E-04 -2.774657E-02 -8.279249E-05 -2.820457E-02 -9.228219E-05 -3.106567E-02 -1.239322E-04 -1.804981E-02 -4.099893E-05 -5.969428E-02 -3.940876E-04 -5.835174E-02 -4.018253E-04 -5.448605E-02 -3.344901E-04 -6.458561E-02 -4.749467E-04 -4.505232E-02 -2.290467E-04 -4.529186E-02 -2.210661E-04 -6.923883E-02 -5.074459E-04 -6.905166E-02 -5.269603E-04 -4.247615E-02 -2.080915E-04 -5.430061E-02 -3.320737E-04 -6.603377E-02 -5.144612E-04 -5.251003E-02 -3.190205E-04 -1.030147E-01 -1.109176E-03 -8.155464E-02 -7.067482E-04 -8.922744E-02 -8.197146E-04 -1.284288E-01 -1.811686E-03 -9.277212E-02 -8.820708E-04 -7.960421E-02 -7.018415E-04 -1.101447E-01 -1.238620E-03 -1.594606E-01 -2.631417E-03 -7.676797E-02 -6.249548E-04 -9.678537E-02 -1.039683E-03 -1.315012E-01 -1.823501E-03 -9.822710E-02 -1.049106E-03 -2.200833E-01 -4.944909E-03 -1.814148E-01 -3.455606E-03 -2.808713E-01 -7.998623E-03 -8.607047E-01 -7.471518E-02 -1.510873E-01 -2.350444E-03 -1.952158E-01 -3.987281E-03 -2.563955E-01 -6.736419E-03 -8.288790E-01 -6.935905E-02 -1.663306E-01 -2.909230E-03 -2.069533E-01 -4.392557E-03 -8.171538E-01 -6.926168E-02 -2.771724E-01 -7.947364E-03 -1.718421E-01 -3.078316E-03 -1.912710E-01 -3.901809E-03 -4.978656E-01 -2.591511E-02 -4.943901E-01 -2.504533E-02 -2.519316E-01 -6.571958E-03 -7.930433E-01 -6.483055E-02 -1.486665E-01 -2.270454E-03 -2.161805E-01 -4.796812E-03 -1.737906E-01 -3.149960E-03 -1.909929E-01 -3.837468E-03 -5.280144E-01 -2.824797E-02 -5.994269E-01 -3.679537E-02 -8.367283E-02 -8.246835E-04 -9.345285E-02 -9.180702E-04 -1.084220E-01 -1.234841E-03 -1.132694E-01 -1.417334E-03 -1.213506E-01 -1.536611E-03 -1.485290E-01 -2.261244E-03 -7.557519E-02 -6.112603E-04 -7.935833E-02 -6.583374E-04 -1.009825E-01 -1.083491E-03 -7.954123E-02 -7.044944E-04 -1.118116E-01 -1.371403E-03 -1.197452E-01 -1.570554E-03 -5.190598E-02 -3.551665E-04 -5.730111E-02 -3.980792E-04 -6.248836E-02 -4.466985E-04 -6.019178E-02 -4.124940E-04 -7.202094E-02 -5.415764E-04 -7.189177E-02 -5.472909E-04 -3.981535E-02 -1.797809E-04 -4.820928E-02 -2.773872E-04 -5.050408E-02 -2.801362E-04 -5.149907E-02 -3.058260E-04 -6.167709E-02 -3.980579E-04 -6.251960E-02 -4.234132E-04 -2.843012E-02 -8.808063E-05 -2.337612E-02 -6.606638E-05 -2.936391E-02 -9.914901E-05 -3.131118E-02 -1.224255E-04 -3.590121E-02 -1.554613E-04 -3.734616E-02 -1.611929E-04 -2.718818E-02 -8.275683E-05 -2.355063E-02 -6.269602E-05 -3.417456E-02 -1.470468E-04 -2.761636E-02 -8.857401E-05 -2.817665E-02 -9.358473E-05 -4.330547E-02 -2.125793E-04 -1.281131E-02 -2.508003E-05 -1.558497E-02 -4.131225E-05 -1.604825E-02 -3.789735E-05 -1.198312E-02 -2.201855E-05 -1.841476E-02 -4.406610E-05 -2.191664E-02 -5.706023E-05 -1.037877E-02 -1.403046E-05 -1.142906E-02 -1.784198E-05 -2.165180E-02 -7.328897E-05 -1.781165E-02 -3.964227E-05 -1.706685E-02 -3.626318E-05 -2.611118E-02 -8.937284E-05 -1.889982E-02 -5.096931E-05 -1.354993E-02 -3.469204E-05 -1.092599E-02 -1.697195E-05 -1.380052E-02 -2.909184E-05 -2.326409E-02 -7.737047E-05 -2.386034E-02 -7.722570E-05 -1.642655E-02 -4.387424E-05 -1.696224E-02 -3.657599E-05 -1.094347E-02 -1.803801E-05 -1.156552E-02 -2.421906E-05 -1.583036E-02 -3.257752E-05 -1.517584E-02 -3.200691E-05 -3.827074E-02 -1.889669E-04 -2.449951E-02 -7.459613E-05 -2.623209E-02 -8.154118E-05 -2.849776E-02 -9.089323E-05 -2.334185E-02 -6.201992E-05 -2.654520E-02 -9.694536E-05 -3.237617E-02 -1.167467E-04 -3.675105E-02 -1.763620E-04 -2.004612E-02 -5.066812E-05 -2.903446E-02 -9.486419E-05 -3.334691E-02 -1.228445E-04 -2.623258E-02 -7.799037E-05 -4.888525E-02 -2.729676E-04 -3.600959E-02 -1.553932E-04 -5.438364E-02 -3.353294E-04 -5.232566E-02 -2.992982E-04 -3.034775E-02 -1.324830E-04 -4.164626E-02 -2.036943E-04 -5.059600E-02 -2.935825E-04 -5.879411E-02 -3.888487E-04 -3.938426E-02 -2.093554E-04 -4.838794E-02 -2.715001E-04 -4.972503E-02 -2.740094E-04 -4.955213E-02 -2.667621E-04 -6.113745E-02 -4.048275E-04 -5.341164E-02 -3.153602E-04 -5.975662E-02 -3.838000E-04 -8.595500E-02 -8.321615E-04 -6.107919E-02 -3.898803E-04 -5.530668E-02 -3.235594E-04 -7.580790E-02 -6.272237E-04 -9.941256E-02 -1.054222E-03 -6.091493E-02 -3.907622E-04 -6.317933E-02 -4.235462E-04 -1.014991E-01 -1.038642E-03 -7.701856E-02 -6.147885E-04 -1.112163E-01 -1.265790E-03 -7.536078E-02 -5.769678E-04 -1.302318E-01 -1.742411E-03 -1.635988E-01 -2.867432E-03 -7.255186E-02 -6.284552E-04 -1.087261E-01 -1.292949E-03 -1.071717E-01 -1.182413E-03 -1.275727E-01 -1.701949E-03 -7.308862E-02 -5.588046E-04 -8.677475E-02 -7.923200E-04 -1.598968E-01 -2.752916E-03 -1.090514E-01 -1.267867E-03 -9.373573E-02 -9.468462E-04 -8.656897E-02 -7.993780E-04 -1.556811E-01 -2.570483E-03 -1.563481E-01 -2.547541E-03 -1.019280E-01 -1.066946E-03 -1.407263E-01 -2.139026E-03 -9.439261E-02 -9.727226E-04 -1.162727E-01 -1.469225E-03 -8.572873E-02 -7.472360E-04 -8.412598E-02 -7.378763E-04 -1.184615E-01 -1.447843E-03 -1.129877E-01 -1.387240E-03 -7.089346E-02 -5.210102E-04 -7.106181E-02 -5.601420E-04 -9.767580E-02 -1.010123E-03 -8.639404E-02 -7.865066E-04 -8.288207E-02 -7.425178E-04 -8.992435E-02 -9.501100E-04 -6.866676E-02 -5.516503E-04 -6.325590E-02 -4.342044E-04 -7.431495E-02 -6.348268E-04 -7.599335E-02 -5.991771E-04 -7.274975E-02 -5.571106E-04 -6.766739E-02 -5.249260E-04 -3.181945E-02 -1.159273E-04 -4.099202E-02 -1.863511E-04 -4.739506E-02 -2.375175E-04 -4.566762E-02 -2.462349E-04 -4.309564E-02 -2.169367E-04 -4.968602E-02 -2.836430E-04 -2.554238E-02 -7.130377E-05 -3.399568E-02 -1.313338E-04 -4.489588E-02 -2.608490E-04 -3.609823E-02 -1.664739E-04 -3.688252E-02 -1.524824E-04 -4.371937E-02 -2.194516E-04 -2.432742E-02 -6.816206E-05 -2.481126E-02 -7.715594E-05 -3.225220E-02 -1.297648E-04 -2.820482E-02 -8.756926E-05 -2.911932E-02 -9.466605E-05 -3.374238E-02 -1.328376E-04 -2.602288E-02 -8.674133E-05 -2.044276E-02 -4.796182E-05 -2.621162E-02 -8.521865E-05 -3.451732E-02 -1.272449E-04 -2.497288E-02 -7.862985E-05 -3.797623E-02 -1.934870E-04 -1.693158E-02 -3.957575E-05 -2.516921E-02 -7.540607E-05 -1.916047E-02 -5.115510E-05 -1.903519E-02 -5.491559E-05 -1.655257E-02 -2.970206E-05 -1.806193E-02 -4.421369E-05 -1.074232E-02 -1.704622E-05 -9.240900E-03 -1.255060E-05 -1.183407E-02 -1.748671E-05 -9.219696E-03 -1.609190E-05 -1.034386E-02 -1.462148E-05 -1.114705E-02 -1.850428E-05 -1.297960E-02 -2.423078E-05 -1.184491E-02 -2.140225E-05 -1.658431E-02 -4.388936E-05 -1.318219E-02 -2.389643E-05 -1.560221E-02 -3.262330E-05 -1.536994E-02 -2.967325E-05 -1.474070E-02 -2.850636E-05 -1.423540E-02 -2.724881E-05 -1.128414E-02 -1.691587E-05 -7.509669E-03 -1.138736E-05 -1.063708E-02 -1.760414E-05 -1.145186E-02 -2.083540E-05 -1.762181E-02 -4.474974E-05 -2.084088E-02 -6.507170E-05 -2.057308E-02 -5.516523E-05 -2.241191E-02 -7.120840E-05 -1.269523E-02 -2.005337E-05 -1.660064E-02 -3.697642E-05 -3.182773E-02 -1.204858E-04 -3.242190E-02 -1.182654E-04 -2.434893E-02 -7.105045E-05 -2.652135E-02 -7.481704E-05 -1.907560E-02 -5.087922E-05 -2.190903E-02 -6.429867E-05 -3.105119E-02 -1.183209E-04 -2.464967E-02 -7.797261E-05 -2.817912E-02 -9.460889E-05 -4.586402E-02 -2.494422E-04 -3.145613E-02 -1.306566E-04 -3.581523E-02 -1.371038E-04 -3.824846E-02 -1.899412E-04 -4.263531E-02 -1.873636E-04 -3.042311E-02 -1.167731E-04 -2.732643E-02 -7.846176E-05 -4.050189E-02 -1.908935E-04 -3.434384E-02 -1.361814E-04 -4.495849E-02 -2.646832E-04 -4.092138E-02 -2.017353E-04 -5.344413E-02 -3.250472E-04 -4.697795E-02 -2.453442E-04 -3.679979E-02 -1.575515E-04 -3.908452E-02 -1.742990E-04 -4.284114E-02 -2.154331E-04 -4.380058E-02 -2.263026E-04 -3.985455E-02 -1.829559E-04 -3.394862E-02 -1.228745E-04 -4.744764E-02 -2.814058E-04 -3.949446E-02 -1.946576E-04 -6.109758E-02 -4.065478E-04 -5.344244E-02 -3.173404E-04 -5.996641E-02 -3.831573E-04 -7.955351E-02 -6.865984E-04 -4.384271E-02 -1.999570E-04 -6.435559E-02 -4.377030E-04 -4.969051E-02 -2.811028E-04 -5.599815E-02 -3.868544E-04 -4.163040E-02 -2.191546E-04 -4.845338E-02 -2.687605E-04 -7.016148E-02 -5.682673E-04 -6.371981E-02 -4.441518E-04 -6.553775E-02 -4.702345E-04 -5.818627E-02 -3.716053E-04 -7.301288E-02 -5.661570E-04 -6.855403E-02 -4.925246E-04 -4.751877E-02 -3.119215E-04 -6.178405E-02 -4.942348E-04 -5.778084E-02 -3.682642E-04 -6.794905E-02 -4.813721E-04 -4.580484E-02 -2.283874E-04 -4.833285E-02 -2.496503E-04 -6.283852E-02 -4.289179E-04 -5.868559E-02 -3.829967E-04 -4.942181E-02 -2.770674E-04 -4.633288E-02 -2.530825E-04 -6.521627E-02 -4.531563E-04 -6.926577E-02 -4.930307E-04 -4.947134E-02 -2.743687E-04 -5.599354E-02 -3.625867E-04 -4.586533E-02 -2.169603E-04 -6.057563E-02 -3.949284E-04 -3.269565E-02 -1.324708E-04 -3.688715E-02 -1.561195E-04 -4.607386E-02 -2.561957E-04 -4.286297E-02 -2.186313E-04 -3.835517E-02 -1.807457E-04 -3.101308E-02 -1.258603E-04 -4.256564E-02 -2.156265E-04 -3.995257E-02 -1.962160E-04 -3.999004E-02 -1.700193E-04 -4.593940E-02 -2.325862E-04 -2.746637E-02 -1.056011E-04 -2.945679E-02 -1.039840E-04 -4.237269E-02 -1.976737E-04 -3.339233E-02 -1.438661E-04 -3.371702E-02 -1.468814E-04 -3.238843E-02 -1.164792E-04 -2.064297E-02 -5.618891E-05 -2.155723E-02 -6.462578E-05 -2.273621E-02 -5.819206E-05 -1.964446E-02 -5.147180E-05 -2.401285E-02 -7.795637E-05 -2.729940E-02 -9.076729E-05 -2.213041E-02 -6.949614E-05 -1.338392E-02 -2.465408E-05 -2.431872E-02 -7.724266E-05 -2.524967E-02 -1.026115E-04 -2.737266E-02 -9.841098E-05 -2.112771E-02 -5.889604E-05 -1.496282E-02 -4.007026E-05 -1.889629E-02 -5.468925E-05 -1.783224E-02 -4.566541E-05 -8.331789E-03 -9.954892E-06 -1.796677E-02 -3.918970E-05 -1.970964E-02 -4.450042E-05 -1.137654E-02 -1.965594E-05 -8.407972E-03 -1.166241E-05 -1.447552E-02 -2.605420E-05 -8.376429E-03 -1.246530E-05 -1.335436E-02 -3.392789E-05 -1.200101E-02 -2.280708E-05 -1.602954E-02 -3.739523E-05 -9.669299E-03 -1.679999E-05 -1.566345E-02 -3.502619E-05 -1.588366E-02 -3.186835E-05 -1.901368E-02 -4.343637E-05 -2.267083E-02 -7.178904E-05 -9.004042E-03 -1.176762E-05 -1.804933E-02 -4.338938E-05 -1.071906E-02 -1.634166E-05 -9.615294E-03 -1.267674E-05 -1.558814E-02 -2.851576E-05 -1.376105E-02 -2.761523E-05 -1.902761E-02 -6.150446E-05 -1.854745E-02 -5.066723E-05 -1.702864E-02 -4.027369E-05 -2.099003E-02 -6.424422E-05 -2.011904E-02 -5.529868E-05 -1.661724E-02 -3.837653E-05 -1.373297E-02 -2.945958E-05 -1.569666E-02 -3.146459E-05 -1.202400E-02 -1.784017E-05 -1.532156E-02 -4.083467E-05 -1.855533E-02 -4.148169E-05 -2.388887E-02 -7.104659E-05 -3.029475E-02 -1.112701E-04 -3.479830E-02 -1.457081E-04 -2.993443E-02 -1.034283E-04 -2.618538E-02 -8.150737E-05 -1.973658E-02 -5.388325E-05 -1.908984E-02 -4.925317E-05 -2.558471E-02 -7.645976E-05 -3.057900E-02 -1.526871E-04 -2.038898E-02 -6.028856E-05 -1.936824E-02 -5.652676E-05 -1.860724E-02 -4.433157E-05 -1.859342E-02 -6.046452E-05 -3.104650E-02 -1.107731E-04 -2.791826E-02 -8.517252E-05 -2.550906E-02 -7.911190E-05 -3.228374E-02 -1.235126E-04 -2.983461E-02 -1.169772E-04 -2.340715E-02 -7.962272E-05 -2.472431E-02 -7.854176E-05 -3.116101E-02 -1.113261E-04 -2.726866E-02 -8.448946E-05 -2.872167E-02 -1.004750E-04 -3.032428E-02 -1.229901E-04 -3.053428E-02 -1.122910E-04 -3.083822E-02 -1.483669E-04 -2.064536E-02 -5.782771E-05 -3.181149E-02 -1.187295E-04 -4.583432E-02 -2.558159E-04 -2.666121E-02 -9.350519E-05 -3.358219E-02 -1.192091E-04 -2.369325E-02 -8.059778E-05 -3.102351E-02 -1.289691E-04 -2.820355E-02 -1.068875E-04 -3.480350E-02 -1.443944E-04 -3.560608E-02 -1.469841E-04 -4.990478E-02 -3.032798E-04 -3.784020E-02 -1.765328E-04 -3.316256E-02 -1.290263E-04 -4.033997E-02 -1.799350E-04 -3.929687E-02 -1.626134E-04 -3.235240E-02 -1.382862E-04 -3.388379E-02 -1.425521E-04 -3.879634E-02 -1.814360E-04 -3.990331E-02 -1.937058E-04 -2.486877E-02 -7.060573E-05 -2.847067E-02 -8.714105E-05 -4.490738E-02 -2.182380E-04 -3.277939E-02 -1.222614E-04 -2.300113E-02 -6.725020E-05 -3.241132E-02 -1.183039E-04 -3.117719E-02 -1.083016E-04 -2.675135E-02 -8.558799E-05 -3.944468E-02 -1.742389E-04 -4.399132E-02 -2.295328E-04 -2.087405E-02 -4.684764E-05 -1.956671E-02 -4.488034E-05 -3.135372E-02 -1.064232E-04 -1.852589E-02 -4.046644E-05 -2.367532E-02 -7.100616E-05 -2.728169E-02 -9.976058E-05 -2.896959E-02 -9.844663E-05 -2.865198E-02 -1.092590E-04 -3.163730E-02 -1.082131E-04 -3.268958E-02 -1.309514E-04 -2.046897E-02 -4.893795E-05 -2.472680E-02 -7.874493E-05 -2.926292E-02 -1.081766E-04 -3.002458E-02 -1.013573E-04 -1.360951E-02 -2.635847E-05 -2.311727E-02 -6.168251E-05 -3.020182E-02 -1.206713E-04 -2.697417E-02 -8.378246E-05 -1.334960E-02 -2.306197E-05 -1.590087E-02 -4.689125E-05 -2.011479E-02 -6.965285E-05 -1.633667E-02 -3.972970E-05 -2.002848E-02 -4.881276E-05 -2.201774E-02 -5.990084E-05 -1.590141E-02 -4.489842E-05 -1.287558E-02 -2.913250E-05 -1.902193E-02 -5.067435E-05 -1.740647E-02 -4.536926E-05 -2.127020E-02 -6.939263E-05 -1.655808E-02 -3.850014E-05 -1.186084E-02 -2.566137E-05 -1.183085E-02 -2.088219E-05 -1.691039E-02 -4.357920E-05 -6.413071E-03 -5.791752E-06 -1.069744E-02 -1.593929E-05 -9.843254E-03 -1.483187E-05 -6.822909E-03 -6.785866E-06 -4.540728E-03 -3.073065E-06 -1.381635E-02 -3.287951E-05 -9.881421E-03 -1.765396E-05 -5.794819E-03 -6.579960E-06 -1.064665E-02 -1.825529E-05 -9.708618E-03 -2.420624E-05 -3.867809E-03 -3.463878E-06 -1.036922E-02 -1.880088E-05 -1.209814E-02 -2.247120E-05 -6.289728E-03 -9.053460E-06 -1.121820E-02 -2.376217E-05 -6.107387E-03 -9.842112E-06 -6.679042E-03 -7.917785E-06 -4.610676E-03 -3.822313E-06 -8.094200E-03 -1.556770E-05 -1.289830E-02 -2.263732E-05 -1.240554E-02 -2.182216E-05 -1.709643E-02 -4.443205E-05 -1.240518E-02 -1.925823E-05 -1.427630E-02 -3.038114E-05 -1.316587E-02 -2.528125E-05 -1.051316E-02 -1.998843E-05 -1.171370E-02 -1.977243E-05 -1.035585E-02 -1.603691E-05 -7.844703E-03 -1.578836E-05 -1.090930E-02 -1.495715E-05 -8.811473E-03 -9.095511E-06 -1.164754E-02 -2.113385E-05 -1.218588E-02 -2.577136E-05 -9.989257E-03 -1.768442E-05 -1.346846E-02 -3.232091E-05 -1.442996E-02 -2.734716E-05 -9.319830E-03 -1.357660E-05 -8.976517E-03 -1.130996E-05 -1.111410E-02 -1.774598E-05 -7.097237E-03 -9.606334E-06 -7.668810E-03 -9.333553E-06 -7.744641E-03 -1.065587E-05 -7.076161E-03 -1.063617E-05 -9.841337E-03 -1.147612E-05 -9.084818E-03 -1.406836E-05 -2.026892E-02 -5.186751E-05 -2.132295E-02 -5.072988E-05 -2.033996E-02 -4.765789E-05 -2.446954E-02 -7.232017E-05 -1.029334E-02 -1.244212E-05 -1.235238E-02 -2.298965E-05 -1.681496E-02 -3.708751E-05 -2.101172E-02 -5.242154E-05 -1.722265E-02 -4.432357E-05 -1.352905E-02 -2.104447E-05 -2.195095E-02 -5.623046E-05 -2.089078E-02 -5.825067E-05 -2.311666E-02 -7.574537E-05 -1.562461E-02 -4.452838E-05 -3.307359E-02 -1.363268E-04 -2.572970E-02 -8.059205E-05 -2.345159E-02 -6.488182E-05 -2.554049E-02 -8.362508E-05 -2.006928E-02 -5.496633E-05 -2.259482E-02 -7.740649E-05 -1.761243E-02 -4.766457E-05 -1.125462E-02 -1.662484E-05 -1.806884E-02 -4.002727E-05 -1.875035E-02 -3.963856E-05 -2.507230E-02 -7.918744E-05 -1.948387E-02 -4.675980E-05 -2.102608E-02 -5.253998E-05 -2.725383E-02 -8.911499E-05 -1.527724E-02 -2.634360E-05 -2.318415E-02 -6.853391E-05 -1.953638E-02 -4.357651E-05 -2.681123E-02 -9.518790E-05 -1.619023E-02 -3.280100E-05 -2.160140E-02 -5.673289E-05 -2.207700E-02 -6.204249E-05 -1.851529E-02 -4.084658E-05 -2.064003E-02 -5.523222E-05 -1.936444E-02 -5.709920E-05 -2.135402E-02 -6.226947E-05 -1.913624E-02 -5.176876E-05 -1.979246E-02 -4.760307E-05 -2.697598E-02 -1.023961E-04 -1.163038E-02 -1.840715E-05 -1.922611E-02 -4.751185E-05 -1.370913E-02 -2.686855E-05 -1.541222E-02 -3.013601E-05 -2.247424E-02 -5.214783E-05 -1.763181E-02 -3.495488E-05 -9.375723E-03 -1.593258E-05 -1.436433E-02 -3.431677E-05 -1.300122E-02 -2.534320E-05 -1.428291E-02 -3.109112E-05 -1.320134E-02 -2.937454E-05 -1.496684E-02 -2.934259E-05 -1.257843E-02 -3.153056E-05 -1.573087E-02 -3.089207E-05 -1.074581E-02 -1.656493E-05 -8.983048E-03 -1.169670E-05 -1.588106E-02 -3.644142E-05 -1.135062E-02 -1.548990E-05 -1.342453E-02 -4.068693E-05 -7.143669E-03 -1.309838E-05 -1.911833E-02 -5.539347E-05 -1.578655E-02 -3.784881E-05 -1.010592E-02 -2.000143E-05 -2.107750E-02 -5.152671E-05 -1.352516E-02 -2.742309E-05 -1.298482E-02 -2.958789E-05 -8.061759E-03 -1.027922E-05 -1.145755E-02 -1.975699E-05 -7.630008E-03 -9.001956E-06 -1.110176E-02 -1.756811E-05 -7.622179E-03 -1.192472E-05 -1.157431E-02 -3.153358E-05 -1.329349E-02 -2.758940E-05 -7.723627E-03 -1.213940E-05 -6.430868E-03 -5.865444E-06 -6.666510E-03 -6.225385E-06 -3.314737E-03 -2.247442E-06 -5.145333E-03 -3.979329E-06 -6.496136E-03 -9.042875E-06 -3.832205E-03 -4.079904E-06 -1.091525E-02 -2.123166E-05 -1.100375E-02 -2.753282E-05 -6.452706E-03 -9.971091E-06 -5.931053E-03 -8.601691E-06 -9.574695E-03 -1.435980E-05 -6.378307E-03 -9.114546E-06 -1.035293E-02 -2.113676E-05 -5.515936E-03 -4.201853E-06 -5.579006E-03 -4.124072E-06 -9.045101E-03 -2.746668E-05 -7.146555E-03 -1.150230E-05 -6.281008E-03 -5.825971E-06 -5.295060E-03 -6.107698E-06 -4.285052E-03 -3.897963E-06 -1.261316E-02 -3.054847E-05 -1.248209E-02 -3.143433E-05 -1.216297E-02 -1.827405E-05 -1.257515E-02 -2.571939E-05 -8.877488E-03 -1.136667E-05 -1.224337E-02 -2.556328E-05 -1.610471E-02 -3.359843E-05 -1.376758E-02 -2.898825E-05 -1.066381E-02 -1.891264E-05 -1.370280E-02 -3.200949E-05 -1.218455E-02 -2.842079E-05 -1.205249E-02 -2.816694E-05 -1.292481E-02 -2.311574E-05 -1.500071E-02 -5.524516E-05 -1.629889E-02 -3.414163E-05 -1.715035E-02 -4.138447E-05 -7.880253E-03 -8.429037E-06 -1.066213E-02 -2.491824E-05 -1.071709E-02 -1.648251E-05 -1.278476E-02 -2.151462E-05 -2.029414E-02 -5.704631E-05 -1.835961E-02 -3.756414E-05 -1.086076E-02 -1.970149E-05 -1.953320E-02 -6.822321E-05 -1.727515E-02 -4.272841E-05 -2.113792E-02 -5.363596E-05 -1.255464E-02 -2.618423E-05 -1.849104E-02 -3.910212E-05 -1.080830E-02 -1.627578E-05 -1.065175E-02 -1.245618E-05 -1.729833E-02 -4.769086E-05 -1.229654E-02 -2.017586E-05 -2.000316E-02 -5.539455E-05 -1.490083E-02 -2.662412E-05 -1.257552E-02 -2.450638E-05 -2.265863E-02 -5.948942E-05 -1.592510E-02 -3.967897E-05 -1.671764E-02 -3.518609E-05 -2.134651E-02 -6.970473E-05 -1.145479E-02 -2.341492E-05 -1.110701E-02 -1.473716E-05 -1.365215E-02 -2.532562E-05 -1.569347E-02 -3.022408E-05 -1.740868E-02 -4.263025E-05 -2.046954E-02 -5.236013E-05 -2.088018E-02 -4.754884E-05 -2.143648E-02 -5.630377E-05 -2.321051E-02 -7.417245E-05 -1.393331E-02 -2.372413E-05 -1.272764E-02 -2.686784E-05 -1.996763E-02 -6.034803E-05 -1.543709E-02 -3.597724E-05 -1.374704E-02 -2.238864E-05 -1.485050E-02 -3.200278E-05 -1.630595E-02 -3.026169E-05 -1.885152E-02 -5.848418E-05 -1.903463E-02 -4.081588E-05 -2.873487E-02 -1.002249E-04 -2.191651E-02 -7.843435E-05 -2.375413E-02 -7.774516E-05 -1.237198E-02 -2.127422E-05 -1.942702E-02 -4.732051E-05 -2.103354E-02 -6.483331E-05 -1.285994E-02 -2.290156E-05 -1.249524E-02 -2.455002E-05 -1.236581E-02 -2.393541E-05 -1.379145E-02 -2.529751E-05 -1.562825E-02 -4.587015E-05 -2.641863E-02 -9.120767E-05 -2.733140E-02 -1.071137E-04 -1.919060E-02 -5.181037E-05 -2.129817E-02 -5.796235E-05 -1.260439E-02 -1.817703E-05 -1.848829E-02 -4.798194E-05 -1.216642E-02 -2.707330E-05 -1.410959E-02 -2.525781E-05 -9.453654E-03 -1.551579E-05 -9.766342E-03 -1.477317E-05 -1.836540E-02 -5.242882E-05 -1.804249E-02 -4.602986E-05 -1.853466E-02 -4.366676E-05 -1.525913E-02 -3.246161E-05 -1.673660E-02 -3.399297E-05 -1.837090E-02 -4.346348E-05 -1.396823E-02 -3.367275E-05 -9.246854E-03 -1.212445E-05 -1.410130E-02 -3.106294E-05 -1.307073E-02 -2.149583E-05 -9.704033E-03 -1.660514E-05 -1.044410E-02 -1.303462E-05 -1.166356E-02 -2.043704E-05 -1.318169E-02 -2.754937E-05 -1.812814E-02 -4.404582E-05 -1.552606E-02 -3.211975E-05 -1.585862E-02 -3.414359E-05 -1.446034E-02 -2.967784E-05 -7.649239E-03 -1.381600E-05 -6.893844E-03 -7.460924E-06 -1.354488E-02 -2.777125E-05 -1.007415E-02 -1.536552E-05 -8.075475E-03 -1.134760E-05 -1.130634E-02 -1.578798E-05 -6.590888E-03 -9.273749E-06 -7.448105E-03 -8.982164E-06 -5.823642E-03 -5.865562E-06 -9.093169E-03 -9.535064E-06 -4.848224E-03 -3.763431E-06 -9.282171E-03 -1.433250E-05 -1.127548E-02 -2.124250E-05 -9.878278E-03 -2.325491E-05 -1.039485E-02 -2.066791E-05 -1.155396E-02 -2.135511E-05 -1.450572E-02 -3.456258E-05 -1.246739E-02 -2.299204E-05 -1.290333E-02 -2.343647E-05 -1.121371E-02 -1.743282E-05 -5.468489E-03 -5.451707E-06 -1.180352E-02 -2.521651E-05 -1.416461E-02 -3.062871E-05 -1.076659E-02 -2.220002E-05 -2.448020E-02 -1.177322E-04 -1.517807E-02 -2.911032E-05 -1.031229E-02 -1.643057E-05 -1.200918E-02 -2.501705E-05 -1.613952E-02 -3.362754E-05 -1.378304E-02 -2.726242E-05 -1.857986E-02 -5.056092E-05 -1.576878E-02 -3.540682E-05 -1.910927E-02 -4.403442E-05 -1.829771E-02 -4.616084E-05 -1.164328E-02 -2.237054E-05 -1.680617E-02 -5.383688E-05 -2.087579E-02 -7.699421E-05 -2.381231E-02 -8.579600E-05 -1.885763E-02 -4.911360E-05 -2.649401E-02 -7.833957E-05 -1.350269E-02 -2.364349E-05 -1.757223E-02 -4.850663E-05 -2.411131E-02 -7.298399E-05 -2.774250E-02 -9.193073E-05 -2.282427E-02 -5.981367E-05 -2.721816E-02 -9.146779E-05 -2.832490E-02 -1.002337E-04 -2.942107E-02 -1.083982E-04 -2.629534E-02 -7.761525E-05 -2.690294E-02 -9.295211E-05 -2.031569E-02 -5.738480E-05 -2.590631E-02 -8.501230E-05 -3.031225E-02 -1.054523E-04 -2.423433E-02 -6.759709E-05 -3.382137E-02 -1.369015E-04 -2.496378E-02 -7.510237E-05 -2.672791E-02 -9.783698E-05 -2.507828E-02 -7.618011E-05 -1.769181E-02 -3.552782E-05 -2.096115E-02 -5.131019E-05 -3.063827E-02 -1.178430E-04 -3.120124E-02 -1.130478E-04 -2.031644E-02 -7.092047E-05 -2.760866E-02 -8.532139E-05 -2.786328E-02 -8.451452E-05 -2.700290E-02 -9.059845E-05 -3.660807E-02 -1.468682E-04 -3.105005E-02 -1.103431E-04 -3.734607E-02 -1.551359E-04 -3.447674E-02 -1.455364E-04 -2.264216E-02 -5.558419E-05 -2.211961E-02 -5.818600E-05 -3.130555E-02 -1.297184E-04 -3.457400E-02 -1.544678E-04 -2.687511E-02 -9.290746E-05 -3.047806E-02 -1.034500E-04 -3.560765E-02 -1.552412E-04 -3.095201E-02 -1.306105E-04 -3.230749E-02 -1.340962E-04 -2.928305E-02 -1.168468E-04 -4.265647E-02 -2.076643E-04 -3.298446E-02 -1.507110E-04 -2.986290E-02 -1.139639E-04 -3.435605E-02 -1.436737E-04 -3.052232E-02 -1.060392E-04 -3.396111E-02 -1.269448E-04 -3.167642E-02 -1.425279E-04 -2.821678E-02 -9.529119E-05 -4.113237E-02 -1.985887E-04 -3.525258E-02 -1.581696E-04 -3.297968E-02 -1.288343E-04 -2.799503E-02 -1.215560E-04 -4.104348E-02 -2.019357E-04 -3.825115E-02 -1.695340E-04 -2.776267E-02 -8.959306E-05 -3.443399E-02 -1.543941E-04 -2.347389E-02 -6.957205E-05 -2.325934E-02 -7.017456E-05 -2.441186E-02 -8.613195E-05 -1.983392E-02 -5.161177E-05 -2.710281E-02 -8.919133E-05 -2.744420E-02 -8.533228E-05 -2.193886E-02 -6.175359E-05 -1.830518E-02 -5.544200E-05 -2.341425E-02 -7.903797E-05 -2.450511E-02 -8.157422E-05 -2.452935E-02 -7.643569E-05 -3.274389E-02 -1.348587E-04 -9.730875E-03 -1.447356E-05 -1.623715E-02 -3.017421E-05 -1.516567E-02 -3.422832E-05 -1.065832E-02 -1.723340E-05 -1.938164E-02 -4.958486E-05 -1.489311E-02 -2.833615E-05 -1.617585E-02 -3.768666E-05 -1.471840E-02 -3.108380E-05 -1.804409E-02 -4.531066E-05 -2.150485E-02 -6.386092E-05 -1.088027E-02 -2.181372E-05 -1.017312E-02 -2.124217E-05 -1.432834E-02 -2.609903E-05 -1.583698E-02 -3.341095E-05 -9.743784E-03 -1.280783E-05 -7.846675E-03 -7.302042E-06 -9.804358E-03 -1.279367E-05 -1.297038E-02 -2.247676E-05 -1.204029E-02 -2.186972E-05 -8.944589E-03 -1.610694E-05 -1.437621E-02 -2.903841E-05 -1.162625E-02 -1.600570E-05 -5.705942E-03 -6.026539E-06 -9.949459E-03 -1.121060E-05 -1.355078E-02 -3.639982E-05 -1.253062E-02 -2.184327E-05 -1.128093E-02 -2.186156E-05 -1.351097E-02 -3.128150E-05 -1.302602E-02 -2.954976E-05 -1.581710E-02 -2.880115E-05 -8.299998E-03 -1.148143E-05 -1.419270E-02 -3.823010E-05 -8.195314E-03 -1.206128E-05 -1.089466E-02 -1.659227E-05 -1.434916E-02 -2.871463E-05 -1.677690E-02 -5.753038E-05 -2.577031E-02 -8.627098E-05 -2.327382E-02 -8.071762E-05 -2.476661E-02 -7.787162E-05 -2.107385E-02 -5.721321E-05 -1.990587E-02 -5.438566E-05 -1.715009E-02 -3.760846E-05 -2.840951E-02 -9.042931E-05 -2.308683E-02 -6.425853E-05 -2.068224E-02 -5.497174E-05 -2.518837E-02 -7.159468E-05 -1.844753E-02 -5.256599E-05 -2.195768E-02 -5.682427E-05 -4.576470E-02 -2.500482E-04 -3.107728E-02 -1.163017E-04 -3.132822E-02 -1.179930E-04 -3.352262E-02 -1.295829E-04 -2.943942E-02 -9.539846E-05 -3.672963E-02 -1.518887E-04 -3.639000E-02 -1.578688E-04 -3.932185E-02 -1.696900E-04 -3.126478E-02 -1.158570E-04 -3.784245E-02 -1.552955E-04 -3.346893E-02 -1.329769E-04 -3.045629E-02 -1.110202E-04 -4.501003E-02 -2.328334E-04 -5.330472E-02 -3.651809E-04 -4.289711E-02 -2.197266E-04 -3.259040E-02 -1.193252E-04 -4.449895E-02 -2.225829E-04 -4.124007E-02 -1.878125E-04 -4.737006E-02 -2.432331E-04 -4.433616E-02 -2.209854E-04 -5.530206E-02 -3.206482E-04 -6.256960E-02 -4.281638E-04 -5.285253E-02 -3.112894E-04 -4.439981E-02 -2.141823E-04 -5.016707E-02 -2.707515E-04 -6.882017E-02 -4.979869E-04 -4.465718E-02 -2.306648E-04 -3.888039E-02 -1.657354E-04 -5.385073E-02 -3.147914E-04 -4.640933E-02 -2.345368E-04 -7.051618E-02 -6.268651E-04 -4.375120E-02 -2.369745E-04 -6.068695E-02 -4.021878E-04 -6.068536E-02 -3.839552E-04 -3.602602E-02 -1.670809E-04 -4.995017E-02 -2.763744E-04 -4.392495E-02 -2.564028E-04 -5.093161E-02 -3.505664E-04 -4.432335E-02 -2.193425E-04 -4.855910E-02 -2.650606E-04 -5.176805E-02 -3.189213E-04 -4.890781E-02 -2.548831E-04 -6.554400E-02 -5.204569E-04 -6.588153E-02 -4.746561E-04 -6.675922E-02 -5.163608E-04 -7.271786E-02 -5.798664E-04 -4.837626E-02 -2.803260E-04 -5.224310E-02 -3.238220E-04 -4.580609E-02 -2.616101E-04 -5.119315E-02 -2.907515E-04 -4.372268E-02 -2.158620E-04 -4.124410E-02 -2.181852E-04 -4.882021E-02 -2.584581E-04 -5.914423E-02 -3.994010E-04 -4.163873E-02 -1.967708E-04 -3.798723E-02 -1.675576E-04 -6.178751E-02 -4.293350E-04 -5.052828E-02 -2.945947E-04 -3.889945E-02 -1.723031E-04 -4.778867E-02 -2.787658E-04 -2.556412E-02 -7.318557E-05 -3.450636E-02 -1.552235E-04 -2.997657E-02 -1.090031E-04 -2.250800E-02 -7.954622E-05 -3.846568E-02 -1.836107E-04 -3.701622E-02 -1.828496E-04 -3.972718E-02 -1.850244E-04 -2.403274E-02 -8.024833E-05 -3.407142E-02 -1.362787E-04 -3.563489E-02 -1.418007E-04 -2.285168E-02 -6.135475E-05 -3.573895E-02 -1.429096E-04 -1.944631E-02 -4.643790E-05 -2.223553E-02 -6.647009E-05 -2.178313E-02 -5.702943E-05 -1.885794E-02 -4.219512E-05 -2.626415E-02 -7.357902E-05 -2.590102E-02 -7.754226E-05 -1.790062E-02 -4.192871E-05 -1.566226E-02 -3.285418E-05 -2.611458E-02 -8.677724E-05 -2.644054E-02 -7.951101E-05 -1.688067E-02 -3.275169E-05 -2.281883E-02 -7.048130E-05 -1.383028E-02 -2.557495E-05 -1.750718E-02 -3.486088E-05 -1.458508E-02 -2.726606E-05 -1.229121E-02 -2.297397E-05 -1.858922E-02 -4.177321E-05 -1.787571E-02 -4.327284E-05 -6.566818E-03 -9.185537E-06 -1.448563E-02 -2.622240E-05 -1.621707E-02 -3.406357E-05 -1.085556E-02 -1.536032E-05 -1.333272E-02 -2.646991E-05 -1.453289E-02 -2.893412E-05 -1.518348E-02 -4.580967E-05 -1.404090E-02 -2.660292E-05 -1.349000E-02 -3.442831E-05 -1.633780E-02 -4.356999E-05 -1.600602E-02 -3.943089E-05 -1.590141E-02 -3.641474E-05 -1.086814E-02 -1.688254E-05 -1.698352E-02 -4.714162E-05 -1.318224E-02 -2.638496E-05 -1.119975E-02 -1.718066E-05 -1.575614E-02 -3.441783E-05 -1.418915E-02 -2.794370E-05 -1.971441E-02 -5.386951E-05 -2.749934E-02 -9.685175E-05 -2.450793E-02 -8.361790E-05 -3.096368E-02 -1.517345E-04 -1.966521E-02 -5.193037E-05 -1.488285E-02 -2.767080E-05 -2.628867E-02 -8.549789E-05 -3.222975E-02 -1.197226E-04 -3.578145E-02 -1.651405E-04 -2.717256E-02 -9.455312E-05 -2.846483E-02 -1.022437E-04 -2.373681E-02 -7.165349E-05 -4.893242E-02 -2.730109E-04 -3.477536E-02 -1.404582E-04 -4.928227E-02 -2.924560E-04 -4.544606E-02 -2.179936E-04 -3.160536E-02 -1.247483E-04 -4.089837E-02 -1.816429E-04 -6.363669E-02 -4.595645E-04 -5.951940E-02 -3.700880E-04 -3.810687E-02 -1.688583E-04 -5.679150E-02 -3.460302E-04 -5.729570E-02 -3.719866E-04 -5.082871E-02 -2.938785E-04 -7.469781E-02 -6.673208E-04 -6.652451E-02 -4.877821E-04 -6.401348E-02 -4.744885E-04 -5.622051E-02 -3.395523E-04 -6.073961E-02 -3.833605E-04 -6.232929E-02 -4.148823E-04 -7.969677E-02 -6.803601E-04 -8.037601E-02 -7.230061E-04 -7.865258E-02 -6.933305E-04 -9.845581E-02 -1.080566E-03 -6.878501E-02 -5.274776E-04 -7.015010E-02 -5.143444E-04 -1.156668E-01 -1.410105E-03 -1.192152E-01 -1.498896E-03 -7.577104E-02 -6.219937E-04 -8.184079E-02 -7.335058E-04 -1.139638E-01 -1.401155E-03 -8.075065E-02 -6.916698E-04 -1.437821E-01 -2.126013E-03 -8.850980E-02 -8.276893E-04 -1.464407E-01 -2.249915E-03 -1.357666E-01 -1.907685E-03 -8.903971E-02 -8.785322E-04 -1.035557E-01 -1.229512E-03 -9.987363E-02 -1.066492E-03 -1.408315E-01 -2.126682E-03 -7.775073E-02 -6.482580E-04 -9.217616E-02 -9.279523E-04 -1.346724E-01 -1.896751E-03 -8.733262E-02 -8.612501E-04 -9.983468E-02 -1.028984E-03 -9.096031E-02 -9.623493E-04 -1.646266E-01 -2.794929E-03 -1.304703E-01 -1.750382E-03 -8.811642E-02 -8.019013E-04 -1.067542E-01 -1.239326E-03 -4.255870E-02 -2.117948E-04 -6.657200E-02 -4.734337E-04 -6.716890E-02 -5.510834E-04 -4.697673E-02 -2.522950E-04 -9.074058E-02 -9.095881E-04 -7.248365E-02 -5.562525E-04 -5.894139E-02 -3.919992E-04 -4.322527E-02 -2.007041E-04 -8.846643E-02 -8.126375E-04 -8.094023E-02 -7.286166E-04 -5.884744E-02 -3.735579E-04 -6.484926E-02 -4.588123E-04 -4.890138E-02 -2.886686E-04 -5.972248E-02 -4.039813E-04 -4.560235E-02 -2.196157E-04 -4.488985E-02 -2.203187E-04 -6.734006E-02 -5.380869E-04 -5.510066E-02 -3.247707E-04 -4.520538E-02 -2.496589E-04 -4.217127E-02 -2.278109E-04 -5.551062E-02 -3.500669E-04 -4.092402E-02 -1.766989E-04 -4.298954E-02 -2.252953E-04 -4.437029E-02 -2.305532E-04 -3.704655E-02 -2.009774E-04 -3.598121E-02 -1.524807E-04 -3.045852E-02 -1.197202E-04 -2.601757E-02 -8.242731E-05 -3.357946E-02 -1.363746E-04 -3.932253E-02 -1.907001E-04 -2.390507E-02 -6.834171E-05 -2.346501E-02 -7.056602E-05 -3.597630E-02 -1.581562E-04 -3.090953E-02 -1.047491E-04 -2.070578E-02 -5.748122E-05 -3.347346E-02 -1.259074E-04 -1.513003E-02 -3.897988E-05 -1.367854E-02 -2.775960E-05 -1.645455E-02 -3.040738E-05 -1.324152E-02 -2.645298E-05 -1.588183E-02 -3.062863E-05 -1.734153E-02 -4.637284E-05 -1.634443E-02 -5.133843E-05 -1.119328E-02 -1.753195E-05 -1.323203E-02 -3.022286E-05 -1.135797E-02 -1.802055E-05 -1.090441E-02 -1.701011E-05 -1.759035E-02 -4.033287E-05 -2.242877E-02 -6.151790E-05 -1.946253E-02 -5.767287E-05 -1.450600E-02 -3.658456E-05 -2.053561E-02 -7.347202E-05 -2.172703E-02 -6.439721E-05 -1.970458E-02 -4.600512E-05 -1.750068E-02 -4.248050E-05 -2.615060E-02 -8.063501E-05 -1.471555E-02 -3.115498E-05 -1.390462E-02 -3.029928E-05 -2.277300E-02 -6.669696E-05 -1.701430E-02 -3.723197E-05 -3.366433E-02 -1.230840E-04 -2.650631E-02 -9.335504E-05 -2.977659E-02 -1.071113E-04 -3.153286E-02 -1.137530E-04 -2.593564E-02 -7.559506E-05 -2.027223E-02 -6.394080E-05 -3.666363E-02 -1.619679E-04 -4.008059E-02 -1.775008E-04 -2.019835E-02 -5.995987E-05 -3.269994E-02 -1.328649E-04 -4.298198E-02 -2.261101E-04 -3.478624E-02 -1.430238E-04 -5.024091E-02 -3.090307E-04 -5.399488E-02 -3.370022E-04 -4.802423E-02 -2.449581E-04 -4.962540E-02 -2.661791E-04 -4.714200E-02 -2.609694E-04 -5.413235E-02 -3.323523E-04 -7.865185E-02 -6.448405E-04 -6.245568E-02 -4.162905E-04 -4.774685E-02 -2.886302E-04 -6.062406E-02 -4.267779E-04 -6.732377E-02 -5.175823E-04 -5.345323E-02 -3.251415E-04 -1.081562E-01 -1.271390E-03 -1.021968E-01 -1.089652E-03 -9.531149E-02 -1.008862E-03 -1.099951E-01 -1.318432E-03 -8.728617E-02 -7.926236E-04 -6.759692E-02 -4.951495E-04 -1.622908E-01 -2.679920E-03 -1.297048E-01 -1.704599E-03 -1.105751E-01 -1.365137E-03 -1.338049E-01 -1.961877E-03 -1.135987E-01 -1.340952E-03 -9.047766E-02 -8.460145E-04 -5.706400E-01 -3.358746E-02 -5.767616E-01 -3.370236E-02 -1.875657E-01 -3.661666E-03 -1.871296E-01 -3.729992E-03 -2.210728E-01 -4.924615E-03 -1.665143E-01 -2.811366E-03 -8.372362E-01 -7.140730E-02 -2.631175E-01 -6.985957E-03 -5.750239E-01 -3.400521E-02 -4.828940E-01 -2.485485E-02 -1.859770E-01 -3.523984E-03 -1.672669E-01 -2.904958E-03 -2.375331E-01 -5.780405E-03 -7.840038E-01 -6.231709E-02 -2.132004E-01 -4.651951E-03 -1.326778E-01 -1.947761E-03 -8.211106E-01 -6.821312E-02 -2.477970E-01 -6.338987E-03 -2.133989E-01 -4.688136E-03 -1.689238E-01 -2.975501E-03 -7.799516E-01 -6.145987E-02 -2.571652E-01 -6.720137E-03 -1.616850E-01 -2.755887E-03 -1.967353E-01 -3.935902E-03 -8.257842E-02 -7.608737E-04 -1.275267E-01 -1.697194E-03 -9.926640E-02 -1.068598E-03 -8.218999E-02 -7.531509E-04 -1.532040E-01 -2.576896E-03 -1.294690E-01 -1.713096E-03 -7.154094E-02 -5.360062E-04 -7.586730E-02 -7.229435E-04 -1.259416E-01 -1.689821E-03 -9.056538E-02 -9.471446E-04 -8.469906E-02 -7.604134E-04 -1.005850E-01 -1.057108E-03 -4.463277E-02 -2.328840E-04 -5.523748E-02 -3.372196E-04 -5.232544E-02 -2.999576E-04 -4.417355E-02 -2.638798E-04 -5.782793E-02 -3.530500E-04 -5.944743E-02 -4.220391E-04 -3.657199E-02 -1.400109E-04 -3.808082E-02 -1.658520E-04 -6.410370E-02 -4.501663E-04 -4.310905E-02 -2.075429E-04 -4.755667E-02 -2.801406E-04 -4.959581E-02 -2.962253E-04 -2.252513E-02 -6.049713E-05 -2.991518E-02 -9.529790E-05 -2.664048E-02 -8.582855E-05 -2.475820E-02 -1.017459E-04 -3.488574E-02 -1.383218E-04 -2.890941E-02 -9.208222E-05 -2.098791E-02 -5.427914E-05 -1.943877E-02 -4.588171E-05 -3.481570E-02 -1.557210E-04 -2.398110E-02 -7.150967E-05 -2.011861E-02 -5.252956E-05 -2.839521E-02 -9.935802E-05 -1.502686E-02 -3.105045E-05 -1.609461E-02 -3.412102E-05 -2.098494E-02 -4.964297E-05 -1.885164E-02 -4.396914E-05 -1.991326E-02 -6.264911E-05 -1.856961E-02 -5.669686E-05 -1.440755E-02 -2.776990E-05 -1.370375E-02 -2.537463E-05 -1.757189E-02 -4.061709E-05 -1.902797E-02 -4.188635E-05 -1.135542E-02 -1.840125E-05 -1.821693E-02 -5.706852E-05 -1.966443E-02 -6.182591E-05 -1.098595E-02 -1.890446E-05 -1.880596E-02 -4.985208E-05 -2.224492E-02 -6.056638E-05 -1.938365E-02 -5.095134E-05 -2.004141E-02 -4.931528E-05 -1.584800E-02 -3.117640E-05 -8.716172E-03 -1.000356E-05 -1.306598E-02 -2.437620E-05 -1.303208E-02 -2.038909E-05 -1.496313E-02 -2.545249E-05 -9.598787E-03 -1.772891E-05 -3.131961E-02 -1.312684E-04 -2.918177E-02 -1.061777E-04 -3.088357E-02 -1.333663E-04 -3.322098E-02 -1.522259E-04 -1.912743E-02 -4.246859E-05 -2.493296E-02 -7.677374E-05 -3.831962E-02 -1.658539E-04 -3.935899E-02 -1.741789E-04 -2.392516E-02 -6.888699E-05 -3.042757E-02 -1.017878E-04 -3.071050E-02 -1.067951E-04 -3.345411E-02 -1.329032E-04 -5.446194E-02 -3.217845E-04 -4.790665E-02 -2.591914E-04 -4.370165E-02 -2.303527E-04 -4.427824E-02 -2.343428E-04 -4.182303E-02 -1.991080E-04 -4.098398E-02 -1.789986E-04 -6.431595E-02 -4.788578E-04 -6.788255E-02 -4.777967E-04 -4.412014E-02 -2.235134E-04 -5.533021E-02 -3.194019E-04 -5.464587E-02 -3.422291E-04 -5.703799E-02 -3.461325E-04 -1.119858E-01 -1.361306E-03 -9.089503E-02 -9.100663E-04 -9.511706E-02 -1.044926E-03 -1.074655E-01 -1.234547E-03 -7.705184E-02 -6.240812E-04 -6.897178E-02 -4.976520E-04 -1.412576E-01 -2.058554E-03 -1.216520E-01 -1.524026E-03 -8.791442E-02 -8.344056E-04 -1.037643E-01 -1.183835E-03 -8.708765E-02 -7.895562E-04 -8.580552E-02 -7.861920E-04 -7.918410E-01 -6.387826E-02 -2.447419E-01 -6.181230E-03 -4.587452E-01 -2.121897E-02 -5.203331E-01 -2.787312E-02 -1.850399E-01 -3.508673E-03 -1.651942E-01 -2.817305E-03 -5.838903E-01 -3.492429E-02 -5.155122E-01 -2.720158E-02 -1.904412E-01 -3.741626E-03 -1.981500E-01 -4.136229E-03 -2.022324E-01 -4.246984E-03 -1.652234E-01 -2.873591E-03 -5.115500E-01 -2.708582E-02 -5.791183E-01 -3.432760E-02 -8.107545E-01 -6.659574E-02 -2.634253E-01 -6.999531E-03 -4.915877E-01 -2.533318E-02 -5.693871E-01 -3.325807E-02 -1.791869E-01 -3.312861E-03 -1.858183E-01 -3.543802E-03 -2.123169E-01 -4.557530E-03 -1.672616E-01 -2.835857E-03 -1.711699E-01 -3.060667E-03 -1.758571E-01 -3.190864E-03 -9.235761E-02 -9.669475E-04 -1.140668E-01 -1.438348E-03 -1.377681E-01 -1.929906E-03 -1.011292E-01 -1.113302E-03 -1.364692E-01 -1.951502E-03 -1.414788E-01 -2.087246E-03 -7.454906E-02 -6.183963E-04 -7.314067E-02 -5.725364E-04 -1.053516E-01 -1.161166E-03 -8.265045E-02 -7.800060E-04 -9.317212E-02 -9.511500E-04 -9.087762E-02 -9.141004E-04 -5.226230E-02 -3.109563E-04 -5.578744E-02 -3.618499E-04 -6.973131E-02 -5.043558E-04 -6.157852E-02 -4.070680E-04 -6.811553E-02 -5.258915E-04 -7.611557E-02 -6.131989E-04 -5.088876E-02 -3.182936E-04 -5.436219E-02 -3.078199E-04 -5.597453E-02 -3.396588E-04 -4.107053E-02 -1.911728E-04 -4.290228E-02 -2.123049E-04 -5.788555E-02 -4.117326E-04 -2.353071E-02 -6.995800E-05 -3.463664E-02 -1.422097E-04 -2.918840E-02 -1.004566E-04 -2.736799E-02 -9.048433E-05 -3.463497E-02 -1.286836E-04 -4.726762E-02 -2.541389E-04 -2.411585E-02 -6.917613E-05 -2.704813E-02 -8.764995E-05 -2.810800E-02 -8.757217E-05 -2.291436E-02 -6.377954E-05 -3.013851E-02 -1.161125E-04 -2.972621E-02 -9.625050E-05 -1.751438E-02 -6.055922E-05 -2.205862E-02 -7.232896E-05 -1.960773E-02 -4.550316E-05 -1.937614E-02 -4.905471E-05 -2.518721E-02 -8.112827E-05 -2.590049E-02 -7.419054E-05 -1.339646E-02 -2.029891E-05 -2.067244E-02 -4.823190E-05 -2.295710E-02 -6.620610E-05 -8.933646E-03 -1.197590E-05 -2.088372E-02 -5.448793E-05 -1.573345E-02 -2.834414E-05 -1.618549E-02 -3.394500E-05 -1.562728E-02 -4.232429E-05 -1.762718E-02 -4.646038E-05 -1.691115E-02 -4.088904E-05 -1.778865E-02 -4.585630E-05 -1.699861E-02 -3.633558E-05 -1.474531E-02 -2.687795E-05 -1.191135E-02 -1.966547E-05 -1.368911E-02 -2.475254E-05 -8.726049E-03 -1.080420E-05 -1.855900E-02 -4.086770E-05 -1.042736E-02 -1.532968E-05 -2.750891E-02 -9.887827E-05 -2.557586E-02 -8.191188E-05 -2.651219E-02 -9.860563E-05 -2.544872E-02 -7.883150E-05 -2.096053E-02 -5.337477E-05 -1.819029E-02 -3.850870E-05 -3.202501E-02 -1.153271E-04 -2.876516E-02 -9.848331E-05 -2.271111E-02 -7.379330E-05 -2.854025E-02 -9.337598E-05 -2.824372E-02 -1.030571E-04 -2.318994E-02 -6.966557E-05 -4.138096E-02 -1.938864E-04 -3.680662E-02 -1.462240E-04 -3.808984E-02 -1.773752E-04 -4.968859E-02 -2.697606E-04 -3.120957E-02 -1.068433E-04 -3.625725E-02 -1.561439E-04 -4.266936E-02 -2.044742E-04 -5.527178E-02 -3.492180E-04 -3.492336E-02 -1.434781E-04 -3.345516E-02 -1.260850E-04 -5.316011E-02 -3.146473E-04 -3.770174E-02 -1.639797E-04 -7.987130E-02 -6.938936E-04 -5.700087E-02 -3.483872E-04 -7.902435E-02 -7.070267E-04 -8.115977E-02 -7.037262E-04 -5.028735E-02 -2.816840E-04 -5.862112E-02 -3.890181E-04 -8.026709E-02 -6.991067E-04 -8.844060E-02 -8.768540E-04 -6.027542E-02 -3.820896E-04 -5.955846E-02 -4.114052E-04 -7.223606E-02 -5.860849E-04 -8.036129E-02 -7.080829E-04 -1.251201E-01 -1.753212E-03 -9.518009E-02 -1.007169E-03 -1.499658E-01 -2.357452E-03 -1.582289E-01 -2.673309E-03 -9.723433E-02 -1.085206E-03 -9.223640E-02 -9.320231E-04 -1.244253E-01 -1.604811E-03 -1.389205E-01 -2.073518E-03 -8.898557E-02 -8.481843E-04 -8.699525E-02 -8.194442E-04 -1.194952E-01 -1.512778E-03 -8.679982E-02 -8.002571E-04 -1.529795E-01 -2.441631E-03 -1.087018E-01 -1.206359E-03 -1.572820E-01 -2.488454E-03 -1.378974E-01 -1.949060E-03 -1.016220E-01 -1.073888E-03 -1.189492E-01 -1.535660E-03 -9.307965E-02 -9.698515E-04 -9.538400E-02 -9.813891E-04 -9.106730E-02 -8.805352E-04 -8.371498E-02 -7.560974E-04 -1.021881E-01 -1.077841E-03 -9.309692E-02 -9.810570E-04 -6.954893E-02 -5.308895E-04 -7.248184E-02 -6.039844E-04 -8.357871E-02 -7.377145E-04 -7.117434E-02 -5.362599E-04 -9.546800E-02 -9.918303E-04 -8.154604E-02 -6.899246E-04 -5.541527E-02 -3.261092E-04 -5.781147E-02 -3.640210E-04 -7.071250E-02 -5.530093E-04 -7.012881E-02 -5.436620E-04 -5.986681E-02 -4.315165E-04 -7.228996E-02 -5.573189E-04 -4.764962E-02 -2.567629E-04 -4.978411E-02 -3.039433E-04 -5.251712E-02 -3.236802E-04 -3.942522E-02 -1.927267E-04 -5.510943E-02 -3.217347E-04 -5.118639E-02 -3.171944E-04 -4.252043E-02 -1.916953E-04 -4.412672E-02 -2.246415E-04 -4.088117E-02 -1.787513E-04 -3.610456E-02 -1.564509E-04 -3.814757E-02 -1.686026E-04 -4.627344E-02 -2.494575E-04 -1.994893E-02 -5.126553E-05 -3.216228E-02 -1.182156E-04 -2.950065E-02 -1.138201E-04 -2.866289E-02 -1.160231E-04 -3.983095E-02 -1.721863E-04 -4.012198E-02 -1.784164E-04 -2.106311E-02 -4.994977E-05 -2.781446E-02 -1.039157E-04 -3.488260E-02 -1.405115E-04 -2.875112E-02 -9.762388E-05 -3.224104E-02 -1.323284E-04 -3.641337E-02 -1.501812E-04 -1.480062E-02 -3.027272E-05 -1.408753E-02 -3.690601E-05 -1.944937E-02 -4.432175E-05 -1.283324E-02 -2.250887E-05 -2.073256E-02 -5.466671E-05 -2.186071E-02 -5.661539E-05 -1.133978E-02 -1.956348E-05 -1.049553E-02 -1.718390E-05 -1.539596E-02 -3.369585E-05 -2.039324E-02 -5.820403E-05 -1.043006E-02 -1.903901E-05 -2.777446E-02 -1.204671E-04 -1.484365E-02 -2.996962E-05 -1.109804E-02 -1.818652E-05 -9.835588E-03 -1.388426E-05 -1.478128E-02 -3.969614E-05 -1.020395E-02 -1.717436E-05 -1.487209E-02 -3.915846E-05 -9.230116E-03 -1.588076E-05 -8.372399E-03 -1.186179E-05 -8.663998E-03 -1.212384E-05 -1.164683E-02 -2.389782E-05 -1.148782E-02 -1.884731E-05 -9.267384E-03 -2.342255E-05 -2.492427E-02 -7.177126E-05 -1.937892E-02 -5.238888E-05 -1.533925E-02 -3.007374E-05 -2.897108E-02 -9.563790E-05 -1.504253E-02 -4.162972E-05 -1.506486E-02 -3.027445E-05 -2.421349E-02 -6.937729E-05 -1.981713E-02 -4.308765E-05 -2.688788E-02 -8.996617E-05 -2.298292E-02 -6.356151E-05 -2.521588E-02 -9.500495E-05 -1.944459E-02 -4.357545E-05 -4.894435E-02 -2.638450E-04 -3.864018E-02 -1.798269E-04 -3.849780E-02 -1.779556E-04 -4.806157E-02 -2.545276E-04 -2.419299E-02 -8.084015E-05 -2.835677E-02 -9.629003E-05 -3.964564E-02 -1.838380E-04 -4.177908E-02 -1.949235E-04 -3.068221E-02 -1.216833E-04 -3.203560E-02 -1.147717E-04 -3.493031E-02 -1.439593E-04 -3.780820E-02 -1.626400E-04 -5.390584E-02 -3.457131E-04 -3.911344E-02 -1.787461E-04 -5.600310E-02 -3.942394E-04 -5.721857E-02 -3.841430E-04 -3.425794E-02 -1.301099E-04 -4.896944E-02 -2.649497E-04 -5.468018E-02 -3.296430E-04 -5.960549E-02 -4.028002E-04 -4.289904E-02 -2.047984E-04 -3.417711E-02 -1.286477E-04 -5.147540E-02 -3.225127E-04 -4.429517E-02 -2.370511E-04 -5.333104E-02 -3.409602E-04 -6.102677E-02 -4.290957E-04 -7.075039E-02 -5.312660E-04 -7.760160E-02 -6.260977E-04 -5.110208E-02 -2.931608E-04 -6.284669E-02 -4.546881E-04 -6.386212E-02 -5.062176E-04 -7.791663E-02 -6.553468E-04 -4.847427E-02 -2.698567E-04 -5.036036E-02 -2.562333E-04 -6.628275E-02 -4.863329E-04 -5.107250E-02 -3.360646E-04 -5.438547E-02 -3.250878E-04 -5.955142E-02 -3.888695E-04 -6.613363E-02 -4.562672E-04 -7.873757E-02 -6.608787E-04 -4.633363E-02 -2.332785E-04 -6.000714E-02 -4.102538E-04 -5.607310E-02 -3.721702E-04 -7.224050E-02 -5.754880E-04 -5.604428E-02 -3.636927E-04 -4.542882E-02 -2.350087E-04 -6.010895E-02 -3.888132E-04 -5.026253E-02 -3.056826E-04 -5.083017E-02 -2.951657E-04 -4.774445E-02 -2.553998E-04 -6.166209E-02 -4.248389E-04 -5.750738E-02 -3.545368E-04 -5.475371E-02 -3.742657E-04 -5.136434E-02 -3.013480E-04 -4.625465E-02 -2.735615E-04 -5.057347E-02 -3.133161E-04 -4.695282E-02 -2.640479E-04 -3.979050E-02 -1.911432E-04 -5.106567E-02 -2.846885E-04 -4.782744E-02 -3.086692E-04 -2.945927E-02 -1.037190E-04 -3.548884E-02 -1.667740E-04 -4.339365E-02 -2.324267E-04 -3.578638E-02 -1.439546E-04 -4.531719E-02 -2.379087E-04 -4.291461E-02 -2.163748E-04 -2.831504E-02 -9.351629E-05 -2.425588E-02 -6.929284E-05 -3.519439E-02 -1.401196E-04 -3.174238E-02 -1.187918E-04 -2.505587E-02 -7.333276E-05 -3.252855E-02 -1.518816E-04 -2.315654E-02 -6.200895E-05 -2.574270E-02 -7.613474E-05 -2.520902E-02 -7.303619E-05 -2.985551E-02 -9.745575E-05 -2.508447E-02 -9.702485E-05 -2.448965E-02 -6.790622E-05 -1.765762E-02 -3.709919E-05 -1.979233E-02 -4.686113E-05 -2.477181E-02 -7.217121E-05 -2.046923E-02 -5.529696E-05 -1.619249E-02 -3.536888E-05 -3.084138E-02 -1.038251E-04 -1.430547E-02 -2.813880E-05 -1.024198E-02 -1.564502E-05 -1.440933E-02 -2.677391E-05 -1.139030E-02 -1.562766E-05 -1.706815E-02 -3.602648E-05 -1.960477E-02 -5.223127E-05 -1.075038E-02 -1.946902E-05 -8.176023E-03 -9.795109E-06 -1.400712E-02 -2.798724E-05 -9.098651E-03 -2.320760E-05 -1.621999E-02 -5.890457E-05 -1.662025E-02 -5.381012E-05 -1.501846E-02 -3.187427E-05 -1.115569E-02 -2.112610E-05 -8.934007E-03 -1.127244E-05 -1.053736E-02 -2.738972E-05 -1.687632E-02 -3.758437E-05 -1.176928E-02 -1.998369E-05 -1.090722E-02 -2.070033E-05 -1.922176E-02 -4.611119E-05 -6.410110E-03 -7.146719E-06 -3.860269E-03 -5.922652E-06 -6.045740E-03 -5.074973E-06 -4.971635E-03 -3.431959E-06 -2.426203E-02 -7.370761E-05 -2.680359E-02 -8.998719E-05 -1.630553E-02 -3.567524E-05 -1.914533E-02 -4.240964E-05 -1.508665E-02 -2.912142E-05 -1.672855E-02 -3.656656E-05 -1.451039E-02 -2.817950E-05 -1.687413E-02 -4.142926E-05 -1.597006E-02 -4.478288E-05 -1.688899E-02 -3.586099E-05 -1.833640E-02 -5.132894E-05 -2.002488E-02 -5.326859E-05 -1.914416E-02 -5.599172E-05 -2.207570E-02 -7.740090E-05 -2.366938E-02 -6.568915E-05 -2.649512E-02 -1.020488E-04 -1.483135E-02 -2.984413E-05 -1.887848E-02 -4.268175E-05 -3.001817E-02 -1.047979E-04 -2.212608E-02 -6.788700E-05 -2.058642E-02 -5.920693E-05 -2.072658E-02 -5.071607E-05 -1.860802E-02 -4.555855E-05 -1.666833E-02 -3.918400E-05 -2.410251E-02 -7.518781E-05 -2.431099E-02 -9.040361E-05 -3.409049E-02 -1.428531E-04 -3.176194E-02 -1.054320E-04 -3.008617E-02 -1.140028E-04 -3.827724E-02 -1.656866E-04 -3.578801E-02 -1.469705E-04 -3.205988E-02 -1.211102E-04 -2.718611E-02 -8.166121E-05 -3.164768E-02 -1.161478E-04 -2.324305E-02 -6.697244E-05 -2.947200E-02 -1.259054E-04 -2.746439E-02 -9.282287E-05 -2.653377E-02 -8.108059E-05 -4.445537E-02 -2.152169E-04 -4.052651E-02 -1.733821E-04 -3.053999E-02 -1.118515E-04 -4.177700E-02 -2.106180E-04 -3.721658E-02 -1.614887E-04 -4.250554E-02 -2.125286E-04 -3.259794E-02 -1.259532E-04 -3.279076E-02 -1.281014E-04 -3.769459E-02 -1.592958E-04 -3.620173E-02 -1.496207E-04 -3.630649E-02 -1.531507E-04 -3.419905E-02 -1.389163E-04 -3.811683E-02 -1.632070E-04 -3.778477E-02 -1.788000E-04 -3.255090E-02 -1.292645E-04 -4.054674E-02 -2.009707E-04 -3.396471E-02 -1.380670E-04 -2.970522E-02 -1.194017E-04 -2.926855E-02 -1.209863E-04 -2.414836E-02 -7.131380E-05 -2.902285E-02 -9.925626E-05 -3.804797E-02 -1.754536E-04 -2.561987E-02 -7.861985E-05 -2.621638E-02 -8.635601E-05 -3.392178E-02 -1.612402E-04 -2.949056E-02 -1.075244E-04 -2.861194E-02 -9.347554E-05 -3.281854E-02 -1.750223E-04 -2.528376E-02 -8.170461E-05 -2.855965E-02 -1.276904E-04 -2.210588E-02 -6.285379E-05 -1.501664E-02 -3.174414E-05 -2.485082E-02 -8.044179E-05 -2.609565E-02 -9.700928E-05 -3.800572E-02 -1.865757E-04 -1.880640E-02 -4.090279E-05 -2.909814E-02 -1.043206E-04 -3.180763E-02 -1.216425E-04 -1.518998E-02 -2.699690E-05 -2.630226E-02 -9.511365E-05 -2.282938E-02 -6.428433E-05 -2.493692E-02 -7.780311E-05 -1.137087E-02 -2.156846E-05 -1.680608E-02 -3.976947E-05 -2.724734E-02 -8.526080E-05 -3.353531E-02 -1.249343E-04 -1.997488E-02 -5.686628E-05 -1.952335E-02 -5.168354E-05 -2.332315E-02 -6.296001E-05 -1.903421E-02 -4.970639E-05 -1.988764E-02 -4.758272E-05 -2.237483E-02 -6.308654E-05 -2.024263E-02 -6.708430E-05 -1.794493E-02 -3.980589E-05 -1.605168E-02 -3.661129E-05 -1.264028E-02 -2.003973E-05 -1.316289E-02 -3.041847E-05 -2.132558E-02 -7.506854E-05 -1.515922E-02 -4.400695E-05 -1.165029E-02 -3.144519E-05 -1.431392E-02 -3.510266E-05 -9.378262E-03 -1.745913E-05 -1.348722E-02 -2.279535E-05 -1.219541E-02 -2.124436E-05 -9.186874E-03 -1.308201E-05 -5.510134E-03 -6.127474E-06 -1.018457E-02 -2.037011E-05 -7.480002E-03 -8.821059E-06 -1.284192E-02 -2.896216E-05 -1.251268E-02 -2.241281E-05 -1.134027E-02 -1.937376E-05 -9.348782E-03 -1.387252E-05 -1.595911E-02 -3.477329E-05 -1.611731E-02 -4.121294E-05 -5.771625E-03 -5.262561E-06 -1.339971E-02 -2.328961E-05 -4.724286E-03 -7.486861E-06 -7.887185E-03 -1.309912E-05 -1.017668E-02 -1.329563E-05 -9.929419E-03 -2.048058E-05 -1.172543E-02 -2.164855E-05 -8.748399E-03 -1.455014E-05 -7.388493E-03 -1.213908E-05 -9.357160E-03 -1.459184E-05 -1.182596E-02 -2.754657E-05 -1.376694E-02 -2.808688E-05 -8.736677E-03 -1.261523E-05 -1.058599E-02 -2.132582E-05 -8.904592E-03 -1.412855E-05 -8.871956E-03 -1.042407E-05 -4.818722E-03 -5.533432E-06 -7.132170E-03 -1.066330E-05 -1.265366E-02 -2.062988E-05 -1.146186E-02 -1.978609E-05 -1.727631E-02 -6.317086E-05 -9.722001E-03 -1.628574E-05 -1.808415E-02 -4.814964E-05 -2.313210E-02 -7.257429E-05 -6.096746E-03 -8.507143E-06 -1.095433E-02 -2.574564E-05 -1.144512E-02 -2.130606E-05 -1.709946E-02 -3.435958E-05 -5.794154E-03 -4.298264E-06 -1.345102E-02 -2.470605E-05 -1.674615E-02 -3.806453E-05 -1.058112E-02 -1.675643E-05 -1.487609E-02 -2.876672E-05 -1.727197E-02 -4.888253E-05 -2.720121E-02 -8.232238E-05 -3.268443E-02 -1.194042E-04 -1.427634E-02 -2.639442E-05 -2.055060E-02 -5.261649E-05 -1.637582E-02 -3.886105E-05 -2.416272E-02 -6.712570E-05 -1.431266E-02 -2.383080E-05 -1.632066E-02 -3.444426E-05 -2.435113E-02 -8.254028E-05 -2.015885E-02 -5.481740E-05 -1.973867E-02 -5.116489E-05 -1.865655E-02 -4.752313E-05 -3.165189E-02 -1.474543E-04 -3.338700E-02 -1.226611E-04 -1.856968E-02 -4.098217E-05 -2.747306E-02 -8.290244E-05 -3.019952E-02 -1.096277E-04 -2.941793E-02 -9.471376E-05 -1.359209E-02 -2.775872E-05 -1.604302E-02 -3.176242E-05 -2.130560E-02 -6.015704E-05 -2.273639E-02 -7.032069E-05 -2.128699E-02 -6.074255E-05 -1.492456E-02 -3.510276E-05 -2.424866E-02 -6.867482E-05 -2.858266E-02 -9.553376E-05 -1.423686E-02 -2.731632E-05 -2.183114E-02 -5.075377E-05 -1.790750E-02 -4.419157E-05 -2.189439E-02 -5.678221E-05 -1.528824E-02 -2.994220E-05 -1.591232E-02 -3.345909E-05 -2.006375E-02 -5.328672E-05 -1.926568E-02 -5.794406E-05 -1.995889E-02 -5.771200E-05 -2.190352E-02 -6.226272E-05 -2.344085E-02 -7.170778E-05 -1.490160E-02 -3.189456E-05 -1.964283E-02 -4.674552E-05 -1.729274E-02 -5.276904E-05 -2.041435E-02 -6.736760E-05 -1.278262E-02 -2.244619E-05 -1.454136E-02 -3.039925E-05 -9.619956E-03 -1.773332E-05 -1.228153E-02 -2.003326E-05 -9.672849E-03 -1.662330E-05 -1.711969E-02 -5.651042E-05 -1.584751E-02 -3.641664E-05 -1.935632E-02 -5.597824E-05 -1.126109E-02 -1.753857E-05 -1.782849E-02 -4.695997E-05 -1.381158E-02 -2.666475E-05 -1.219375E-02 -1.818815E-05 -1.066559E-02 -2.272800E-05 -1.442020E-02 -2.898674E-05 -1.020932E-02 -1.446241E-05 -1.118415E-02 -1.992502E-05 -8.105784E-03 -1.344204E-05 -1.498438E-02 -4.583095E-05 -1.019191E-02 -1.878658E-05 -1.933752E-02 -5.597403E-05 -1.355162E-02 -2.693353E-05 -1.009187E-02 -1.416651E-05 -1.762231E-02 -4.342948E-05 -8.265997E-03 -1.115148E-05 -7.740105E-03 -9.350881E-06 -6.132761E-03 -5.990846E-06 -8.311803E-03 -1.337310E-05 -1.148410E-02 -2.801843E-05 -1.254654E-02 -2.859306E-05 -5.729723E-03 -7.417371E-06 -3.329556E-03 -2.852362E-06 -8.995510E-03 -1.519541E-05 -9.885451E-03 -1.504316E-05 -8.000781E-03 -1.318972E-05 -8.116139E-03 -1.599393E-05 -5.483106E-03 -5.909268E-06 -7.119776E-03 -7.398166E-06 -5.696562E-03 -8.267139E-06 -3.253311E-03 -3.490487E-06 -7.617341E-03 -1.591870E-05 -4.477136E-03 -4.507128E-06 -4.186692E-03 -4.522007E-06 -9.913744E-03 -1.537264E-05 -5.097688E-03 -5.151058E-06 -3.958711E-03 -4.681053E-06 -8.278901E-03 -1.024219E-05 -8.835906E-03 -1.016849E-05 -8.456874E-03 -1.177568E-05 -1.261330E-02 -3.084433E-05 -4.949744E-03 -4.615766E-06 -3.553617E-03 -2.732540E-06 -9.121684E-03 -1.577246E-05 -1.008197E-02 -2.084343E-05 -1.384668E-02 -3.110319E-05 -6.699315E-03 -6.544515E-06 -9.247650E-03 -1.217430E-05 -1.382529E-02 -2.309065E-05 -1.239699E-02 -2.979052E-05 -7.678409E-03 -9.823153E-06 -1.313584E-02 -2.664709E-05 -1.402449E-02 -3.295040E-05 -1.404510E-02 -2.328440E-05 -1.395545E-02 -3.064026E-05 -6.468889E-03 -1.142683E-05 -1.117536E-02 -2.247013E-05 -7.150789E-03 -7.587094E-06 -1.607791E-02 -4.401441E-05 -1.397191E-02 -2.767660E-05 -1.377649E-02 -3.853853E-05 -9.740469E-03 -1.610242E-05 -1.439220E-02 -3.310057E-05 -1.312482E-02 -2.812275E-05 -9.319391E-03 -1.598131E-05 -1.513017E-02 -3.596580E-05 -1.246425E-02 -2.621144E-05 -1.701677E-02 -4.718452E-05 -1.345504E-02 -2.750081E-05 -1.915828E-02 -6.206762E-05 -1.487706E-02 -3.498164E-05 -1.808302E-02 -5.043185E-05 -1.413342E-02 -3.559061E-05 -1.138146E-02 -1.786839E-05 -1.140044E-02 -1.941117E-05 -1.554299E-02 -2.899451E-05 -1.350147E-02 -2.424308E-05 -2.075976E-02 -7.307325E-05 -1.356231E-02 -2.080054E-05 -1.518370E-02 -3.316853E-05 -1.189204E-02 -1.901003E-05 -2.058005E-02 -5.143687E-05 -2.045751E-02 -5.497205E-05 -1.513733E-02 -2.666165E-05 -1.753330E-02 -3.936189E-05 -1.683976E-02 -3.581825E-05 -1.511066E-02 -2.645123E-05 -1.175520E-02 -1.609321E-05 -1.932866E-02 -4.358405E-05 -2.152647E-02 -5.525043E-05 -1.359163E-02 -2.720874E-05 -1.591814E-02 -3.441323E-05 -1.498891E-02 -3.242215E-05 -1.394632E-02 -2.541758E-05 -1.898119E-02 -4.729644E-05 -1.689587E-02 -3.933248E-05 -1.435155E-02 -2.582993E-05 -1.316763E-02 -2.353008E-05 -1.157136E-02 -1.833702E-05 -1.655168E-02 -3.162804E-05 -1.516917E-02 -4.365366E-05 -2.271626E-02 -6.252949E-05 -2.415893E-02 -6.451266E-05 -1.132077E-02 -1.979205E-05 -1.218269E-02 -1.887580E-05 -1.457860E-02 -2.603959E-05 -1.799378E-02 -3.831940E-05 -1.973304E-02 -5.131280E-05 -1.650072E-02 -4.421326E-05 -1.309761E-02 -2.093703E-05 -9.968119E-03 -1.734048E-05 -2.388636E-02 -8.126332E-05 -1.459477E-02 -3.488715E-05 -2.918991E-02 -9.400845E-05 -2.039305E-02 -4.974054E-05 -1.348400E-02 -3.490462E-05 -1.687564E-02 -4.279173E-05 -1.513373E-02 -2.823507E-05 -1.994888E-02 -5.071283E-05 -1.764059E-02 -4.346435E-05 -1.859116E-02 -5.299840E-05 -1.380401E-02 -2.348130E-05 -1.635474E-02 -3.937702E-05 -2.064198E-02 -5.607997E-05 -1.842824E-02 -4.282589E-05 -2.078636E-02 -5.236688E-05 -1.710805E-02 -3.985969E-05 -1.543166E-02 -3.277155E-05 -1.311119E-02 -2.103436E-05 -1.223030E-02 -1.772840E-05 -1.428400E-02 -2.879115E-05 -1.711044E-02 -3.871812E-05 -1.431954E-02 -2.831455E-05 -7.837091E-03 -1.187250E-05 -7.756280E-03 -8.554725E-06 -7.995791E-03 -1.095905E-05 -8.605179E-03 -1.177231E-05 -8.958756E-03 -1.332678E-05 -1.034574E-02 -2.171120E-05 -6.001176E-03 -7.941410E-06 -1.257551E-02 -2.312537E-05 -4.129076E-03 -2.677243E-06 -8.501326E-03 -1.658402E-05 -5.816010E-03 -5.905913E-06 -7.839173E-03 -8.421697E-06 -5.918322E-03 -9.025041E-06 -5.736561E-03 -7.633742E-06 -9.243956E-03 -1.198288E-05 -8.562607E-03 -9.900387E-06 -8.517736E-03 -1.400152E-05 -8.058111E-03 -8.346635E-06 -6.429181E-03 -8.483646E-06 -9.766782E-03 -1.860263E-05 -8.854052E-03 -1.734509E-05 -8.929637E-03 -1.491512E-05 -5.233001E-03 -5.925028E-06 -9.295152E-03 -1.768126E-05 -1.257918E-02 -2.632079E-05 -1.127993E-02 -1.815069E-05 -1.522302E-02 -3.459869E-05 -2.039110E-02 -6.104303E-05 -7.018288E-03 -8.011469E-06 -1.296372E-02 -2.439899E-05 -1.344461E-02 -3.144942E-05 -1.591259E-02 -3.662184E-05 -1.592134E-02 -3.534978E-05 -1.397292E-02 -2.673400E-05 -1.387726E-02 -2.413584E-05 -1.403292E-02 -2.450220E-05 -1.623686E-02 -3.934040E-05 -1.455326E-02 -3.956697E-05 -1.848869E-02 -4.594096E-05 -1.562335E-02 -2.895160E-05 -1.934214E-02 -6.527205E-05 -2.762082E-02 -1.002001E-04 -1.646443E-02 -4.757237E-05 -1.974118E-02 -5.871473E-05 -1.812863E-02 -4.571190E-05 -2.841456E-02 -1.256913E-04 -1.572056E-02 -3.604561E-05 -1.479375E-02 -4.114818E-05 -2.139729E-02 -5.158299E-05 -1.827112E-02 -4.755220E-05 -2.918330E-02 -9.121760E-05 -1.800084E-02 -4.226384E-05 -2.357640E-02 -6.593693E-05 -2.849119E-02 -1.014082E-04 -2.107159E-02 -6.738763E-05 -2.185077E-02 -6.120235E-05 -1.870103E-02 -5.126759E-05 -2.228030E-02 -5.885366E-05 -1.218959E-02 -1.923700E-05 -1.518833E-02 -2.980819E-05 -2.131881E-02 -6.164198E-05 -1.857691E-02 -5.149996E-05 -1.967993E-02 -4.929073E-05 -2.037752E-02 -5.254462E-05 -2.428694E-02 -7.415972E-05 -2.311199E-02 -5.830300E-05 -1.781340E-02 -5.016711E-05 -2.109282E-02 -5.439384E-05 -2.646402E-02 -7.981598E-05 -2.872928E-02 -1.157883E-04 -2.296882E-02 -5.600052E-05 -2.145069E-02 -6.153603E-05 -3.194172E-02 -1.130937E-04 -2.556185E-02 -8.976008E-05 -2.450563E-02 -7.657853E-05 -2.632135E-02 -8.318821E-05 -2.937358E-02 -9.572656E-05 -2.779408E-02 -9.729157E-05 -2.029619E-02 -5.815728E-05 -2.107188E-02 -5.405801E-05 -2.864120E-02 -9.187238E-05 -3.189900E-02 -1.158697E-04 -2.174543E-02 -6.126120E-05 -1.866666E-02 -3.831554E-05 -4.074514E-02 -2.019874E-04 -3.077997E-02 -1.155565E-04 -2.845511E-02 -9.760556E-05 -2.358164E-02 -7.014622E-05 -3.662127E-02 -1.520968E-04 -3.309700E-02 -1.256130E-04 -2.775361E-02 -1.078304E-04 -3.177889E-02 -1.327385E-04 -2.523394E-02 -8.921547E-05 -3.189185E-02 -1.647022E-04 -2.967954E-02 -1.071890E-04 -2.119897E-02 -5.752892E-05 -3.434445E-02 -1.342008E-04 -2.714518E-02 -8.681886E-05 -2.897958E-02 -9.065153E-05 -2.696222E-02 -8.540024E-05 -2.987888E-02 -9.648932E-05 -2.811456E-02 -9.230152E-05 -2.638667E-02 -8.721739E-05 -3.440902E-02 -1.421180E-04 -2.352488E-02 -7.481613E-05 -3.172354E-02 -1.853144E-04 -2.170608E-02 -6.022568E-05 -1.913037E-02 -5.905188E-05 -3.295652E-02 -1.433947E-04 -2.543187E-02 -9.035364E-05 -1.729217E-02 -3.824473E-05 -1.983528E-02 -6.034582E-05 -3.265521E-02 -1.315037E-04 -2.437483E-02 -7.364625E-05 -1.554696E-02 -3.206723E-05 -2.145024E-02 -5.715848E-05 -1.057830E-02 -1.743678E-05 -1.046397E-02 -1.560587E-05 -9.479417E-03 -1.712347E-05 -1.254587E-02 -3.235855E-05 -1.579355E-02 -4.509260E-05 -1.484926E-02 -2.799512E-05 -1.228686E-02 -2.031169E-05 -1.212962E-02 -2.512835E-05 -1.657642E-02 -4.655608E-05 -1.385236E-02 -2.324629E-05 -1.422372E-02 -4.206039E-05 -1.285229E-02 -2.234088E-05 -1.063684E-02 -2.048991E-05 -1.054286E-02 -1.650301E-05 -7.250865E-03 -8.349919E-06 -4.846842E-03 -5.611575E-06 -6.655706E-03 -8.978424E-06 -1.009164E-02 -1.254527E-05 -8.369823E-03 -8.564886E-06 -7.078759E-03 -7.850385E-06 -6.511893E-03 -7.778046E-06 -9.070684E-03 -1.106525E-05 -6.662311E-03 -5.901628E-06 -5.341082E-03 -4.512027E-06 -2.019250E-02 -5.835499E-05 -1.804537E-02 -4.752474E-05 -2.143023E-02 -6.883510E-05 -1.472836E-02 -3.235428E-05 -2.050065E-02 -5.392162E-05 -1.244535E-02 -2.314093E-05 -1.512563E-02 -2.709722E-05 -1.844357E-02 -4.096498E-05 -1.230849E-02 -2.204180E-05 -1.094959E-02 -1.878473E-05 -1.443675E-02 -2.665387E-05 -1.322804E-02 -2.121836E-05 -2.663562E-02 -9.639039E-05 -3.653391E-02 -1.705389E-04 -2.009980E-02 -5.226492E-05 -2.269292E-02 -7.235254E-05 -2.630232E-02 -8.600830E-05 -2.058472E-02 -6.670991E-05 -2.641568E-02 -9.197032E-05 -2.754053E-02 -1.038311E-04 -3.121142E-02 -1.199600E-04 -2.717065E-02 -9.654646E-05 -2.242629E-02 -1.009563E-04 -2.075151E-02 -5.816770E-05 -3.942214E-02 -1.987805E-04 -3.882076E-02 -1.939596E-04 -2.920978E-02 -1.084721E-04 -2.600676E-02 -8.334881E-05 -2.923234E-02 -1.017212E-04 -3.586528E-02 -1.334235E-04 -3.797598E-02 -1.702103E-04 -3.232846E-02 -1.218180E-04 -3.377761E-02 -1.378231E-04 -4.192066E-02 -2.072314E-04 -2.738955E-02 -8.865239E-05 -3.568104E-02 -1.804686E-04 -3.548082E-02 -1.615200E-04 -5.031302E-02 -3.122101E-04 -3.009267E-02 -1.144558E-04 -3.199905E-02 -1.145847E-04 -3.777360E-02 -1.663536E-04 -3.384202E-02 -1.350575E-04 -4.560705E-02 -2.708256E-04 -3.136124E-02 -1.138594E-04 -4.666445E-02 -2.341642E-04 -4.832203E-02 -2.735704E-04 -2.846160E-02 -9.724755E-05 -3.958876E-02 -1.723911E-04 -3.564894E-02 -1.460291E-04 -5.846112E-02 -3.948391E-04 -3.546238E-02 -1.608118E-04 -3.537206E-02 -1.460004E-04 -5.554597E-02 -3.566908E-04 -5.109455E-02 -3.208060E-04 -5.520597E-02 -3.527162E-04 -4.201987E-02 -1.917230E-04 -6.660358E-02 -4.711986E-04 -5.432898E-02 -3.242512E-04 -4.089084E-02 -1.879065E-04 -3.784277E-02 -1.557193E-04 -5.046288E-02 -2.834904E-04 -5.513093E-02 -3.595041E-04 -3.711842E-02 -1.494648E-04 -4.392972E-02 -2.015209E-04 -4.988699E-02 -2.634722E-04 -3.989393E-02 -1.816607E-04 -5.144002E-02 -2.769398E-04 -4.565016E-02 -2.355239E-04 -6.482286E-02 -4.539075E-04 -4.470289E-02 -2.139336E-04 -4.617957E-02 -2.463023E-04 -4.418753E-02 -2.283032E-04 -4.649993E-02 -2.511721E-04 -4.930410E-02 -2.838380E-04 -3.882151E-02 -1.837693E-04 -2.680799E-02 -8.238578E-05 -5.755520E-02 -4.130996E-04 -4.496883E-02 -2.557734E-04 -3.602591E-02 -1.512327E-04 -2.530643E-02 -9.160435E-05 -4.057178E-02 -1.911452E-04 -3.912871E-02 -1.943839E-04 -2.823744E-02 -1.084509E-04 -3.870972E-02 -1.741549E-04 -2.669650E-02 -9.821873E-05 -3.580275E-02 -1.589657E-04 -2.195198E-02 -5.837423E-05 -2.079687E-02 -6.448549E-05 -3.002400E-02 -1.160249E-04 -2.957314E-02 -1.189151E-04 -2.377935E-02 -6.808549E-05 -1.839041E-02 -4.555435E-05 -3.430294E-02 -1.387042E-04 -2.592718E-02 -9.050017E-05 -1.535960E-02 -3.794370E-05 -2.078265E-02 -7.317160E-05 -1.576237E-02 -2.996656E-05 -2.216535E-02 -6.517900E-05 -1.811146E-02 -4.238302E-05 -1.455371E-02 -3.667385E-05 -2.634667E-02 -8.364438E-05 -1.996539E-02 -4.953189E-05 -1.816175E-02 -3.710475E-05 -1.218979E-02 -2.484826E-05 -2.442424E-02 -8.794676E-05 -2.372381E-02 -7.702420E-05 -2.231590E-02 -9.784571E-05 -2.172844E-02 -5.916333E-05 -1.145998E-02 -1.962910E-05 -1.702504E-02 -5.111810E-05 -6.914654E-03 -8.875412E-06 -1.075152E-02 -2.522276E-05 -1.773996E-02 -4.090619E-05 -9.269666E-03 -1.495738E-05 -1.266630E-02 -2.461812E-05 -1.423043E-02 -3.464040E-05 -1.459824E-02 -3.078483E-05 -9.387052E-03 -1.202926E-05 -9.667799E-03 -1.454321E-05 -9.447707E-03 -1.271935E-05 -1.549107E-02 -2.931692E-05 -1.961989E-02 -5.758509E-05 -1.679927E-02 -4.376812E-05 -1.729049E-02 -4.546154E-05 -2.214419E-02 -5.744861E-05 -1.574631E-02 -3.404805E-05 -1.512389E-02 -2.852541E-05 -1.564070E-02 -2.961719E-05 -1.156911E-02 -1.664384E-05 -1.199574E-02 -1.963095E-05 -1.575890E-02 -3.865614E-05 -8.533212E-03 -1.165342E-05 -2.974207E-02 -1.029138E-04 -3.078702E-02 -1.267340E-04 -2.895029E-02 -9.417641E-05 -2.705881E-02 -1.080454E-04 -2.385421E-02 -7.531760E-05 -2.083704E-02 -6.502784E-05 -3.429066E-02 -1.504413E-04 -3.503625E-02 -1.408918E-04 -2.706075E-02 -8.947448E-05 -3.189758E-02 -1.230472E-04 -2.544089E-02 -8.519294E-05 -2.520959E-02 -8.797041E-05 -5.070461E-02 -3.189504E-04 -4.884044E-02 -2.572701E-04 -3.547633E-02 -1.862450E-04 -4.388084E-02 -2.567378E-04 -3.492269E-02 -1.420917E-04 -2.806485E-02 -1.009649E-04 -5.059445E-02 -2.907485E-04 -4.808491E-02 -2.424314E-04 -4.942392E-02 -3.097207E-04 -3.897127E-02 -1.994484E-04 -3.839420E-02 -1.746523E-04 -3.244417E-02 -1.184480E-04 -5.623036E-02 -3.492166E-04 -6.425888E-02 -4.935669E-04 -4.877756E-02 -2.579133E-04 -4.955342E-02 -2.926551E-04 -5.396104E-02 -3.055339E-04 -5.871614E-02 -3.961828E-04 -6.708244E-02 -4.826510E-04 -5.278021E-02 -2.835069E-04 -6.918022E-02 -5.087677E-04 -5.576575E-02 -3.607685E-04 -4.871576E-02 -2.820267E-04 -4.103199E-02 -1.970300E-04 -7.773659E-02 -6.470582E-04 -8.599878E-02 -8.479845E-04 -6.886056E-02 -5.267028E-04 -5.721217E-02 -3.681350E-04 -7.713996E-02 -6.425519E-04 -5.865857E-02 -4.103676E-04 -9.128533E-02 -9.001212E-04 -6.876681E-02 -4.977191E-04 -8.130881E-02 -7.186877E-04 -7.550488E-02 -6.383683E-04 -6.074888E-02 -4.148348E-04 -5.625163E-02 -3.768206E-04 -7.967025E-02 -7.546239E-04 -1.062008E-01 -1.178026E-03 -7.335119E-02 -5.749705E-04 -4.820386E-02 -2.843684E-04 -8.285561E-02 -7.331262E-04 -6.377785E-02 -4.639306E-04 -6.793437E-02 -5.094372E-04 -5.119790E-02 -2.807423E-04 -9.613221E-02 -9.824061E-04 -7.536222E-02 -5.822997E-04 -4.761788E-02 -2.477789E-04 -6.084099E-02 -4.333532E-04 -4.906915E-02 -3.040989E-04 -7.222981E-02 -6.031356E-04 -4.171024E-02 -1.905756E-04 -4.528343E-02 -2.268220E-04 -6.279446E-02 -4.327452E-04 -5.128421E-02 -2.753808E-04 -6.231043E-02 -4.457498E-04 -4.764799E-02 -2.670171E-04 -6.361492E-02 -4.379323E-04 -5.965053E-02 -3.939821E-04 -5.490029E-02 -3.380966E-04 -6.827868E-02 -4.968827E-04 -3.309475E-02 -1.331187E-04 -4.603502E-02 -2.672301E-04 -3.793331E-02 -1.804286E-04 -2.266265E-02 -5.756031E-05 -5.949869E-02 -3.999807E-04 -3.963197E-02 -1.762880E-04 -3.899893E-02 -1.642926E-04 -3.082622E-02 -1.074710E-04 -5.463795E-02 -3.382616E-04 -4.577997E-02 -2.244114E-04 -2.946043E-02 -1.104054E-04 -3.390553E-02 -1.311928E-04 -2.012911E-02 -4.877248E-05 -3.010504E-02 -1.111957E-04 -2.530155E-02 -8.217373E-05 -2.188500E-02 -5.370079E-05 -3.794688E-02 -1.630376E-04 -3.361882E-02 -1.162902E-04 -2.106804E-02 -5.229501E-05 -2.245974E-02 -5.982159E-05 -2.505198E-02 -8.478230E-05 -2.287601E-02 -6.940045E-05 -2.246447E-02 -6.064445E-05 -2.014030E-02 -4.596852E-05 -1.547790E-02 -2.802405E-05 -1.827409E-02 -3.706838E-05 -1.250681E-02 -2.504890E-05 -1.523959E-02 -3.397988E-05 -1.959572E-02 -4.638773E-05 -2.192689E-02 -6.243400E-05 -1.209982E-02 -1.817250E-05 -1.262662E-02 -2.516249E-05 -1.684816E-02 -3.966147E-05 -6.158783E-03 -6.145844E-06 -1.336232E-02 -2.474390E-05 -1.990714E-02 -5.523583E-05 -2.459172E-02 -7.004859E-05 -1.772252E-02 -4.853898E-05 -1.485696E-02 -2.873818E-05 -1.413071E-02 -3.178019E-05 -3.091676E-02 -1.063557E-04 -2.309515E-02 -6.359073E-05 -1.550213E-02 -4.421294E-05 -2.192641E-02 -6.676847E-05 -1.438379E-02 -2.502494E-05 -9.323835E-03 -1.058149E-05 -1.950987E-02 -5.777120E-05 -1.128375E-02 -1.672801E-05 -2.835693E-02 -1.026638E-04 -3.616127E-02 -1.505209E-04 -2.584643E-02 -7.628678E-05 -3.372488E-02 -1.431161E-04 -2.972258E-02 -1.013165E-04 -2.718135E-02 -9.239632E-05 -4.046046E-02 -2.091323E-04 -3.184363E-02 -1.406831E-04 -2.906162E-02 -9.931921E-05 -3.473938E-02 -1.672263E-04 -3.247790E-02 -1.200800E-04 -2.283247E-02 -6.591169E-05 -5.157721E-02 -2.857973E-04 -5.318589E-02 -3.355446E-04 -5.396648E-02 -3.222881E-04 -3.455908E-02 -1.577620E-04 -4.000627E-02 -1.811544E-04 -3.827765E-02 -1.701640E-04 -6.111711E-02 -4.195077E-04 -4.813123E-02 -2.551093E-04 -5.248302E-02 -3.053078E-04 -6.395395E-02 -4.882393E-04 -4.085525E-02 -2.131190E-04 -4.537763E-02 -2.817462E-04 -9.621518E-02 -9.753697E-04 -8.338063E-02 -7.366007E-04 -9.141446E-02 -9.402148E-04 -6.429772E-02 -4.457199E-04 -6.537278E-02 -4.627356E-04 -6.465671E-02 -4.504472E-04 -8.801279E-02 -8.983416E-04 -7.192007E-02 -5.371364E-04 -6.236850E-02 -5.079591E-04 -7.006768E-02 -5.856072E-04 -6.331101E-02 -4.271617E-04 -5.772433E-02 -3.721594E-04 -1.415443E-01 -2.106304E-03 -1.333977E-01 -1.819393E-03 -9.668520E-02 -1.012563E-03 -7.876408E-02 -7.180366E-04 -1.082944E-01 -1.226100E-03 -7.858017E-02 -6.820487E-04 -1.233753E-01 -1.710493E-03 -8.940326E-02 -8.711409E-04 -1.097575E-01 -1.284214E-03 -1.079765E-01 -1.255092E-03 -8.764292E-02 -8.394106E-04 -8.007939E-02 -7.022808E-04 -1.214238E-01 -1.596170E-03 -1.421555E-01 -2.105744E-03 -9.333869E-02 -9.022978E-04 -7.506639E-02 -6.221271E-04 -1.304706E-01 -1.799634E-03 -7.986544E-02 -7.526969E-04 -9.702135E-02 -9.836576E-04 -7.294081E-02 -5.906372E-04 -1.297603E-01 -1.782812E-03 -9.082392E-02 -8.669323E-04 -6.578070E-02 -5.122022E-04 -8.815494E-02 -8.142597E-04 -8.155167E-02 -7.217382E-04 -1.018109E-01 -1.086157E-03 -7.197825E-02 -5.331106E-04 -5.627838E-02 -3.449520E-04 -9.010939E-02 -8.605735E-04 -7.405764E-02 -5.833698E-04 -6.370311E-02 -4.307727E-04 -5.655449E-02 -4.002735E-04 -7.828799E-02 -6.754020E-04 -6.189231E-02 -4.227420E-04 -6.662691E-02 -5.039861E-04 -5.981533E-02 -4.042598E-04 -4.616563E-02 -2.512572E-04 -5.126904E-02 -2.954937E-04 -4.940896E-02 -3.013823E-04 -3.651484E-02 -1.509448E-04 -6.169343E-02 -4.343928E-04 -5.276526E-02 -3.035402E-04 -3.757296E-02 -1.669063E-04 -3.701904E-02 -1.472772E-04 -5.041077E-02 -2.708039E-04 -3.789204E-02 -1.599636E-04 -3.750354E-02 -1.758214E-04 -3.841079E-02 -1.623123E-04 -2.067644E-02 -6.321573E-05 -2.854616E-02 -9.473813E-05 -2.444174E-02 -7.627675E-05 -1.711406E-02 -4.015948E-05 -3.249873E-02 -1.518375E-04 -3.257016E-02 -1.207336E-04 -2.196857E-02 -6.460695E-05 -2.336435E-02 -6.693447E-05 -3.087108E-02 -1.244672E-04 -2.930263E-02 -1.042844E-04 -2.973494E-02 -9.755987E-05 -2.663199E-02 -7.916063E-05 -8.110891E-03 -9.147184E-06 -1.596974E-02 -3.945176E-05 -1.126941E-02 -1.972987E-05 -1.498580E-02 -2.597248E-05 -2.296704E-02 -6.870338E-05 -1.864901E-02 -4.568849E-05 -9.655003E-03 -1.218230E-05 -8.917615E-03 -1.497980E-05 -1.991649E-02 -4.425777E-05 -1.938334E-02 -4.450084E-05 -1.468869E-02 -2.783700E-05 -1.990846E-02 -4.552853E-05 -1.774831E-02 -4.009193E-05 -1.266322E-02 -2.320002E-05 -1.658764E-02 -3.986878E-05 -1.698328E-02 -4.091919E-05 -2.128494E-02 -6.048618E-05 -2.030808E-02 -5.574087E-05 -1.270134E-02 -2.233463E-05 -1.683769E-02 -3.536069E-05 -9.809511E-03 -1.445622E-05 -8.599393E-03 -1.081146E-05 -1.931983E-02 -6.640579E-05 -2.037581E-02 -5.286217E-05 -3.532151E-02 -1.703716E-04 -2.959688E-02 -1.000744E-04 -2.794173E-02 -9.833908E-05 -2.721311E-02 -8.705508E-05 -2.259018E-02 -6.498840E-05 -2.999787E-02 -1.185856E-04 -4.036794E-02 -2.072046E-04 -3.144378E-02 -1.169125E-04 -2.933694E-02 -1.105938E-04 -3.942738E-02 -1.729860E-04 -3.260874E-02 -1.481232E-04 -2.353895E-02 -7.836100E-05 -5.876129E-02 -3.975363E-04 -4.766425E-02 -2.519722E-04 -4.033714E-02 -1.807849E-04 -4.729862E-02 -2.401753E-04 -4.304677E-02 -2.096845E-04 -3.822586E-02 -1.740610E-04 -5.685525E-02 -3.389362E-04 -5.053276E-02 -3.049584E-04 -4.125673E-02 -1.768692E-04 -4.228077E-02 -2.108012E-04 -4.501666E-02 -2.298865E-04 -3.308537E-02 -1.337489E-04 -9.085434E-02 -8.630952E-04 -8.043671E-02 -6.998250E-04 -6.790115E-02 -5.151102E-04 -9.822017E-02 -1.054933E-03 -6.615956E-02 -4.705198E-04 -5.651598E-02 -3.870178E-04 -9.749998E-02 -1.051885E-03 -8.349892E-02 -7.963070E-04 -6.591239E-02 -4.655083E-04 -6.485664E-02 -4.646649E-04 -5.727855E-02 -3.614456E-04 -5.982342E-02 -3.951869E-04 -1.437862E-01 -2.121299E-03 -1.381644E-01 -1.981530E-03 -1.258886E-01 -1.695067E-03 -1.148436E-01 -1.441218E-03 -9.597117E-02 -1.101364E-03 -8.065670E-02 -7.548241E-04 -1.190394E-01 -1.472809E-03 -1.202733E-01 -1.590349E-03 -9.488301E-02 -9.917086E-04 -8.590582E-02 -8.395527E-04 -8.538615E-02 -7.662543E-04 -6.840866E-02 -5.508149E-04 -1.309341E-01 -1.788800E-03 -1.431432E-01 -2.124925E-03 -1.253592E-01 -1.673315E-03 -1.027124E-01 -1.115422E-03 -1.257192E-01 -1.618975E-03 -1.009903E-01 -1.140739E-03 -8.317957E-02 -7.328749E-04 -8.230977E-02 -7.081126E-04 -1.122030E-01 -1.312445E-03 -9.156529E-02 -9.645560E-04 -7.593394E-02 -6.433631E-04 -7.347627E-02 -5.887314E-04 -9.176044E-02 -8.793675E-04 -9.196817E-02 -9.288935E-04 -8.602817E-02 -7.934807E-04 -6.540851E-02 -5.134819E-04 -7.784855E-02 -7.374549E-04 -7.145641E-02 -5.431170E-04 -6.586554E-02 -4.742469E-04 -6.889110E-02 -5.369623E-04 -5.852275E-02 -3.823255E-04 -5.130633E-02 -2.938484E-04 -5.009392E-02 -2.807458E-04 -5.334814E-02 -3.079752E-04 -4.264743E-02 -1.994080E-04 -4.606571E-02 -2.446337E-04 -4.932925E-02 -2.518310E-04 -3.172175E-02 -1.173609E-04 -4.799012E-02 -2.513615E-04 -5.034796E-02 -2.882966E-04 -3.398215E-02 -1.394103E-04 -3.801944E-02 -1.702277E-04 -3.688050E-02 -1.492715E-04 -2.945875E-02 -1.064246E-04 -3.293199E-02 -1.292973E-04 -5.057135E-02 -2.972879E-04 -2.902424E-02 -1.009778E-04 -2.994301E-02 -1.055013E-04 -3.820998E-02 -1.811607E-04 -2.790027E-02 -1.186505E-04 -3.074064E-02 -1.078519E-04 -3.248893E-02 -1.169499E-04 -2.353194E-02 -8.076603E-05 -1.809505E-02 -4.072324E-05 -3.396351E-02 -1.453516E-04 -2.286807E-02 -5.496448E-05 -1.987060E-02 -5.376353E-05 -3.044601E-02 -1.115521E-04 -1.248945E-02 -2.198679E-05 -1.803171E-02 -4.771632E-05 -1.378131E-02 -4.134383E-05 -1.555134E-02 -4.721156E-05 -2.534113E-02 -8.351023E-05 -1.500299E-02 -2.635770E-05 -1.071802E-02 -2.136252E-05 -1.137076E-02 -1.975771E-05 -1.456502E-02 -2.501472E-05 -9.727505E-03 -1.483236E-05 -1.454436E-02 -2.710978E-05 -1.978067E-02 -5.285365E-05 -1.360981E-02 -2.336372E-05 -9.847294E-03 -1.509563E-05 -1.524613E-02 -3.552566E-05 -1.773316E-02 -4.971550E-05 -1.867569E-02 -4.914571E-05 -1.751220E-02 -3.467782E-05 -1.304981E-02 -3.349123E-05 -1.082428E-02 -1.865766E-05 -9.584944E-03 -1.325745E-05 -9.180830E-03 -1.262337E-05 -1.640308E-02 -3.160798E-05 -9.874242E-03 -1.450077E-05 -2.800307E-02 -9.021643E-05 -2.465166E-02 -8.472087E-05 -2.134009E-02 -5.841535E-05 -2.840708E-02 -9.453242E-05 -1.817238E-02 -4.004063E-05 -2.108886E-02 -5.388334E-05 -3.062270E-02 -1.286139E-04 -2.538387E-02 -8.871389E-05 -1.989607E-02 -5.232630E-05 -3.196356E-02 -1.567584E-04 -2.768337E-02 -8.817683E-05 -1.657009E-02 -3.128845E-05 -6.221175E-02 -5.006436E-04 -4.068999E-02 -2.287368E-04 -3.891057E-02 -1.725015E-04 -3.661313E-02 -1.700136E-04 -4.325252E-02 -2.377359E-04 -3.132785E-02 -1.053139E-04 -4.447018E-02 -2.340749E-04 -4.887097E-02 -2.828548E-04 -3.659403E-02 -1.932574E-04 -3.839512E-02 -1.702624E-04 -3.434064E-02 -1.413220E-04 -3.562809E-02 -1.569627E-04 -6.182234E-02 -4.171544E-04 -6.100628E-02 -4.051419E-04 -6.647923E-02 -4.900740E-04 -7.287500E-02 -5.551039E-04 -4.730174E-02 -2.512478E-04 -5.228155E-02 -3.234257E-04 -6.367674E-02 -4.355277E-04 -6.127620E-02 -5.160754E-04 -4.996635E-02 -2.866391E-04 -4.386223E-02 -2.253152E-04 -5.319524E-02 -3.262631E-04 -5.127473E-02 -2.891196E-04 -1.048530E-01 -1.182483E-03 -8.170362E-02 -7.286542E-04 -7.965260E-02 -6.956308E-04 -8.522862E-02 -7.597262E-04 -6.792526E-02 -5.514875E-04 -5.653238E-02 -4.231108E-04 -8.040674E-02 -7.324397E-04 -6.538609E-02 -5.208188E-04 -6.236588E-02 -4.356839E-04 -6.164613E-02 -4.180417E-04 -7.331991E-02 -6.180767E-04 -5.657072E-02 -3.533489E-04 -7.827811E-02 -6.870284E-04 -7.791072E-02 -6.465127E-04 -1.083139E-01 -1.260161E-03 -8.407475E-02 -7.634468E-04 -6.669029E-02 -4.841099E-04 -7.629250E-02 -6.708847E-04 -6.145772E-02 -4.524294E-04 -6.227290E-02 -4.295289E-04 -6.532027E-02 -4.771341E-04 -5.543686E-02 -3.242450E-04 -6.507036E-02 -4.562060E-04 -6.918175E-02 -5.208846E-04 -5.915840E-02 -4.026369E-04 -7.574284E-02 -6.257363E-04 -6.443876E-02 -4.697084E-04 -4.704047E-02 -2.420667E-04 -6.871095E-02 -5.703679E-04 -7.652879E-02 -6.996775E-04 -4.488343E-02 -2.199039E-04 -4.340162E-02 -2.117909E-04 -5.505629E-02 -3.299335E-04 -5.395493E-02 -3.792417E-04 -4.982738E-02 -2.664394E-04 -5.864743E-02 -4.020406E-04 -3.700812E-02 -1.675101E-04 -3.590296E-02 -1.636877E-04 -3.701403E-02 -2.028641E-04 -3.559772E-02 -1.412606E-04 -4.420940E-02 -2.126895E-04 -3.967986E-02 -1.668324E-04 -2.751914E-02 -1.031787E-04 -3.350726E-02 -1.282541E-04 -3.400056E-02 -1.396521E-04 -2.928242E-02 -1.253649E-04 -3.696302E-02 -1.675271E-04 -2.971538E-02 -1.050504E-04 -2.147157E-02 -6.672784E-05 -2.068698E-02 -5.314224E-05 -3.024410E-02 -1.161314E-04 -2.564259E-02 -8.893011E-05 -2.244324E-02 -6.402041E-05 -3.352393E-02 -1.269472E-04 -2.503002E-02 -1.001149E-04 -2.271412E-02 -6.142137E-05 -2.662779E-02 -8.519648E-05 -2.760352E-02 -9.770118E-05 -3.168086E-02 -1.170083E-04 -2.636525E-02 -8.041298E-05 -1.168923E-02 -2.309996E-05 -1.341095E-02 -2.785722E-05 -9.527042E-03 -1.301984E-05 -8.579383E-03 -1.158986E-05 -1.770829E-02 -3.709440E-05 -1.285206E-02 -2.277856E-05 -5.715674E-03 -5.170007E-06 -7.751126E-03 -1.014219E-05 -1.566218E-02 -2.874454E-05 -1.550600E-02 -4.482121E-05 -1.310088E-02 -2.996692E-05 -1.266810E-02 -2.452354E-05 -1.328220E-02 -2.441758E-05 -9.553257E-03 -1.316655E-05 -1.072874E-02 -2.962475E-05 -1.419443E-02 -2.691293E-05 -1.389066E-02 -2.460204E-05 -1.520080E-02 -2.942326E-05 -9.725298E-03 -1.180312E-05 -9.645833E-03 -1.760954E-05 -8.964384E-03 -1.441525E-05 -7.797734E-03 -7.875870E-06 -1.222531E-02 -2.143962E-05 -9.811025E-03 -1.223418E-05 -2.347719E-02 -7.632207E-05 -2.624065E-02 -8.577440E-05 -1.872843E-02 -5.642768E-05 -2.884633E-02 -1.120462E-04 -1.331677E-02 -2.169139E-05 -2.073785E-02 -6.410937E-05 -2.069105E-02 -5.558996E-05 -2.409694E-02 -8.187520E-05 -1.689558E-02 -4.223843E-05 -1.624061E-02 -3.418316E-05 -2.151976E-02 -5.877173E-05 -2.360183E-02 -9.731432E-05 -3.349038E-02 -1.331399E-04 -3.071585E-02 -1.134216E-04 -3.064437E-02 -1.380256E-04 -3.411658E-02 -1.385980E-04 -1.902680E-02 -4.966952E-05 -2.548619E-02 -8.563915E-05 -2.627494E-02 -8.806514E-05 -3.156158E-02 -1.348057E-04 -2.555848E-02 -8.827726E-05 -2.109045E-02 -5.840881E-05 -3.141908E-02 -1.092575E-04 -2.542704E-02 -7.869948E-05 -4.409974E-02 -2.370516E-04 -4.511455E-02 -2.385642E-04 -3.493033E-02 -1.329289E-04 -3.573369E-02 -1.645671E-04 -3.658071E-02 -1.701779E-04 -2.875736E-02 -1.063832E-04 -4.612939E-02 -2.341592E-04 -4.488569E-02 -2.371072E-04 -3.283008E-02 -1.266329E-04 -4.421310E-02 -2.144763E-04 -3.688239E-02 -1.597994E-04 -3.783217E-02 -1.715032E-04 -7.381035E-02 -5.788223E-04 -5.920057E-02 -3.764365E-04 -5.566865E-02 -3.245756E-04 -5.294945E-02 -3.171578E-04 -5.308993E-02 -3.020899E-04 -4.015025E-02 -1.763785E-04 -5.449993E-02 -4.477957E-04 -6.098529E-02 -4.698598E-04 -4.562321E-02 -2.217931E-04 -4.372709E-02 -2.205027E-04 -5.027493E-02 -3.024221E-04 -5.350904E-02 -3.184156E-04 -4.989979E-02 -2.950952E-04 -5.166796E-02 -2.972852E-04 -5.157410E-02 -3.015135E-04 -4.679395E-02 -2.264100E-04 -4.534145E-02 -2.596978E-04 -4.610982E-02 -2.659116E-04 -4.420313E-02 -2.280910E-04 -4.382650E-02 -2.133009E-04 -4.090414E-02 -1.993174E-04 -3.320114E-02 -1.287562E-04 -4.502626E-02 -2.259113E-04 -3.962180E-02 -1.762251E-04 -4.967895E-02 -3.145518E-04 -4.494669E-02 -2.411637E-04 -4.339354E-02 -2.011700E-04 -4.176613E-02 -2.014202E-04 -4.442877E-02 -2.331921E-04 -3.398932E-02 -1.379211E-04 -3.482327E-02 -1.541052E-04 -3.556969E-02 -1.558964E-04 -3.691095E-02 -1.643955E-04 -2.226522E-02 -6.975395E-05 -3.915757E-02 -1.657456E-04 -2.866444E-02 -9.309362E-05 -2.855915E-02 -1.131346E-04 -2.537276E-02 -7.948738E-05 -3.789334E-02 -1.685996E-04 -2.751514E-02 -9.186058E-05 -3.178599E-02 -1.355963E-04 -3.907421E-02 -2.055557E-04 -2.791323E-02 -1.069896E-04 -3.339152E-02 -1.362152E-04 -2.050184E-02 -4.863200E-05 -2.291794E-02 -8.183025E-05 -2.124223E-02 -7.063324E-05 -2.399149E-02 -7.407918E-05 -1.796496E-02 -3.936797E-05 -2.078322E-02 -6.098637E-05 -3.024688E-02 -1.288165E-04 -2.239542E-02 -6.306787E-05 -2.616525E-02 -8.655425E-05 -2.919821E-02 -1.113037E-04 -1.673318E-02 -3.601539E-05 -1.608480E-02 -3.314965E-05 -2.821455E-02 -8.921863E-05 -1.974153E-02 -4.431146E-05 -2.003418E-02 -5.758763E-05 -2.061128E-02 -6.512772E-05 -1.559522E-02 -3.594737E-05 -1.934663E-02 -5.001512E-05 -1.075360E-02 -1.792213E-05 -8.194926E-03 -1.028496E-05 -2.034805E-02 -5.444881E-05 -1.609037E-02 -3.970183E-05 -1.347309E-02 -2.123107E-05 -1.407160E-02 -3.569412E-05 -1.624142E-02 -3.714221E-05 -1.299629E-02 -2.459818E-05 -1.122458E-02 -2.237869E-05 -1.211612E-02 -2.219160E-05 -1.408358E-02 -4.218855E-05 -6.735065E-03 -8.482615E-06 -6.793305E-03 -7.845095E-06 -8.478782E-03 -1.709477E-05 -1.133766E-02 -1.980469E-05 -9.653050E-03 -1.415133E-05 -5.831329E-03 -8.381192E-06 -6.218892E-03 -7.102476E-06 -7.592811E-03 -9.310767E-06 -6.506941E-03 -1.095631E-05 -9.452991E-03 -1.264596E-05 -4.768636E-03 -3.507810E-06 -2.077918E-02 -5.890772E-05 -1.762060E-02 -3.525407E-05 -1.205428E-02 -2.724892E-05 -1.469350E-02 -2.710234E-05 -8.551477E-03 -8.546946E-06 -1.223789E-02 -2.595121E-05 -1.076533E-02 -2.013472E-05 -1.864688E-02 -4.804170E-05 -1.271229E-02 -2.770046E-05 -1.016698E-02 -1.439334E-05 -1.713823E-02 -4.788386E-05 -1.253277E-02 -2.231383E-05 -3.154747E-02 -1.214804E-04 -1.749980E-02 -3.647612E-05 -1.830338E-02 -4.569688E-05 -2.308843E-02 -6.321283E-05 -1.594268E-02 -4.383360E-05 -2.466928E-02 -8.077902E-05 -2.901767E-02 -1.088029E-04 -2.554390E-02 -8.895350E-05 -2.146121E-02 -5.921450E-05 -1.943347E-02 -4.952984E-05 -1.991855E-02 -5.703083E-05 -1.411715E-02 -3.539989E-05 -2.706714E-02 -1.408378E-04 -2.476826E-02 -8.960297E-05 -3.280672E-02 -1.342648E-04 -2.769569E-02 -8.949765E-05 -2.254343E-02 -7.118925E-05 -2.821645E-02 -1.008088E-04 -2.332049E-02 -7.198089E-05 -3.472802E-02 -1.359900E-04 -2.098228E-02 -6.775921E-05 -2.011754E-02 -4.899916E-05 -2.887549E-02 -1.210569E-04 -2.169717E-02 -5.971602E-05 -4.083980E-02 -2.063696E-04 -2.682109E-02 -9.162241E-05 -4.087409E-02 -1.874734E-04 -3.397314E-02 -1.355352E-04 -2.503682E-02 -7.832902E-05 -3.341902E-02 -1.279805E-04 -3.470031E-02 -1.457506E-04 -3.434771E-02 -1.308149E-04 -1.924341E-02 -5.117189E-05 -2.610217E-02 -8.124782E-05 -2.672020E-02 -8.422537E-05 -2.913338E-02 -1.020506E-04 -3.799748E-02 -1.583935E-04 -2.396284E-02 -7.323177E-05 -4.085381E-02 -1.817575E-04 -3.429492E-02 -1.389255E-04 -2.955640E-02 -9.833007E-05 -3.550154E-02 -1.507005E-04 -3.041263E-02 -1.000878E-04 -3.068456E-02 -1.089623E-04 -2.480886E-02 -6.953343E-05 -2.214997E-02 -5.427501E-05 -2.822289E-02 -1.091821E-04 -2.246583E-02 -6.845501E-05 -3.548869E-02 -1.564383E-04 -3.342714E-02 -1.555114E-04 -3.584609E-02 -1.533905E-04 -2.543087E-02 -6.947810E-05 -3.395479E-02 -1.360453E-04 -3.067172E-02 -1.109996E-04 -2.290343E-02 -6.048129E-05 -1.985458E-02 -4.438216E-05 -3.051514E-02 -1.050122E-04 -2.086630E-02 -6.348306E-05 -2.009163E-02 -5.579571E-05 -2.312023E-02 -7.424842E-05 -2.349909E-02 -7.517591E-05 -2.465382E-02 -7.402420E-05 -2.149996E-02 -6.067745E-05 -2.388756E-02 -6.487269E-05 -1.918145E-02 -4.328933E-05 -1.951810E-02 -4.931597E-05 -1.743909E-02 -3.923120E-05 -1.959452E-02 -4.325142E-05 -1.836248E-02 -3.960818E-05 -1.633324E-02 -3.551742E-05 -1.863545E-02 -4.322646E-05 -2.548530E-02 -8.566650E-05 -1.370389E-02 -3.068862E-05 -1.702755E-02 -3.470486E-05 -2.110298E-02 -5.769891E-05 -1.950441E-02 -4.624026E-05 -1.773108E-02 -3.976096E-05 -2.121981E-02 -6.568857E-05 -1.052851E-02 -1.559210E-05 -1.304079E-02 -2.231298E-05 -1.944748E-02 -4.683893E-05 -1.445243E-02 -2.703435E-05 -1.597053E-02 -3.429994E-05 -1.650649E-02 -6.011419E-05 -8.568107E-03 -1.035370E-05 -5.963022E-03 -7.941116E-06 -1.005055E-02 -1.089546E-05 -9.025557E-03 -1.235125E-05 -1.058696E-02 -1.548458E-05 -1.428111E-02 -3.101021E-05 -6.332158E-03 -7.284553E-06 -1.107873E-02 -1.880555E-05 -9.074481E-03 -1.488548E-05 -8.633788E-03 -1.494006E-05 -9.852689E-03 -2.277679E-05 -1.279962E-02 -3.267960E-05 -1.096915E-02 -1.703897E-05 -6.543158E-03 -6.896083E-06 -7.823512E-03 -1.199583E-05 -6.285302E-03 -8.223945E-06 -7.196837E-03 -8.695066E-06 -6.939525E-03 -8.067024E-06 -4.593174E-03 -3.678022E-06 -4.766649E-03 -6.980855E-06 -4.492564E-03 -4.106885E-06 -5.836560E-03 -5.166762E-06 -4.091860E-03 -3.202729E-06 -2.851717E-03 -1.441790E-06 -1.131813E-02 -1.990562E-05 -8.044232E-03 -9.338147E-06 -9.601976E-03 -1.551372E-05 -1.271494E-02 -2.673243E-05 -6.142371E-03 -6.616946E-06 -7.543735E-03 -1.129556E-05 -1.054461E-02 -1.314345E-05 -1.244849E-02 -1.966250E-05 -9.734137E-03 -1.778830E-05 -1.360778E-02 -2.168963E-05 -8.980518E-03 -9.981098E-06 -5.673726E-03 -6.390047E-06 -1.495065E-02 -3.570344E-05 -1.139512E-02 -2.213315E-05 -1.578970E-02 -3.505969E-05 -1.629500E-02 -3.158196E-05 -1.481063E-02 -3.260955E-05 -1.632506E-02 -3.683693E-05 -1.058030E-02 -1.860245E-05 -1.014653E-02 -1.703372E-05 -1.053471E-02 -1.946614E-05 -1.087794E-02 -1.741794E-05 -1.749138E-02 -4.059625E-05 -1.441949E-02 -4.081297E-05 -2.238698E-02 -8.411387E-05 -1.355243E-02 -2.880517E-05 -2.176689E-02 -7.043906E-05 -1.938423E-02 -5.171647E-05 -1.365868E-02 -3.105929E-05 -1.615560E-02 -3.821091E-05 -1.688664E-02 -4.289252E-05 -1.314826E-02 -2.645963E-05 -1.505920E-02 -3.910868E-05 -1.696322E-02 -4.410507E-05 -1.531505E-02 -2.627391E-05 -1.302530E-02 -2.447578E-05 -2.215989E-02 -5.624695E-05 -1.558271E-02 -3.591163E-05 -1.660587E-02 -4.451639E-05 -2.152716E-02 -6.525487E-05 -1.897151E-02 -5.231568E-05 -1.728826E-02 -5.214345E-05 -1.902678E-02 -5.314163E-05 -2.333937E-02 -8.182246E-05 -9.443854E-03 -1.172248E-05 -1.198026E-02 -2.003714E-05 -2.138685E-02 -6.175227E-05 -1.274599E-02 -2.433959E-05 -1.947534E-02 -5.207291E-05 -1.815170E-02 -4.398794E-05 -3.169585E-02 -1.333543E-04 -1.905286E-02 -4.657423E-05 -1.494275E-02 -3.505332E-05 -1.763876E-02 -4.461835E-05 -1.182033E-02 -2.214931E-05 -1.968821E-02 -5.319218E-05 -1.722391E-02 -3.689380E-05 -1.164039E-02 -2.017520E-05 -1.987702E-02 -4.962563E-05 -1.593272E-02 -4.198443E-05 -1.295575E-02 -3.292363E-05 -1.574293E-02 -3.434465E-05 -2.730586E-02 -8.253402E-05 -2.116215E-02 -5.127586E-05 -1.343828E-02 -2.408890E-05 -1.857254E-02 -4.513774E-05 -1.500919E-02 -3.231350E-05 -1.678659E-02 -3.962936E-05 -1.488856E-02 -2.386764E-05 -1.230601E-02 -2.575268E-05 -2.037392E-02 -5.965465E-05 -1.096474E-02 -1.759636E-05 -2.187484E-02 -6.525702E-05 -1.374639E-02 -2.555749E-05 -1.511590E-02 -3.826384E-05 -1.563929E-02 -3.222315E-05 -1.684288E-02 -4.027516E-05 -1.773599E-02 -6.209617E-05 -1.720006E-02 -3.431594E-05 -1.841885E-02 -4.086225E-05 -1.218119E-02 -2.095533E-05 -1.065430E-02 -1.713440E-05 -1.058773E-02 -1.668419E-05 -1.135705E-02 -1.500764E-05 -1.158632E-02 -2.852635E-05 -8.247777E-03 -1.228385E-05 -1.907771E-02 -4.597570E-05 -1.777609E-02 -4.816332E-05 -1.285738E-02 -2.710713E-05 -1.336306E-02 -2.768885E-05 -7.498631E-03 -1.438567E-05 -1.168505E-02 -2.200300E-05 -8.575921E-03 -8.903420E-06 -6.935145E-03 -9.218014E-06 -9.998062E-03 -1.881829E-05 -8.939471E-03 -1.098615E-05 -7.860596E-03 -1.449285E-05 -1.082459E-02 -1.896980E-05 -5.782585E-03 -5.702033E-06 -1.004504E-02 -2.149248E-05 -7.503097E-03 -1.089878E-05 -6.843525E-03 -9.834532E-06 -1.381318E-03 -8.252352E-07 -5.676320E-03 -7.883845E-06 -5.874523E-03 -3.759183E-06 -4.793821E-03 -2.805686E-06 -6.125800E-03 -6.903423E-06 -8.300434E-03 -1.293197E-05 -8.058716E-03 -1.160479E-05 -6.964643E-03 -9.276538E-06 -3.462172E-03 -2.364550E-06 -4.968406E-03 -4.433251E-06 -6.306240E-03 -6.070974E-06 -6.018210E-03 -7.366216E-06 -8.837800E-03 -9.688936E-06 -5.096909E-03 -4.382163E-06 -3.815130E-03 -6.161058E-06 -4.515200E-03 -4.595497E-06 -6.864810E-03 -1.010895E-05 -6.423468E-03 -9.143165E-06 -9.538660E-03 -1.560977E-05 -1.261072E-02 -2.250707E-05 -1.026530E-02 -1.637414E-05 -9.557506E-03 -1.527890E-05 -8.827930E-03 -1.066139E-05 -5.478962E-03 -5.569868E-06 -9.080187E-03 -1.204024E-05 -1.528742E-02 -3.325338E-05 -1.337087E-02 -1.956015E-05 -1.675398E-02 -4.575869E-05 -1.281954E-02 -3.179463E-05 -8.332353E-03 -1.484699E-05 -1.617850E-02 -3.768574E-05 -1.581638E-02 -5.060008E-05 -1.558998E-02 -3.489775E-05 -1.170539E-02 -1.670388E-05 -9.403406E-03 -1.210849E-05 -6.538447E-03 -7.590433E-06 -1.533222E-02 -3.995585E-05 -9.660974E-03 -1.337453E-05 -1.612761E-02 -3.190924E-05 -1.123730E-02 -1.881522E-05 -6.517842E-03 -9.004602E-06 -1.039422E-02 -1.944508E-05 -1.196688E-02 -2.077099E-05 -1.825266E-02 -5.884867E-05 -1.114412E-02 -1.635734E-05 -6.855502E-03 -9.696697E-06 -7.937283E-03 -1.011203E-05 -1.305061E-02 -2.491232E-05 -2.084992E-02 -5.566232E-05 -9.019271E-03 -1.005494E-05 -1.029368E-02 -1.905328E-05 -1.763980E-02 -4.024339E-05 -9.329184E-03 -1.252705E-05 -1.325489E-02 -2.563636E-05 -1.556775E-02 -3.949834E-05 -2.061223E-02 -6.392049E-05 -1.758113E-02 -3.625999E-05 -1.157256E-02 -2.047838E-05 -8.563192E-03 -1.228712E-05 -1.031763E-02 -1.411955E-05 -1.512631E-02 -4.654173E-05 -1.486827E-02 -2.864290E-05 -2.251974E-02 -5.878585E-05 -1.538325E-02 -2.676876E-05 -1.352613E-02 -3.627305E-05 -1.449237E-02 -3.612781E-05 -1.192626E-02 -2.019597E-05 -1.304918E-02 -2.426503E-05 -1.605822E-02 -3.012824E-05 -1.727927E-02 -3.755550E-05 -9.669506E-03 -1.630871E-05 -1.276773E-02 -2.526258E-05 -1.428968E-02 -3.134862E-05 -1.703682E-02 -4.403612E-05 -1.839993E-02 -4.005997E-05 -1.709877E-02 -3.494229E-05 -1.020568E-02 -2.306756E-05 -1.556313E-02 -2.961997E-05 -1.367798E-02 -2.209767E-05 -1.479845E-02 -2.697536E-05 -1.064351E-02 -1.544937E-05 -1.457069E-02 -3.339049E-05 -1.022585E-02 -1.440893E-05 -1.072359E-02 -1.406891E-05 -2.091105E-02 -5.324749E-05 -1.381809E-02 -3.338254E-05 -1.600746E-02 -3.282400E-05 -1.918686E-02 -4.509327E-05 -1.087716E-02 -1.741352E-05 -1.712809E-02 -3.956314E-05 -1.312095E-02 -1.981242E-05 -1.874899E-02 -4.287445E-05 -1.113725E-02 -2.004136E-05 -7.103716E-03 -7.213595E-06 -1.369370E-02 -3.063256E-05 -9.195487E-03 -1.412638E-05 -1.141322E-02 -1.860950E-05 -1.114463E-02 -1.989362E-05 -1.766608E-02 -3.977019E-05 -1.831950E-02 -5.100972E-05 -9.842311E-03 -1.641174E-05 -1.050669E-02 -1.611304E-05 -9.275886E-03 -1.425314E-05 -1.489107E-02 -2.846318E-05 -1.024738E-02 -1.638704E-05 -1.238766E-02 -2.905937E-05 -8.220882E-03 -1.610701E-05 -7.911660E-03 -1.204791E-05 -6.724074E-03 -8.382502E-06 -7.246664E-03 -1.223989E-05 -9.981199E-03 -1.983647E-05 -7.195641E-03 -1.261848E-05 -5.805501E-03 -6.427362E-06 -7.853739E-03 -1.109259E-05 -5.029358E-03 -4.590158E-06 -7.362545E-03 -9.705721E-06 -6.742269E-03 -5.672589E-06 -8.306759E-03 -1.150294E-05 -2.269532E-03 -1.413152E-06 -5.728846E-03 -6.054291E-06 -6.497853E-03 -7.598936E-06 -5.211040E-03 -7.706367E-06 -1.091775E-02 -2.620541E-05 -7.186930E-03 -7.840316E-06 -5.773406E-03 -6.737773E-06 -8.315747E-03 -1.614189E-05 -1.045710E-02 -1.408567E-05 -1.373927E-02 -2.417114E-05 -4.474588E-03 -2.860895E-06 -7.106756E-03 -8.922176E-06 -1.703428E-02 -3.411039E-05 -7.032210E-03 -5.972986E-06 -7.519022E-03 -9.923714E-06 -1.019763E-02 -1.870985E-05 -1.003939E-02 -1.665677E-05 -5.257180E-03 -7.163497E-06 -6.709259E-03 -1.080672E-05 -6.660210E-03 -8.371462E-06 -1.339388E-02 -2.963406E-05 -2.041319E-02 -6.283530E-05 -1.277136E-02 -2.113278E-05 -1.095487E-02 -1.788661E-05 -1.210895E-02 -2.269317E-05 -9.526669E-03 -1.459758E-05 -1.595135E-02 -4.590455E-05 -1.583142E-02 -3.433698E-05 -1.814067E-02 -5.361094E-05 -2.020117E-02 -5.966829E-05 -1.078165E-02 -2.464395E-05 -1.503699E-02 -3.786272E-05 -1.636813E-02 -3.872799E-05 -2.272579E-02 -7.702949E-05 -1.895617E-02 -5.205980E-05 -1.364788E-02 -2.639491E-05 -1.991314E-02 -7.408716E-05 -1.011963E-02 -1.544567E-05 -1.853803E-02 -4.673643E-05 -1.601464E-02 -3.647742E-05 -2.688432E-02 -8.994718E-05 -1.910229E-02 -4.828596E-05 -1.310107E-02 -2.599395E-05 -2.493961E-02 -8.021464E-05 -2.136141E-02 -5.184829E-05 -2.544806E-02 -7.994724E-05 -1.143679E-02 -1.661591E-05 -1.709579E-02 -4.337367E-05 -2.083317E-02 -5.556329E-05 -1.866022E-02 -4.397347E-05 -3.145206E-02 -1.280431E-04 -2.210382E-02 -5.769121E-05 -1.823923E-02 -4.074602E-05 -1.528594E-02 -3.104999E-05 -1.711154E-02 -4.112452E-05 -1.507762E-02 -2.770618E-05 -2.206514E-02 -6.022082E-05 -2.319277E-02 -5.836530E-05 -1.896072E-02 -4.024144E-05 -1.655441E-02 -4.163720E-05 -1.804406E-02 -4.084019E-05 -2.452803E-02 -7.291599E-05 -2.802970E-02 -1.259325E-04 -2.453139E-02 -7.079239E-05 -2.489486E-02 -6.688553E-05 -2.178711E-02 -7.105053E-05 -1.906738E-02 -4.476745E-05 -2.527667E-02 -8.182112E-05 -2.331217E-02 -6.232971E-05 -2.781953E-02 -9.708263E-05 -1.997547E-02 -4.443113E-05 -2.088515E-02 -5.171598E-05 -2.303205E-02 -8.024769E-05 -1.915503E-02 -4.593806E-05 -2.571317E-02 -8.223527E-05 -1.506654E-02 -3.035107E-05 -2.999022E-02 -1.183624E-04 -2.875220E-02 -1.019916E-04 -1.859536E-02 -4.941826E-05 -2.237740E-02 -6.098459E-05 -1.818345E-02 -4.548133E-05 -2.387148E-02 -8.542723E-05 -1.893980E-02 -4.021031E-05 -2.098013E-02 -5.353962E-05 -2.652625E-02 -8.621445E-05 -1.979268E-02 -5.173051E-05 -2.005599E-02 -5.014567E-05 -1.869207E-02 -4.863434E-05 -2.062895E-02 -5.960259E-05 -2.035386E-02 -4.757091E-05 -1.380341E-02 -2.450580E-05 -1.716354E-02 -4.220694E-05 -1.514915E-02 -2.661478E-05 -9.617277E-03 -1.245420E-05 -1.625088E-02 -3.575586E-05 -1.556949E-02 -3.732096E-05 -1.713751E-02 -3.918991E-05 -1.288808E-02 -2.331234E-05 -1.411636E-02 -2.581158E-05 -8.295504E-03 -1.118839E-05 -1.490088E-02 -3.193897E-05 -1.120118E-02 -1.562213E-05 -1.159281E-02 -1.980721E-05 -1.203548E-02 -2.711369E-05 -1.339573E-02 -2.158602E-05 -1.280581E-02 -2.079765E-05 -1.083998E-02 -1.727408E-05 -1.315255E-02 -2.519274E-05 -1.207379E-02 -2.152665E-05 -1.232904E-02 -2.825538E-05 -1.209938E-02 -2.379986E-05 -7.347293E-03 -8.920021E-06 -1.420050E-02 -2.556068E-05 -1.406290E-02 -2.882913E-05 -1.226796E-02 -2.904134E-05 -1.529742E-02 -3.412784E-05 -9.361135E-03 -1.953641E-05 -7.653780E-03 -9.090398E-06 -1.172034E-02 -1.741493E-05 -6.481510E-03 -6.366134E-06 -1.483462E-02 -3.419849E-05 -7.287071E-03 -9.994231E-06 -7.014536E-03 -8.683589E-06 -3.914638E-03 -3.851978E-06 -1.039507E-02 -1.247058E-05 -1.449371E-02 -3.268737E-05 -6.107315E-03 -1.275657E-05 -5.912533E-03 -5.291900E-06 -1.241975E-02 -2.776459E-05 -1.652474E-02 -3.478228E-05 -9.924557E-03 -2.111611E-05 -1.112655E-02 -2.134337E-05 -1.490879E-02 -4.120682E-05 -1.214873E-02 -1.828276E-05 -1.878471E-02 -4.595384E-05 -1.068338E-02 -2.052816E-05 -9.957982E-03 -1.661273E-05 -8.679245E-03 -1.539340E-05 -7.944462E-03 -1.238828E-05 -9.333064E-03 -1.407498E-05 -2.180639E-02 -5.625519E-05 -2.409216E-02 -8.351837E-05 -2.095008E-02 -5.330117E-05 -2.458431E-02 -7.696318E-05 -1.741223E-02 -4.634669E-05 -2.052947E-02 -5.641630E-05 -2.507271E-02 -7.084493E-05 -2.192019E-02 -5.996923E-05 -1.687601E-02 -3.734411E-05 -2.188351E-02 -5.331644E-05 -2.625531E-02 -8.987505E-05 -1.947839E-02 -5.362737E-05 -2.561506E-02 -8.555702E-05 -3.072429E-02 -1.057779E-04 -2.548154E-02 -8.649311E-05 -2.215408E-02 -5.389914E-05 -2.857258E-02 -9.992187E-05 -2.271191E-02 -6.458178E-05 -3.417289E-02 -1.353676E-04 -2.406069E-02 -7.233156E-05 -2.858289E-02 -1.096000E-04 -3.086685E-02 -1.088635E-04 -1.806766E-02 -4.316705E-05 -2.440571E-02 -6.395626E-05 -3.255341E-02 -1.376111E-04 -4.078794E-02 -1.950454E-04 -2.169319E-02 -5.623674E-05 -2.222088E-02 -7.931181E-05 -3.507566E-02 -1.330312E-04 -3.153876E-02 -1.223922E-04 -3.739825E-02 -1.655348E-04 -3.186914E-02 -1.164407E-04 -3.186332E-02 -1.211760E-04 -3.046668E-02 -1.045865E-04 -2.468913E-02 -7.595312E-05 -2.725403E-02 -9.530282E-05 -3.669370E-02 -1.633028E-04 -4.142607E-02 -2.202378E-04 -2.274887E-02 -6.024427E-05 -2.852673E-02 -1.084379E-04 -3.552692E-02 -1.550998E-04 -1.953889E-02 -4.742931E-05 -4.288258E-02 -2.326284E-04 -3.527647E-02 -1.532875E-04 -3.831735E-02 -1.712470E-04 -3.740668E-02 -1.804477E-04 -3.067443E-02 -1.226235E-04 -2.859755E-02 -9.502065E-05 -3.656012E-02 -1.473474E-04 -4.428469E-02 -2.135422E-04 -3.876237E-02 -1.826793E-04 -3.168779E-02 -1.136526E-04 -4.132746E-02 -1.944335E-04 -3.080999E-02 -1.075137E-04 -3.809598E-02 -1.579372E-04 -3.950419E-02 -1.784657E-04 -5.023062E-02 -3.049706E-04 -4.789752E-02 -2.554387E-04 -3.458880E-02 -1.324150E-04 -4.090067E-02 -1.965130E-04 -3.045883E-02 -1.164732E-04 -3.897593E-02 -1.862204E-04 -1.900547E-02 -4.239429E-05 -2.523518E-02 -8.151515E-05 -3.480501E-02 -1.535823E-04 -2.911328E-02 -1.131135E-04 -3.135787E-02 -1.158315E-04 -2.364659E-02 -7.666160E-05 -3.489798E-02 -1.613478E-04 -3.305825E-02 -1.226465E-04 -3.424037E-02 -1.511504E-04 -3.144976E-02 -1.315971E-04 -2.386686E-02 -9.966283E-05 -2.343488E-02 -7.865295E-05 -1.809482E-02 -3.639503E-05 -1.592339E-02 -3.078924E-05 -3.395399E-02 -1.387775E-04 -2.173542E-02 -6.688037E-05 -1.139408E-02 -1.661367E-05 -1.191973E-02 -1.884978E-05 -2.149843E-02 -5.543388E-05 -2.139730E-02 -5.948238E-05 -1.878719E-02 -3.863865E-05 -2.861748E-02 -9.903233E-05 -1.663769E-02 -3.586183E-05 -2.392378E-02 -7.633777E-05 -1.826846E-02 -4.768962E-05 -1.258024E-02 -2.584217E-05 -1.836655E-02 -4.161069E-05 -1.896044E-02 -4.007711E-05 -1.782079E-02 -3.615894E-05 -1.682346E-02 -3.435373E-05 -1.864611E-02 -4.004514E-05 -2.027124E-02 -4.802356E-05 -1.572197E-02 -4.060890E-05 -1.809973E-02 -5.222844E-05 -8.922954E-03 -1.541312E-05 -1.042804E-02 -1.919810E-05 -1.663952E-02 -3.230801E-05 -1.416041E-02 -3.240599E-05 -1.364911E-02 -2.047968E-05 -1.466030E-02 -2.789488E-05 -7.525078E-03 -8.889694E-06 -6.730764E-03 -9.941085E-06 -1.007468E-02 -1.767697E-05 -1.050555E-02 -1.620042E-05 -8.346548E-03 -1.182646E-05 -9.053361E-03 -1.420984E-05 -1.665200E-02 -5.444887E-05 -1.527178E-02 -2.919176E-05 -1.380005E-02 -2.920152E-05 -9.043029E-03 -1.785555E-05 -1.950145E-02 -4.990311E-05 -1.401098E-02 -2.753310E-05 -1.823652E-02 -4.520948E-05 -1.710659E-02 -4.707810E-05 -1.381860E-02 -3.468506E-05 -1.372395E-02 -3.175759E-05 -1.525561E-02 -3.615913E-05 -1.061450E-02 -1.832455E-05 -2.040145E-02 -5.544400E-05 -2.550363E-02 -8.291428E-05 -1.927840E-02 -6.340108E-05 -1.775966E-02 -3.592996E-05 -1.876698E-02 -5.056466E-05 -1.520416E-02 -3.176693E-05 -2.911719E-02 -1.110393E-04 -2.789335E-02 -1.015992E-04 -2.154244E-02 -6.800841E-05 -2.176042E-02 -6.210896E-05 -2.465235E-02 -8.769146E-05 -2.163089E-02 -6.884278E-05 -4.263944E-02 -2.012458E-04 -3.780523E-02 -1.771907E-04 -3.718152E-02 -1.480234E-04 -3.477776E-02 -1.409494E-04 -3.400758E-02 -1.358091E-04 -2.463564E-02 -6.885284E-05 -3.580510E-02 -1.389221E-04 -2.581734E-02 -7.795599E-05 -4.453340E-02 -2.222774E-04 -3.924298E-02 -2.048002E-04 -2.186414E-02 -7.503195E-05 -2.414346E-02 -6.884988E-05 -4.920381E-02 -2.986041E-04 -4.271226E-02 -2.407998E-04 -3.511456E-02 -1.659456E-04 -3.959546E-02 -1.811704E-04 -4.332790E-02 -2.327770E-04 -3.005794E-02 -1.236765E-04 -4.201285E-02 -1.831068E-04 -3.539806E-02 -1.349359E-04 -4.860033E-02 -2.640914E-04 -3.738028E-02 -1.608258E-04 -3.344288E-02 -1.192143E-04 -3.329781E-02 -1.262633E-04 -5.050402E-02 -3.082255E-04 -6.167912E-02 -4.537041E-04 -4.061325E-02 -2.373203E-04 -4.113180E-02 -2.244264E-04 -4.273661E-02 -2.061457E-04 -3.909923E-02 -1.794019E-04 -4.467948E-02 -2.422984E-04 -3.644554E-02 -1.488516E-04 -6.208616E-02 -4.066650E-04 -5.622731E-02 -3.479864E-04 -3.604649E-02 -1.527817E-04 -4.489847E-02 -2.244948E-04 -4.852333E-02 -2.452380E-04 -5.916579E-02 -3.675786E-04 -5.027889E-02 -3.063063E-04 -5.223329E-02 -3.266355E-04 -5.303921E-02 -3.002831E-04 -4.084190E-02 -2.089059E-04 -4.269058E-02 -1.927344E-04 -4.181196E-02 -1.890866E-04 -4.239920E-02 -2.054052E-04 -4.178606E-02 -2.121443E-04 -3.962721E-02 -1.900681E-04 -3.998106E-02 -1.671101E-04 -4.097110E-02 -1.864628E-04 -5.252376E-02 -2.919653E-04 -4.986589E-02 -2.923184E-04 -4.281919E-02 -2.017141E-04 -5.707825E-02 -3.443945E-04 -4.193701E-02 -1.801946E-04 -3.890873E-02 -1.604416E-04 -2.771300E-02 -8.201460E-05 -4.516255E-02 -2.288159E-04 -3.504308E-02 -1.480297E-04 -3.354671E-02 -1.345202E-04 -4.155997E-02 -1.900709E-04 -2.311887E-02 -6.056166E-05 -4.403743E-02 -2.374715E-04 -1.996468E-02 -5.260666E-05 -3.196737E-02 -1.421833E-04 -4.035771E-02 -1.965928E-04 -3.317174E-02 -1.529423E-04 -2.357781E-02 -6.449390E-05 -2.467934E-02 -6.935734E-05 -4.424222E-02 -2.197069E-04 -3.398377E-02 -1.404692E-04 -2.969392E-02 -9.932175E-05 -2.586642E-02 -8.639774E-05 -2.018938E-02 -4.704545E-05 -2.460053E-02 -8.337921E-05 -2.140944E-02 -5.566819E-05 -1.750162E-02 -4.330128E-05 -1.823667E-02 -3.807392E-05 -2.182322E-02 -5.835666E-05 -1.996510E-02 -4.925786E-05 -2.054306E-02 -5.967285E-05 -2.448060E-02 -6.724552E-05 -2.420255E-02 -7.171101E-05 -2.224533E-02 -7.074407E-05 -2.688010E-02 -1.048025E-04 -1.252593E-02 -1.992438E-05 -1.627058E-02 -3.557197E-05 -1.458375E-02 -2.939085E-05 -6.673846E-03 -7.665690E-06 -1.782098E-02 -4.530371E-05 -1.873366E-02 -5.530746E-05 -1.014089E-02 -1.628821E-05 -9.132953E-03 -1.379610E-05 -1.363905E-02 -2.661815E-05 -1.338513E-02 -2.253429E-05 -8.423240E-03 -1.911938E-05 -1.484012E-02 -4.135636E-05 -1.418073E-02 -3.323365E-05 -1.543051E-02 -2.985655E-05 -1.632770E-02 -3.517623E-05 -2.008031E-02 -5.274018E-05 -1.602450E-02 -3.958879E-05 -1.750372E-02 -3.314152E-05 -1.219742E-02 -2.258004E-05 -1.433533E-02 -3.618336E-05 -9.075804E-03 -1.317139E-05 -7.380199E-03 -7.236358E-06 -1.782556E-02 -3.957064E-05 -1.184192E-02 -2.021154E-05 -2.859581E-02 -1.054928E-04 -2.697349E-02 -9.676496E-05 -2.792217E-02 -1.147611E-04 -1.382301E-02 -2.417383E-05 -2.411827E-02 -7.056685E-05 -1.982700E-02 -4.766108E-05 -2.818071E-02 -9.359800E-05 -2.219449E-02 -6.075282E-05 -2.314416E-02 -6.478789E-05 -2.443285E-02 -8.381567E-05 -2.019536E-02 -5.428133E-05 -1.353132E-02 -2.740754E-05 -4.406952E-02 -2.190081E-04 -3.446584E-02 -1.346151E-04 -3.753184E-02 -1.527576E-04 -3.033111E-02 -1.034049E-04 -2.855894E-02 -9.711291E-05 -2.771557E-02 -8.771393E-05 -3.898957E-02 -1.908877E-04 -4.305288E-02 -1.970134E-04 -3.486097E-02 -1.545961E-04 -3.499273E-02 -1.436152E-04 -3.110132E-02 -1.127757E-04 -3.008751E-02 -1.139676E-04 -6.319815E-02 -4.327893E-04 -5.504272E-02 -3.344552E-04 -4.591545E-02 -2.365710E-04 -3.617316E-02 -1.560924E-04 -5.218885E-02 -3.077972E-04 -4.686114E-02 -2.529342E-04 -5.728060E-02 -4.031273E-04 -3.978176E-02 -1.860198E-04 -5.004767E-02 -3.136544E-04 -4.523333E-02 -2.208929E-04 -3.194586E-02 -1.141458E-04 -3.721301E-02 -1.553134E-04 -7.477681E-02 -6.639195E-04 -6.780397E-02 -4.776195E-04 -6.358469E-02 -4.696106E-04 -6.032980E-02 -4.286567E-04 -5.153943E-02 -2.983568E-04 -5.818770E-02 -4.058262E-04 -6.750249E-02 -5.176461E-04 -5.373447E-02 -3.160363E-04 -6.782408E-02 -5.197564E-04 -5.101341E-02 -3.150794E-04 -3.969145E-02 -1.890305E-04 -4.675755E-02 -2.829176E-04 -4.899174E-02 -2.593746E-04 -7.529462E-02 -5.829369E-04 -5.523513E-02 -3.275402E-04 -3.691494E-02 -1.532620E-04 -6.863824E-02 -5.261979E-04 -5.665568E-02 -3.399426E-04 -5.021140E-02 -2.920406E-04 -5.159035E-02 -3.147743E-04 -8.420431E-02 -7.633948E-04 -6.502238E-02 -5.063282E-04 -4.930238E-02 -2.812855E-04 -4.815381E-02 -2.482742E-04 -4.967200E-02 -2.680540E-04 -5.523567E-02 -3.370328E-04 -4.300524E-02 -2.081672E-04 -3.836521E-02 -1.711214E-04 -5.253824E-02 -3.333353E-04 -4.027059E-02 -1.822925E-04 -3.645004E-02 -1.495178E-04 -3.990377E-02 -1.790132E-04 -4.399597E-02 -2.136921E-04 -4.313937E-02 -2.093007E-04 -3.431599E-02 -1.398795E-04 -4.051219E-02 -1.961233E-04 -4.595652E-02 -2.564444E-04 -3.332030E-02 -1.206481E-04 -4.197497E-02 -2.029057E-04 -3.566567E-02 -1.599693E-04 -3.563813E-02 -1.390992E-04 -3.428900E-02 -1.439048E-04 -3.548443E-02 -1.582734E-04 -3.147837E-02 -1.431168E-04 -3.473859E-02 -1.320531E-04 -3.340003E-02 -1.534631E-04 -2.995214E-02 -1.115961E-04 -3.783601E-02 -1.612451E-04 -2.593349E-02 -7.511302E-05 -2.220229E-02 -5.650585E-05 -2.396506E-02 -6.330344E-05 -2.415591E-02 -7.979178E-05 -3.230518E-02 -1.226469E-04 -3.148292E-02 -1.291525E-04 -2.555369E-02 -7.214626E-05 -2.008785E-02 -5.277321E-05 -2.623915E-02 -1.052699E-04 -1.687230E-02 -3.802212E-05 -1.828669E-02 -5.222647E-05 -2.568728E-02 -1.046345E-04 -1.666034E-02 -3.189735E-05 -1.441649E-02 -2.909234E-05 -1.961081E-02 -5.252583E-05 -1.446604E-02 -2.906063E-05 -2.499966E-02 -7.290861E-05 -2.036936E-02 -5.610925E-05 -9.247556E-03 -1.678837E-05 -1.093088E-02 -2.167681E-05 -1.260502E-02 -2.987668E-05 -9.613002E-03 -1.269981E-05 -1.504319E-02 -4.123453E-05 -1.804155E-02 -4.790967E-05 -1.950375E-02 -5.823817E-05 -1.292954E-02 -2.425508E-05 -9.979565E-03 -1.879915E-05 -1.627001E-02 -4.007960E-05 -1.727217E-02 -3.779588E-05 -1.821599E-02 -4.516560E-05 -1.246029E-02 -2.877178E-05 -7.419932E-03 -1.075442E-05 -1.027139E-02 -1.750906E-05 -6.011548E-03 -5.357981E-06 -1.284150E-02 -2.684216E-05 -7.873217E-03 -1.261811E-05 -3.056254E-02 -1.119101E-04 -2.367459E-02 -7.012832E-05 -3.125473E-02 -1.349511E-04 -2.032722E-02 -6.005633E-05 -1.966372E-02 -4.807430E-05 -1.888280E-02 -4.353141E-05 -3.148229E-02 -1.087028E-04 -2.569052E-02 -8.345601E-05 -1.988235E-02 -5.083043E-05 -3.033509E-02 -1.353984E-04 -2.455111E-02 -8.578087E-05 -2.292969E-02 -6.706042E-05 -3.506638E-02 -1.418820E-04 -2.811805E-02 -8.950254E-05 -3.758364E-02 -1.763474E-04 -4.852969E-02 -3.045767E-04 -3.345491E-02 -1.265446E-04 -3.311976E-02 -1.271997E-04 -4.406586E-02 -2.049053E-04 -3.977663E-02 -1.944792E-04 -3.568273E-02 -1.393831E-04 -4.714003E-02 -2.666571E-04 -3.938142E-02 -1.784684E-04 -3.504464E-02 -1.619498E-04 -6.038764E-02 -4.057914E-04 -5.523867E-02 -3.480079E-04 -5.012020E-02 -2.738532E-04 -5.300843E-02 -3.225930E-04 -4.819845E-02 -2.912996E-04 -4.192723E-02 -2.042929E-04 -5.437230E-02 -3.831173E-04 -4.855340E-02 -2.801897E-04 -5.632430E-02 -3.684549E-04 -3.908351E-02 -1.754220E-04 -3.723203E-02 -1.565670E-04 -3.662475E-02 -1.409549E-04 -7.828360E-02 -6.808841E-04 -6.139852E-02 -4.032303E-04 -6.455776E-02 -4.632098E-04 -4.996191E-02 -2.795507E-04 -5.586957E-02 -3.475409E-04 -4.296598E-02 -2.317524E-04 -6.386330E-02 -4.717762E-04 -5.291090E-02 -3.247736E-04 -5.483498E-02 -3.421555E-04 -4.502506E-02 -2.409819E-04 -5.510776E-02 -3.436300E-04 -4.165980E-02 -1.924264E-04 -5.979906E-02 -3.843900E-04 -5.815532E-02 -3.901796E-04 -6.144432E-02 -4.159903E-04 -5.918684E-02 -3.926095E-04 -7.241076E-02 -5.527725E-04 -5.729097E-02 -3.531500E-04 -5.051843E-02 -2.810907E-04 -5.618582E-02 -3.776148E-04 -5.589932E-02 -3.335647E-04 -6.161520E-02 -4.831295E-04 -5.389348E-02 -3.065669E-04 -4.348189E-02 -2.135964E-04 -4.695822E-02 -2.384098E-04 -6.006602E-02 -3.844395E-04 -4.864361E-02 -2.423012E-04 -4.186798E-02 -1.902022E-04 -4.797983E-02 -2.448734E-04 -4.768596E-02 -2.637688E-04 -4.383760E-02 -2.023880E-04 -3.729270E-02 -2.011170E-04 -4.671060E-02 -2.774443E-04 -4.261256E-02 -2.074667E-04 -3.057615E-02 -1.013174E-04 -3.642349E-02 -1.538488E-04 -2.640419E-02 -8.839937E-05 -5.298488E-02 -3.147305E-04 -2.462926E-02 -7.609382E-05 -3.274348E-02 -1.347060E-04 -4.085845E-02 -2.061636E-04 -3.613681E-02 -1.608827E-04 -3.188424E-02 -1.170448E-04 -3.090210E-02 -1.209556E-04 -4.916591E-02 -2.638816E-04 -3.463680E-02 -1.731202E-04 -2.473796E-02 -8.278777E-05 -2.876733E-02 -1.050578E-04 -2.818020E-02 -9.186488E-05 -2.922451E-02 -9.710630E-05 -2.867494E-02 -1.008386E-04 -2.048136E-02 -5.645393E-05 -2.709023E-02 -9.906565E-05 -2.060958E-02 -6.002022E-05 -2.400813E-02 -7.048793E-05 -2.041445E-02 -5.660380E-05 -3.022382E-02 -1.232007E-04 -2.428404E-02 -7.261848E-05 -1.317267E-02 -4.498520E-05 -1.725417E-02 -4.131436E-05 -1.439809E-02 -2.877223E-05 -1.849952E-02 -3.964099E-05 -1.298598E-02 -2.510194E-05 -1.158199E-02 -1.753824E-05 -2.399736E-02 -7.914398E-05 -1.618716E-02 -3.923219E-05 -1.582299E-02 -3.446241E-05 -1.281460E-02 -2.948723E-05 -1.687823E-02 -3.916635E-05 -1.992356E-02 -5.363759E-05 -9.964687E-03 -2.281351E-05 -9.741073E-03 -1.461704E-05 -1.016266E-02 -1.578045E-05 -1.165241E-02 -1.448412E-05 -1.645559E-02 -3.314741E-05 -1.019874E-02 -1.584348E-05 -1.400560E-02 -2.950132E-05 -1.339261E-02 -2.462337E-05 -9.864617E-03 -2.007031E-05 -1.033312E-02 -1.570664E-05 -1.150186E-02 -1.612123E-05 -8.165735E-03 -9.980334E-06 -1.039922E-02 -2.137334E-05 -7.778314E-03 -1.402972E-05 -2.352819E-02 -7.324745E-05 -1.832670E-02 -5.260061E-05 -1.958916E-02 -4.738463E-05 -2.752632E-02 -1.321234E-04 -1.290240E-02 -2.435598E-05 -1.180814E-02 -1.989960E-05 -1.968230E-02 -4.672576E-05 -2.074128E-02 -5.154692E-05 -1.506303E-02 -2.974396E-05 -2.158432E-02 -6.994225E-05 -2.106432E-02 -6.601892E-05 -1.297999E-02 -2.683746E-05 -4.398209E-02 -2.260240E-04 -3.493420E-02 -1.634600E-04 -3.877993E-02 -1.929775E-04 -2.663088E-02 -8.262555E-05 -2.385591E-02 -7.188146E-05 -2.756789E-02 -8.724472E-05 -4.223824E-02 -2.331366E-04 -3.372408E-02 -1.376633E-04 -2.877054E-02 -1.172152E-04 -2.505284E-02 -6.877934E-05 -2.710121E-02 -9.089126E-05 -2.491989E-02 -7.126133E-05 -4.605415E-02 -2.550818E-04 -3.674502E-02 -1.551214E-04 -3.682502E-02 -1.637234E-04 -4.294065E-02 -2.131753E-04 -3.770825E-02 -1.835164E-04 -3.445084E-02 -1.375944E-04 -3.954845E-02 -1.848016E-04 -4.320951E-02 -2.332661E-04 -2.976462E-02 -1.143727E-04 -3.263580E-02 -1.287108E-04 -4.185045E-02 -1.965114E-04 -3.879064E-02 -1.631517E-04 -6.117745E-02 -4.170051E-04 -5.208900E-02 -2.961117E-04 -5.034441E-02 -2.985461E-04 -4.763203E-02 -2.520770E-04 -4.424593E-02 -2.210065E-04 -4.633482E-02 -2.857246E-04 -6.172917E-02 -4.365884E-04 -4.667374E-02 -2.416468E-04 -4.615839E-02 -2.447158E-04 -4.202865E-02 -1.984881E-04 -3.687271E-02 -1.549029E-04 -3.781817E-02 -1.658791E-04 -5.350156E-02 -3.106556E-04 -5.738403E-02 -3.535711E-04 -5.581047E-02 -3.600168E-04 -5.548481E-02 -3.477589E-04 -5.976117E-02 -4.066921E-04 -3.679728E-02 -1.797632E-04 -4.181607E-02 -2.319380E-04 -4.475783E-02 -2.471295E-04 -5.073065E-02 -2.731166E-04 -4.282973E-02 -2.376864E-04 -3.923270E-02 -1.868782E-04 -3.175293E-02 -1.204289E-04 -3.821514E-02 -1.563696E-04 -4.698886E-02 -2.394623E-04 -4.501447E-02 -2.402396E-04 -3.789686E-02 -1.636683E-04 -4.356985E-02 -2.501184E-04 -3.738072E-02 -1.625968E-04 -3.207025E-02 -1.387594E-04 -3.204040E-02 -1.219128E-04 -4.516239E-02 -2.412503E-04 -4.133319E-02 -1.807484E-04 -2.760180E-02 -8.957336E-05 -4.274635E-02 -2.203768E-04 -3.183938E-02 -1.177784E-04 -3.431517E-02 -1.538918E-04 -3.335945E-02 -1.452163E-04 -3.036081E-02 -1.173248E-04 -3.231608E-02 -1.473817E-04 -3.026666E-02 -1.062646E-04 -2.381248E-02 -6.812346E-05 -2.438669E-02 -7.616674E-05 -2.657091E-02 -8.997086E-05 -2.155185E-02 -7.092421E-05 -2.241945E-02 -5.990905E-05 -3.070897E-02 -1.109960E-04 -2.580881E-02 -7.863309E-05 -2.134721E-02 -6.345767E-05 -3.007460E-02 -1.199346E-04 -2.158487E-02 -5.631137E-05 -2.267038E-02 -6.523479E-05 -2.199756E-02 -6.744627E-05 -1.920160E-02 -4.676740E-05 -1.927293E-02 -5.882526E-05 -1.309800E-02 -2.176798E-05 -1.864318E-02 -4.222760E-05 -2.427004E-02 -7.787837E-05 -2.143559E-02 -5.842615E-05 -1.187245E-02 -3.400806E-05 -1.386855E-02 -2.215090E-05 -1.089761E-02 -1.742663E-05 -1.054008E-02 -1.719743E-05 -1.573092E-02 -2.947697E-05 -1.260841E-02 -2.538616E-05 -9.335769E-03 -1.220699E-05 -9.218607E-03 -1.346226E-05 -1.232919E-02 -2.739036E-05 -1.540769E-02 -3.545632E-05 -1.047417E-02 -1.403218E-05 -8.091074E-03 -9.942405E-06 -9.757397E-03 -1.839030E-05 -1.147311E-02 -2.272680E-05 -6.625367E-03 -9.542469E-06 -1.132951E-02 -2.290402E-05 -1.276799E-02 -2.736073E-05 -1.641171E-02 -4.522620E-05 -1.038484E-02 -2.444781E-05 -1.059856E-02 -2.086366E-05 -1.070727E-02 -3.120073E-05 -9.434079E-03 -2.288235E-05 -1.260589E-02 -2.115523E-05 -8.609571E-03 -1.014235E-05 -1.906718E-02 -4.342750E-05 -1.697807E-02 -3.746587E-05 -1.655549E-02 -3.279717E-05 -2.335945E-02 -8.680066E-05 -1.947075E-02 -4.582833E-05 -2.334564E-02 -6.827484E-05 -1.970475E-02 -5.161446E-05 -2.176597E-02 -8.091171E-05 -1.794831E-02 -4.826888E-05 -1.453865E-02 -2.543285E-05 -2.285340E-02 -7.733631E-05 -1.495227E-02 -3.812970E-05 -2.993202E-02 -1.105328E-04 -2.591986E-02 -7.889731E-05 -2.891690E-02 -1.170195E-04 -2.591958E-02 -8.245187E-05 -2.472591E-02 -6.792489E-05 -1.984583E-02 -4.399055E-05 -2.723781E-02 -1.063286E-04 -2.214049E-02 -6.647832E-05 -1.976025E-02 -4.630788E-05 -2.362519E-02 -9.840049E-05 -2.147637E-02 -5.680267E-05 -1.716604E-02 -3.707514E-05 -3.853916E-02 -1.905886E-04 -3.220009E-02 -1.166545E-04 -3.072451E-02 -1.105417E-04 -3.416322E-02 -1.445852E-04 -3.081335E-02 -1.205464E-04 -2.381824E-02 -7.421006E-05 -2.919176E-02 -1.443329E-04 -3.165320E-02 -1.643449E-04 -2.399334E-02 -7.031311E-05 -2.931290E-02 -1.089751E-04 -2.541529E-02 -9.960904E-05 -2.496511E-02 -9.522684E-05 -4.191717E-02 -2.319644E-04 -4.194175E-02 -2.515421E-04 -4.029489E-02 -2.034800E-04 -2.949880E-02 -1.001309E-04 -3.311240E-02 -2.069653E-04 -3.311074E-02 -1.645850E-04 -2.609443E-02 -7.829184E-05 -2.523310E-02 -7.753633E-05 -3.376196E-02 -1.542562E-04 -3.370657E-02 -1.584460E-04 -2.316126E-02 -6.322560E-05 -2.849745E-02 -1.006036E-04 -4.156928E-02 -1.941410E-04 -3.356649E-02 -1.323048E-04 -3.504110E-02 -1.432793E-04 -4.188246E-02 -2.087133E-04 -2.686062E-02 -1.008920E-04 -3.264472E-02 -1.519956E-04 -3.182824E-02 -1.190821E-04 -3.230472E-02 -1.432567E-04 -3.014385E-02 -1.104329E-04 -3.127630E-02 -1.124605E-04 -3.215046E-02 -1.161615E-04 -3.028866E-02 -1.020886E-04 -3.771125E-02 -1.568073E-04 -3.370873E-02 -1.337798E-04 -4.025498E-02 -1.918052E-04 -2.635483E-02 -7.350768E-05 -2.361243E-02 -7.420144E-05 -3.747673E-02 -2.016310E-04 -2.747199E-02 -8.499093E-05 -3.168249E-02 -1.354519E-04 -2.123972E-02 -5.349966E-05 -2.029422E-02 -4.819609E-05 -2.757353E-02 -9.520625E-05 -3.241854E-02 -1.263115E-04 -2.339411E-02 -8.048390E-05 -2.881367E-02 -9.755501E-05 -2.959500E-02 -1.180648E-04 -2.255464E-02 -6.753101E-05 -4.047075E-02 -1.783640E-04 -3.116618E-02 -1.271595E-04 -2.360487E-02 -7.555159E-05 -2.020519E-02 -5.400702E-05 -2.646647E-02 -7.518930E-05 -2.547209E-02 -9.058727E-05 -1.278101E-02 -2.432873E-05 -2.394671E-02 -7.236070E-05 -1.399890E-02 -2.909745E-05 -1.693268E-02 -4.013852E-05 -1.661474E-02 -3.646989E-05 -1.683699E-02 -3.576439E-05 -1.878683E-02 -4.752490E-05 -1.779186E-02 -5.032257E-05 -1.615460E-02 -3.517429E-05 -1.467092E-02 -2.974604E-05 -1.693919E-02 -3.956534E-05 -1.570365E-02 -3.373372E-05 -1.503755E-02 -3.242993E-05 -1.350241E-02 -2.471502E-05 -1.132128E-02 -2.210554E-05 -1.394835E-02 -2.288474E-05 -7.707010E-03 -1.408651E-05 -1.105673E-02 -1.950348E-05 -1.597153E-02 -2.996363E-05 -1.072279E-02 -1.642956E-05 -9.931403E-03 -1.596209E-05 -3.110131E-03 -1.979116E-06 -1.465778E-02 -2.665954E-05 -8.711776E-03 -1.658595E-05 -3.336279E-03 -3.121041E-06 -1.220546E-02 -2.078425E-05 -1.225808E-02 -2.498648E-05 -5.941201E-03 -5.858248E-06 -8.017505E-03 -9.987361E-06 -1.032012E-02 -1.689346E-05 -1.122116E-02 -2.547015E-05 -7.303253E-03 -1.021165E-05 -1.018508E-02 -1.390217E-05 -6.537846E-03 -1.231905E-05 -3.757225E-03 -2.146236E-06 -6.253529E-03 -8.259456E-06 -8.429652E-03 -1.132331E-05 -6.846152E-03 -1.089548E-05 -1.540905E-02 -4.707146E-05 -1.380105E-02 -2.885252E-05 -1.506988E-02 -3.657193E-05 -1.379370E-02 -2.325176E-05 -9.540645E-03 -2.912938E-05 -1.053361E-02 -1.745746E-05 -8.796644E-03 -1.302715E-05 -9.798877E-03 -1.776907E-05 -1.287457E-02 -2.307421E-05 -1.194402E-02 -2.276268E-05 -7.875525E-03 -1.292927E-05 -5.533880E-03 -9.384723E-06 -1.864321E-02 -4.177596E-05 -1.252461E-02 -2.176958E-05 -1.644030E-02 -4.131255E-05 -1.697179E-02 -3.953043E-05 -9.140492E-03 -1.319242E-05 -9.958402E-03 -1.459665E-05 -1.425287E-02 -3.897232E-05 -1.522358E-02 -3.028879E-05 -1.361118E-02 -2.815573E-05 -1.173677E-02 -3.088583E-05 -1.617474E-02 -3.012921E-05 -1.598127E-02 -3.641752E-05 -1.724536E-02 -3.982285E-05 -2.253691E-02 -6.632574E-05 -2.268227E-02 -5.893945E-05 -2.582333E-02 -9.202078E-05 -1.982485E-02 -6.093449E-05 -1.544437E-02 -3.045457E-05 -1.968776E-02 -5.933245E-05 -2.092753E-02 -6.834686E-05 -1.664629E-02 -4.979724E-05 -1.855281E-02 -6.404621E-05 -2.195650E-02 -6.222690E-05 -2.186198E-02 -5.700014E-05 -2.078639E-02 -4.826480E-05 -2.147531E-02 -5.352146E-05 -2.309193E-02 -6.508444E-05 -2.306719E-02 -7.442212E-05 -2.181171E-02 -6.065432E-05 -2.787754E-02 -1.071646E-04 -2.038266E-02 -6.322425E-05 -2.066861E-02 -6.321760E-05 -2.341755E-02 -6.599464E-05 -1.940287E-02 -5.285084E-05 -2.311451E-02 -6.931733E-05 -2.685211E-02 -9.869681E-05 -2.577665E-02 -9.741583E-05 -2.127713E-02 -7.233352E-05 -3.534780E-02 -1.462501E-04 -2.985988E-02 -1.001501E-04 -2.157060E-02 -5.933210E-05 -2.112991E-02 -6.091345E-05 -1.791858E-02 -4.849939E-05 -2.317059E-02 -5.941629E-05 -1.585209E-02 -3.680922E-05 -1.761272E-02 -4.005327E-05 -2.673873E-02 -8.415236E-05 -2.195005E-02 -7.869690E-05 -2.169063E-02 -6.115181E-05 -1.902034E-02 -4.602942E-05 -2.149684E-02 -6.272837E-05 -2.152317E-02 -5.647475E-05 -1.267811E-02 -2.551472E-05 -1.902212E-02 -4.501454E-05 -1.468929E-02 -2.854665E-05 -2.098348E-02 -5.162857E-05 -1.448437E-02 -3.165154E-05 -1.385842E-02 -2.946322E-05 -2.214565E-02 -6.004180E-05 -1.824698E-02 -3.872549E-05 -1.212354E-02 -2.133339E-05 -2.018308E-02 -5.701150E-05 -2.166624E-02 -5.196544E-05 -2.142212E-02 -5.674884E-05 -1.267848E-02 -2.171626E-05 -1.758911E-02 -4.711902E-05 -1.111921E-02 -2.058872E-05 -1.517506E-02 -2.980149E-05 -1.147226E-02 -1.573079E-05 -1.249264E-02 -2.217903E-05 -1.604063E-02 -3.750625E-05 -1.275295E-02 -2.494568E-05 -1.612605E-02 -3.577658E-05 -1.249864E-02 -3.018969E-05 -1.188510E-02 -2.704321E-05 -1.571000E-02 -3.615816E-05 -1.235323E-02 -2.593348E-05 -1.676586E-02 -4.472755E-05 -1.093806E-02 -1.930162E-05 -1.176929E-02 -2.511180E-05 -1.397747E-02 -2.813515E-05 -9.701315E-03 -1.930872E-05 -1.399983E-02 -3.413054E-05 -1.986885E-02 -5.082098E-05 -8.243555E-03 -1.200852E-05 -1.024429E-02 -1.296572E-05 -1.258003E-02 -1.952799E-05 -1.610829E-02 -3.961341E-05 -1.010586E-02 -1.549208E-05 -1.252095E-02 -2.770018E-05 -8.230588E-03 -1.450505E-05 -8.380929E-03 -1.203824E-05 -8.828715E-03 -1.191588E-05 -6.588527E-03 -7.910780E-06 -6.380189E-03 -6.533479E-06 -5.052056E-03 -5.336338E-06 -3.648351E-03 -2.706990E-06 -2.149471E-03 -1.225876E-06 -9.270100E-03 -1.169691E-05 -1.117400E-02 -2.249063E-05 -5.230590E-03 -4.655893E-06 -7.224676E-03 -1.008003E-05 -3.887357E-03 -3.670860E-06 -3.183273E-03 -1.847498E-06 -3.708170E-03 -2.623818E-06 -5.854425E-03 -1.020941E-05 -5.406569E-03 -5.391988E-06 -9.036302E-03 -1.748353E-05 -8.903194E-03 -1.445005E-05 -9.987482E-03 -2.075751E-05 -8.426663E-03 -1.155563E-05 -8.912136E-03 -1.175066E-05 -4.122838E-03 -3.170046E-06 -4.845177E-03 -4.237971E-06 -6.822194E-03 -1.051328E-05 -4.425000E-03 -4.417976E-06 -7.424172E-03 -9.891741E-06 -4.992950E-03 -5.945799E-06 -7.149030E-03 -1.123994E-05 -4.625444E-03 -5.049047E-06 -1.123851E-02 -2.017630E-05 -1.074541E-02 -1.709195E-05 -1.430110E-02 -3.117294E-05 -9.806637E-03 -1.530391E-05 -1.043812E-02 -1.609521E-05 -1.012866E-02 -1.391247E-05 -1.428192E-02 -2.902409E-05 -1.605416E-02 -3.562493E-05 -9.063095E-03 -1.372032E-05 -9.587205E-03 -1.473939E-05 -9.634515E-03 -1.858518E-05 -1.164975E-02 -2.656325E-05 -1.361864E-02 -2.518422E-05 -1.167329E-02 -2.323748E-05 -1.622385E-02 -3.856234E-05 -1.214097E-02 -2.153842E-05 -1.381854E-02 -2.815505E-05 -1.415211E-02 -4.135908E-05 -1.568971E-02 -3.813496E-05 -1.198123E-02 -2.333564E-05 -8.499425E-03 -1.126897E-05 -1.312625E-02 -3.325212E-05 -1.172860E-02 -2.042689E-05 -1.005348E-02 -1.782865E-05 -1.548579E-02 -3.103766E-05 -1.744521E-02 -3.960003E-05 -1.654740E-02 -3.986988E-05 -1.620467E-02 -3.009003E-05 -1.948116E-02 -5.036150E-05 -1.418148E-02 -2.971887E-05 -1.337833E-02 -2.666872E-05 -1.314218E-02 -2.648418E-05 -1.436798E-02 -2.490823E-05 -9.444986E-03 -1.101269E-05 -1.962001E-02 -8.135189E-05 -1.158154E-02 -1.952295E-05 -1.471099E-02 -3.133120E-05 -1.541812E-02 -3.170497E-05 -1.674531E-02 -4.322938E-05 -1.669392E-02 -3.544709E-05 -1.333115E-02 -3.211381E-05 -1.738951E-02 -4.841020E-05 -1.007222E-02 -1.774930E-05 -1.295078E-02 -2.638113E-05 -1.271717E-02 -2.298976E-05 -1.404483E-02 -2.889248E-05 -1.576832E-02 -3.529377E-05 -1.252846E-02 -2.718789E-05 -2.519173E-02 -8.963887E-05 -1.493179E-02 -3.748831E-05 -1.259822E-02 -2.429153E-05 -1.224327E-02 -1.829117E-05 -1.391862E-02 -2.860704E-05 -8.357253E-03 -1.029121E-05 -1.570825E-02 -3.413707E-05 -1.359733E-02 -2.641606E-05 -1.091443E-02 -1.715332E-05 -8.155749E-03 -1.261821E-05 -1.313846E-02 -2.597075E-05 -8.293564E-03 -1.056976E-05 -1.638901E-02 -3.229628E-05 -1.331850E-02 -3.096114E-05 -1.103350E-02 -1.929035E-05 -1.727217E-02 -4.051979E-05 -9.518809E-03 -1.410510E-05 -1.300800E-02 -2.937228E-05 -1.046353E-02 -1.575334E-05 -1.036019E-02 -1.658144E-05 -8.892217E-03 -1.726775E-05 -9.649910E-03 -1.409080E-05 -1.146930E-02 -2.018867E-05 -8.918398E-03 -1.307570E-05 -1.525733E-02 -3.596673E-05 -1.004009E-02 -1.246514E-05 -1.066698E-02 -2.185894E-05 -7.770912E-03 -1.480934E-05 -7.264717E-03 -8.442828E-06 -6.706343E-03 -6.852573E-06 -5.559456E-03 -4.744752E-06 -5.773360E-03 -7.781275E-06 -1.331078E-02 -3.353599E-05 -8.788678E-03 -1.329146E-05 -4.884040E-03 -4.077409E-06 -8.675118E-03 -1.319086E-05 -4.761159E-03 -3.796641E-06 -5.277064E-03 -5.807731E-06 -1.108365E-02 -1.916299E-05 -5.331867E-03 -3.733672E-06 -7.054526E-03 -6.862094E-06 -5.863290E-03 -8.388015E-06 -7.544134E-03 -1.042553E-05 -5.750484E-03 -5.095339E-06 -5.249358E-03 -3.213161E-06 -7.899479E-03 -9.484436E-06 -4.160278E-03 -4.806702E-06 -7.679996E-03 -2.071836E-05 -6.241972E-03 -1.620756E-05 -5.913948E-03 -7.527048E-06 -4.733716E-03 -5.631878E-06 -2.903451E-03 -2.658722E-06 -3.127420E-03 -2.299870E-06 -2.273717E-03 -1.344135E-06 -6.534306E-03 -1.124316E-05 -7.835003E-03 -1.715904E-05 -4.882363E-03 -4.758963E-06 -3.720325E-03 -3.452423E-06 -1.428452E-03 -1.333526E-06 -3.504443E-03 -2.552735E-06 -8.268492E-03 -1.053438E-05 -9.457152E-03 -1.477101E-05 -4.409644E-03 -4.661365E-06 -5.391744E-03 -4.980226E-06 -7.357835E-03 -8.692158E-06 -4.819563E-03 -3.684180E-06 -7.257471E-03 -8.300854E-06 -8.234387E-03 -1.648552E-05 -7.005187E-03 -8.215133E-06 -8.218179E-03 -1.271584E-05 -4.248638E-03 -4.615897E-06 -6.471659E-03 -7.110986E-06 -8.889291E-03 -1.460347E-05 -8.616060E-03 -1.124291E-05 -8.973876E-03 -1.442105E-05 -6.214996E-03 -1.237961E-05 -6.199460E-03 -8.437599E-06 -2.474487E-03 -1.269623E-06 -1.303002E-02 -3.064014E-05 -1.148543E-02 -2.273454E-05 -1.266333E-02 -2.372659E-05 -1.058814E-02 -2.391393E-05 -5.834966E-03 -9.147127E-06 -5.517822E-03 -5.264691E-06 -1.270359E-02 -2.820747E-05 -1.820335E-02 -4.287032E-05 -1.202094E-02 -2.800408E-05 -7.736254E-03 -1.836141E-05 -6.145051E-03 -6.272679E-06 -7.726439E-03 -1.228446E-05 -1.411190E-02 -2.865658E-05 -6.550872E-03 -6.014982E-06 -2.088300E-02 -7.573466E-05 -1.388048E-02 -3.669556E-05 -1.132008E-02 -1.910843E-05 -1.611954E-02 -3.536212E-05 -1.481933E-02 -3.146401E-05 -1.247993E-02 -2.294126E-05 -1.394123E-02 -2.867543E-05 -1.072461E-02 -1.866919E-05 -8.610956E-03 -1.120688E-05 -8.083860E-03 -8.161992E-06 -1.162751E-02 -1.795366E-05 -9.128039E-03 -1.252646E-05 -9.364221E-03 -1.603445E-05 -8.294106E-03 -1.019170E-05 -5.833363E-03 -4.941650E-06 -9.811611E-03 -1.274458E-05 -1.603475E-02 -3.880395E-05 -1.271422E-02 -2.182851E-05 -1.519349E-02 -3.269445E-05 -9.595042E-03 -1.801667E-05 -7.059973E-03 -7.819618E-06 -5.876271E-03 -1.068936E-05 -1.340147E-02 -3.113903E-05 -4.145261E-03 -3.270808E-06 -1.596720E-02 -3.465819E-05 -1.433346E-02 -2.905578E-05 -8.468040E-03 -1.341062E-05 -1.395194E-02 -2.559614E-05 -1.200470E-02 -1.982352E-05 -1.665933E-02 -3.793148E-05 -1.726359E-02 -3.968046E-05 -6.540447E-03 -5.939253E-06 -8.142400E-03 -1.045929E-05 -6.566817E-03 -8.688594E-06 -1.015592E-02 -1.244647E-05 -1.077665E-02 -1.554617E-05 -1.360651E-02 -3.317478E-05 -1.060123E-02 -1.533418E-05 -3.815593E-03 -2.480255E-06 -9.569339E-03 -1.129499E-05 -9.012545E-03 -1.643164E-05 -1.246360E-02 -3.026280E-05 -8.297915E-03 -9.462041E-06 -5.099837E-03 -5.488747E-06 -8.276566E-03 -1.499072E-05 -3.692279E-03 -3.276555E-06 -8.083217E-03 -1.216780E-05 -9.547609E-03 -1.541880E-05 -1.043999E-02 -1.447586E-05 -9.984558E-03 -1.393147E-05 -6.036500E-03 -1.256268E-05 -5.023445E-03 -4.154969E-06 -7.024538E-03 -1.085504E-05 -7.537608E-03 -1.160621E-05 -5.017595E-03 -4.922120E-06 -5.050436E-03 -4.024516E-06 -3.123518E-03 -1.695608E-06 -3.831129E-03 -3.485286E-06 -5.964101E-03 -9.872984E-06 -3.809889E-03 -3.837033E-06 -4.120333E-03 -3.567476E-06 -3.925178E-03 -5.899336E-06 -3.890602E-03 -8.542757E-06 -6.033356E-03 -7.736141E-06 -4.271392E-03 -3.643602E-06 -2.282046E-03 -1.281394E-06 -6.262911E-03 -1.134696E-05 -4.313813E-03 -4.443331E-06 -1.308832E-03 -4.103970E-07 -2.590108E-03 -2.620429E-06 -5.831777E-03 -4.892875E-06 -5.376268E-03 -5.205758E-06 -9.496856E-03 -1.606537E-05 -7.245952E-03 -7.095242E-06 -7.623819E-03 -1.187689E-05 -5.536252E-03 -5.148443E-06 -1.245396E-02 -2.199505E-05 -7.641760E-03 -1.306388E-05 -1.005116E-02 -1.904677E-05 -1.159156E-02 -2.899326E-05 -1.114868E-02 -2.094752E-05 -1.010147E-02 -1.978005E-05 -8.874213E-03 -1.298622E-05 -1.283785E-02 -2.632677E-05 -5.598145E-03 -4.564302E-06 -4.874451E-03 -6.380741E-06 -4.166868E-03 -4.546806E-06 -5.450652E-03 -4.552973E-06 -9.123569E-03 -1.521157E-05 -1.341420E-02 -2.773663E-05 -9.949075E-03 -1.548877E-05 -1.128600E-02 -1.714535E-05 -9.944157E-03 -1.206624E-05 -9.339838E-03 -1.587210E-05 -1.407135E-02 -3.845557E-05 -1.079969E-02 -1.419090E-05 -1.806763E-02 -5.775082E-05 -1.538811E-02 -3.591506E-05 -9.283522E-03 -1.519392E-05 -8.256501E-03 -1.262631E-05 -1.236476E-02 -1.963613E-05 -2.020990E-02 -5.409171E-05 -1.152596E-02 -2.039197E-05 -1.050303E-02 -2.499041E-05 -1.326414E-02 -2.783213E-05 -7.696971E-03 -9.711977E-06 -1.473705E-02 -2.646008E-05 -1.062292E-02 -1.576562E-05 -1.707377E-02 -4.212601E-05 -8.778679E-03 -1.128995E-05 -9.710118E-03 -1.623481E-05 -7.636475E-03 -1.094355E-05 -1.387905E-02 -2.941626E-05 -1.776651E-02 -4.396460E-05 -1.818565E-02 -4.350858E-05 -1.761749E-02 -4.630235E-05 -2.043671E-02 -6.064648E-05 -1.635160E-02 -4.147100E-05 -1.364022E-02 -2.386011E-05 -1.724650E-02 -5.086651E-05 -1.457393E-02 -3.218203E-05 -1.625704E-02 -4.109869E-05 -1.536770E-02 -3.150971E-05 -1.415214E-02 -2.531338E-05 -1.764117E-02 -3.839917E-05 -1.856292E-02 -4.105079E-05 -1.122011E-02 -2.240362E-05 -1.237537E-02 -1.904481E-05 -1.725511E-02 -3.218693E-05 -1.246019E-02 -2.168729E-05 -2.268477E-02 -6.638152E-05 -1.772582E-02 -4.134691E-05 -1.846727E-02 -4.453692E-05 -2.211594E-02 -6.148331E-05 -1.337994E-02 -2.272968E-05 -1.800735E-02 -4.594283E-05 -1.767634E-02 -3.572802E-05 -2.109287E-02 -7.624418E-05 -2.116135E-02 -5.023945E-05 -1.511244E-02 -3.916543E-05 -2.555283E-02 -8.270634E-05 -2.320248E-02 -6.856995E-05 -1.490210E-02 -3.025554E-05 -1.650536E-02 -3.349063E-05 -2.005340E-02 -6.329046E-05 -1.876604E-02 -4.632276E-05 -2.017935E-02 -5.608622E-05 -1.623543E-02 -3.183867E-05 -1.059288E-02 -1.746867E-05 -1.923413E-02 -5.626395E-05 -1.874482E-02 -5.158196E-05 -1.436557E-02 -3.262805E-05 -1.736130E-02 -4.360560E-05 -2.044766E-02 -5.055246E-05 -1.508312E-02 -3.245600E-05 -1.328001E-02 -2.673879E-05 -1.466660E-02 -2.878429E-05 -1.289951E-02 -2.132152E-05 -1.410955E-02 -2.671457E-05 -1.814074E-02 -4.435357E-05 -2.012953E-02 -5.472686E-05 -1.186784E-02 -2.108638E-05 -1.158902E-02 -2.017687E-05 -9.605924E-03 -1.492077E-05 -1.409567E-02 -2.579784E-05 -1.311103E-02 -2.570267E-05 -1.707665E-02 -4.130920E-05 -1.248159E-02 -2.515719E-05 -2.097524E-02 -5.151201E-05 -1.444457E-02 -2.981175E-05 -1.072187E-02 -1.803695E-05 -9.014009E-03 -1.215827E-05 -9.559055E-03 -1.537581E-05 -1.354384E-02 -2.184405E-05 -4.828517E-03 -6.482158E-06 -6.253387E-03 -5.785292E-06 -1.379191E-02 -2.710641E-05 -1.011815E-02 -1.374848E-05 -1.342278E-02 -3.152030E-05 -8.883639E-03 -1.651249E-05 -1.847885E-02 -6.016422E-05 -8.586243E-03 -1.066056E-05 -6.968205E-03 -8.792141E-06 -1.218017E-02 -2.375039E-05 -4.288567E-03 -2.617468E-06 -6.568856E-03 -6.417620E-06 -9.364388E-03 -1.515485E-05 -7.810927E-03 -9.503941E-06 -1.316444E-02 -2.494672E-05 -8.772821E-03 -1.075467E-05 -9.114583E-03 -1.293064E-05 -5.120610E-03 -5.597666E-06 -1.156077E-02 -2.090644E-05 -1.044752E-02 -1.460984E-05 -4.306774E-03 -7.024156E-06 -6.743680E-03 -9.397053E-06 -1.132063E-02 -2.916783E-05 -9.875394E-03 -1.234227E-05 -8.066518E-03 -1.631648E-05 -1.197445E-02 -2.795662E-05 -1.196930E-02 -2.592654E-05 -1.424965E-02 -2.937215E-05 -9.362177E-03 -1.637802E-05 -1.115631E-02 -2.100489E-05 -6.612368E-03 -1.091592E-05 -1.030811E-02 -2.480048E-05 -1.024176E-02 -1.540918E-05 -8.799976E-03 -1.413045E-05 -1.817266E-02 -5.223457E-05 -1.320418E-02 -2.287606E-05 -1.190482E-02 -2.042598E-05 -1.296875E-02 -2.780267E-05 -1.250433E-02 -2.092242E-05 -1.114237E-02 -1.800945E-05 -1.312112E-02 -2.394975E-05 -1.206217E-02 -2.206129E-05 -1.913237E-02 -5.391475E-05 -2.015987E-02 -4.368668E-05 -1.126423E-02 -1.875823E-05 -1.360231E-02 -2.402648E-05 -1.853506E-02 -4.518491E-05 -1.582011E-02 -3.202477E-05 -1.750058E-02 -4.932715E-05 -1.849790E-02 -5.325631E-05 -2.129000E-02 -5.323882E-05 -1.294333E-02 -2.153410E-05 -2.386151E-02 -7.053393E-05 -2.237759E-02 -6.198345E-05 -1.402168E-02 -3.040550E-05 -2.166707E-02 -7.200843E-05 -1.815377E-02 -4.704586E-05 -2.159074E-02 -9.800484E-05 -2.096789E-02 -5.417603E-05 -2.036776E-02 -5.114163E-05 -1.950805E-02 -5.196224E-05 -1.203115E-02 -2.466652E-05 -2.375017E-02 -7.101139E-05 -2.078435E-02 -4.980028E-05 -1.967736E-02 -5.349609E-05 -1.492202E-02 -3.151061E-05 -2.448505E-02 -7.345642E-05 -2.875745E-02 -9.624049E-05 -1.308936E-02 -2.260928E-05 -1.883770E-02 -5.294121E-05 -2.132133E-02 -6.019067E-05 -2.681408E-02 -8.810170E-05 -1.857403E-02 -3.926673E-05 -1.774032E-02 -4.025585E-05 -2.588771E-02 -8.059759E-05 -2.050194E-02 -4.648784E-05 -2.612192E-02 -8.558295E-05 -1.686319E-02 -3.786866E-05 -2.745753E-02 -9.703052E-05 -2.602614E-02 -7.340705E-05 -2.099702E-02 -6.252305E-05 -2.411550E-02 -6.674417E-05 -2.570206E-02 -6.917677E-05 -2.753425E-02 -1.088979E-04 -2.289782E-02 -6.832742E-05 -1.728790E-02 -4.390010E-05 -2.345865E-02 -7.335296E-05 -1.670224E-02 -4.918814E-05 -2.958698E-02 -1.008389E-04 -2.262155E-02 -8.019776E-05 -2.171260E-02 -5.857256E-05 -2.739550E-02 -9.699654E-05 -1.898108E-02 -5.073772E-05 -2.158139E-02 -5.640212E-05 -2.653056E-02 -9.005949E-05 -2.557757E-02 -8.651930E-05 -1.719263E-02 -3.746877E-05 -1.270461E-02 -2.702019E-05 -2.371990E-02 -6.567867E-05 -2.133774E-02 -5.624387E-05 -2.412439E-02 -8.327847E-05 -2.144516E-02 -5.810134E-05 -2.096771E-02 -5.764847E-05 -2.010218E-02 -6.106688E-05 -2.662466E-02 -1.031716E-04 -2.128503E-02 -6.330504E-05 -2.077576E-02 -5.659759E-05 -1.910625E-02 -4.810183E-05 -1.994507E-02 -5.522830E-05 -1.318359E-02 -2.202071E-05 -1.847673E-02 -3.890771E-05 -1.525363E-02 -3.383373E-05 -2.323360E-02 -7.009641E-05 -2.160465E-02 -6.270896E-05 -2.815837E-02 -9.926554E-05 -2.825700E-02 -1.156243E-04 -1.661438E-02 -3.352935E-05 -1.707558E-02 -4.079662E-05 -1.663970E-02 -5.171770E-05 -1.537161E-02 -3.951696E-05 -1.515887E-02 -2.815234E-05 -1.262479E-02 -2.666049E-05 -2.270831E-02 -7.402779E-05 -1.637425E-02 -3.359896E-05 -1.433044E-02 -3.016015E-05 -1.086052E-02 -2.105095E-05 -1.827948E-02 -5.404440E-05 -1.512356E-02 -4.090325E-05 -1.341869E-02 -3.777277E-05 -1.144273E-02 -2.320403E-05 -1.032338E-02 -2.111004E-05 -1.324432E-02 -2.369582E-05 -1.323267E-02 -2.312589E-05 -7.378365E-03 -1.207116E-05 -1.198900E-02 -2.410816E-05 -1.143776E-02 -1.697429E-05 -5.960596E-03 -5.491388E-06 -8.671020E-03 -1.423578E-05 -1.046649E-02 -1.660152E-05 -1.015412E-02 -1.356611E-05 -4.437919E-03 -4.449282E-06 -8.070532E-03 -1.158768E-05 -1.339134E-02 -2.307637E-05 -1.588497E-02 -3.768975E-05 -9.869781E-03 -2.027492E-05 -1.346984E-02 -2.839713E-05 -1.481565E-02 -2.641963E-05 -1.622974E-02 -3.155688E-05 -1.228654E-02 -2.276535E-05 -9.854382E-03 -2.229720E-05 -8.655941E-03 -1.144096E-05 -8.579998E-03 -1.286624E-05 -1.701319E-02 -3.924030E-05 -9.817319E-03 -1.540490E-05 -2.169451E-02 -7.317248E-05 -2.105716E-02 -5.189702E-05 -1.963335E-02 -5.619116E-05 -2.183187E-02 -6.185293E-05 -1.721212E-02 -3.966296E-05 -1.657518E-02 -3.547912E-05 -1.888518E-02 -5.599282E-05 -1.856419E-02 -5.261571E-05 -2.022215E-02 -6.235701E-05 -1.750049E-02 -3.437263E-05 -2.144519E-02 -5.816976E-05 -1.487741E-02 -2.693460E-05 -1.556861E-02 -3.808581E-05 -2.287702E-02 -6.471426E-05 -1.502995E-02 -3.547384E-05 -2.333943E-02 -7.259310E-05 -1.682037E-02 -3.512266E-05 -1.559563E-02 -3.813310E-05 -2.534274E-02 -7.364335E-05 -2.480122E-02 -7.936460E-05 -1.877264E-02 -4.302495E-05 -2.255108E-02 -6.304951E-05 -1.327871E-02 -2.341276E-05 -1.978158E-02 -5.064958E-05 -2.719711E-02 -8.697999E-05 -3.450441E-02 -1.360084E-04 -2.418992E-02 -8.130715E-05 -2.755861E-02 -9.435699E-05 -2.906789E-02 -1.164895E-04 -2.492886E-02 -9.051357E-05 -2.149004E-02 -5.322652E-05 -1.882200E-02 -4.828092E-05 -3.559788E-02 -1.520518E-04 -2.750436E-02 -8.574229E-05 -2.225856E-02 -5.803244E-05 -2.338675E-02 -7.020780E-05 -3.715161E-02 -1.672352E-04 -2.832183E-02 -9.613959E-05 -3.620891E-02 -1.820751E-04 -1.842315E-02 -4.292893E-05 -3.073714E-02 -1.167359E-04 -2.239778E-02 -6.910113E-05 -3.816869E-02 -1.838537E-04 -3.039700E-02 -1.010395E-04 -2.746450E-02 -9.520787E-05 -2.678376E-02 -8.958685E-05 -1.959265E-02 -4.827447E-05 -2.161190E-02 -5.224194E-05 -2.866820E-02 -9.300929E-05 -3.051657E-02 -1.108463E-04 -2.779434E-02 -9.578374E-05 -2.381065E-02 -7.364073E-05 -3.580671E-02 -1.702522E-04 -3.245610E-02 -1.420184E-04 -3.074561E-02 -1.162297E-04 -2.368174E-02 -7.463994E-05 -3.053229E-02 -1.165041E-04 -2.791292E-02 -9.751321E-05 -1.851477E-02 -4.170606E-05 -2.647316E-02 -8.255669E-05 -2.688714E-02 -9.392494E-05 -3.140666E-02 -1.096021E-04 -2.467545E-02 -7.718465E-05 -1.941157E-02 -5.524811E-05 -3.374306E-02 -1.292663E-04 -2.025089E-02 -5.179623E-05 -2.535741E-02 -7.702579E-05 -2.469316E-02 -7.765177E-05 -2.952585E-02 -9.607704E-05 -2.637948E-02 -7.897857E-05 -2.244297E-02 -6.198303E-05 -2.299365E-02 -6.928750E-05 -2.384571E-02 -7.218845E-05 -3.265249E-02 -1.473318E-04 -2.949982E-02 -1.044208E-04 -2.330464E-02 -7.007243E-05 -1.937666E-02 -4.990461E-05 -1.836576E-02 -3.977508E-05 -2.200706E-02 -5.431158E-05 -1.760216E-02 -3.954597E-05 -2.161653E-02 -5.051231E-05 -2.685593E-02 -8.262522E-05 -2.031027E-02 -5.469024E-05 -1.729977E-02 -3.470083E-05 -2.245610E-02 -6.463192E-05 -2.480332E-02 -6.706820E-05 -1.636383E-02 -3.512895E-05 -1.629117E-02 -3.227987E-05 -1.777456E-02 -3.877314E-05 -1.523888E-02 -2.794713E-05 -1.933720E-02 -5.130630E-05 -2.073569E-02 -5.667785E-05 -2.159324E-02 -5.953356E-05 -2.252773E-02 -7.064923E-05 -1.497171E-02 -3.139235E-05 -1.702700E-02 -3.702921E-05 -1.197831E-02 -2.110040E-05 -1.326288E-02 -2.668205E-05 -9.877691E-03 -1.417188E-05 -9.222468E-03 -1.493126E-05 -1.916119E-02 -5.100381E-05 -1.618530E-02 -3.741295E-05 -9.744935E-03 -1.312151E-05 -4.205073E-03 -5.238984E-06 -1.606532E-02 -3.801359E-05 -1.115862E-02 -2.015015E-05 -1.312318E-02 -3.970688E-05 -1.092582E-02 -1.677629E-05 -8.988789E-03 -1.272128E-05 -1.116462E-02 -1.435361E-05 -1.177363E-02 -2.013606E-05 -1.392896E-02 -3.825189E-05 -7.595272E-03 -1.080161E-05 -1.243058E-02 -1.937258E-05 -8.823743E-03 -1.585993E-05 -1.072892E-02 -2.225554E-05 -9.610737E-03 -1.152464E-05 -7.143175E-03 -1.112162E-05 -9.471167E-03 -1.466213E-05 -1.060489E-02 -1.784667E-05 -2.767925E-02 -9.174943E-05 -1.613735E-02 -3.391468E-05 -1.980520E-02 -6.099070E-05 -2.710790E-02 -1.005842E-04 -1.498945E-02 -3.548209E-05 -2.090399E-02 -6.229284E-05 -1.867828E-02 -5.023418E-05 -2.101525E-02 -5.304849E-05 -1.495603E-02 -3.564927E-05 -2.206499E-02 -8.477764E-05 -1.774373E-02 -4.204463E-05 -1.321048E-02 -2.339091E-05 -3.032303E-02 -1.133561E-04 -2.906158E-02 -1.116010E-04 -1.416547E-02 -3.074243E-05 -2.396880E-02 -6.563656E-05 -2.277934E-02 -6.079700E-05 -1.978949E-02 -5.275993E-05 -2.518782E-02 -7.984951E-05 -2.706826E-02 -9.202956E-05 -2.708490E-02 -9.059865E-05 -1.986295E-02 -4.689709E-05 -2.616568E-02 -8.883803E-05 -2.130847E-02 -6.518411E-05 -2.806912E-02 -8.769554E-05 -3.084102E-02 -1.172123E-04 -2.897371E-02 -1.021030E-04 -2.903836E-02 -1.108727E-04 -2.605433E-02 -8.468280E-05 -2.332233E-02 -6.130579E-05 -3.234471E-02 -1.225845E-04 -3.300429E-02 -1.500123E-04 -2.311117E-02 -6.190932E-05 -2.436504E-02 -7.178823E-05 -3.008481E-02 -1.005979E-04 -1.928369E-02 -4.312116E-05 -3.011679E-02 -1.179304E-04 -4.008683E-02 -1.751489E-04 -3.496459E-02 -1.796935E-04 -3.097461E-02 -1.095044E-04 -2.638565E-02 -8.181552E-05 -2.620917E-02 -8.987429E-05 -3.998881E-02 -1.948994E-04 -3.258728E-02 -1.341640E-04 -2.561706E-02 -9.239575E-05 -2.588589E-02 -8.721515E-05 -2.944746E-02 -1.052164E-04 -2.989662E-02 -1.085775E-04 -4.206933E-02 -1.998885E-04 -4.066845E-02 -1.897463E-04 -3.508215E-02 -1.528988E-04 -3.120069E-02 -1.132167E-04 -3.286119E-02 -1.352262E-04 -2.909519E-02 -9.371026E-05 -3.840859E-02 -1.674324E-04 -2.454802E-02 -8.382699E-05 -3.347874E-02 -1.411597E-04 -3.060526E-02 -1.367041E-04 -3.069666E-02 -1.180094E-04 -2.561383E-02 -8.330685E-05 -2.628522E-02 -7.556832E-05 -3.582384E-02 -1.465406E-04 -2.376316E-02 -6.457049E-05 -2.495648E-02 -7.422669E-05 -3.093179E-02 -1.305765E-04 -2.264932E-02 -6.586890E-05 -2.047598E-02 -6.261296E-05 -1.933007E-02 -4.833006E-05 -3.257759E-02 -1.237865E-04 -2.571050E-02 -9.464525E-05 -2.463103E-02 -7.258362E-05 -2.557654E-02 -9.353236E-05 -2.284662E-02 -6.268018E-05 -2.491527E-02 -7.859483E-05 -1.681318E-02 -3.501371E-05 -1.365219E-02 -2.989006E-05 -2.210617E-02 -7.141867E-05 -1.271132E-02 -2.094510E-05 -1.683888E-02 -3.625752E-05 -2.058113E-02 -5.450372E-05 -1.739717E-02 -3.555630E-05 -1.829530E-02 -4.644023E-05 -1.776594E-02 -4.124796E-05 -1.418883E-02 -2.672278E-05 -1.760442E-02 -4.591128E-05 -1.891750E-02 -4.822430E-05 -2.178972E-02 -6.432900E-05 -1.443029E-02 -3.016588E-05 -2.256303E-02 -6.869229E-05 -2.251806E-02 -6.650662E-05 -1.323995E-02 -2.512378E-05 -1.335985E-02 -2.654692E-05 -1.740401E-02 -4.340090E-05 -1.330095E-02 -2.489342E-05 -1.207888E-02 -2.391548E-05 -1.667832E-02 -4.429778E-05 -1.489741E-02 -3.841273E-05 -1.143220E-02 -2.495364E-05 -1.240317E-02 -2.477660E-05 -1.342275E-02 -2.581056E-05 -1.621941E-02 -4.158232E-05 -1.159374E-02 -2.088416E-05 -9.404397E-03 -1.695010E-05 -1.056534E-02 -1.863945E-05 -1.162004E-02 -2.136658E-05 -1.095144E-02 -1.546737E-05 -1.144118E-02 -2.034439E-05 -1.322456E-02 -2.558634E-05 -1.172292E-02 -1.883806E-05 -8.086459E-03 -8.879405E-06 -9.613483E-03 -1.997653E-05 -9.179038E-03 -1.284128E-05 -1.897153E-02 -6.588307E-05 -1.524499E-02 -3.816344E-05 -1.007550E-02 -2.293895E-05 -1.297928E-02 -3.448976E-05 -6.475985E-03 -7.421175E-06 -7.302236E-03 -9.360854E-06 -1.268448E-02 -2.201962E-05 -1.331579E-02 -2.325496E-05 -1.816213E-02 -4.264316E-05 -1.951988E-02 -5.119100E-05 -1.565522E-02 -3.615086E-05 -2.309450E-02 -9.333193E-05 -1.557690E-02 -3.930051E-05 -9.451777E-03 -1.526280E-05 -2.041329E-02 -5.032056E-05 -2.673356E-02 -9.468703E-05 -1.094269E-02 -1.894088E-05 -1.679176E-02 -3.840079E-05 -1.732446E-02 -4.238174E-05 -1.027564E-02 -2.013019E-05 -1.861457E-02 -4.141552E-05 -2.507166E-02 -6.889370E-05 -2.041729E-02 -4.876339E-05 -2.283941E-02 -6.377266E-05 -1.892643E-02 -4.163099E-05 -2.187765E-02 -5.763946E-05 -2.028538E-02 -6.131503E-05 -2.103645E-02 -5.318838E-05 -1.715798E-02 -3.499249E-05 -1.485800E-02 -3.358359E-05 -1.984948E-02 -5.445579E-05 -1.703301E-02 -4.679668E-05 -3.309820E-02 -1.234499E-04 -2.275776E-02 -6.157529E-05 -3.261448E-02 -1.319771E-04 -2.554946E-02 -8.196680E-05 -2.004888E-02 -4.990133E-05 -1.939965E-02 -6.076153E-05 -2.548646E-02 -8.064466E-05 -2.855104E-02 -1.012305E-04 -2.762007E-02 -1.037910E-04 -1.839340E-02 -4.279463E-05 -1.686751E-02 -4.608015E-05 -2.084644E-02 -5.002926E-05 -4.132950E-02 -1.846233E-04 -3.460120E-02 -1.298466E-04 -3.552467E-02 -1.570092E-04 -2.933106E-02 -9.609808E-05 -2.684190E-02 -7.922498E-05 -2.487139E-02 -6.891792E-05 -3.728495E-02 -1.689194E-04 -2.818457E-02 -8.676068E-05 -3.441368E-02 -1.333471E-04 -2.818973E-02 -1.038355E-04 -2.356477E-02 -6.812284E-05 -2.343339E-02 -6.179023E-05 -4.344270E-02 -2.389724E-04 -4.294905E-02 -2.093287E-04 -3.836620E-02 -1.778263E-04 -2.841149E-02 -8.871119E-05 -4.014563E-02 -1.768024E-04 -2.920772E-02 -1.000217E-04 -2.615172E-02 -8.720279E-05 -2.000501E-02 -4.816703E-05 -4.057421E-02 -1.875221E-04 -2.445887E-02 -8.924764E-05 -2.817821E-02 -8.605351E-05 -2.110986E-02 -5.069715E-05 -2.927821E-02 -9.308711E-05 -3.362175E-02 -1.332596E-04 -3.475808E-02 -1.323338E-04 -3.289085E-02 -1.244729E-04 -3.022584E-02 -1.318809E-04 -3.196364E-02 -1.297968E-04 -1.687072E-02 -3.179809E-05 -1.801683E-02 -4.078604E-05 -2.960756E-02 -1.160952E-04 -3.466058E-02 -1.382880E-04 -2.412524E-02 -9.376449E-05 -3.370586E-02 -1.749860E-04 -1.744924E-02 -4.569761E-05 -2.327075E-02 -7.176336E-05 -2.067060E-02 -5.038568E-05 -2.065122E-02 -5.674893E-05 -1.185396E-02 -1.904589E-05 -1.187338E-02 -1.832962E-05 -1.361451E-02 -2.819447E-05 -1.785195E-02 -4.401066E-05 -2.526233E-02 -8.640741E-05 -1.793604E-02 -5.730915E-05 -1.571041E-02 -3.175367E-05 -1.678219E-02 -3.936420E-05 -1.486918E-02 -3.526965E-05 -2.794130E-02 -1.195339E-04 -1.996768E-02 -7.226779E-05 -1.162384E-02 -2.197081E-05 -2.096325E-02 -5.793563E-05 -1.837867E-02 -4.515139E-05 -1.703731E-02 -3.926907E-05 -1.268651E-02 -2.343106E-05 -1.879725E-02 -4.716744E-05 -1.709910E-02 -3.710250E-05 -1.228622E-02 -3.339394E-05 -1.352711E-02 -2.975058E-05 -1.155424E-02 -1.880536E-05 -1.121092E-02 -2.205103E-05 -1.476077E-02 -3.330714E-05 -1.255099E-02 -2.385469E-05 -1.347481E-02 -2.408221E-05 -1.470344E-02 -2.583766E-05 -9.187814E-03 -1.701394E-05 -1.028524E-02 -2.190585E-05 -1.204921E-02 -2.386486E-05 -1.214004E-02 -1.925520E-05 -1.294814E-02 -2.325545E-05 -1.470310E-02 -3.633457E-05 -7.703457E-03 -8.319661E-06 -9.053019E-03 -1.860099E-05 -3.939164E-03 -3.269741E-06 -9.533908E-03 -1.325293E-05 -1.227483E-02 -2.491268E-05 -1.259791E-02 -2.146362E-05 -4.323047E-03 -3.283543E-06 -9.255709E-03 -2.197638E-05 -4.603184E-03 -3.514783E-06 -4.229364E-03 -6.002727E-06 -1.275699E-02 -2.227133E-05 -1.051975E-02 -1.875691E-05 -1.939046E-02 -4.923847E-05 -1.621043E-02 -4.902401E-05 -1.162276E-02 -2.501804E-05 -1.178308E-02 -2.464996E-05 -1.264103E-02 -2.350170E-05 -1.829675E-02 -4.158987E-05 -1.935990E-02 -4.658558E-05 -2.050484E-02 -5.542682E-05 -1.261518E-02 -2.496199E-05 -2.338316E-02 -6.665179E-05 -1.422276E-02 -2.677276E-05 -1.418978E-02 -3.508840E-05 -3.161101E-02 -1.103014E-04 -2.806809E-02 -9.416094E-05 -1.970337E-02 -5.207680E-05 -2.017517E-02 -5.029958E-05 -2.476977E-02 -9.671590E-05 -1.993423E-02 -5.639950E-05 -2.220619E-02 -5.949033E-05 -2.663071E-02 -8.742719E-05 -1.716007E-02 -3.560277E-05 -1.620846E-02 -3.298124E-05 -2.027814E-02 -5.137992E-05 -2.241055E-02 -6.475298E-05 -3.149111E-02 -1.240574E-04 -3.301990E-02 -1.297806E-04 -2.289529E-02 -7.022820E-05 -2.770187E-02 -1.305531E-04 -2.647167E-02 -8.125885E-05 -2.470047E-02 -6.618469E-05 -1.972532E-02 -5.720249E-05 -2.301743E-02 -6.802664E-05 -1.851738E-02 -4.051694E-05 -1.781820E-02 -3.908008E-05 -2.011234E-02 -4.945371E-05 -1.788427E-02 -4.535953E-05 -3.355984E-02 -1.399308E-04 -3.141513E-02 -1.324421E-04 -3.123214E-02 -1.131961E-04 -2.990237E-02 -1.043368E-04 -2.483001E-02 -7.814014E-05 -2.780841E-02 -9.315514E-05 -2.944866E-02 -1.125594E-04 -3.115948E-02 -1.382235E-04 -3.135984E-02 -1.125014E-04 -2.632324E-02 -9.453811E-05 -3.001476E-02 -1.041868E-04 -2.694256E-02 -8.321200E-05 -2.693560E-02 -7.578608E-05 -3.360780E-02 -1.359544E-04 -3.373802E-02 -1.372586E-04 -2.236018E-02 -6.411192E-05 -2.962695E-02 -1.020105E-04 -2.783038E-02 -9.884369E-05 -2.407145E-02 -7.259162E-05 -2.611296E-02 -9.023141E-05 -3.127878E-02 -1.135229E-04 -2.950226E-02 -9.691197E-05 -2.305590E-02 -6.460450E-05 -2.321268E-02 -7.274626E-05 -2.804961E-02 -9.491270E-05 -2.841803E-02 -1.054470E-04 -2.423205E-02 -7.810874E-05 -2.714164E-02 -9.975157E-05 -2.873229E-02 -1.122814E-04 -2.098758E-02 -5.056677E-05 -2.557067E-02 -8.748635E-05 -2.267297E-02 -8.906984E-05 -2.139317E-02 -5.463244E-05 -2.294894E-02 -7.412557E-05 -1.571457E-02 -3.620884E-05 -2.240494E-02 -5.721050E-05 -2.805329E-02 -9.134972E-05 -3.310961E-02 -1.309241E-04 -1.731055E-02 -3.757453E-05 -1.519094E-02 -3.101001E-05 -3.089642E-02 -1.273235E-04 -2.655794E-02 -1.020322E-04 -1.930391E-02 -4.470231E-05 -1.322122E-02 -2.280479E-05 -2.267352E-02 -6.781380E-05 -2.377301E-02 -7.062640E-05 -1.061766E-02 -1.383149E-05 -1.989374E-02 -6.361786E-05 -2.625067E-02 -9.026084E-05 -2.498518E-02 -8.804371E-05 -2.106620E-02 -5.099756E-05 -1.201370E-02 -2.098230E-05 -2.562363E-02 -8.111943E-05 -1.462361E-02 -2.865648E-05 -2.187381E-02 -5.860668E-05 -1.613482E-02 -4.384305E-05 -1.621606E-02 -3.449398E-05 -1.444365E-02 -3.579161E-05 -1.323646E-02 -2.542031E-05 -1.061139E-02 -1.881978E-05 -8.801940E-03 -1.018150E-05 -1.139983E-02 -2.019736E-05 -1.681598E-02 -3.690065E-05 -1.399493E-02 -3.175563E-05 -1.420691E-02 -2.466458E-05 -1.368415E-02 -2.617814E-05 -4.389391E-03 -3.790342E-06 -8.841774E-03 -1.306452E-05 -6.705949E-03 -6.428293E-06 -3.807333E-03 -3.521598E-06 -8.526487E-03 -1.179686E-05 -6.365036E-03 -6.849920E-06 -9.633887E-03 -1.541102E-05 -1.123781E-02 -1.889506E-05 -6.073970E-03 -7.042592E-06 -5.473005E-03 -4.622656E-06 -9.289202E-03 -1.795737E-05 -1.030725E-02 -2.040465E-05 -6.753476E-03 -7.775016E-06 -5.874996E-03 -7.699447E-06 -4.803223E-03 -5.048987E-06 -9.387232E-03 -2.256112E-05 -6.779777E-03 -7.696560E-06 -7.302784E-03 -1.204717E-05 -1.858153E-02 -4.240154E-05 -1.133429E-02 -1.757188E-05 -1.090884E-02 -1.956959E-05 -1.761310E-02 -4.472462E-05 -1.208597E-02 -2.241660E-05 -8.447260E-03 -1.837826E-05 -2.154735E-02 -7.183466E-05 -1.059584E-02 -1.725144E-05 -1.078223E-02 -1.741743E-05 -1.142825E-02 -1.725056E-05 -8.052096E-03 -1.288141E-05 -8.852506E-03 -1.479325E-05 -1.503791E-02 -2.827810E-05 -1.427595E-02 -2.627358E-05 -1.151486E-02 -1.821048E-05 -1.479919E-02 -3.026089E-05 -1.396424E-02 -2.976716E-05 -1.102878E-02 -1.569768E-05 -1.486600E-02 -4.640833E-05 -2.329305E-02 -6.388242E-05 -1.077778E-02 -2.630229E-05 -1.187595E-02 -2.158652E-05 -1.706939E-02 -3.589833E-05 -9.894219E-03 -1.748099E-05 -2.294330E-02 -6.429613E-05 -2.112393E-02 -5.525797E-05 -1.888943E-02 -4.229674E-05 -1.872573E-02 -5.200290E-05 -2.190997E-02 -6.830515E-05 -1.857860E-02 -5.271689E-05 -1.824118E-02 -4.252719E-05 -1.695109E-02 -4.377846E-05 -1.942043E-02 -4.638640E-05 -2.430706E-02 -8.617790E-05 -1.728181E-02 -4.311630E-05 -1.163790E-02 -2.136880E-05 -2.488767E-02 -7.230908E-05 -2.919997E-02 -9.955761E-05 -2.584993E-02 -1.031623E-04 -2.268255E-02 -6.949158E-05 -3.146416E-02 -1.268057E-04 -2.236165E-02 -6.966628E-05 -2.724737E-02 -9.874601E-05 -2.224496E-02 -7.784128E-05 -2.189895E-02 -6.090239E-05 -1.839473E-02 -4.209614E-05 -2.542360E-02 -8.396121E-05 -2.052856E-02 -4.913619E-05 -3.120344E-02 -1.291172E-04 -2.944557E-02 -1.038017E-04 -3.259220E-02 -1.571837E-04 -2.454004E-02 -8.331616E-05 -2.553435E-02 -9.440752E-05 -2.541943E-02 -9.586937E-05 -2.061350E-02 -7.364042E-05 -2.329153E-02 -9.064116E-05 -2.078416E-02 -4.749189E-05 -2.761182E-02 -8.657370E-05 -2.266317E-02 -6.326775E-05 -2.686321E-02 -1.043542E-04 -2.182754E-02 -7.805811E-05 -2.766205E-02 -9.723951E-05 -1.892390E-02 -5.005671E-05 -2.382207E-02 -6.769044E-05 -2.015930E-02 -6.032256E-05 -1.427037E-02 -3.798052E-05 -1.767507E-02 -6.512871E-05 -2.123882E-02 -4.907487E-05 -2.157816E-02 -6.678135E-05 -1.919870E-02 -5.942855E-05 -2.192776E-02 -5.912113E-05 -1.744859E-02 -5.000179E-05 -1.577374E-02 -3.368332E-05 -2.305821E-02 -6.854360E-05 -2.058939E-02 -5.959718E-05 -1.350163E-02 -3.072943E-05 -1.774684E-02 -4.504160E-05 -1.929554E-02 -5.149838E-05 -1.225057E-02 -1.846451E-05 -1.522995E-02 -3.345735E-05 -1.488842E-02 -2.657892E-05 -1.308637E-02 -2.575083E-05 -1.550443E-02 -4.365385E-05 -1.262294E-02 -2.496948E-05 -2.022153E-02 -5.150955E-05 -2.016439E-02 -5.276144E-05 -1.850133E-02 -4.648369E-05 -9.903588E-03 -1.610212E-05 -1.242472E-02 -2.311415E-05 -1.448322E-02 -3.103193E-05 -1.821292E-02 -6.275309E-05 -1.275608E-02 -5.475363E-05 -1.800193E-02 -4.234061E-05 -8.782455E-03 -1.649077E-05 -1.220656E-02 -2.499398E-05 -1.247528E-02 -3.099809E-05 -4.915298E-03 -5.476965E-06 -9.391788E-03 -1.606380E-05 -6.771266E-03 -8.450336E-06 -5.095855E-03 -3.842657E-06 -1.019612E-02 -1.603701E-05 -1.266368E-02 -2.593740E-05 -1.437121E-03 -9.237680E-07 -7.738730E-03 -1.337549E-05 -1.101810E-02 -1.737730E-05 -3.189798E-03 -4.831494E-06 -8.313307E-03 -1.236384E-05 -4.760766E-03 -2.835247E-06 -8.211101E-03 -1.004626E-05 -4.023937E-03 -3.045420E-06 -3.795398E-03 -3.753905E-06 -6.439348E-03 -6.445882E-06 -8.789930E-03 -9.641461E-06 -1.003210E-02 -1.871561E-05 -7.188383E-03 -7.745644E-06 -8.884373E-03 -1.210321E-05 -4.201731E-03 -3.267512E-06 -6.156152E-03 -6.439386E-06 -6.368503E-03 -6.063000E-06 -5.865149E-03 -6.343581E-06 -8.242843E-03 -1.255409E-05 -6.079907E-03 -8.904171E-06 -1.068165E-02 -1.977482E-05 -9.934550E-03 -1.529904E-05 -9.391801E-03 -1.672164E-05 -9.638027E-03 -1.793124E-05 -1.056323E-02 -2.728241E-05 -7.672750E-03 -1.245311E-05 -5.340667E-03 -9.209395E-06 -1.355760E-02 -6.094725E-05 -8.047003E-03 -9.586050E-06 -1.285221E-02 -3.064011E-05 -1.569140E-02 -3.403460E-05 -1.271345E-02 -2.195068E-05 -1.226525E-02 -2.591890E-05 -1.571067E-02 -3.670153E-05 -1.000122E-02 -2.251836E-05 -1.263608E-02 -2.857491E-05 -1.467989E-02 -3.065952E-05 -1.992801E-02 -5.197568E-05 -1.634631E-02 -3.854791E-05 -9.765387E-03 -1.929348E-05 -1.567478E-02 -4.572326E-05 -1.122681E-02 -1.875141E-05 -1.438219E-02 -2.962331E-05 -2.198487E-02 -5.569638E-05 -1.350017E-02 -2.505385E-05 -1.864402E-02 -5.207299E-05 -1.588104E-02 -4.725574E-05 -1.091009E-02 -1.886410E-05 -2.047245E-02 -6.638244E-05 -1.595713E-02 -4.444025E-05 -1.240157E-02 -1.827292E-05 -8.920440E-03 -1.437145E-05 -1.325339E-02 -2.805692E-05 -1.008187E-02 -1.747750E-05 -1.944508E-02 -6.365499E-05 -2.320316E-02 -7.205046E-05 -2.067013E-02 -4.788913E-05 -1.843860E-02 -4.912723E-05 -2.314076E-02 -7.987697E-05 -2.082200E-02 -6.799905E-05 -1.477196E-02 -2.747397E-05 -1.645001E-02 -3.474675E-05 -2.251395E-02 -7.952533E-05 -1.283424E-02 -2.565340E-05 -1.302741E-02 -2.183293E-05 -1.627063E-02 -3.188570E-05 -1.694690E-02 -4.854666E-05 -1.768424E-02 -4.519650E-05 -1.688309E-02 -3.658427E-05 -2.081183E-02 -4.936469E-05 -1.521899E-02 -3.630188E-05 -1.538105E-02 -4.012152E-05 -1.852161E-02 -4.542222E-05 -2.027891E-02 -7.078559E-05 -1.530632E-02 -4.004295E-05 -1.251099E-02 -2.014674E-05 -1.766586E-02 -3.643771E-05 -1.011259E-02 -1.798254E-05 -1.785577E-02 -4.209205E-05 -1.591733E-02 -2.948407E-05 -1.973149E-02 -5.858390E-05 -2.318672E-02 -6.821666E-05 -1.606927E-02 -3.208169E-05 -1.561903E-02 -3.274947E-05 -8.671178E-03 -1.320312E-05 -1.321121E-02 -2.770105E-05 -1.276830E-02 -2.392358E-05 -8.675945E-03 -1.600673E-05 -1.631955E-02 -3.628609E-05 -1.268176E-02 -2.698295E-05 -1.305170E-02 -3.392622E-05 -1.151833E-02 -2.146481E-05 -1.382454E-02 -2.543511E-05 -1.268190E-02 -2.770093E-05 -1.670138E-02 -4.515019E-05 -1.411501E-02 -2.397072E-05 -1.054121E-02 -1.734424E-05 -1.038572E-02 -1.525222E-05 -1.239423E-02 -1.885637E-05 -8.231991E-03 -1.095003E-05 -1.346736E-02 -2.994113E-05 -1.282791E-02 -2.684630E-05 -1.377004E-02 -3.238869E-05 -1.142596E-02 -1.735764E-05 -1.062508E-02 -1.743282E-05 -1.257942E-02 -2.057274E-05 -1.169000E-02 -2.474569E-05 -6.423798E-03 -8.173196E-06 -7.740555E-03 -1.569990E-05 -8.666292E-03 -1.364526E-05 -1.430519E-02 -3.144468E-05 -1.034424E-02 -1.702524E-05 -1.041966E-02 -1.828082E-05 -7.823260E-03 -1.110455E-05 -7.252199E-03 -9.925677E-06 -2.994718E-03 -1.593375E-06 -1.048984E-02 -2.573976E-05 -5.641360E-03 -5.936595E-06 -5.229774E-03 -7.041393E-06 -9.930813E-03 -1.689201E-05 -4.386542E-03 -4.754619E-06 -5.133633E-03 -4.043150E-06 -3.269722E-03 -1.970563E-06 -4.951504E-03 -6.193676E-06 -4.410157E-03 -4.926957E-06 -8.875217E-03 -1.522029E-05 -6.496616E-03 -8.828653E-06 -5.733530E-03 -7.478095E-06 -4.584225E-03 -3.801824E-06 -5.014833E-03 -4.595598E-06 -4.918988E-03 -9.258105E-06 -5.483056E-03 -9.195257E-06 -5.228430E-03 -7.940306E-06 -5.117645E-03 -7.631754E-06 -5.072391E-03 -5.531082E-06 -3.632197E-03 -2.983079E-06 -4.693965E-03 -5.099544E-06 -2.051996E-03 -1.135888E-06 -7.304764E-03 -1.039220E-05 -3.647755E-03 -4.449912E-06 -3.476959E-03 -2.187942E-06 -9.489533E-03 -2.316279E-05 -5.751162E-03 -8.659685E-06 -4.963073E-03 -5.706592E-06 -6.138111E-03 -1.068804E-05 -9.307217E-03 -1.519475E-05 -4.545925E-03 -6.535861E-06 -8.294344E-03 -1.556852E-05 -7.173393E-03 -9.233319E-06 -1.078215E-02 -1.572789E-05 -9.705333E-03 -3.544244E-05 -7.945812E-03 -1.276101E-05 -1.168294E-02 -2.467022E-05 -6.645644E-03 -7.587545E-06 -5.760001E-03 -9.365634E-06 -8.601051E-03 -1.363162E-05 -8.005982E-03 -1.328267E-05 -1.261419E-02 -3.207736E-05 -5.901262E-03 -6.935112E-06 -6.430185E-03 -9.087951E-06 -6.884844E-03 -8.313889E-06 -4.278770E-03 -3.441959E-06 -1.137804E-02 -2.133967E-05 -1.214381E-02 -2.232652E-05 -1.740437E-02 -3.543180E-05 -1.246386E-02 -2.766736E-05 -1.214083E-02 -1.935442E-05 -1.166031E-02 -2.273794E-05 -1.042912E-02 -2.534376E-05 -1.253440E-02 -2.906078E-05 -3.580466E-03 -2.034018E-06 -9.607729E-03 -1.395640E-05 -9.100903E-03 -1.455039E-05 -5.382398E-03 -1.056575E-05 -1.720209E-02 -4.611390E-05 -1.189486E-02 -2.081478E-05 -2.098921E-02 -5.268336E-05 -1.295792E-02 -2.491469E-05 -1.359035E-02 -2.476233E-05 -1.320672E-02 -2.504384E-05 -1.004322E-02 -1.517085E-05 -6.328234E-03 -7.222517E-06 -7.696931E-03 -8.736384E-06 -9.062335E-03 -1.635044E-05 -1.220397E-02 -2.109811E-05 -9.542900E-03 -1.414586E-05 -2.001020E-02 -5.728709E-05 -1.200225E-02 -1.611148E-05 -1.851644E-02 -5.098601E-05 -1.159430E-02 -2.048997E-05 -1.198809E-02 -2.688531E-05 -1.067777E-02 -1.707832E-05 -1.686877E-02 -4.601130E-05 -1.514223E-02 -3.020548E-05 -9.412452E-03 -1.296937E-05 -8.734687E-03 -1.791696E-05 -7.182753E-03 -7.961564E-06 -7.131070E-03 -8.835775E-06 -1.176780E-02 -2.003672E-05 -1.238591E-02 -1.851917E-05 -1.482210E-02 -4.681620E-05 -8.008500E-03 -8.162099E-06 -1.099128E-02 -1.643104E-05 -1.234719E-02 -2.252478E-05 -9.121398E-03 -1.338217E-05 -9.649799E-03 -1.957300E-05 -1.239326E-02 -2.066867E-05 -7.925470E-03 -9.921111E-06 -6.952410E-03 -1.209167E-05 -1.305189E-02 -2.993481E-05 -1.011683E-02 -1.814412E-05 -9.138673E-03 -1.288657E-05 -9.613647E-03 -1.454996E-05 -9.587432E-03 -1.425997E-05 -4.100970E-03 -3.581979E-06 -6.384398E-03 -9.449653E-06 -5.332770E-03 -4.810910E-06 -8.205597E-03 -1.367429E-05 -4.458035E-03 -3.530187E-06 -3.214674E-03 -1.992488E-06 -6.119990E-03 -6.950277E-06 -4.194752E-03 -3.822292E-06 -7.270772E-03 -8.358483E-06 -7.963665E-03 -1.211727E-05 -1.139690E-02 -2.688370E-05 -1.072253E-02 -1.923075E-05 -9.310029E-03 -2.222401E-05 -6.635931E-03 -6.447355E-06 -6.067077E-03 -9.323989E-06 -6.686810E-03 -6.491688E-06 -7.266522E-03 -7.228635E-06 -2.677445E-03 -2.282809E-06 -7.272519E-03 -9.053976E-06 -5.368115E-03 -6.336538E-06 -3.059467E-03 -2.284551E-06 -3.148000E-03 -2.437724E-06 -3.588594E-03 -3.284908E-06 -4.456313E-03 -7.520958E-06 -2.798347E-03 -2.236317E-06 -6.840217E-03 -9.048482E-06 -1.575769E-03 -9.897286E-07 -2.611268E-03 -1.755955E-06 -1.750700E-03 -1.182162E-06 -2.239940E-03 -2.450046E-06 -1.507196E-03 -1.074090E-06 -6.865521E-03 -7.835832E-06 -2.312288E-03 -2.250952E-06 -4.625802E-03 -5.487129E-06 -2.141186E-03 -9.777142E-07 -1.364855E-03 -9.688473E-07 -3.118005E-03 -3.532909E-06 -2.850409E-03 -1.719105E-06 -3.414722E-03 -2.946427E-06 -5.867545E-03 -6.300551E-06 -3.511602E-03 -3.730751E-06 -2.231751E-03 -2.052805E-06 -2.099977E-03 -1.555011E-06 -2.936833E-03 -2.400849E-06 -5.147373E-03 -6.410685E-06 -9.334636E-03 -1.426974E-05 -5.355177E-03 -5.994759E-06 -5.939796E-03 -1.063177E-05 -5.190027E-03 -5.190207E-06 -5.258137E-03 -4.118191E-06 -6.041649E-03 -6.155598E-06 -5.622082E-03 -4.644624E-06 -1.149290E-02 -2.616444E-05 -7.309708E-03 -8.042562E-06 -3.694461E-03 -3.564990E-06 -6.879292E-03 -1.011779E-05 -9.522458E-03 -1.559720E-05 -5.391999E-03 -4.832231E-06 -5.698037E-03 -5.073533E-06 -1.893957E-03 -1.407429E-06 -7.904603E-03 -1.129256E-05 -5.420913E-03 -6.627464E-06 -6.982381E-03 -6.809893E-06 -6.325870E-03 -7.532835E-06 -4.682244E-03 -3.460830E-06 -6.920599E-03 -8.574244E-06 -3.733359E-03 -3.700527E-06 -4.030359E-03 -3.986131E-06 -5.047907E-03 -4.426148E-06 -9.528005E-03 -1.550618E-05 -7.877900E-03 -1.006685E-05 -9.780723E-03 -1.657015E-05 -4.812077E-03 -4.098229E-06 -7.016687E-03 -7.149177E-06 -4.037284E-03 -3.400182E-06 -3.277155E-03 -2.337977E-06 -6.137543E-03 -6.092367E-06 -6.278125E-03 -9.707219E-06 -2.576720E-03 -3.914988E-06 -4.835272E-03 -7.752895E-06 -6.736014E-03 -7.302508E-06 -6.970854E-03 -6.682760E-06 -6.313642E-03 -5.551521E-06 -7.635449E-03 -8.984939E-06 -8.683849E-03 -1.560378E-05 -7.530787E-03 -9.173765E-06 -1.569516E-02 -3.949851E-05 -1.097068E-02 -2.119987E-05 -7.309184E-03 -7.015811E-06 -8.665715E-03 -1.546974E-05 -7.723895E-03 -1.518562E-05 -4.905466E-03 -4.093288E-06 -1.077564E-02 -2.805109E-05 -8.569399E-03 -1.273736E-05 -1.038612E-02 -1.719585E-05 -7.354212E-03 -8.198097E-06 -9.350739E-03 -1.720908E-05 -4.787259E-03 -6.741535E-06 -7.220875E-03 -8.792305E-06 -5.379919E-03 -5.042148E-06 -8.788109E-03 -1.266781E-05 -7.402327E-03 -7.894813E-06 -4.207623E-03 -3.460121E-06 -7.902663E-03 -9.560483E-06 -5.174573E-03 -4.082169E-06 -1.104658E-02 -2.127916E-05 -5.782523E-03 -5.112492E-06 -3.632634E-03 -2.502751E-06 -5.337278E-03 -4.486802E-06 -4.249421E-03 -4.213447E-06 -7.491058E-03 -9.439118E-06 -5.243136E-03 -5.575342E-06 -1.237618E-02 -2.148757E-05 -8.041605E-03 -1.026055E-05 -6.349704E-03 -1.417430E-05 -6.087425E-03 -8.075635E-06 -4.602367E-03 -3.651645E-06 -4.154919E-03 -5.935728E-06 -1.040123E-02 -1.813267E-05 -8.425233E-03 -1.322959E-05 -5.094732E-03 -6.066657E-06 -3.282128E-03 -2.438795E-06 -4.478721E-03 -5.352957E-06 -4.621930E-03 -3.979448E-06 -3.591134E-03 -3.542812E-06 -3.931582E-03 -3.195193E-06 -7.517289E-03 -1.202563E-05 -4.445629E-03 -3.532948E-06 -5.122199E-03 -4.733178E-06 -6.079088E-03 -5.645343E-06 -5.005001E-03 -5.722868E-06 -4.017636E-03 -3.624033E-06 -3.326000E-03 -2.420695E-06 -3.011641E-03 -3.505638E-06 -2.136977E-03 -8.951049E-07 -1.175447E-03 -4.124064E-07 -7.823514E-03 -1.228293E-05 -4.920698E-03 -4.693144E-06 -3.285795E-03 -2.440402E-06 -4.028141E-03 -3.884837E-06 -2.781317E-03 -1.300197E-06 -3.926044E-03 -3.109193E-06 -4.628411E-03 -5.325271E-06 -4.051196E-03 -3.567334E-06 -3.272251E-03 -4.622968E-06 -3.143217E-03 -2.173404E-06 -2.374311E-03 -1.308003E-06 -1.326934E-03 -9.031223E-07 -3.522915E-03 -4.391282E-06 -5.770000E-04 -1.329958E-07 -2.830130E-03 -3.011060E-06 -2.210339E-03 -1.635883E-06 -3.721215E-03 -3.080210E-06 -6.120484E-03 -5.355595E-06 -3.678377E-03 -3.709180E-06 -2.413835E-03 -1.404139E-06 -5.860060E-03 -6.753555E-06 -6.142222E-03 -6.895111E-06 -4.689827E-03 -4.987858E-06 -3.810291E-03 -3.578451E-06 -4.720787E-03 -7.371870E-06 -1.759885E-03 -1.135197E-06 -4.523542E-03 -5.744713E-06 -4.512130E-03 -5.150402E-06 -8.505535E-03 -9.989819E-06 -8.615921E-03 -1.014660E-05 -6.886961E-03 -6.750505E-06 -6.626919E-03 -8.244527E-06 -9.376957E-03 -1.235456E-05 -9.638288E-03 -1.489772E-05 -1.001130E-02 -1.552242E-05 -1.028076E-02 -1.757757E-05 -3.716261E-03 -3.414503E-06 -1.351126E-02 -2.981345E-05 -7.730660E-03 -1.296080E-05 -8.324008E-03 -1.413367E-05 -9.824794E-03 -1.737391E-05 -8.406562E-03 -1.011462E-05 -6.244264E-03 -9.615840E-06 -5.753008E-03 -5.938129E-06 -1.161932E-02 -1.759635E-05 -7.019897E-03 -6.911798E-06 -1.124973E-02 -2.227076E-05 -9.974498E-03 -1.531239E-05 -1.750036E-02 -3.960636E-05 -1.123801E-02 -2.603078E-05 -6.620195E-03 -8.846067E-06 -8.621539E-03 -1.476285E-05 -1.144148E-02 -2.816625E-05 -9.236578E-03 -2.087006E-05 -1.022756E-02 -2.015506E-05 -8.313712E-03 -1.245193E-05 -8.472316E-03 -1.040768E-05 -1.100682E-02 -1.897584E-05 -7.072576E-03 -7.806879E-06 -8.257296E-03 -1.365708E-05 -8.677246E-03 -1.251818E-05 -1.847630E-03 -1.221029E-06 -3.514354E-03 -2.801550E-06 -4.635307E-03 -4.312440E-06 -1.719934E-02 -3.993958E-05 -1.168750E-02 -1.940685E-05 -1.392387E-02 -2.505551E-05 -9.162097E-03 -2.003276E-05 -1.068024E-02 -1.620043E-05 -6.143827E-03 -6.330015E-06 -9.757303E-03 -1.665448E-05 -1.205259E-02 -2.076304E-05 -1.099561E-02 -1.990285E-05 -8.729352E-03 -1.425139E-05 -5.036499E-03 -3.597216E-06 -4.573901E-03 -4.390016E-06 -1.098170E-02 -1.803805E-05 -1.450456E-02 -2.780429E-05 -6.373863E-03 -6.571288E-06 -9.888037E-03 -1.449978E-05 -1.253128E-02 -2.292463E-05 -1.075201E-02 -1.610392E-05 -8.884084E-03 -1.322461E-05 -5.357264E-03 -5.936993E-06 -1.493177E-02 -2.673706E-05 -5.699430E-03 -5.340117E-06 -8.188668E-03 -1.253943E-05 -4.838650E-03 -3.385865E-06 -1.000094E-02 -1.476818E-05 -1.063068E-02 -1.693667E-05 -9.830500E-03 -1.503630E-05 -1.025245E-02 -1.763545E-05 -1.028864E-02 -1.849793E-05 -5.900779E-03 -6.170864E-06 -1.177015E-02 -2.226737E-05 -7.509689E-03 -8.719533E-06 -1.206431E-02 -2.678780E-05 -9.200857E-03 -1.643527E-05 -5.468133E-03 -4.453812E-06 -7.516784E-03 -9.212924E-06 -1.111732E-02 -1.394083E-05 -1.179677E-02 -1.966452E-05 -4.112066E-03 -2.360474E-06 -4.867401E-03 -6.026552E-06 -8.104035E-03 -1.092048E-05 -1.016367E-02 -1.245298E-05 -8.572029E-03 -1.314727E-05 -5.230427E-03 -4.291571E-06 -1.462827E-02 -3.660743E-05 -1.133693E-02 -1.987800E-05 -8.632273E-03 -1.741700E-05 -9.505339E-03 -1.209742E-05 -3.350003E-03 -1.830051E-06 -1.189483E-02 -1.823139E-05 -6.269531E-03 -9.062971E-06 -5.745106E-03 -7.814173E-06 -5.494288E-03 -5.220575E-06 -8.224322E-03 -1.417410E-05 -7.485116E-03 -7.578271E-06 -4.217646E-03 -3.209044E-06 -9.597899E-03 -1.579356E-05 -9.222350E-03 -1.143785E-05 -5.917673E-03 -5.975846E-06 -4.191188E-03 -2.824383E-06 -5.746277E-03 -5.928494E-06 -5.146451E-03 -4.826427E-06 -3.536845E-03 -2.903999E-06 -2.348669E-03 -2.151234E-06 -7.673915E-03 -1.147821E-05 -4.006830E-03 -3.592361E-06 -4.896990E-03 -5.179793E-06 -5.024089E-03 -5.234216E-06 -6.039638E-03 -6.322254E-06 -8.201689E-03 -1.116771E-05 -6.614252E-03 -1.021009E-05 -4.191696E-03 -4.388316E-06 -1.128526E-02 -1.976083E-05 -1.067513E-02 -1.823942E-05 -3.764982E-03 -3.222515E-06 -3.948813E-03 -4.451513E-06 -1.000152E-02 -2.107330E-05 -6.164282E-03 -1.724834E-05 -1.006014E-02 -2.134032E-05 -1.286695E-02 -3.067989E-05 -8.978723E-03 -1.251581E-05 -5.346415E-03 -5.350269E-06 -3.635013E-03 -4.204044E-06 -6.583641E-03 -8.886395E-06 -6.112499E-03 -7.100313E-06 -9.075416E-03 -1.361298E-05 -9.360748E-03 -1.479673E-05 -1.287873E-02 -2.436676E-05 -1.196275E-02 -1.944035E-05 -9.130004E-03 -1.555860E-05 -9.992520E-03 -1.245497E-05 -1.115802E-02 -1.670908E-05 -1.081540E-02 -1.882462E-05 -1.372827E-02 -2.757409E-05 -8.020036E-03 -1.222038E-05 -6.290610E-03 -4.837074E-06 -1.576319E-02 -3.661568E-05 -1.430139E-02 -3.954353E-05 -1.550808E-02 -5.413485E-05 -9.529567E-03 -2.562847E-05 -1.252063E-02 -2.284038E-05 -8.998915E-03 -1.107600E-05 -1.128710E-02 -3.131513E-05 -8.894334E-03 -1.612799E-05 -9.936476E-03 -1.499512E-05 -1.085220E-02 -1.879083E-05 -7.842398E-03 -1.147755E-05 -9.478975E-03 -1.366773E-05 -8.870185E-03 -1.264668E-05 -1.265677E-02 -3.003173E-05 -7.082798E-03 -9.455651E-06 -6.207587E-03 -1.035866E-05 -9.389246E-03 -1.904336E-05 -6.809780E-03 -1.110927E-05 -1.415613E-02 -3.324858E-05 -1.411282E-02 -2.597041E-05 -1.427512E-02 -3.295595E-05 -1.943942E-02 -5.877560E-05 -8.220965E-03 -9.356934E-06 -9.250583E-03 -1.365956E-05 -1.656914E-02 -3.475091E-05 -1.843152E-02 -4.655130E-05 -1.153837E-02 -1.618003E-05 -1.196430E-02 -2.260049E-05 -1.575201E-02 -2.932265E-05 -1.575583E-02 -3.216253E-05 -1.342670E-02 -3.092689E-05 -1.018784E-02 -1.786160E-05 -1.303515E-02 -2.169915E-05 -1.219880E-02 -3.353411E-05 -7.032946E-03 -7.709601E-06 -1.229650E-02 -2.705058E-05 -1.615409E-02 -3.472477E-05 -1.821028E-02 -3.933712E-05 -1.470364E-02 -2.795706E-05 -1.050280E-02 -2.133231E-05 -1.154231E-02 -1.968046E-05 -7.645500E-03 -1.039514E-05 -1.741509E-02 -3.584004E-05 -8.821919E-03 -1.428153E-05 -1.857457E-02 -4.195598E-05 -1.552507E-02 -2.892265E-05 -7.183282E-03 -9.228195E-06 -1.217604E-02 -1.939061E-05 -1.541421E-02 -3.334972E-05 -2.052820E-02 -5.660936E-05 -9.353019E-03 -1.426621E-05 -1.251082E-02 -2.228447E-05 -1.368721E-02 -2.531714E-05 -1.226667E-02 -2.368414E-05 -1.371101E-02 -2.448795E-05 -1.201033E-02 -2.393841E-05 -1.280636E-02 -2.115640E-05 -1.264839E-02 -2.387771E-05 -9.055229E-03 -1.568024E-05 -9.856570E-03 -1.373965E-05 -1.373503E-02 -2.764304E-05 -1.700784E-02 -3.483410E-05 -1.212809E-02 -2.883883E-05 -1.598449E-02 -4.084533E-05 -1.457711E-02 -2.808561E-05 -1.586485E-02 -3.242222E-05 -1.605131E-02 -4.105521E-05 -1.119495E-02 -1.615746E-05 -1.324399E-02 -2.727755E-05 -9.867968E-03 -1.269902E-05 -1.261254E-02 -2.302293E-05 -1.153152E-02 -1.778319E-05 -9.627863E-03 -1.562429E-05 -9.050432E-03 -2.011089E-05 -1.012233E-02 -1.405672E-05 -8.911922E-03 -1.231699E-05 -1.264744E-02 -3.182267E-05 -1.122935E-02 -1.396398E-05 -1.004271E-02 -2.482202E-05 -7.894125E-03 -1.026568E-05 -8.738637E-03 -1.294773E-05 -1.219349E-02 -2.177467E-05 -9.967898E-03 -1.619041E-05 -7.763622E-03 -1.314523E-05 -7.889504E-03 -1.517342E-05 -7.877888E-03 -9.676691E-06 -7.292674E-03 -8.328724E-06 -9.035083E-03 -1.302638E-05 -9.363679E-03 -1.575034E-05 -9.640810E-03 -1.648917E-05 -6.094059E-03 -5.041308E-06 -4.386721E-03 -4.583987E-06 -8.186442E-03 -1.162059E-05 -6.547204E-03 -5.040030E-06 -4.382851E-03 -3.779716E-06 -4.722787E-03 -2.981327E-06 -1.235870E-02 -2.314605E-05 -1.222127E-02 -2.292500E-05 -7.743975E-03 -1.242313E-05 -5.687154E-03 -5.021930E-06 -7.628599E-03 -1.334325E-05 -8.144837E-03 -9.544871E-06 -8.964376E-03 -1.242765E-05 -9.619105E-03 -1.533853E-05 -1.084215E-02 -2.414197E-05 -6.098310E-03 -6.403053E-06 -3.971375E-03 -2.559046E-06 -4.956486E-03 -4.314077E-06 -1.650323E-02 -3.308582E-05 -1.785233E-02 -4.022948E-05 -1.226233E-02 -2.222264E-05 -1.324548E-02 -2.290739E-05 -1.170726E-02 -2.043035E-05 -1.128481E-02 -1.783746E-05 -1.446854E-02 -2.465804E-05 -1.308709E-02 -2.086517E-05 -1.532165E-02 -3.175587E-05 -1.176741E-02 -2.528693E-05 -6.104662E-03 -5.418027E-06 -5.028482E-03 -7.038340E-06 -1.452720E-02 -2.907362E-05 -2.155983E-02 -5.793543E-05 -8.953378E-03 -1.144318E-05 -1.193284E-02 -1.941045E-05 -1.898089E-02 -4.676936E-05 -8.078140E-03 -1.008669E-05 -1.345743E-02 -3.010294E-05 -1.166065E-02 -2.971259E-05 -2.140319E-02 -6.942386E-05 -1.156518E-02 -2.123401E-05 -1.063148E-02 -1.679318E-05 -1.035058E-02 -1.786819E-05 -1.141576E-02 -1.488341E-05 -2.132304E-02 -6.407400E-05 -1.417013E-02 -2.428688E-05 -1.578758E-02 -4.383560E-05 -1.510669E-02 -4.330783E-05 -1.061421E-02 -2.120867E-05 -9.836246E-03 -1.836620E-05 -1.170301E-02 -2.304010E-05 -2.297562E-02 -7.378766E-05 -2.077496E-02 -6.096800E-05 -8.479570E-03 -1.315595E-05 -1.334700E-02 -2.778150E-05 -1.518804E-02 -2.768676E-05 -1.665283E-02 -3.264844E-05 -1.337650E-02 -2.908404E-05 -1.642169E-02 -3.787016E-05 -1.532468E-02 -3.092775E-05 -1.264533E-02 -1.889367E-05 -1.570062E-02 -2.982615E-05 -1.201853E-02 -1.991814E-05 -1.956945E-02 -4.268136E-05 -1.749406E-02 -3.890385E-05 -9.761579E-03 -1.266596E-05 -1.131974E-02 -1.669105E-05 -1.583139E-02 -3.408051E-05 -1.813736E-02 -4.024179E-05 -1.749162E-02 -4.075339E-05 -1.806084E-02 -4.998400E-05 -1.798603E-02 -3.527089E-05 -1.363365E-02 -2.545698E-05 -1.735765E-02 -3.680633E-05 -1.313196E-02 -2.758452E-05 -1.257866E-02 -2.792705E-05 -1.371026E-02 -2.875442E-05 -1.449637E-02 -2.956474E-05 -1.184945E-02 -2.021992E-05 -1.160263E-02 -1.677863E-05 -1.486874E-02 -2.727812E-05 -1.334215E-02 -2.696922E-05 -8.375076E-03 -1.127780E-05 -1.398722E-02 -2.987500E-05 -1.431399E-02 -2.337988E-05 -1.685216E-02 -4.010946E-05 -1.220900E-02 -2.279807E-05 -1.359371E-02 -3.406479E-05 -1.497337E-02 -3.514338E-05 -1.077240E-02 -1.570437E-05 -1.286688E-02 -2.067034E-05 -1.804016E-02 -3.897670E-05 -1.720304E-02 -3.923736E-05 -1.248667E-02 -2.229239E-05 -9.872479E-03 -1.547272E-05 -1.404898E-02 -3.940008E-05 -1.102828E-02 -2.118933E-05 -9.527731E-03 -1.232685E-05 -1.183452E-02 -2.230399E-05 -1.235818E-02 -2.095622E-05 -9.045793E-03 -1.310065E-05 -7.573693E-03 -7.912407E-06 -7.098140E-03 -1.002223E-05 -1.087704E-02 -1.644644E-05 -1.403508E-02 -2.293868E-05 -1.102953E-02 -2.108622E-05 -1.278029E-02 -2.765609E-05 -1.000153E-02 -1.489998E-05 -1.023204E-02 -1.425642E-05 -1.077781E-02 -1.616312E-05 -1.504135E-02 -3.226364E-05 -8.290904E-03 -1.302393E-05 -1.081631E-02 -1.596914E-05 -1.061779E-02 -1.936956E-05 -9.609920E-03 -1.233187E-05 -7.536017E-03 -8.104937E-06 -1.064514E-02 -1.960743E-05 -4.303700E-03 -3.157059E-06 -4.544730E-03 -3.063284E-06 -9.403522E-03 -1.530342E-05 -6.146303E-03 -5.481247E-06 -5.504300E-03 -4.029062E-06 -2.514628E-03 -2.046696E-06 -1.150234E-02 -3.434888E-05 -6.476274E-03 -8.282595E-06 -5.180114E-03 -8.533421E-06 -6.593161E-03 -7.470575E-06 -1.087271E-02 -2.875725E-05 -1.114108E-02 -1.774790E-05 -9.707234E-03 -2.320447E-05 -7.474544E-03 -7.620487E-06 -1.189904E-02 -1.770896E-05 -8.306981E-03 -1.023805E-05 -1.044243E-02 -2.358835E-05 -7.956950E-03 -1.442236E-05 -6.383279E-03 -9.306381E-06 -6.540012E-03 -7.870617E-06 -7.547221E-03 -8.818582E-06 -8.118902E-03 -1.018402E-05 -1.219458E-02 -2.121863E-05 -9.183066E-03 -1.021558E-05 -8.537042E-03 -2.163043E-05 -9.801780E-03 -1.896248E-05 -7.436543E-03 -6.348631E-06 -8.783038E-03 -1.450508E-05 -1.198218E-02 -2.299991E-05 -1.176673E-02 -1.772108E-05 -7.610905E-03 -1.295193E-05 -1.236441E-02 -2.279242E-05 -1.374811E-02 -2.984764E-05 -8.276450E-03 -9.063020E-06 -2.838721E-02 -8.556398E-05 -1.876318E-02 -4.502643E-05 -1.545136E-02 -3.024495E-05 -1.307062E-02 -3.391324E-05 -1.321284E-02 -2.975134E-05 -1.195812E-02 -2.014099E-05 -2.398471E-02 -7.682355E-05 -1.699635E-02 -4.513757E-05 -1.109010E-02 -1.505508E-05 -1.463044E-02 -3.130849E-05 -1.445299E-02 -3.424691E-05 -1.847738E-02 -4.339745E-05 -2.269521E-02 -6.754400E-05 -1.622681E-02 -3.412820E-05 -1.867140E-02 -4.737811E-05 -1.704633E-02 -5.070683E-05 -2.137995E-02 -5.989756E-05 -1.262222E-02 -2.579396E-05 -1.649755E-02 -3.278397E-05 -1.021540E-02 -1.333464E-05 -1.399361E-02 -4.091195E-05 -1.481283E-02 -2.778259E-05 -1.110529E-02 -1.543650E-05 -1.370454E-02 -2.585161E-05 -2.187939E-02 -5.698664E-05 -2.156023E-02 -5.655243E-05 -1.819754E-02 -4.650955E-05 -1.667214E-02 -4.523740E-05 -1.212974E-02 -1.755527E-05 -1.664077E-02 -4.676917E-05 -1.598420E-02 -3.367421E-05 -2.197091E-02 -6.311844E-05 -1.554809E-02 -4.350026E-05 -1.662573E-02 -3.639737E-05 -1.377054E-02 -2.979629E-05 -1.718171E-02 -6.738915E-05 -2.224715E-02 -5.208199E-05 -2.028701E-02 -5.025768E-05 -2.346558E-02 -7.283542E-05 -2.047816E-02 -5.352208E-05 -2.103499E-02 -5.936660E-05 -1.795967E-02 -4.651152E-05 -2.451667E-02 -7.714718E-05 -2.018245E-02 -4.706972E-05 -1.870240E-02 -5.089247E-05 -1.414303E-02 -2.510243E-05 -1.249731E-02 -1.910688E-05 -1.461191E-02 -2.931018E-05 -1.772779E-02 -3.502831E-05 -2.405954E-02 -7.243269E-05 -1.698120E-02 -5.235943E-05 -1.363172E-02 -2.557442E-05 -2.085610E-02 -5.559729E-05 -1.434576E-02 -2.498955E-05 -2.151921E-02 -5.863979E-05 -1.804978E-02 -3.738678E-05 -1.626456E-02 -3.204521E-05 -1.416110E-02 -2.454304E-05 -1.349966E-02 -2.225683E-05 -1.376360E-02 -2.288533E-05 -1.304562E-02 -2.360964E-05 -1.204823E-02 -2.235548E-05 -1.581808E-02 -3.833344E-05 -1.458683E-02 -2.424983E-05 -1.532417E-02 -3.313140E-05 -1.674514E-02 -3.698941E-05 -8.820684E-03 -1.237269E-05 -1.325954E-02 -2.839297E-05 -1.346247E-02 -2.329594E-05 -1.218804E-02 -2.010088E-05 -7.816904E-03 -8.061895E-06 -1.115974E-02 -1.481199E-05 -1.126424E-02 -1.437812E-05 -1.155465E-02 -2.043504E-05 -1.330918E-02 -2.482809E-05 -1.359646E-02 -2.349457E-05 -6.879034E-03 -6.482253E-06 -1.025078E-02 -1.436298E-05 -1.124296E-02 -1.766927E-05 -9.157794E-03 -1.732026E-05 -1.080917E-02 -1.759748E-05 -1.224046E-02 -1.895136E-05 -1.132499E-02 -1.940702E-05 -1.055316E-02 -1.421109E-05 -6.950077E-03 -8.886009E-06 -1.146153E-02 -2.328053E-05 -6.534133E-03 -9.046637E-06 -5.113551E-03 -7.352574E-06 -1.096783E-02 -1.762871E-05 -8.670928E-03 -1.642712E-05 -6.666016E-03 -7.936419E-06 -4.024813E-03 -3.317441E-06 -6.604655E-03 -6.471414E-06 -7.218922E-03 -7.914816E-06 -7.543650E-03 -1.006296E-05 -8.390292E-03 -1.280688E-05 -6.833212E-03 -9.885211E-06 -8.859184E-03 -1.491876E-05 -4.271802E-03 -4.619779E-06 -8.415017E-03 -1.758541E-05 -7.880304E-03 -1.207340E-05 -6.190265E-03 -5.244062E-06 -1.255358E-02 -3.409848E-05 -4.588111E-03 -6.615112E-06 -7.290138E-03 -8.053715E-06 -2.708550E-03 -1.222054E-06 -7.971324E-03 -1.019545E-05 -2.186309E-03 -3.012755E-06 -1.730168E-02 -4.007549E-05 -9.437870E-03 -1.374155E-05 -8.710815E-03 -1.058748E-05 -6.876864E-03 -1.161077E-05 -1.183718E-02 -2.088043E-05 -1.317392E-02 -2.144719E-05 -1.202134E-02 -2.244800E-05 -1.219063E-02 -1.871706E-05 -9.457262E-03 -1.576600E-05 -8.456836E-03 -1.009365E-05 -6.722846E-03 -7.231603E-06 -9.056257E-03 -1.228372E-05 -1.680554E-02 -5.472386E-05 -1.583876E-02 -4.084512E-05 -1.451889E-02 -2.656784E-05 -1.154750E-02 -2.200804E-05 -8.960773E-03 -1.608168E-05 -1.021117E-02 -2.175200E-05 -1.537603E-02 -3.295291E-05 -1.346321E-02 -3.199823E-05 -1.020297E-02 -2.083570E-05 -1.574785E-02 -3.797866E-05 -1.224712E-02 -2.543224E-05 -1.213857E-02 -2.596422E-05 -1.978514E-02 -5.572712E-05 -1.721724E-02 -3.343383E-05 -1.917708E-02 -6.017423E-05 -1.746870E-02 -4.670195E-05 -1.601664E-02 -3.757776E-05 -1.391434E-02 -2.787908E-05 -2.112923E-02 -6.179734E-05 -1.494974E-02 -3.147270E-05 -2.027112E-02 -5.409774E-05 -1.762825E-02 -3.716705E-05 -1.490183E-02 -2.636325E-05 -1.144233E-02 -1.895260E-05 -2.252480E-02 -6.008716E-05 -2.061068E-02 -4.967689E-05 -1.978565E-02 -5.823807E-05 -1.627207E-02 -3.508510E-05 -2.308648E-02 -7.339685E-05 -9.920567E-03 -1.588060E-05 -1.582116E-02 -3.710173E-05 -1.359849E-02 -3.693990E-05 -2.449553E-02 -7.939873E-05 -1.712995E-02 -3.401990E-05 -1.578519E-02 -2.835653E-05 -1.996773E-02 -5.153308E-05 -1.784633E-02 -3.372557E-05 -2.328886E-02 -7.016669E-05 -1.908795E-02 -5.601183E-05 -2.149293E-02 -6.345938E-05 -2.432779E-02 -7.634443E-05 -2.394049E-02 -6.577991E-05 -1.506740E-02 -3.094281E-05 -1.388336E-02 -2.886613E-05 -1.614618E-02 -3.561899E-05 -1.595134E-02 -4.065708E-05 -1.340027E-02 -2.204671E-05 -1.472267E-02 -2.861240E-05 -1.790210E-02 -5.856300E-05 -2.646480E-02 -1.056654E-04 -1.880864E-02 -4.142515E-05 -1.426154E-02 -2.963840E-05 -1.165132E-02 -2.176252E-05 -1.428324E-02 -2.725319E-05 -1.588976E-02 -3.970327E-05 -1.833160E-02 -4.559784E-05 -1.772585E-02 -4.748240E-05 -1.848953E-02 -5.090661E-05 -1.272374E-02 -2.030829E-05 -1.412613E-02 -3.146328E-05 -1.649479E-02 -4.133012E-05 -1.209661E-02 -4.450673E-05 -1.369084E-02 -2.392580E-05 -1.531641E-02 -3.089017E-05 -1.580566E-02 -3.194323E-05 -1.132108E-02 -1.399526E-05 -8.190342E-03 -9.145143E-06 -7.902353E-03 -1.504796E-05 -1.258132E-02 -2.292738E-05 -6.958024E-03 -9.709387E-06 -1.037667E-02 -1.407859E-05 -1.201319E-02 -2.053522E-05 -9.379406E-03 -1.510952E-05 -1.279685E-02 -2.736611E-05 -1.191802E-02 -2.186093E-05 -6.155316E-03 -6.209415E-06 -8.817944E-03 -1.322568E-05 -7.497837E-03 -1.365620E-05 -9.976490E-03 -1.734874E-05 -7.558291E-03 -1.424385E-05 -8.801002E-03 -1.425950E-05 -9.075301E-03 -1.401503E-05 -5.681746E-03 -5.798447E-06 -4.827386E-03 -3.466370E-06 -5.884691E-03 -5.754861E-06 -1.211858E-02 -2.621006E-05 -5.629512E-03 -6.112612E-06 -5.768978E-03 -5.711093E-06 -1.075576E-02 -1.954724E-05 -5.977668E-03 -7.957209E-06 -6.775985E-03 -8.582096E-06 -3.503043E-03 -2.684132E-06 -8.113430E-03 -1.099167E-05 -8.263729E-03 -1.412028E-05 -3.781179E-03 -3.287824E-06 -8.368219E-03 -9.803361E-06 -1.068347E-02 -1.390398E-05 -1.118234E-02 -1.591023E-05 -1.016006E-02 -2.761365E-05 -6.634695E-03 -7.415821E-06 -1.042368E-02 -1.700083E-05 -1.024339E-02 -1.630692E-05 -5.465831E-03 -6.271754E-06 -1.117655E-02 -1.998616E-05 -8.708982E-03 -1.145629E-05 -3.321055E-03 -3.386533E-06 -8.951176E-03 -1.175568E-05 -7.413974E-03 -1.358203E-05 -1.289235E-02 -2.359140E-05 -9.817553E-03 -1.518261E-05 -9.879433E-03 -1.602972E-05 -1.177378E-02 -2.259438E-05 -5.796294E-03 -6.245744E-06 -1.138092E-02 -2.136416E-05 -8.927027E-03 -9.348267E-06 -9.673957E-03 -1.246216E-05 -1.280715E-02 -4.429778E-05 -8.808327E-03 -1.178528E-05 -7.125501E-03 -7.483806E-06 -4.620428E-03 -9.310180E-06 -1.680437E-02 -3.451199E-05 -2.026938E-02 -4.346902E-05 -1.635252E-02 -5.026111E-05 -1.278583E-02 -2.192134E-05 -1.301566E-02 -2.384432E-05 -9.286694E-03 -1.400657E-05 -1.107901E-02 -1.953576E-05 -1.150656E-02 -2.102378E-05 -1.772830E-02 -3.480088E-05 -1.063150E-02 -1.401509E-05 -1.374475E-02 -2.299888E-05 -6.867995E-03 -1.027748E-05 -1.580376E-02 -3.847030E-05 -1.668637E-02 -3.953893E-05 -1.442844E-02 -3.008408E-05 -1.827058E-02 -4.753438E-05 -1.440759E-02 -3.206648E-05 -1.592968E-02 -4.677348E-05 -1.718273E-02 -3.488836E-05 -1.688712E-02 -4.196255E-05 -2.047044E-02 -4.959256E-05 -1.306218E-02 -3.057713E-05 -8.848692E-03 -1.833823E-05 -1.117548E-02 -2.553563E-05 -2.169142E-02 -6.306137E-05 -2.218276E-02 -5.572871E-05 -1.802302E-02 -4.705313E-05 -1.782656E-02 -4.017437E-05 -1.925947E-02 -4.703183E-05 -1.641263E-02 -3.693267E-05 -1.516396E-02 -3.126646E-05 -1.512145E-02 -3.336386E-05 -1.579023E-02 -3.449473E-05 -1.368503E-02 -3.144328E-05 -1.108096E-02 -1.983330E-05 -1.447670E-02 -3.832266E-05 -1.831886E-02 -3.928136E-05 -1.893457E-02 -5.869250E-05 -1.764892E-02 -5.005397E-05 -1.637340E-02 -3.702753E-05 -9.686698E-03 -1.449037E-05 -1.006947E-02 -1.933484E-05 -1.884484E-02 -4.561680E-05 -1.468883E-02 -2.792875E-05 -1.670400E-02 -3.477624E-05 -1.632153E-02 -3.402425E-05 -9.187335E-03 -1.053828E-05 -9.985824E-03 -1.585776E-05 -1.303759E-02 -3.027649E-05 -1.872005E-02 -5.130980E-05 -1.633753E-02 -4.260466E-05 -1.020425E-02 -1.707335E-05 -1.320758E-02 -2.695180E-05 -1.208449E-02 -3.007055E-05 -1.286574E-02 -2.373357E-05 -1.095090E-02 -2.877553E-05 -1.692473E-02 -4.961965E-05 -1.190920E-02 -2.375507E-05 -5.682089E-03 -4.636856E-06 -1.035844E-02 -1.997872E-05 -9.666688E-03 -1.483243E-05 -1.613905E-02 -3.984840E-05 -1.572095E-02 -3.368762E-05 -1.108904E-02 -1.539438E-05 -1.486260E-02 -4.070529E-05 -1.042750E-02 -1.813263E-05 -1.042360E-02 -1.689881E-05 -8.718623E-03 -1.199955E-05 -1.039440E-02 -1.674301E-05 -5.162509E-03 -6.408708E-06 -9.563193E-03 -1.362142E-05 -6.648598E-03 -1.021485E-05 -1.122742E-02 -1.540873E-05 -7.837929E-03 -1.128828E-05 -1.114590E-02 -2.553030E-05 -1.141215E-02 -2.510840E-05 -1.152836E-02 -2.344713E-05 -7.971937E-03 -1.506387E-05 -1.248527E-02 -2.125446E-05 -6.066628E-03 -9.574143E-06 -1.087280E-02 -1.688717E-05 -9.550524E-03 -1.250483E-05 -5.306519E-03 -4.846453E-06 -7.512202E-03 -8.651766E-06 -7.508820E-03 -8.634705E-06 -5.185887E-03 -4.163304E-06 -1.379038E-02 -3.492367E-05 -9.235196E-03 -1.206055E-05 -1.392051E-02 -3.803496E-05 -7.049846E-03 -9.147756E-06 -5.323140E-03 -4.281730E-06 -8.229124E-03 -1.548991E-05 -7.235167E-03 -1.441034E-05 -2.366009E-03 -3.874657E-06 -5.945679E-03 -1.067197E-05 -5.871383E-03 -5.656813E-06 -9.732147E-03 -2.269675E-05 -6.582049E-03 -7.833573E-06 -3.066363E-03 -3.098185E-06 -5.796032E-03 -6.625438E-06 -8.122368E-03 -9.655803E-06 -1.121766E-02 -1.750086E-05 -1.036527E-02 -1.842391E-05 -5.944076E-03 -6.439480E-06 -2.283818E-03 -1.352210E-06 -1.446587E-03 -7.807258E-07 -4.944507E-03 -3.694344E-06 -3.281169E-03 -2.925608E-06 -9.286754E-03 -1.260048E-05 -1.272772E-02 -2.464058E-05 -1.270753E-02 -3.173555E-05 -1.419952E-02 -2.835999E-05 -9.671107E-03 -1.686465E-05 -9.583433E-03 -1.323773E-05 -1.186931E-02 -2.163029E-05 -1.267789E-02 -2.021594E-05 -8.275560E-03 -9.163723E-06 -1.062420E-02 -1.977462E-05 -1.198470E-02 -1.979838E-05 -1.324773E-02 -2.853129E-05 -1.727253E-02 -3.432691E-05 -1.381997E-02 -2.842333E-05 -1.578301E-02 -3.356518E-05 -1.555696E-02 -3.183947E-05 -8.166265E-03 -8.629223E-06 -1.747785E-02 -4.250397E-05 -9.541135E-03 -1.246077E-05 -1.393808E-02 -3.012112E-05 -1.209324E-02 -2.429593E-05 -1.079402E-02 -2.325836E-05 -1.374417E-02 -2.491779E-05 -9.500289E-03 -1.572888E-05 -1.222769E-02 -2.456217E-05 -1.097825E-02 -1.938425E-05 -1.410107E-02 -2.407486E-05 -2.024375E-02 -6.980050E-05 -1.220648E-02 -3.245536E-05 -1.137633E-02 -1.667373E-05 -1.038617E-02 -1.405080E-05 -1.243797E-02 -2.413125E-05 -9.998308E-03 -1.605906E-05 -8.805567E-03 -1.128015E-05 -1.388695E-02 -3.039013E-05 -1.242352E-02 -2.040627E-05 -2.645008E-02 -8.679965E-05 -2.076456E-02 -5.640973E-05 -1.805209E-02 -4.120924E-05 -1.246120E-02 -3.006639E-05 -1.810150E-02 -4.756486E-05 -1.472448E-02 -3.309142E-05 -2.571400E-02 -8.648041E-05 -1.341319E-02 -3.280431E-05 -1.535941E-02 -4.819536E-05 -2.373309E-02 -7.918529E-05 -5.757795E-03 -4.733061E-06 -1.174264E-02 -2.108284E-05 -1.884131E-02 -4.721518E-05 -1.750610E-02 -4.223243E-05 -1.716739E-02 -3.617002E-05 -1.252851E-02 -2.099071E-05 -1.388277E-02 -3.109337E-05 -1.350362E-02 -3.082057E-05 -1.238489E-02 -3.728424E-05 -9.966253E-03 -2.035860E-05 -2.285240E-02 -8.446864E-05 -1.327084E-02 -3.045931E-05 -1.193699E-02 -2.204142E-05 -6.923219E-03 -9.602029E-06 -1.838247E-02 -4.807008E-05 -1.863076E-02 -4.604209E-05 -1.334000E-02 -2.761396E-05 -1.141315E-02 -2.474696E-05 -2.286809E-02 -1.083380E-04 -1.645875E-02 -3.520343E-05 -1.229791E-02 -2.380600E-05 -1.284486E-02 -2.254139E-05 -1.854605E-02 -4.752272E-05 -1.049692E-02 -1.983824E-05 -7.207727E-03 -7.286730E-06 -1.686364E-02 -3.977916E-05 -1.314115E-02 -2.189974E-05 -1.520236E-02 -3.635654E-05 -1.096706E-02 -2.475645E-05 -7.932651E-03 -1.085523E-05 -1.515150E-02 -4.556316E-05 -1.171402E-02 -3.284784E-05 -1.087897E-02 -1.567310E-05 -8.199381E-03 -9.564718E-06 -9.456870E-03 -1.207744E-05 -8.827517E-03 -1.658191E-05 -5.512807E-03 -6.038391E-06 -8.573999E-03 -1.207656E-05 -1.047145E-02 -1.674083E-05 -9.666379E-03 -1.231732E-05 -1.046000E-02 -1.782331E-05 -6.009504E-03 -6.261797E-06 -1.111917E-02 -1.768100E-05 -1.105060E-02 -1.934982E-05 -5.280218E-03 -7.191057E-06 -7.637218E-03 -9.728555E-06 -1.009144E-02 -1.219856E-05 -4.974619E-03 -4.499916E-06 -6.589686E-03 -1.040697E-05 -7.787208E-03 -1.023212E-05 -6.068811E-03 -5.842725E-06 -3.416649E-03 -3.146550E-06 -9.213350E-03 -2.154615E-05 -6.752158E-03 -9.801770E-06 -4.758057E-03 -6.649209E-06 -6.129868E-03 -8.342508E-06 -2.783932E-03 -2.140712E-06 -6.308493E-03 -1.039901E-05 -2.249137E-03 -1.153739E-06 -2.514780E-03 -1.593412E-06 -6.018183E-03 -8.965827E-06 -3.234291E-03 -2.042484E-06 -7.860270E-03 -1.035245E-05 -3.414591E-03 -2.249770E-06 -7.941000E-03 -1.449758E-05 -3.591791E-03 -2.245391E-06 -7.807797E-03 -1.011270E-05 -7.726893E-03 -1.478217E-05 -2.506930E-03 -2.954554E-06 -5.955023E-03 -7.260418E-06 -5.294809E-03 -8.225926E-06 -2.802166E-03 -1.996795E-06 -2.404798E-03 -1.266950E-06 -4.125341E-03 -2.979146E-06 -7.417991E-03 -8.566910E-06 -8.158248E-03 -1.230927E-05 -7.976311E-03 -1.332178E-05 -8.873852E-03 -1.480843E-05 -9.373684E-03 -1.259336E-05 -8.214068E-03 -1.186084E-05 -6.962685E-03 -7.715598E-06 -8.834013E-03 -1.972166E-05 -5.435947E-03 -4.590835E-06 -5.487949E-03 -5.939754E-06 -6.678491E-03 -8.355182E-06 -4.058912E-03 -5.880001E-06 -1.360573E-02 -2.671641E-05 -1.143610E-02 -2.018223E-05 -7.702740E-03 -1.433952E-05 -5.873725E-03 -6.789166E-06 -1.088086E-02 -3.437425E-05 -7.089361E-03 -1.461407E-05 -8.765624E-03 -1.242854E-05 -9.453645E-03 -1.575322E-05 -1.001462E-02 -1.448625E-05 -1.282490E-02 -2.290074E-05 -8.545890E-03 -1.273760E-05 -7.387390E-03 -9.140356E-06 -1.399357E-02 -2.449459E-05 -1.337581E-02 -3.113825E-05 -1.423915E-02 -2.729711E-05 -6.580885E-03 -8.347839E-06 -7.779718E-03 -1.007558E-05 -7.480792E-03 -1.083253E-05 -1.275448E-02 -2.722719E-05 -1.292106E-02 -2.421066E-05 -9.755948E-03 -1.426055E-05 -9.117272E-03 -1.368142E-05 -7.455531E-03 -8.688003E-06 -1.042236E-02 -1.801425E-05 -1.296287E-02 -2.809397E-05 -1.390180E-02 -2.674920E-05 -1.265490E-02 -3.041327E-05 -1.269961E-02 -2.824134E-05 -1.133938E-02 -1.625519E-05 -9.821144E-03 -2.178670E-05 -1.530578E-02 -3.585772E-05 -1.299672E-02 -2.588612E-05 -1.544212E-02 -3.143850E-05 -8.347818E-03 -1.132876E-05 -1.393101E-02 -2.890996E-05 -6.639867E-03 -7.587375E-06 -1.839132E-02 -4.637735E-05 -1.188258E-02 -1.953879E-05 -1.588672E-02 -3.134987E-05 -1.425154E-02 -2.751603E-05 -4.920276E-03 -4.432356E-06 -1.374206E-02 -2.842285E-05 -1.673110E-02 -4.601232E-05 -1.254973E-02 -2.188833E-05 -6.444727E-03 -5.222041E-06 -1.023529E-02 -1.572480E-05 -1.087382E-02 -1.692895E-05 -9.038702E-03 -1.572012E-05 -1.398299E-02 -2.818260E-05 -8.766186E-03 -1.494609E-05 -1.321841E-02 -2.007819E-05 -1.562133E-02 -3.892744E-05 -8.289134E-03 -1.177987E-05 -8.947084E-03 -1.094585E-05 -1.594810E-02 -3.949297E-05 -1.285384E-02 -2.278617E-05 -5.689452E-03 -7.280228E-06 -9.766401E-03 -2.148390E-05 -8.496573E-03 -8.437729E-06 -7.991945E-03 -9.550933E-06 -9.608813E-03 -1.425597E-05 -1.052303E-02 -1.935336E-05 -8.548009E-03 -1.333735E-05 -7.337588E-03 -9.975447E-06 -1.423505E-02 -3.258201E-05 -1.136654E-02 -2.510870E-05 -7.860115E-03 -8.206414E-06 -8.309608E-03 -1.504702E-05 -1.110051E-02 -2.051470E-05 -6.172688E-03 -9.409184E-06 -9.740436E-03 -1.437599E-05 -8.997021E-03 -1.150131E-05 -1.279849E-02 -3.206319E-05 -1.028663E-02 -1.505898E-05 -1.122879E-02 -1.673337E-05 -8.223903E-03 -9.720008E-06 -8.464761E-03 -1.103940E-05 -6.670166E-03 -6.071848E-06 -4.620811E-03 -6.527856E-06 -3.824735E-03 -3.340845E-06 -5.634323E-03 -5.012909E-06 -3.166012E-03 -3.619323E-06 -9.167802E-03 -1.056621E-05 -3.221327E-03 -1.773076E-06 -5.412502E-03 -5.413007E-06 -6.702745E-03 -8.187361E-06 -4.863392E-03 -3.807791E-06 -4.405641E-03 -4.058186E-06 -6.131083E-03 -7.250341E-06 -4.913764E-03 -5.744987E-06 -4.588314E-03 -3.202328E-06 -6.804591E-03 -8.479426E-06 -7.252910E-03 -8.758909E-06 -4.312950E-03 -3.434388E-06 -1.807777E-03 -1.380521E-06 -4.671070E-03 -5.538033E-06 -7.067950E-03 -1.174764E-05 -2.930719E-03 -1.441738E-06 -1.279222E-03 -1.078804E-06 -3.507572E-03 -5.821750E-06 -9.678721E-03 -2.476852E-05 -2.874671E-03 -2.470595E-06 -2.182691E-03 -1.629987E-06 -3.621537E-03 -9.191521E-06 -1.740159E-03 -6.544183E-07 -3.203873E-03 -3.602845E-06 -2.890800E-03 -2.526106E-06 -3.068561E-03 -4.213711E-06 -6.078860E-03 -7.760574E-06 -7.287103E-03 -7.524310E-06 -7.509754E-03 -2.547783E-05 -2.646412E-03 -2.157075E-06 -6.255289E-03 -6.947406E-06 -4.941751E-03 -7.943248E-06 -9.224779E-03 -1.410566E-05 -4.906800E-03 -4.350073E-06 -7.923221E-03 -1.421559E-05 -2.900094E-03 -1.652857E-06 -3.445014E-03 -1.996755E-06 -2.978919E-03 -3.704572E-06 -8.310828E-03 -1.128557E-05 -8.914816E-03 -1.105293E-05 -6.453823E-03 -7.530365E-06 -8.529848E-03 -1.250596E-05 -1.241639E-02 -1.968019E-05 -3.654989E-03 -2.852818E-06 -6.194753E-03 -4.637962E-06 -7.168410E-03 -9.747482E-06 -7.074135E-03 -8.723001E-06 -3.088414E-03 -2.165114E-06 -4.736900E-03 -4.690360E-06 -5.496866E-03 -6.084993E-06 -9.992636E-03 -2.331975E-05 -5.785665E-03 -9.290287E-06 -9.268775E-03 -1.727651E-05 -1.110749E-02 -2.320101E-05 -6.035119E-03 -6.006884E-06 -6.097186E-03 -8.477525E-06 -9.286855E-03 -1.325325E-05 -5.725710E-03 -5.595748E-06 -5.225326E-03 -5.682741E-06 -8.374162E-03 -1.358989E-05 -8.070266E-03 -8.525794E-06 -5.663014E-03 -8.326352E-06 -1.077577E-02 -1.774824E-05 -6.823384E-03 -7.461723E-06 -1.309151E-02 -2.293933E-05 -9.734117E-03 -1.567869E-05 -1.064840E-02 -1.998801E-05 -6.598548E-03 -1.247960E-05 -8.533419E-03 -1.698271E-05 -1.187770E-02 -2.246194E-05 -3.905374E-03 -2.559130E-06 -2.608870E-03 -1.558560E-06 -6.052893E-03 -5.374399E-06 -5.291540E-03 -8.103686E-06 -7.976421E-03 -1.021030E-05 -9.757093E-03 -1.668583E-05 -7.385489E-03 -1.285403E-05 -7.929263E-03 -7.987788E-06 -9.704986E-03 -2.962975E-05 -7.525077E-03 -1.306302E-05 -6.991034E-03 -1.064873E-05 -5.005358E-03 -9.835012E-06 -8.092808E-03 -1.147394E-05 -4.474087E-03 -3.834905E-06 -6.834524E-03 -6.982199E-06 -4.812916E-03 -4.467796E-06 -5.990512E-03 -7.842268E-06 -4.574709E-03 -3.970763E-06 -4.788106E-03 -3.924811E-06 -8.916249E-03 -1.850608E-05 -5.259665E-03 -8.293438E-06 -9.696546E-03 -1.517672E-05 -4.590212E-03 -5.096431E-06 -7.314462E-03 -1.714769E-05 -3.860185E-03 -3.127895E-06 -1.784310E-03 -9.235975E-07 -5.547235E-03 -5.112055E-06 -5.865507E-03 -6.708947E-06 -4.328438E-03 -4.229047E-06 -5.092655E-03 -5.828910E-06 -6.709671E-03 -7.080990E-06 -4.327802E-03 -3.188523E-06 -4.937391E-03 -4.569445E-06 -8.293491E-03 -1.319561E-05 -3.930138E-03 -6.730814E-06 -4.416256E-03 -4.105889E-06 -3.467315E-03 -2.354469E-06 -5.332491E-03 -9.029639E-06 -2.694692E-03 -1.279641E-06 -5.342987E-03 -6.713119E-06 -7.519588E-03 -1.092346E-05 -5.285743E-03 -5.723628E-06 -6.665656E-03 -8.544653E-06 -3.846760E-03 -2.666108E-06 -6.284377E-03 -9.724369E-06 -6.196868E-03 -8.329996E-06 -5.286331E-03 -6.920429E-06 -7.052727E-03 -1.143953E-05 -5.029353E-03 -3.735294E-06 -2.407489E-03 -2.444788E-06 -4.133935E-04 -9.497986E-08 -6.027018E-03 -1.025712E-05 -3.598018E-03 -3.018229E-06 -4.613658E-03 -4.076030E-06 -3.342670E-03 -3.029962E-06 -3.733008E-03 -2.515848E-06 -5.141865E-03 -9.007182E-06 -7.728541E-03 -1.349554E-05 -1.579734E-03 -1.531714E-06 -3.519217E-03 -3.673203E-06 -5.032814E-03 -4.394432E-06 -1.399643E-03 -6.119406E-07 -3.930826E-03 -4.507517E-06 -4.767309E-03 -5.014044E-06 diff --git a/tests/regression_tests/unstructured_mesh/test.py b/tests/regression_tests/unstructured_mesh/test.py index c921a78482..7607531d8b 100644 --- a/tests/regression_tests/unstructured_mesh/test.py +++ b/tests/regression_tests/unstructured_mesh/test.py @@ -1,6 +1,8 @@ +import filecmp import glob from itertools import product import os +import warnings import openmc import openmc.lib @@ -9,18 +11,52 @@ import numpy as np import pytest from tests.testing_harness import PyAPITestHarness -TETS_PER_VOXEL = 12 - class UnstructuredMeshTest(PyAPITestHarness): - def __init__(self, statepoint_name, model, inputs_true, holes): + ELEM_PER_VOXEL = 12 + + def __init__(self, + statepoint_name, + model, + inputs_true='inputs_true.dat', + holes=False, + scale_factor=10.0): super().__init__(statepoint_name, model, inputs_true) self.holes = holes # holes in the test mesh + self.scale_bounding_cell(scale_factor) + + def scale_bounding_cell(self, scale_factor): + geometry = self._model.geometry + for surface in geometry.get_all_surfaces().values(): + if surface.boundary_type != 'vacuum': + continue + for coeff in surface._coefficients: + surface._coefficients[coeff] *= scale_factor def _compare_results(self): with openmc.StatePoint(self._sp_name) as sp: + # check some properties of the unstructured mesh + umesh = None + for m in sp.meshes.values(): + if isinstance(m, openmc.UnstructuredMesh): + umesh = m + assert umesh is not None + + # check that the first element centroid is correct + # this will depend on whether the tet mesh or hex mesh + # file is being used in this test + if umesh.element_types[0] == umesh._LINEAR_TET: + exp_vertex = (-10.0, -10.0, -10.0) + exp_centroid = (-8.75, -9.75, -9.25) + else: + exp_vertex = (-10.0, -10.0, 10.0) + exp_centroid = (-9.0, -9.0, 9.0) + + np.testing.assert_array_equal(umesh.vertices[0], exp_vertex) + np.testing.assert_array_equal(umesh.centroid(0), exp_centroid) + # loop over the tallies and get data for tally in sp.tallies.values(): # find the regular and unstructured meshes @@ -28,32 +64,39 @@ class UnstructuredMeshTest(PyAPITestHarness): flt = tally.find_filter(openmc.MeshFilter) if isinstance(flt.mesh, openmc.RegularMesh): - reg_mesh_data, reg_mesh_std_dev = self.get_mesh_tally_data(tally) + reg_mesh_data = self.get_mesh_tally_data(tally) if self.holes: reg_mesh_data = np.delete(reg_mesh_data, self.holes) - reg_mesh_std_dev = np.delete(reg_mesh_std_dev, self.holes) else: umesh_tally = tally - unstructured_data, unstructured_std_dev = self.get_mesh_tally_data(tally, True) + unstructured_data = self.get_mesh_tally_data(tally, True) - # we expect these results to be the same to within at least ten - # decimal places - decimals = 10 if umesh_tally.estimator == 'collision' else 8 - np.testing.assert_array_almost_equal(unstructured_data, - reg_mesh_data, - decimals) + # we expect these results to be the same to within at least ten + # decimal places + decimals = 10 if umesh_tally.estimator == 'collision' else 8 + np.testing.assert_array_almost_equal(np.sort(unstructured_data), + np.sort(reg_mesh_data), + decimals) - @staticmethod - def get_mesh_tally_data(tally, structured=False): + def get_mesh_tally_data(self, tally, structured=False): data = tally.get_reshaped_data(value='mean') - std_dev = tally.get_reshaped_data(value='std_dev') if structured: - data.shape = (data.size // TETS_PER_VOXEL, TETS_PER_VOXEL) - std_dev.shape = (std_dev.size // TETS_PER_VOXEL, TETS_PER_VOXEL) + data = data.reshape((-1, self.ELEM_PER_VOXEL)) else: data.shape = (data.size, 1) - std_dev.shape = (std_dev.size, 1) - return np.sum(data, axis=1), np.sum(std_dev, axis=1) + return np.sum(data, axis=1) + + def update_results(self): + """Update results_true.dat and inputs_true.dat""" + try: + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._overwrite_inputs() + self._run_openmc() + self._test_output_created() + finally: + self._cleanup() def _cleanup(self): super()._cleanup() @@ -64,35 +107,11 @@ class UnstructuredMeshTest(PyAPITestHarness): os.remove(f) -param_values = (['libmesh', 'moab'], # mesh libraries - ['collision', 'tracklength'], # estimators - [True, False], # geometry outside of the mesh - [(333, 90, 77), None]) # location of holes in the mesh -test_cases = [] -for i, (lib, estimator, ext_geom, holes) in enumerate(product(*param_values)): - test_cases.append({'library' : lib, - 'estimator' : estimator, - 'external_geom' : ext_geom, - 'holes' : holes, - 'inputs_true' : 'inputs_true{}.dat'.format(i)}) - - -@pytest.mark.parametrize("test_opts", test_cases) -def test_unstructured_mesh(test_opts): - +@pytest.fixture +def model(): openmc.reset_auto_ids() - # skip the test if the library is not enabled - if test_opts['library'] == 'moab' and not openmc.lib._dagmc_enabled(): - pytest.skip("DAGMC (and MOAB) mesh not enbaled in this build.") - - if test_opts['library'] == 'libmesh' and not openmc.lib._libmesh_enabled(): - pytest.skip("LibMesh is not enabled in this build.") - - # skip the tracklength test for libmesh - if test_opts['library'] == 'libmesh' and \ - test_opts['estimator'] == 'tracklength': - pytest.skip("Tracklength tallies are not supported using libmesh.") + model = openmc.Model() ### Materials ### materials = openmc.Materials() @@ -113,7 +132,7 @@ def test_unstructured_mesh(test_opts): water_mat.set_density("atom/b-cm", 0.07416) materials.append(water_mat) - materials.export_to_xml() + model.materials = materials ### Geometry ### fuel_min_x = openmc.XPlane(-5.0, name="minimum x") @@ -149,29 +168,26 @@ def test_unstructured_mesh(test_opts): +clad_min_z & -clad_max_z) clad_cell.fill = zirc_mat - if test_opts['external_geom']: - bounds = (15, 15, 15) - else: - bounds = (10, 10, 10) - - water_min_x = openmc.XPlane(x0=-bounds[0], + # set bounding cell dimension to one + # this will be updated later according to the test case parameters + water_min_x = openmc.XPlane(x0=-1.0, name="minimum x", boundary_type='vacuum') - water_max_x = openmc.XPlane(x0=bounds[0], + water_max_x = openmc.XPlane(x0=1.0, name="maximum x", boundary_type='vacuum') - water_min_y = openmc.YPlane(y0=-bounds[1], + water_min_y = openmc.YPlane(y0=-1.0, name="minimum y", boundary_type='vacuum') - water_max_y = openmc.YPlane(y0=bounds[1], + water_max_y = openmc.YPlane(y0=1.0, name="maximum y", boundary_type='vacuum') - water_min_z = openmc.ZPlane(z0=-bounds[2], + water_min_z = openmc.ZPlane(z0=-1.0, name="minimum z", boundary_type='vacuum') - water_max_z = openmc.ZPlane(z0=bounds[2], + water_max_z = openmc.ZPlane(z0=1.0, name="maximum z", boundary_type='vacuum') @@ -185,9 +201,9 @@ def test_unstructured_mesh(test_opts): water_cell.fill = water_mat # create a containing universe - geometry = openmc.Geometry([fuel_cell, clad_cell, water_cell]) + model.geometry = openmc.Geometry([fuel_cell, clad_cell, water_cell]) - ### Tallies ### + ### Reference Tally ### # create meshes and mesh filters regular_mesh = openmc.RegularMesh() @@ -196,29 +212,11 @@ def test_unstructured_mesh(test_opts): regular_mesh.upper_right = (10.0, 10.0, 10.0) regular_mesh_filter = openmc.MeshFilter(mesh=regular_mesh) - - if test_opts['holes']: - mesh_filename = "test_mesh_tets_w_holes.e" - else: - mesh_filename = "test_mesh_tets.e" - - uscd_mesh = openmc.UnstructuredMesh(mesh_filename, test_opts['library']) - uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) - - # create tallies - tallies = openmc.Tallies() - regular_mesh_tally = openmc.Tally(name="regular mesh tally") regular_mesh_tally.filters = [regular_mesh_filter] regular_mesh_tally.scores = ['flux'] - regular_mesh_tally.estimator = test_opts['estimator'] - tallies.append(regular_mesh_tally) - uscd_tally = openmc.Tally(name="unstructured mesh tally") - uscd_tally.filters = [uscd_filter] - uscd_tally.scores = ['flux'] - uscd_tally.estimator = test_opts['estimator'] - tallies.append(uscd_tally) + model.tallies = openmc.Tallies([regular_mesh_tally]) ### Settings ### settings = openmc.Settings() @@ -228,21 +226,101 @@ def test_unstructured_mesh(test_opts): # source setup r = openmc.stats.Uniform(a=0.0, b=0.0) - theta = openmc.stats.Discrete(x=[0.0], p=[1.0]) + cos_theta = openmc.stats.Discrete(x=[1.0], p=[1.0]) phi = openmc.stats.Discrete(x=[0.0], p=[1.0]) - space = openmc.stats.SphericalIndependent(r, theta, phi) + space = openmc.stats.SphericalIndependent(r, cos_theta, phi) energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) - source = openmc.Source(space=space, energy=energy) + source = openmc.IndependentSource(space=space, energy=energy) settings.source = source - model = openmc.model.Model(geometry=geometry, - materials=materials, - tallies=tallies, - settings=settings) + model.settings = settings + + return model + + +param_values = (['libmesh', 'moab'], # mesh libraries + ['collision', 'tracklength'], # estimators + [True, False], # geometry outside of the mesh + [(333, 90, 77), None]) # location of holes in the mesh +test_cases = [] +for i, (lib, estimator, ext_geom, holes) in enumerate(product(*param_values)): + test_cases.append({'library' : lib, + 'estimator' : estimator, + 'external_geom' : ext_geom, + 'holes' : holes, + 'inputs_true' : 'inputs_true{}.dat'.format(i)}) + + +@pytest.mark.parametrize("test_opts", test_cases) +def test_unstructured_mesh_tets(model, test_opts): + # skip the test if the library is not enabled + if test_opts['library'] == 'moab' and not openmc.lib._dagmc_enabled(): + pytest.skip("DAGMC (and MOAB) mesh not enabled in this build.") + + if test_opts['library'] == 'libmesh' and not openmc.lib._libmesh_enabled(): + pytest.skip("LibMesh is not enabled in this build.") + + # skip the tracklength test for libmesh + if test_opts['library'] == 'libmesh' and \ + test_opts['estimator'] == 'tracklength': + pytest.skip("Tracklength tallies are not supported using libmesh.") + + if test_opts['holes']: + mesh_filename = "test_mesh_tets_w_holes.e" + else: + mesh_filename = "test_mesh_tets.e" + + # add reference mesh tally + regular_mesh_tally = model.tallies[0] + regular_mesh_tally.estimator = test_opts['estimator'] + + # add analagous unstructured mesh tally + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, test_opts['library']) + if test_opts['library'] == 'moab': + uscd_mesh.options = 'MAX_DEPTH=15;PLANE_SET=2' + uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) + + # create tallies + uscd_tally = openmc.Tally(name="unstructured mesh tally") + uscd_tally.filters = [uscd_filter] + uscd_tally.scores = ['flux'] + uscd_tally.estimator = test_opts['estimator'] + model.tallies.append(uscd_tally) + + # modify model geometry according to test opts + if test_opts['external_geom']: + scale_factor = 15.0 + else: + scale_factor = 10.0 harness = UnstructuredMeshTest('statepoint.10.h5', model, test_opts['inputs_true'], - test_opts['holes']) + test_opts['holes'], + scale_factor) + harness.main() + + +@pytest.mark.skipif(not openmc.lib._libmesh_enabled(), + reason='LibMesh is not enabled in this build.') +def test_unstructured_mesh_hexes(model): + regular_mesh_tally = model.tallies[0] + regular_mesh_tally.estimator = 'collision' + + # add analagous unstructured mesh tally + uscd_mesh = openmc.UnstructuredMesh('test_mesh_hexes.e', 'libmesh') + uscd_filter = openmc.MeshFilter(mesh=uscd_mesh) + + # create tallies + uscd_tally = openmc.Tally(name="unstructured mesh tally") + uscd_tally.filters = [uscd_filter] + uscd_tally.scores = ['flux'] + uscd_tally.estimator = 'collision' + model.tallies.append(uscd_tally) + + harness = UnstructuredMeshTest('statepoint.10.h5', + model) + harness.ELEM_PER_VOXEL = 1 + harness.main() diff --git a/tests/regression_tests/unstructured_mesh/test_mesh_hexes.e b/tests/regression_tests/unstructured_mesh/test_mesh_hexes.e new file mode 120000 index 0000000000..421bf6a89a --- /dev/null +++ b/tests/regression_tests/unstructured_mesh/test_mesh_hexes.e @@ -0,0 +1 @@ +test_mesh_hexes.exo \ No newline at end of file diff --git a/tests/regression_tests/unstructured_mesh/test_mesh_hexes.exo b/tests/regression_tests/unstructured_mesh/test_mesh_hexes.exo new file mode 100644 index 0000000000..03682c2756 Binary files /dev/null and b/tests/regression_tests/unstructured_mesh/test_mesh_hexes.exo differ diff --git a/tests/regression_tests/void/inputs_true.dat b/tests/regression_tests/void/inputs_true.dat index 6449520975..cd2d14206b 100644 --- a/tests/regression_tests/void/inputs_true.dat +++ b/tests/regression_tests/void/inputs_true.dat @@ -1,132 +1,131 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 1000 - 3 - - - 0.0 0.0 0.0 - - - - - - - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 - - - 1 - total - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 1000 + 3 + + + 0.0 0.0 0.0 + + + + + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 + + + 1 + total + + + diff --git a/tests/regression_tests/void/test.py b/tests/regression_tests/void/test.py index af1e3887b8..76c15eb39f 100644 --- a/tests/regression_tests/void/test.py +++ b/tests/regression_tests/void/test.py @@ -23,7 +23,7 @@ def model(): model.settings.run_mode = 'fixed source' model.settings.batches = 3 model.settings.particles = 1000 - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) cell_filter = openmc.CellFilter(cells) tally = openmc.Tally() diff --git a/tests/regression_tests/volume_calc/inputs_true.dat b/tests/regression_tests/volume_calc/inputs_true.dat index aaf6d8b005..ed7024c577 100644 --- a/tests/regression_tests/volume_calc/inputs_true.dat +++ b/tests/regression_tests/volume_calc/inputs_true.dat @@ -1,75 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - volume - - cell - 1 2 3 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - material - 1 2 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - universe - 0 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - cell - 1 2 3 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - - material - 1 2 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - - cell - 1 2 3 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + volume + + cell + 1 2 3 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + material + 1 2 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + universe + 0 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + material + 1 2 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + diff --git a/tests/regression_tests/volume_calc/inputs_true_mg.dat b/tests/regression_tests/volume_calc/inputs_true_mg.dat index 21c2a9834a..127566084e 100644 --- a/tests/regression_tests/volume_calc/inputs_true_mg.dat +++ b/tests/regression_tests/volume_calc/inputs_true_mg.dat @@ -1,76 +1,76 @@ - - - - - - - - - - - - - mg_lib.h5 - - - - - - - - - - - - - - - volume - multi-group - - cell - 1 2 3 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - material - 1 2 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - universe - 0 - 100000 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - cell - 1 2 3 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - - material - 1 2 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - - cell - 1 2 3 - 100 - -1.0 -1.0 -6.0 - 1.0 1.0 6.0 - - - + + + mg_lib.h5 + + + + + + + + + + + + + + + + + + + + + + + + volume + multi-group + + cell + 1 2 3 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + material + 1 2 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + universe + 0 + 100000 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + material + 1 2 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index e04eac7ff7..c94d16c797 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -21,7 +21,6 @@ class VolumeTest(PyAPITestHarness): if not is_ce: self.inputs_true = 'inputs_true_mg.dat' - def _build_inputs(self): # Define materials water = openmc.Material(1) water.add_nuclide('H1', 2.0) @@ -39,7 +38,7 @@ class VolumeTest(PyAPITestHarness): materials = openmc.Materials((water, fuel)) if not self.is_ce: materials.cross_sections = 'mg_lib.h5' - materials.export_to_xml() + self._model.materials = materials cyl = openmc.ZCylinder(surface_id=1, r=1.0, boundary_type='vacuum') top_sphere = openmc.Sphere(surface_id=2, z0=5., r=1., boundary_type='vacuum') @@ -53,8 +52,7 @@ class VolumeTest(PyAPITestHarness): bottom_hemisphere = openmc.Cell(3, fill=water, region=-bottom_sphere & -top_plane) root = openmc.Universe(0, cells=(inside_cyl, top_hemisphere, bottom_hemisphere)) - geometry = openmc.Geometry(root) - geometry.export_to_xml() + self._model.geometry = openmc.Geometry(root) # Set up stochastic volume calculation ll, ur = root.bounding_box @@ -79,7 +77,7 @@ class VolumeTest(PyAPITestHarness): if not self.is_ce: settings.energy_mode = 'multi-group' settings.volume_calculations = vol_calcs - settings.export_to_xml() + self._model.settings = settings # Create the MGXS file if necessary if not self.is_ce: @@ -170,5 +168,5 @@ class VolumeTest(PyAPITestHarness): @pytest.mark.parametrize('is_ce', [True, False]) def test_volume_calc(is_ce): - harness = VolumeTest(is_ce, '') + harness = VolumeTest(is_ce, '', model=openmc.Model()) harness.main() diff --git a/tests/regression_tests/weightwindows/generators/__init__.py b/tests/regression_tests/weightwindows/generators/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/weightwindows/generators/test.py b/tests/regression_tests/weightwindows/generators/test.py new file mode 100644 index 0000000000..4dc0ab80d3 --- /dev/null +++ b/tests/regression_tests/weightwindows/generators/test.py @@ -0,0 +1,61 @@ +import os + +import numpy as np +import openmc +import pytest + + +def test_ww_generator(run_in_tmpdir): + # create a simple spherical shield model + model = openmc.Model() + + water = openmc.Material() + water.set_density('g/cc', 1.0) + water.add_nuclide('H1', 0.66) + water.add_nuclide('O16', 0.34) + + s = openmc.Sphere(r=50, boundary_type='vacuum') + c = openmc.Cell(fill=water, region=-s) + + model.geometry = openmc.Geometry([c]) + + model.settings.particles = 500 + model.settings.batches = 5 + model.settings.run_mode = 'fixed source' + model.settings.max_history_splits = 100 + + mesh = openmc.RegularMesh.from_domain(model.geometry.root_universe) + energy_bounds = np.linspace(0.0, 1e6, 70) + particle = 'neutron' + + # include another tally to make sure user-specified tallies and those automaticaly + # created by weight window generators can coexist + tally = openmc.Tally() + ef = openmc.EnergyFilter(energy_bounds) + tally.filters = [ef] + tally.scores = ['flux'] + model.tallies = [tally] + + wwg = openmc.WeightWindowGenerator(mesh, energy_bounds, particle) + wwg.update_parameters = {'ratio': 5.0, 'threshold': 0.8, 'value': 'mean'} + + model.settings.weight_window_generators = wwg + model.run() + + # we test the effectiveness of the update method elsewhere, so + # just test that the generation happens successfully here + assert os.path.exists('weight_windows.h5') + + wws_mean = openmc.WeightWindowsList.from_hdf5() + assert len(wws_mean) == 1 + + # check that generation using the relative error works too + wwg.update_parameters['value'] = 'rel_err' + model.run() + + wws_rel_err = openmc.WeightWindowsList.from_hdf5() + assert len(wws_rel_err) == 1 + + # we should not get the same set of weight windows when switching to use of + # rel. err. + assert (wws_mean[0].lower_ww_bounds != wws_rel_err[0].lower_ww_bounds).any() diff --git a/tests/regression_tests/weightwindows/inputs_true.dat b/tests/regression_tests/weightwindows/inputs_true.dat index 13641bf7aa..eb9393179f 100644 --- a/tests/regression_tests/weightwindows/inputs_true.dat +++ b/tests/regression_tests/weightwindows/inputs_true.dat @@ -1,96 +1,95 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - fixed source - 200 - 2 - - - 0.001 0.001 0.001 - - - 14000000.0 1.0 - - - true - - 2 - neutron - 0.0 0.5 20000000.0 - -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 7.695031155172816e-14 -1.0 7.476545089278482e-06 -1.0 2.1612150425221703e-06 -1.0 -1.0 -1.0 2.1360401784344975e-18 -1.0 1.3455341306162382e-05 -1.0 3.353295403842037e-05 -1.0 3.0160758454323657e-07 -1.0 1.0262431550731535e-13 -1.0 1.6859219614058024e-19 -1.0 3.853097455417877e-06 -1.0 4.354946981329799e-05 -1.0 7.002861620919232e-08 -1.0 -1.0 -1.0 -1.0 -1.0 1.6205064883026195e-11 -1.0 1.7041669224797384e-09 -1.0 1.8747824667478637e-14 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 5.619835883512353e-14 -1.0 1.951356547084204e-15 -1.0 8.48176145671274e-10 -1.0 -1.0 -1.0 1.4508262073256659e-13 -1.0 4.109066205029878e-05 -1.0 0.00020573181450240786 -1.0 2.124999182004655e-05 -1.0 5.5379154134462336e-08 -1.0 1.5550455959178486e-06 -1.0 0.0007213722022489493 -1.0 0.007673145137236567 -1.0 0.0008175033526488423 -1.0 1.008394964750947e-06 -1.0 6.848642863465585e-07 -1.0 0.0010737081832502356 -1.0 0.007197662162700777 -1.0 0.0007151459162818186 -1.0 2.253382683081525e-06 -1.0 1.3181562352445092e-10 -1.0 1.93847081892941e-05 -1.0 0.00046029617115127556 -1.0 2.5629068330446215e-05 -1.0 5.504813693036933e-12 -1.0 -1.0 -1.0 3.3164825349503764e-16 -1.0 3.937160538176268e-07 -1.0 -1.0 -1.0 -1.0 -1.0 1.5172912057312398e-14 -1.0 6.315154082213375e-07 -1.0 3.855884234344845e-06 -1.0 1.7770136411255912e-05 -1.0 1.6421491993751715e-11 -1.0 5.97653814110781e-06 -1.0 0.001959411999677113 -1.0 0.013330619853379314 -1.0 0.0017950613169359904 -1.0 2.4306375693722205e-06 -1.0 6.922231352729155e-05 -1.0 0.08657573566388353 -1.0 -1.0 -1.0 0.0835648710074336 -1.0 0.00016419031150495913 -1.0 9.03108551826469e-05 -1.0 0.0848872967381878 -1.0 -1.0 -1.0 0.08288806458368345 -1.0 5.6949597066784976e-05 -1.0 8.037131813528501e-07 -1.0 0.0016637126543321873 -1.0 0.01664109232689093 -1.0 0.0018327593437608 -1.0 6.542700644645627e-07 -1.0 -1.0 -1.0 6.655926357459275e-08 -1.0 5.783974359937857e-06 -1.0 2.679340942769232e-06 -1.0 -1.0 -1.0 -1.0 -1.0 2.1040609012865134e-05 -1.0 2.9282097947816224e-05 -1.0 1.284759166582202e-05 -1.0 1.0092905083158804e-11 -1.0 1.3228013765525015e-05 -1.0 0.007136750029575816 -1.0 0.06539005235506369 -1.0 0.0064803314120165335 -1.0 2.6011787393916806e-06 -1.0 8.887234042637684e-05 -1.0 0.5 -1.0 -1.0 -1.0 0.4877856021170283 -1.0 0.00017699858357744463 -1.0 0.0001670700632870335 -1.0 0.4899999304038166 -1.0 -1.0 -1.0 0.48573842213878143 -1.0 0.00021534568699157805 -1.0 7.84289689494925e-06 -1.0 0.0063790654507599135 -1.0 0.06550426960512012 -1.0 0.006639379668946672 -1.0 7.754700849337902e-06 -1.0 2.7207433165796702e-11 -1.0 5.985108617124989e-06 -1.0 3.634644995026692e-05 -1.0 3.932389299316285e-06 -1.0 -1.0 -1.0 -1.0 -1.0 1.271128669411295e-07 -1.0 8.846493399850663e-06 -1.0 2.499931357628383e-07 -1.0 8.533030345699353e-17 -1.0 1.6699254277734109e-06 -1.0 0.0017541380096893788 -1.0 0.015148978428271613 -1.0 0.0016279794552427574 -1.0 2.380955631371226e-06 -1.0 2.1648918040467363e-05 -1.0 0.0833931797381589 -1.0 -1.0 -1.0 0.08285899874014539 -1.0 2.762955748645507e-05 -1.0 5.1161740516205715e-05 -1.0 0.08095280568305474 -1.0 -1.0 -1.0 0.08636569925236791 -1.0 3.452537179033895e-05 -1.0 3.5981031766416143e-06 -1.0 0.001570829313580841 -1.0 0.01621821782442112 -1.0 0.0015447823995470042 -1.0 7.989579285134734e-06 -1.0 -1.0 -1.0 2.594951085377588e-06 -1.0 4.323138781337963e-05 -1.0 6.948292975998466e-07 -1.0 -1.0 -1.0 -1.0 -1.0 2.4469456008493614e-09 -1.0 9.041149893307806e-07 -1.0 2.2060992580622125e-11 -1.0 -1.0 -1.0 1.681305446488884e-11 -1.0 7.091263906557291e-05 -1.0 0.00027464244062887683 -1.0 1.28584557092134e-05 -1.0 2.0295213621716706e-09 -1.0 1.2911827198500372e-08 -1.0 0.0009127050763987511 -1.0 0.007385384405891666 -1.0 0.0010019432401508087 -1.0 1.080711295768551e-05 -1.0 8.020767115181574e-07 -1.0 0.0009252532821720597 -1.0 0.007188410694198128 -1.0 0.0007245451610090619 -1.0 2.5157355790201773e-07 -1.0 1.4825219199133353e-12 -1.0 4.756872959630257e-05 -1.0 0.0002668579837809081 -1.0 2.8226276944532696e-05 -1.0 5.182269672663266e-10 -1.0 -1.0 -1.0 2.936268747135548e-14 -1.0 2.5447281241730366e-07 -1.0 3.89841667138598e-08 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 2.639268952045579e-10 -1.0 3.7051518220083886e-06 -1.0 1.1333627604823673e-09 -1.0 -1.0 -1.0 -1.0 -1.0 1.2296747260635405e-06 -1.0 7.962899789920809e-06 -1.0 8.211982683649991e-09 -1.0 -1.0 -1.0 -1.0 -1.0 2.5639656866250453e-06 -1.0 2.2487617828938326e-05 -1.0 3.2799045883372075e-06 -1.0 4.076762579823379e-17 -1.0 1.5476769237571295e-14 -1.0 1.0305672698745657e-11 -1.0 1.957092814065688e-05 -1.0 3.8523247550378815e-12 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 2.7697233171399416e-13 -1.0 -1.0 -1.0 -1.0 -1.0 - -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 7.695031155172816e-13 -10.0 7.476545089278483e-05 -10.0 2.1612150425221703e-05 -10.0 -10.0 -10.0 2.1360401784344976e-17 -10.0 0.00013455341306162382 -10.0 0.00033532954038420373 -10.0 3.0160758454323656e-06 -10.0 1.0262431550731535e-12 -10.0 1.6859219614058023e-18 -10.0 3.853097455417877e-05 -10.0 0.00043549469813297995 -10.0 7.002861620919232e-07 -10.0 -10.0 -10.0 -10.0 -10.0 1.6205064883026195e-10 -10.0 1.7041669224797384e-08 -10.0 1.8747824667478637e-13 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 5.619835883512353e-13 -10.0 1.951356547084204e-14 -10.0 8.48176145671274e-09 -10.0 -10.0 -10.0 1.4508262073256658e-12 -10.0 0.0004109066205029878 -10.0 0.0020573181450240785 -10.0 0.00021249991820046548 -10.0 5.537915413446234e-07 -10.0 1.5550455959178486e-05 -10.0 0.0072137220224894934 -10.0 0.07673145137236567 -10.0 0.008175033526488424 -10.0 1.0083949647509469e-05 -10.0 6.848642863465585e-06 -10.0 0.010737081832502356 -10.0 0.07197662162700777 -10.0 0.007151459162818186 -10.0 2.253382683081525e-05 -10.0 1.3181562352445092e-09 -10.0 0.000193847081892941 -10.0 0.004602961711512756 -10.0 0.00025629068330446217 -10.0 5.504813693036933e-11 -10.0 -10.0 -10.0 3.3164825349503764e-15 -10.0 3.937160538176268e-06 -10.0 -10.0 -10.0 -10.0 -10.0 1.51729120573124e-13 -10.0 6.3151540822133754e-06 -10.0 3.855884234344845e-05 -10.0 0.0001777013641125591 -10.0 1.6421491993751715e-10 -10.0 5.97653814110781e-05 -10.0 0.01959411999677113 -10.0 0.13330619853379314 -10.0 0.017950613169359905 -10.0 2.4306375693722206e-05 -10.0 0.0006922231352729155 -10.0 0.8657573566388354 -10.0 -10.0 -10.0 0.8356487100743359 -10.0 0.0016419031150495913 -10.0 0.000903108551826469 -10.0 0.848872967381878 -10.0 -10.0 -10.0 0.8288806458368345 -10.0 0.0005694959706678497 -10.0 8.037131813528501e-06 -10.0 0.016637126543321872 -10.0 0.1664109232689093 -10.0 0.018327593437608 -10.0 6.5427006446456266e-06 -10.0 -10.0 -10.0 6.655926357459275e-07 -10.0 5.7839743599378564e-05 -10.0 2.679340942769232e-05 -10.0 -10.0 -10.0 -10.0 -10.0 0.00021040609012865135 -10.0 0.00029282097947816227 -10.0 0.0001284759166582202 -10.0 1.0092905083158804e-10 -10.0 0.00013228013765525016 -10.0 0.07136750029575815 -10.0 0.6539005235506369 -10.0 0.06480331412016534 -10.0 2.6011787393916804e-05 -10.0 0.0008887234042637684 -10.0 5.0 -10.0 -10.0 -10.0 4.877856021170283 -10.0 0.0017699858357744464 -10.0 0.001670700632870335 -10.0 4.899999304038166 -10.0 -10.0 -10.0 4.8573842213878144 -10.0 0.0021534568699157807 -10.0 7.84289689494925e-05 -10.0 0.06379065450759913 -10.0 0.6550426960512012 -10.0 0.06639379668946672 -10.0 7.754700849337902e-05 -10.0 2.7207433165796704e-10 -10.0 5.985108617124989e-05 -10.0 0.0003634644995026692 -10.0 3.9323892993162844e-05 -10.0 -10.0 -10.0 -10.0 -10.0 1.271128669411295e-06 -10.0 8.846493399850663e-05 -10.0 2.499931357628383e-06 -10.0 8.533030345699353e-16 -10.0 1.669925427773411e-05 -10.0 0.017541380096893787 -10.0 0.15148978428271614 -10.0 0.016279794552427576 -10.0 2.380955631371226e-05 -10.0 0.00021648918040467362 -10.0 0.8339317973815891 -10.0 -10.0 -10.0 0.828589987401454 -10.0 0.0002762955748645507 -10.0 0.0005116174051620571 -10.0 0.8095280568305474 -10.0 -10.0 -10.0 0.8636569925236791 -10.0 0.0003452537179033895 -10.0 3.5981031766416144e-05 -10.0 0.015708293135808408 -10.0 0.16218217824421122 -10.0 0.015447823995470043 -10.0 7.989579285134734e-05 -10.0 -10.0 -10.0 2.594951085377588e-05 -10.0 0.0004323138781337963 -10.0 6.948292975998466e-06 -10.0 -10.0 -10.0 -10.0 -10.0 2.4469456008493615e-08 -10.0 9.041149893307805e-06 -10.0 2.2060992580622125e-10 -10.0 -10.0 -10.0 1.681305446488884e-10 -10.0 0.0007091263906557291 -10.0 0.002746424406288768 -10.0 0.000128584557092134 -10.0 2.0295213621716707e-08 -10.0 1.2911827198500372e-07 -10.0 0.009127050763987512 -10.0 0.07385384405891665 -10.0 0.010019432401508087 -10.0 0.0001080711295768551 -10.0 8.020767115181574e-06 -10.0 0.009252532821720597 -10.0 0.07188410694198127 -10.0 0.0072454516100906195 -10.0 2.515735579020177e-06 -10.0 1.4825219199133352e-11 -10.0 0.0004756872959630257 -10.0 0.0026685798378090807 -10.0 0.000282262769445327 -10.0 5.182269672663266e-09 -10.0 -10.0 -10.0 2.9362687471355483e-13 -10.0 2.5447281241730365e-06 -10.0 3.89841667138598e-07 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 2.639268952045579e-09 -10.0 3.7051518220083885e-05 -10.0 1.1333627604823672e-08 -10.0 -10.0 -10.0 -10.0 -10.0 1.2296747260635405e-05 -10.0 7.96289978992081e-05 -10.0 8.211982683649991e-08 -10.0 -10.0 -10.0 -10.0 -10.0 2.5639656866250452e-05 -10.0 0.00022487617828938325 -10.0 3.2799045883372076e-05 -10.0 4.076762579823379e-16 -10.0 1.5476769237571296e-13 -10.0 1.0305672698745658e-10 -10.0 0.0001957092814065688 -10.0 3.852324755037882e-11 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 2.7697233171399415e-12 -10.0 -10.0 -10.0 -10.0 -10.0 - 3 - 1.5 - 10 - 1e-38 - - - 5 6 7 - -240 -240 -240 - 240 240 240 - - - 2 - neutron - 0.0 0.5 20000000.0 - -1.0 -1.0 -1.0 5.447493368067179e-10 -1.0 4.0404626147344e-13 -1.0 9.382800796426366e-17 -1.0 -1.0 -1.0 5.752120832330886e-11 -1.0 3.520674728719278e-05 -1.0 8.76041935303527e-05 -1.0 3.136829920007009e-05 -1.0 -1.0 -1.0 4.233410073487772e-06 -1.0 0.00025519632243117644 -1.0 0.0004777967757842694 -1.0 0.00035983547999740665 -1.0 4.6258592060517866e-07 -1.0 4.587143797469485e-08 -1.0 0.00012918433677593975 -1.0 0.000469589568463265 -1.0 8.862760180332873e-05 -1.0 3.2790914744011534e-06 -1.0 1.1514579117272292e-10 -1.0 2.2950716170641463e-05 -1.0 6.92296299242814e-05 -1.0 2.1606550732170693e-05 -1.0 2.617878055106949e-16 -1.0 -1.0 -1.0 -1.0 -1.0 1.722191123879782e-08 -1.0 -1.0 -1.0 -1.0 -1.0 2.1234980618763222e-13 -1.0 1.4628847959717225e-05 -1.0 2.777791741571174e-05 -1.0 9.53380658669839e-06 -1.0 1.078687446476881e-14 -1.0 3.0571050855707084e-06 -1.0 0.0008231482263927564 -1.0 0.0038811025614225057 -1.0 0.0012297303081805393 -1.0 4.6842350882367666e-05 -1.0 0.00016757997590048827 -1.0 0.011724714376848187 -1.0 0.06827568359008944 -1.0 0.011440381012864086 -1.0 0.0003782322077390033 -1.0 5.098747893974248e-05 -1.0 0.01208261392046878 -1.0 0.06428782790571179 -1.0 0.011294708566804167 -1.0 8.168081568291806e-05 -1.0 2.34750482787598e-05 -1.0 0.0008787411098754915 -1.0 0.0051513292132259495 -1.0 0.0011028020390507682 -1.0 1.0882011018684852e-06 -1.0 1.0588257623722672e-07 -1.0 2.7044817176109556e-05 -1.0 2.6211239246796104e-05 -1.0 2.0707648529746894e-06 -1.0 -1.0 -1.0 1.3806577785135177e-06 -1.0 0.00010077009726691265 -1.0 0.00043130977711323417 -1.0 0.0001335621430311088 -1.0 3.792209399652396e-06 -1.0 0.00022057970761624768 -1.0 0.01884234453311008 -1.0 0.11510120013926417 -1.0 0.01825795827020865 -1.0 0.00020848309220162036 -1.0 0.0013332554437480327 -1.0 0.49317081643076643 -1.0 -1.0 -1.0 0.4899952354672477 -1.0 0.0014957033281168012 -1.0 0.001550036616828061 -1.0 0.4897735047310072 -1.0 -1.0 -1.0 0.5 -1.0 0.0011076796092102186 -1.0 0.0002792896409558745 -1.0 0.01721806295736377 -1.0 0.11891251244906934 -1.0 0.019040707071165303 -1.0 0.00017329528628152923 -1.0 4.670673837764874e-08 -1.0 0.00012637534695610975 -1.0 0.00027052054298385255 -1.0 0.0001497958445994315 -1.0 3.2478301680021854e-08 -1.0 7.239087311622819e-08 -1.0 0.00046693784586018913 -1.0 0.001537590300149361 -1.0 0.00023686818807101741 -1.0 8.616027294555462e-07 -1.0 0.0002362355168421952 -1.0 0.05708498338352203 -1.0 0.42372713683725016 -1.0 0.054902816619122045 -1.0 0.00030002326388383244 -1.0 0.0023295401596005313 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 0.0027847329307350423 -1.0 0.003432919201293737 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 0.002672923773363389 -1.0 0.0003182108781955478 -1.0 0.05639071613735579 -1.0 0.4346984808472855 -1.0 0.05500108683629957 -1.0 0.00024175998138228048 -1.0 2.774067049392004e-07 -1.0 0.0003257646134837451 -1.0 0.0012648333883989995 -1.0 0.0002981891705231209 -1.0 4.986698316296507e-06 -1.0 3.419181071650816e-06 -1.0 0.00010814452764860558 -1.0 0.0006934466049804711 -1.0 0.00025106741008066317 -1.0 5.515522196186723e-06 -1.0 0.00014242053195529864 -1.0 0.018541112907970003 -1.0 0.12364069326350118 -1.0 0.017602971074583987 -1.0 0.00021305826462238593 -1.0 0.0015016500045374082 -1.0 0.4903495510314233 -1.0 -1.0 -1.0 0.4952432605379829 -1.0 0.0017693279949094218 -1.0 0.0009304497896205121 -1.0 0.4873897581604287 -1.0 -1.0 -1.0 0.4981497862127628 -1.0 0.0011273340972061078 -1.0 0.00014126153603265097 -1.0 0.01729816946709781 -1.0 0.11819165666519223 -1.0 0.017802624844035688 -1.0 0.0001319677450153934 -1.0 2.210707325798542e-07 -1.0 0.00017287873387910357 -1.0 0.0005197081842709918 -1.0 9.256616392487858e-05 -1.0 1.0325207105027347e-08 -1.0 2.027381220364608e-16 -1.0 1.2257360246712472e-05 -1.0 3.8561696337086434e-05 -1.0 1.8064550990294753e-05 -1.0 7.691527781829036e-17 -1.0 1.298062961617927e-05 -1.0 0.001178616843705956 -1.0 0.004441328419270369 -1.0 0.0008919466185572302 -1.0 5.272021975060514e-06 -1.0 0.00016079768514218546 -1.0 0.011128424196387618 -1.0 0.06433819434431046 -1.0 0.010955637485319249 -1.0 8.370896020975689e-05 -1.0 0.00022234471595443313 -1.0 0.01102706140784316 -1.0 0.06684484191345574 -1.0 0.011113024641218336 -1.0 0.00012558879474108074 -1.0 1.0044390728858277e-05 -1.0 0.000808100901275472 -1.0 0.005384625561469684 -1.0 0.001146714816952061 -1.0 3.5740607788414942e-06 -1.0 2.2512327083641865e-10 -1.0 6.717625881826167e-05 -1.0 6.949587369646497e-05 -1.0 2.9093345975075226e-05 -1.0 -1.0 -1.0 -1.0 -1.0 8.582011716859219e-15 -1.0 8.304845400451923e-08 -1.0 4.5757238372058234e-11 -1.0 -1.0 -1.0 1.3116807133030035e-15 -1.0 2.6866894852481906e-05 -1.0 4.3166052799897375e-05 -1.0 6.401055165502384e-06 -1.0 5.726152701849347e-16 -1.0 8.541606727879512e-07 -1.0 0.0002029396545382813 -1.0 0.0004299195570308035 -1.0 8.544159282151438e-05 -1.0 1.0995322456277382e-06 -1.0 1.289814511028015e-06 -1.0 0.00011042047857307253 -1.0 0.0006870228272606287 -1.0 0.0001399807456616496 -1.0 4.279460329782799e-09 -1.0 1.2907171695294846e-13 -1.0 1.9257248009114363e-05 -1.0 9.332371598523186e-05 -1.0 1.3106335518133802e-05 -1.0 6.574706543646946e-16 -1.0 -1.0 -1.0 3.8270536025799805e-15 -1.0 2.101790478097517e-09 -1.0 9.904481358675478e-13 -1.0 -1.0 - -10.0 -10.0 -10.0 5.447493368067179e-09 -10.0 4.0404626147344005e-12 -10.0 9.382800796426367e-16 -10.0 -10.0 -10.0 5.752120832330886e-10 -10.0 0.0003520674728719278 -10.0 0.0008760419353035269 -10.0 0.0003136829920007009 -10.0 -10.0 -10.0 4.233410073487772e-05 -10.0 0.0025519632243117644 -10.0 0.004777967757842694 -10.0 0.0035983547999740664 -10.0 4.625859206051786e-06 -10.0 4.587143797469485e-07 -10.0 0.0012918433677593976 -10.0 0.0046958956846326495 -10.0 0.0008862760180332873 -10.0 3.279091474401153e-05 -10.0 1.151457911727229e-09 -10.0 0.00022950716170641464 -10.0 0.0006922962992428139 -10.0 0.00021606550732170693 -10.0 2.6178780551069492e-15 -10.0 -10.0 -10.0 -10.0 -10.0 1.722191123879782e-07 -10.0 -10.0 -10.0 -10.0 -10.0 2.123498061876322e-12 -10.0 0.00014628847959717226 -10.0 0.0002777791741571174 -10.0 9.53380658669839e-05 -10.0 1.078687446476881e-13 -10.0 3.0571050855707086e-05 -10.0 0.008231482263927564 -10.0 0.03881102561422506 -10.0 0.012297303081805393 -10.0 0.00046842350882367664 -10.0 0.0016757997590048828 -10.0 0.11724714376848187 -10.0 0.6827568359008944 -10.0 0.11440381012864086 -10.0 0.003782322077390033 -10.0 0.0005098747893974248 -10.0 0.1208261392046878 -10.0 0.6428782790571179 -10.0 0.11294708566804167 -10.0 0.0008168081568291806 -10.0 0.000234750482787598 -10.0 0.008787411098754914 -10.0 0.0515132921322595 -10.0 0.011028020390507681 -10.0 1.0882011018684853e-05 -10.0 1.0588257623722672e-06 -10.0 0.00027044817176109554 -10.0 0.000262112392467961 -10.0 2.0707648529746893e-05 -10.0 -10.0 -10.0 1.3806577785135176e-05 -10.0 0.0010077009726691265 -10.0 0.004313097771132342 -10.0 0.001335621430311088 -10.0 3.792209399652396e-05 -10.0 0.0022057970761624767 -10.0 0.1884234453311008 -10.0 1.1510120013926417 -10.0 0.1825795827020865 -10.0 0.0020848309220162036 -10.0 0.013332554437480326 -10.0 4.931708164307665 -10.0 -10.0 -10.0 4.899952354672477 -10.0 0.014957033281168012 -10.0 0.01550036616828061 -10.0 4.897735047310072 -10.0 -10.0 -10.0 5.0 -10.0 0.011076796092102187 -10.0 0.002792896409558745 -10.0 0.17218062957363772 -10.0 1.1891251244906933 -10.0 0.19040707071165303 -10.0 0.0017329528628152924 -10.0 4.670673837764874e-07 -10.0 0.0012637534695610975 -10.0 0.0027052054298385255 -10.0 0.001497958445994315 -10.0 3.2478301680021856e-07 -10.0 7.239087311622819e-07 -10.0 0.004669378458601891 -10.0 0.01537590300149361 -10.0 0.002368681880710174 -10.0 8.61602729455546e-06 -10.0 0.002362355168421952 -10.0 0.5708498338352204 -10.0 4.237271368372502 -10.0 0.5490281661912204 -10.0 0.0030002326388383245 -10.0 0.023295401596005315 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 0.027847329307350423 -10.0 0.03432919201293737 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 0.026729237733633893 -10.0 0.0031821087819554777 -10.0 0.5639071613735579 -10.0 4.346984808472855 -10.0 0.5500108683629957 -10.0 0.0024175998138228046 -10.0 2.774067049392004e-06 -10.0 0.0032576461348374514 -10.0 0.012648333883989995 -10.0 0.002981891705231209 -10.0 4.986698316296507e-05 -10.0 3.419181071650816e-05 -10.0 0.0010814452764860557 -10.0 0.006934466049804711 -10.0 0.0025106741008066318 -10.0 5.515522196186723e-05 -10.0 0.0014242053195529865 -10.0 0.18541112907970003 -10.0 1.2364069326350118 -10.0 0.17602971074583987 -10.0 0.0021305826462238594 -10.0 0.015016500045374082 -10.0 4.903495510314233 -10.0 -10.0 -10.0 4.952432605379829 -10.0 0.01769327994909422 -10.0 0.00930449789620512 -10.0 4.873897581604287 -10.0 -10.0 -10.0 4.981497862127628 -10.0 0.011273340972061077 -10.0 0.0014126153603265096 -10.0 0.1729816946709781 -10.0 1.1819165666519222 -10.0 0.1780262484403569 -10.0 0.001319677450153934 -10.0 2.210707325798542e-06 -10.0 0.0017287873387910357 -10.0 0.0051970818427099184 -10.0 0.0009256616392487858 -10.0 1.0325207105027347e-07 -10.0 2.027381220364608e-15 -10.0 0.00012257360246712473 -10.0 0.00038561696337086434 -10.0 0.00018064550990294753 -10.0 7.6915277818290365e-16 -10.0 0.00012980629616179268 -10.0 0.01178616843705956 -10.0 0.04441328419270369 -10.0 0.008919466185572301 -10.0 5.272021975060514e-05 -10.0 0.0016079768514218546 -10.0 0.11128424196387618 -10.0 0.6433819434431045 -10.0 0.10955637485319249 -10.0 0.0008370896020975689 -10.0 0.0022234471595443312 -10.0 0.1102706140784316 -10.0 0.6684484191345574 -10.0 0.11113024641218336 -10.0 0.0012558879474108074 -10.0 0.00010044390728858278 -10.0 0.00808100901275472 -10.0 0.05384625561469684 -10.0 0.01146714816952061 -10.0 3.574060778841494e-05 -10.0 2.2512327083641864e-09 -10.0 0.0006717625881826168 -10.0 0.0006949587369646498 -10.0 0.0002909334597507523 -10.0 -10.0 -10.0 -10.0 -10.0 8.582011716859219e-14 -10.0 8.304845400451923e-07 -10.0 4.5757238372058235e-10 -10.0 -10.0 -10.0 1.3116807133030034e-14 -10.0 0.00026866894852481903 -10.0 0.00043166052799897375 -10.0 6.401055165502385e-05 -10.0 5.726152701849347e-15 -10.0 8.541606727879512e-06 -10.0 0.0020293965453828133 -10.0 0.004299195570308035 -10.0 0.0008544159282151438 -10.0 1.0995322456277382e-05 -10.0 1.289814511028015e-05 -10.0 0.0011042047857307254 -10.0 0.006870228272606287 -10.0 0.0013998074566164962 -10.0 4.279460329782799e-08 -10.0 1.2907171695294846e-12 -10.0 0.00019257248009114364 -10.0 0.0009332371598523186 -10.0 0.000131063355181338 -10.0 6.574706543646946e-15 -10.0 -10.0 -10.0 3.8270536025799805e-14 -10.0 2.101790478097517e-08 -10.0 9.904481358675478e-12 -10.0 -10.0 - 3 - 1.5 - 10 - 1e-38 - - 200 - - - - - 5 10 15 - -240 -240 -240 - 240 240 240 - - - 1 - - - 0.0 0.5 20000000.0 - - - neutron photon - - - 1 2 3 - flux - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fixed source + 200 + 2 + + + 0.001 0.001 0.001 + + + 14000000.0 1.0 + + + true + + 2 + neutron + 0.0 0.5 20000000.0 + -1.0 0.007673145137236567 6.542700644645627e-07 0.0017541380096893788 0.0007245451610090619 7.476545089278482e-06 2.253382683081525e-06 2.1040609012865134e-05 0.08285899874014539 -1.0 1.0262431550731535e-13 3.3164825349503764e-16 0.0064803314120165335 3.5981031766416143e-06 -1.0 1.6205064883026195e-11 1.7770136411255912e-05 0.0001670700632870335 4.323138781337963e-05 -1.0 -1.0 6.922231352729155e-05 0.06550426960512012 -1.0 2.5639656866250453e-06 1.4508262073256659e-13 -1.0 -1.0 0.0009127050763987511 3.8523247550378815e-12 -1.0 0.0008175033526488423 -1.0 0.015148978428271613 2.5157355790201773e-07 2.1612150425221703e-06 1.3181562352445092e-10 2.9282097947816224e-05 2.762955748645507e-05 2.936268747135548e-14 1.6859219614058024e-19 3.937160538176268e-07 2.6011787393916806e-06 0.001570829313580841 -1.0 1.7041669224797384e-09 1.6421491993751715e-11 0.4899999304038166 6.948292975998466e-07 -1.0 -1.0 0.08657573566388353 0.006639379668946672 1.681305446488884e-11 2.2487617828938326e-05 4.109066205029878e-05 0.08288806458368345 -1.0 0.007385384405891666 -1.0 -1.0 1.008394964750947e-06 6.655926357459275e-08 0.0016279794552427574 1.4825219199133353e-12 -1.0 1.93847081892941e-05 1.284759166582202e-05 5.1161740516205715e-05 2.5447281241730366e-07 3.853097455417877e-06 -1.0 8.887234042637684e-05 0.01621821782442112 -1.0 1.8747824667478637e-14 5.97653814110781e-06 -1.0 -1.0 1.2296747260635405e-06 -1.0 -1.0 7.754700849337902e-06 7.091263906557291e-05 3.2799045883372075e-06 0.00020573181450240786 5.6949597066784976e-05 1.271128669411295e-07 0.0010019432401508087 -1.0 -1.0 6.848642863465585e-07 5.783974359937857e-06 2.380955631371226e-06 4.756872959630257e-05 2.1360401784344975e-18 0.00046029617115127556 1.0092905083158804e-11 0.08095280568305474 3.89841667138598e-08 4.354946981329799e-05 -1.0 0.5 0.0015447823995470042 -1.0 -1.0 0.001959411999677113 0.48573842213878143 -1.0 7.962899789920809e-06 5.619835883512353e-14 0.0835648710074336 2.7207433165796702e-11 0.00027464244062887683 4.076762579823379e-17 2.124999182004655e-05 8.037131813528501e-07 8.846493399850663e-06 1.080711295768551e-05 -1.0 -1.0 0.0010737081832502356 2.679340942769232e-06 2.1648918040467363e-05 0.0002668579837809081 1.3455341306162382e-05 2.5629068330446215e-05 1.3228013765525015e-05 -1.0 -1.0 7.002861620919232e-08 1.5172912057312398e-14 -1.0 7.989579285134734e-06 2.639268952045579e-10 -1.0 0.013330619853379314 0.00021534568699157805 2.4469456008493614e-09 8.211982683649991e-09 1.951356547084204e-15 0.00016419031150495913 5.985108617124989e-06 1.28584557092134e-05 1.5476769237571295e-14 5.5379154134462336e-08 0.0016637126543321873 2.499931357628383e-07 8.020767115181574e-07 2.7697233171399416e-13 -1.0 0.007197662162700777 -1.0 0.0833931797381589 2.8226276944532696e-05 3.353295403842037e-05 5.504813693036933e-12 0.007136750029575816 0.08636569925236791 -1.0 -1.0 6.315154082213375e-07 0.4877856021170283 -1.0 3.7051518220083886e-06 -1.0 0.0017950613169359904 7.84289689494925e-06 9.041149893307806e-07 -1.0 8.48176145671274e-10 9.03108551826469e-05 3.634644995026692e-05 2.0295213621716706e-09 1.0305672698745657e-11 1.5550455959178486e-06 0.01664109232689093 8.533030345699353e-17 0.0009252532821720597 -1.0 7.695031155172816e-14 0.0007151459162818186 -1.0 -1.0 5.182269672663266e-10 3.0160758454323657e-07 -1.0 0.06539005235506369 3.452537179033895e-05 -1.0 -1.0 3.855884234344845e-06 0.00017699858357744463 2.594951085377588e-06 1.1333627604823673e-09 -1.0 2.4306375693722205e-06 0.0063790654507599135 2.2060992580622125e-11 -1.0 -1.0 0.0848872967381878 3.932389299316285e-06 1.2911827198500372e-08 1.957092814065688e-05 0.0007213722022489493 0.0018327593437608 1.6699254277734109e-06 0.007188410694198128 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 + -10.0 0.07673145137236567 6.5427006446456266e-06 0.017541380096893787 0.0072454516100906195 7.476545089278483e-05 2.253382683081525e-05 0.00021040609012865135 0.828589987401454 -10.0 1.0262431550731535e-12 3.3164825349503764e-15 0.06480331412016534 3.5981031766416144e-05 -10.0 1.6205064883026195e-10 0.0001777013641125591 0.001670700632870335 0.0004323138781337963 -10.0 -10.0 0.0006922231352729155 0.6550426960512012 -10.0 2.5639656866250452e-05 1.4508262073256658e-12 -10.0 -10.0 0.009127050763987512 3.852324755037882e-11 -10.0 0.008175033526488424 -10.0 0.15148978428271614 2.515735579020177e-06 2.1612150425221703e-05 1.3181562352445092e-09 0.00029282097947816227 0.0002762955748645507 2.9362687471355483e-13 1.6859219614058023e-18 3.937160538176268e-06 2.6011787393916804e-05 0.015708293135808408 -10.0 1.7041669224797384e-08 1.6421491993751715e-10 4.899999304038166 6.948292975998466e-06 -10.0 -10.0 0.8657573566388354 0.06639379668946672 1.681305446488884e-10 0.00022487617828938325 0.0004109066205029878 0.8288806458368345 -10.0 0.07385384405891665 -10.0 -10.0 1.0083949647509469e-05 6.655926357459275e-07 0.016279794552427576 1.4825219199133352e-11 -10.0 0.000193847081892941 0.0001284759166582202 0.0005116174051620571 2.5447281241730365e-06 3.853097455417877e-05 -10.0 0.0008887234042637684 0.16218217824421122 -10.0 1.8747824667478637e-13 5.97653814110781e-05 -10.0 -10.0 1.2296747260635405e-05 -10.0 -10.0 7.754700849337902e-05 0.0007091263906557291 3.2799045883372076e-05 0.0020573181450240785 0.0005694959706678497 1.271128669411295e-06 0.010019432401508087 -10.0 -10.0 6.848642863465585e-06 5.7839743599378564e-05 2.380955631371226e-05 0.0004756872959630257 2.1360401784344976e-17 0.004602961711512756 1.0092905083158804e-10 0.8095280568305474 3.89841667138598e-07 0.00043549469813297995 -10.0 5.0 0.015447823995470043 -10.0 -10.0 0.01959411999677113 4.8573842213878144 -10.0 7.96289978992081e-05 5.619835883512353e-13 0.8356487100743359 2.7207433165796704e-10 0.002746424406288768 4.076762579823379e-16 0.00021249991820046548 8.037131813528501e-06 8.846493399850663e-05 0.0001080711295768551 -10.0 -10.0 0.010737081832502356 2.679340942769232e-05 0.00021648918040467362 0.0026685798378090807 0.00013455341306162382 0.00025629068330446217 0.00013228013765525016 -10.0 -10.0 7.002861620919232e-07 1.51729120573124e-13 -10.0 7.989579285134734e-05 2.639268952045579e-09 -10.0 0.13330619853379314 0.0021534568699157807 2.4469456008493615e-08 8.211982683649991e-08 1.951356547084204e-14 0.0016419031150495913 5.985108617124989e-05 0.000128584557092134 1.5476769237571296e-13 5.537915413446234e-07 0.016637126543321872 2.499931357628383e-06 8.020767115181574e-06 2.7697233171399415e-12 -10.0 0.07197662162700777 -10.0 0.8339317973815891 0.000282262769445327 0.00033532954038420373 5.504813693036933e-11 0.07136750029575815 0.8636569925236791 -10.0 -10.0 6.3151540822133754e-06 4.877856021170283 -10.0 3.7051518220083885e-05 -10.0 0.017950613169359905 7.84289689494925e-05 9.041149893307805e-06 -10.0 8.48176145671274e-09 0.000903108551826469 0.0003634644995026692 2.0295213621716707e-08 1.0305672698745658e-10 1.5550455959178486e-05 0.1664109232689093 8.533030345699353e-16 0.009252532821720597 -10.0 7.695031155172816e-13 0.007151459162818186 -10.0 -10.0 5.182269672663266e-09 3.0160758454323656e-06 -10.0 0.6539005235506369 0.0003452537179033895 -10.0 -10.0 3.855884234344845e-05 0.0017699858357744464 2.594951085377588e-05 1.1333627604823672e-08 -10.0 2.4306375693722206e-05 0.06379065450759913 2.2060992580622125e-10 -10.0 -10.0 0.848872967381878 3.9323892993162844e-05 1.2911827198500372e-07 0.0001957092814065688 0.0072137220224894934 0.018327593437608 1.669925427773411e-05 0.07188410694198127 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 + 3.0 + 1.5 + 10 + 1e-38 + + + 5 6 7 + -240 -240 -240 + 240 240 240 + + + 2 + neutron + 0.0 0.5 20000000.0 + -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 0.06827568359008944 0.00017329528628152923 0.018541112907970003 0.011113024641218336 8.76041935303527e-05 8.168081568291806e-05 0.00046693784586018913 0.4952432605379829 2.2512327083641865e-10 4.6258592060517866e-07 2.7044817176109556e-05 0.054902816619122045 0.00014126153603265097 8.304845400451923e-08 2.2950716170641463e-05 0.0001335621430311088 0.003432919201293737 0.0005197081842709918 5.726152701849347e-16 -1.0 0.0013332554437480327 0.4346984808472855 7.691527781829036e-17 0.00011042047857307253 3.0571050855707084e-06 -1.0 4.986698316296507e-06 0.011128424196387618 1.3106335518133802e-05 5.447493368067179e-10 0.011440381012864086 4.670673837764874e-08 0.12364069326350118 0.00012558879474108074 3.136829920007009e-05 2.34750482787598e-05 0.001537590300149361 0.0017693279949094218 6.717625881826167e-05 4.587143797469485e-08 2.6211239246796104e-05 0.00030002326388383244 0.01729816946709781 4.5757238372058234e-11 6.92296299242814e-05 3.792209399652396e-06 -1.0 9.256616392487858e-05 8.541606727879512e-07 -1.0 0.49317081643076643 0.05500108683629957 1.298062961617927e-05 0.0006870228272606287 0.0008231482263927564 0.5 3.419181071650816e-06 0.06433819434431046 6.574706543646946e-16 4.0404626147344e-13 0.0003782322077390033 0.00012637534695610975 0.017602971074583987 1.0044390728858277e-05 -1.0 0.0008787411098754915 0.00023686818807101741 0.0009304497896205121 6.949587369646497e-05 0.00012918433677593975 2.0707648529746894e-06 0.0023295401596005313 0.11819165666519223 -1.0 2.1606550732170693e-05 0.00022057970761624768 -1.0 1.0325207105027347e-08 0.0002029396545382813 2.1234980618763222e-13 -1.0 0.00024175998138228048 0.001178616843705956 0.0001399807456616496 0.0038811025614225057 0.0011076796092102186 0.00010814452764860558 0.010955637485319249 -1.0 9.382800796426366e-17 5.098747893974248e-05 0.00027052054298385255 0.00021305826462238593 0.000808100901275472 4.233410073487772e-06 0.0051513292132259495 8.616027294555462e-07 0.4873897581604287 2.9093345975075226e-05 0.000469589568463265 -1.0 -1.0 0.017802624844035688 1.3116807133030035e-15 2.617878055106949e-16 0.01884234453311008 -1.0 2.027381220364608e-16 0.0004299195570308035 1.4628847959717225e-05 0.4899952354672477 2.774067049392004e-07 0.004441328419270369 4.279460329782799e-09 0.0012297303081805393 0.0002792896409558745 0.0006934466049804711 8.370896020975689e-05 3.8270536025799805e-15 -1.0 0.01208261392046878 0.0001497958445994315 0.0015016500045374082 0.005384625561469684 0.00025519632243117644 0.0011028020390507682 0.0002362355168421952 -1.0 -1.0 8.862760180332873e-05 1.3806577785135177e-06 -1.0 0.0001319677450153934 2.6866894852481906e-05 -1.0 0.11510120013926417 0.002672923773363389 1.2257360246712472e-05 8.544159282151438e-05 2.777791741571174e-05 0.0014957033281168012 0.0003257646134837451 0.0008919466185572302 1.2907171695294846e-13 4.6842350882367666e-05 0.01721806295736377 0.00025106741008066317 0.00022234471595443313 2.101790478097517e-09 5.752120832330886e-11 0.06428782790571179 3.2478301680021854e-08 0.4903495510314233 0.001146714816952061 0.0004777967757842694 1.0882011018684852e-06 0.05708498338352203 0.4981497862127628 -1.0 3.2790914744011534e-06 0.00010077009726691265 -1.0 2.210707325798542e-07 4.3166052799897375e-05 -1.0 0.01825795827020865 0.0003182108781955478 3.8561696337086434e-05 1.0995322456277382e-06 9.53380658669839e-06 0.001550036616828061 0.0012648333883989995 5.272021975060514e-06 1.9257248009114363e-05 0.00016757997590048827 0.11891251244906934 5.515522196186723e-06 0.01102706140784316 9.904481358675478e-13 3.520674728719278e-05 0.011294708566804167 7.239087311622819e-08 -1.0 3.5740607788414942e-06 0.00035983547999740665 1.0588257623722672e-07 0.42372713683725016 0.0011273340972061078 8.582011716859219e-15 1.1514579117272292e-10 0.00043130977711323417 0.0027847329307350423 0.00017287873387910357 6.401055165502384e-06 1.722191123879782e-08 0.00020848309220162036 0.05639071613735579 1.8064550990294753e-05 1.289814511028015e-06 1.078687446476881e-14 0.4897735047310072 0.0002981891705231209 0.00016079768514218546 9.332371598523186e-05 0.011724714376848187 0.019040707071165303 0.00014242053195529864 0.06684484191345574 -1.0 + -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 -10.0 0.6827568359008944 0.0017329528628152924 0.18541112907970003 0.11113024641218336 0.0008760419353035269 0.0008168081568291806 0.004669378458601891 4.952432605379829 2.2512327083641864e-09 4.625859206051786e-06 0.00027044817176109554 0.5490281661912204 0.0014126153603265096 8.304845400451923e-07 0.00022950716170641464 0.001335621430311088 0.03432919201293737 0.0051970818427099184 5.726152701849347e-15 -10.0 0.013332554437480326 4.346984808472855 7.6915277818290365e-16 0.0011042047857307254 3.0571050855707086e-05 -10.0 4.986698316296507e-05 0.11128424196387618 0.000131063355181338 5.447493368067179e-09 0.11440381012864086 4.670673837764874e-07 1.2364069326350118 0.0012558879474108074 0.0003136829920007009 0.000234750482787598 0.01537590300149361 0.01769327994909422 0.0006717625881826168 4.587143797469485e-07 0.000262112392467961 0.0030002326388383245 0.1729816946709781 4.5757238372058235e-10 0.0006922962992428139 3.792209399652396e-05 -10.0 0.0009256616392487858 8.541606727879512e-06 -10.0 4.931708164307665 0.5500108683629957 0.00012980629616179268 0.006870228272606287 0.008231482263927564 5.0 3.419181071650816e-05 0.6433819434431045 6.574706543646946e-15 4.0404626147344005e-12 0.003782322077390033 0.0012637534695610975 0.17602971074583987 0.00010044390728858278 -10.0 0.008787411098754914 0.002368681880710174 0.00930449789620512 0.0006949587369646498 0.0012918433677593976 2.0707648529746893e-05 0.023295401596005315 1.1819165666519222 -10.0 0.00021606550732170693 0.0022057970761624767 -10.0 1.0325207105027347e-07 0.0020293965453828133 2.123498061876322e-12 -10.0 0.0024175998138228046 0.01178616843705956 0.0013998074566164962 0.03881102561422506 0.011076796092102187 0.0010814452764860557 0.10955637485319249 -10.0 9.382800796426367e-16 0.0005098747893974248 0.0027052054298385255 0.0021305826462238594 0.00808100901275472 4.233410073487772e-05 0.0515132921322595 8.61602729455546e-06 4.873897581604287 0.0002909334597507523 0.0046958956846326495 -10.0 -10.0 0.1780262484403569 1.3116807133030034e-14 2.6178780551069492e-15 0.1884234453311008 -10.0 2.027381220364608e-15 0.004299195570308035 0.00014628847959717226 4.899952354672477 2.774067049392004e-06 0.04441328419270369 4.279460329782799e-08 0.012297303081805393 0.002792896409558745 0.006934466049804711 0.0008370896020975689 3.8270536025799805e-14 -10.0 0.1208261392046878 0.001497958445994315 0.015016500045374082 0.05384625561469684 0.0025519632243117644 0.011028020390507681 0.002362355168421952 -10.0 -10.0 0.0008862760180332873 1.3806577785135176e-05 -10.0 0.001319677450153934 0.00026866894852481903 -10.0 1.1510120013926417 0.026729237733633893 0.00012257360246712473 0.0008544159282151438 0.0002777791741571174 0.014957033281168012 0.0032576461348374514 0.008919466185572301 1.2907171695294846e-12 0.00046842350882367664 0.17218062957363772 0.0025106741008066318 0.0022234471595443312 2.101790478097517e-08 5.752120832330886e-10 0.6428782790571179 3.2478301680021856e-07 4.903495510314233 0.01146714816952061 0.004777967757842694 1.0882011018684853e-05 0.5708498338352204 4.981497862127628 -10.0 3.279091474401153e-05 0.0010077009726691265 -10.0 2.210707325798542e-06 0.00043166052799897375 -10.0 0.1825795827020865 0.0031821087819554777 0.00038561696337086434 1.0995322456277382e-05 9.53380658669839e-05 0.01550036616828061 0.012648333883989995 5.272021975060514e-05 0.00019257248009114364 0.0016757997590048828 1.1891251244906933 5.515522196186723e-05 0.1102706140784316 9.904481358675478e-12 0.0003520674728719278 0.11294708566804167 7.239087311622819e-07 -10.0 3.574060778841494e-05 0.0035983547999740664 1.0588257623722672e-06 4.237271368372502 0.011273340972061077 8.582011716859219e-14 1.151457911727229e-09 0.004313097771132342 0.027847329307350423 0.0017287873387910357 6.401055165502385e-05 1.722191123879782e-07 0.0020848309220162036 0.5639071613735579 0.00018064550990294753 1.289814511028015e-05 1.078687446476881e-13 4.897735047310072 0.002981891705231209 0.0016079768514218546 0.0009332371598523186 0.11724714376848187 0.19040707071165303 0.0014242053195529865 0.6684484191345574 -10.0 + 3.0 + 1.5 + 10 + 1e-38 + + 200 + + + + 5 10 15 + -240 -240 -240 + 240 240 240 + + + 1 + + + 0.0 0.5 20000000.0 + + + neutron photon + + + 1 2 3 + flux + + + diff --git a/tests/regression_tests/weightwindows/results_true.dat b/tests/regression_tests/weightwindows/results_true.dat index 971401995b..122c5d8909 100644 --- a/tests/regression_tests/weightwindows/results_true.dat +++ b/tests/regression_tests/weightwindows/results_true.dat @@ -1 +1 @@ -f10c722e27d1f0a69f700bc72c4b7751f375dc0a74e049c9abb4b261d2265155c0e516e7e38f9751b83a24eac07e944e51740d398b720524cf8cd6bf0b8c51fc \ No newline at end of file +5df0c08573ccee3fd3495c877c7dc0c4b69c245faf8473b848c89fb837dea7fd437936ffebeb6455c793e66046c46eb0ad6941cc5ef1699fc5fc8a7fcb9b5a0c \ No newline at end of file diff --git a/tests/regression_tests/weightwindows/test.py b/tests/regression_tests/weightwindows/test.py index 3d3cf9d7ae..cfb651338c 100644 --- a/tests/regression_tests/weightwindows/test.py +++ b/tests/regression_tests/weightwindows/test.py @@ -1,4 +1,3 @@ -from copy import deepcopy import pytest import numpy as np @@ -8,6 +7,7 @@ from openmc.stats import Discrete, Point from tests.testing_harness import HashedPyAPITestHarness + @pytest.fixture def model(): model = openmc.Model() @@ -48,12 +48,12 @@ def model(): settings.run_mode = 'fixed source' settings.particles = 200 settings.batches = 2 - settings.max_splits = 200 + settings.max_history_splits = 200 settings.photon_transport = True space = Point((0.001, 0.001, 0.001)) energy = Discrete([14E6], [1.0]) - settings.source = openmc.Source(space=space, energy=energy) + settings.source = openmc.IndependentSource(space=space, energy=energy) # tally mesh = openmc.RegularMesh() @@ -110,3 +110,65 @@ def model(): def test_weightwindows(model): test = HashedPyAPITestHarness('statepoint.2.h5', model) test.main() + + +def test_wwinp_cylindrical(): + + ww = openmc.WeightWindowsList.from_wwinp('ww_n_cyl.txt')[0] + + mesh = ww.mesh + + assert mesh.dimension == (8, 8, 7) + + # make sure that the mesh grids are correct + exp_r_grid = np.hstack((np.linspace(0.0, 3.02, 3, endpoint=False), + np.linspace(3.02, 6.0001, 6))).flatten() + + exp_phi_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 1.5707, 1, endpoint=False), + np.linspace(1.5707, 3.1415, 2, endpoint=False), + np.linspace(3.1415, 4.7124, 4))).flatten() + + exp_z_grid = np.hstack((np.linspace(0.0, 8.008, 4, endpoint=False), + np.linspace(8.008, 14.002, 4))).flatten() + + assert isinstance(mesh, openmc.CylindricalMesh) + + np.testing.assert_equal(mesh.r_grid, exp_r_grid) + np.testing.assert_equal(mesh.phi_grid, exp_phi_grid) + np.testing.assert_equal(mesh.z_grid, exp_z_grid) + np.testing.assert_equal(mesh.origin, (0, 0, -9.0001)) + assert ww.lower_ww_bounds.flat[0] == 0.0 + assert ww.lower_ww_bounds.flat[-1] == np.prod(mesh.dimension) - 1 + + +def test_wwinp_spherical(): + + ww = openmc.WeightWindowsList.from_wwinp('ww_n_sph.txt')[0] + + mesh = ww.mesh + + assert mesh.dimension == (8, 7, 8) + + # make sure that the mesh grids are correct + exp_r_grid = np.hstack((np.linspace(0.0, 3.02, 3, endpoint=False), + np.linspace(3.02, 6.0001, 6))).flatten() + + exp_theta_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 0.5, 1, endpoint=False), + np.linspace(0.5, 0.75, 2, endpoint=False), + np.linspace(0.75, 1.5707, 3))).flatten() + + exp_phi_grid = np.hstack((np.linspace(0.0, 0.25, 2, endpoint=False), + np.linspace(0.25, 0.5, 1, endpoint=False), + np.linspace(0.5, 1.5707, 2, endpoint=False), + np.linspace(1.5707, 3.1415, 4))).flatten() + + assert isinstance(mesh, openmc.SphericalMesh) + + np.testing.assert_equal(mesh.r_grid, exp_r_grid) + np.testing.assert_equal(mesh.theta_grid, exp_theta_grid) + np.testing.assert_equal(mesh.phi_grid, exp_phi_grid) + np.testing.assert_equal(mesh.origin, (0, 0, -9.0001)) + assert ww.lower_ww_bounds.flat[0] == 0.0 + assert ww.lower_ww_bounds.flat[-1] == np.prod(mesh.dimension) - 1 diff --git a/tests/regression_tests/weightwindows/ww_n_cyl.txt b/tests/regression_tests/weightwindows/ww_n_cyl.txt new file mode 100644 index 0000000000..6f6144ca30 --- /dev/null +++ b/tests/regression_tests/weightwindows/ww_n_cyl.txt @@ -0,0 +1,88 @@ +1 1 1 16 +1 +8.0000 7.0000 8.0000 0.0000 0.0000 -9.0001 +2.0000 2.0000 4.0000 0.0000 0.0000 0.0000 +1.0000 0.0000 -9.0001 2.0000 +0.0000 3.0000 3.0200 1.0000 5.0000 6.0001 +1.0000 +0.0000 4.0000 8.0080 1.0000 3.0000 14.002 +1.0000 +0.0000 2.0000 0.25000 1.0000 1.0000 1.57070 +1.0000 2.0000 3.1415 1.0000 3.0000 4.7124 +1.0000 +100.00 +0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 +6.000000 7.000000 8.000000 9.000000 10.000000 11.000000 +12.000000 13.000000 14.000000 15.000000 16.000000 17.000000 +18.000000 19.000000 20.000000 21.000000 22.000000 23.000000 +24.000000 25.000000 26.000000 27.000000 28.000000 29.000000 +30.000000 31.000000 32.000000 33.000000 34.000000 35.000000 +36.000000 37.000000 38.000000 39.000000 40.000000 41.000000 +42.000000 43.000000 44.000000 45.000000 46.000000 47.000000 +48.000000 49.000000 50.000000 51.000000 52.000000 53.000000 +54.000000 55.000000 56.000000 57.000000 58.000000 59.000000 +60.000000 61.000000 62.000000 63.000000 64.000000 65.000000 +66.000000 67.000000 68.000000 69.000000 70.000000 71.000000 +72.000000 73.000000 74.000000 75.000000 76.000000 77.000000 +78.000000 79.000000 80.000000 81.000000 82.000000 83.000000 +84.000000 85.000000 86.000000 87.000000 88.000000 89.000000 +90.000000 91.000000 92.000000 93.000000 94.000000 95.000000 +96.000000 97.000000 98.000000 99.000000 100.000000 101.000000 +102.000000 103.000000 104.000000 105.000000 106.000000 107.000000 +108.000000 109.000000 110.000000 111.000000 112.000000 113.000000 +114.000000 115.000000 116.000000 117.000000 118.000000 119.000000 +120.000000 121.000000 122.000000 123.000000 124.000000 125.000000 +126.000000 127.000000 128.000000 129.000000 130.000000 131.000000 +132.000000 133.000000 134.000000 135.000000 136.000000 137.000000 +138.000000 139.000000 140.000000 141.000000 142.000000 143.000000 +144.000000 145.000000 146.000000 147.000000 148.000000 149.000000 +150.000000 151.000000 152.000000 153.000000 154.000000 155.000000 +156.000000 157.000000 158.000000 159.000000 160.000000 161.000000 +162.000000 163.000000 164.000000 165.000000 166.000000 167.000000 +168.000000 169.000000 170.000000 171.000000 172.000000 173.000000 +174.000000 175.000000 176.000000 177.000000 178.000000 179.000000 +180.000000 181.000000 182.000000 183.000000 184.000000 185.000000 +186.000000 187.000000 188.000000 189.000000 190.000000 191.000000 +192.000000 193.000000 194.000000 195.000000 196.000000 197.000000 +198.000000 199.000000 200.000000 201.000000 202.000000 203.000000 +204.000000 205.000000 206.000000 207.000000 208.000000 209.000000 +210.000000 211.000000 212.000000 213.000000 214.000000 215.000000 +216.000000 217.000000 218.000000 219.000000 220.000000 221.000000 +222.000000 223.000000 224.000000 225.000000 226.000000 227.000000 +228.000000 229.000000 230.000000 231.000000 232.000000 233.000000 +234.000000 235.000000 236.000000 237.000000 238.000000 239.000000 +240.000000 241.000000 242.000000 243.000000 244.000000 245.000000 +246.000000 247.000000 248.000000 249.000000 250.000000 251.000000 +252.000000 253.000000 254.000000 255.000000 256.000000 257.000000 +258.000000 259.000000 260.000000 261.000000 262.000000 263.000000 +264.000000 265.000000 266.000000 267.000000 268.000000 269.000000 +270.000000 271.000000 272.000000 273.000000 274.000000 275.000000 +276.000000 277.000000 278.000000 279.000000 280.000000 281.000000 +282.000000 283.000000 284.000000 285.000000 286.000000 287.000000 +288.000000 289.000000 290.000000 291.000000 292.000000 293.000000 +294.000000 295.000000 296.000000 297.000000 298.000000 299.000000 +300.000000 301.000000 302.000000 303.000000 304.000000 305.000000 +306.000000 307.000000 308.000000 309.000000 310.000000 311.000000 +312.000000 313.000000 314.000000 315.000000 316.000000 317.000000 +318.000000 319.000000 320.000000 321.000000 322.000000 323.000000 +324.000000 325.000000 326.000000 327.000000 328.000000 329.000000 +330.000000 331.000000 332.000000 333.000000 334.000000 335.000000 +336.000000 337.000000 338.000000 339.000000 340.000000 341.000000 +342.000000 343.000000 344.000000 345.000000 346.000000 347.000000 +348.000000 349.000000 350.000000 351.000000 352.000000 353.000000 +354.000000 355.000000 356.000000 357.000000 358.000000 359.000000 +360.000000 361.000000 362.000000 363.000000 364.000000 365.000000 +366.000000 367.000000 368.000000 369.000000 370.000000 371.000000 +372.000000 373.000000 374.000000 375.000000 376.000000 377.000000 +378.000000 379.000000 380.000000 381.000000 382.000000 383.000000 +384.000000 385.000000 386.000000 387.000000 388.000000 389.000000 +390.000000 391.000000 392.000000 393.000000 394.000000 395.000000 +396.000000 397.000000 398.000000 399.000000 400.000000 401.000000 +402.000000 403.000000 404.000000 405.000000 406.000000 407.000000 +408.000000 409.000000 410.000000 411.000000 412.000000 413.000000 +414.000000 415.000000 416.000000 417.000000 418.000000 419.000000 +420.000000 421.000000 422.000000 423.000000 424.000000 425.000000 +426.000000 427.000000 428.000000 429.000000 430.000000 431.000000 +432.000000 433.000000 434.000000 435.000000 436.000000 437.000000 +438.000000 439.000000 440.000000 441.000000 442.000000 443.000000 +444.000000 445.000000 446.000000 447.000000 diff --git a/tests/regression_tests/weightwindows/ww_n_sph.txt b/tests/regression_tests/weightwindows/ww_n_sph.txt new file mode 100644 index 0000000000..a4418ceeb5 --- /dev/null +++ b/tests/regression_tests/weightwindows/ww_n_sph.txt @@ -0,0 +1,89 @@ +1 1 1 16 +1 +8.0000 7.0000 8.0000 0.0000 0.0000 -9.0001 +2.0000 4.0000 4.0000 0.0000 0.0000 0.0000 +1.0000 0.0000 -9.0001 3.0000 +0.0000 3.0000 3.0200 1.0000 5.0000 6.0001 +1.0000 +0.0000 2.0000 0.25000 1.0000 1.0000 0.50000 +1.0000 2.0000 0.75000 1.0000 2.0000 1.5707 +1.0000 +0.0000 2.0000 0.25000 1.0000 1.0000 0.50000 +1.0000 2.0000 1.5707 1.0000 3.0000 3.1415 +1.0000 +100.00 +0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 +6.000000 7.000000 8.000000 9.000000 10.000000 11.000000 +12.000000 13.000000 14.000000 15.000000 16.000000 17.000000 +18.000000 19.000000 20.000000 21.000000 22.000000 23.000000 +24.000000 25.000000 26.000000 27.000000 28.000000 29.000000 +30.000000 31.000000 32.000000 33.000000 34.000000 35.000000 +36.000000 37.000000 38.000000 39.000000 40.000000 41.000000 +42.000000 43.000000 44.000000 45.000000 46.000000 47.000000 +48.000000 49.000000 50.000000 51.000000 52.000000 53.000000 +54.000000 55.000000 56.000000 57.000000 58.000000 59.000000 +60.000000 61.000000 62.000000 63.000000 64.000000 65.000000 +66.000000 67.000000 68.000000 69.000000 70.000000 71.000000 +72.000000 73.000000 74.000000 75.000000 76.000000 77.000000 +78.000000 79.000000 80.000000 81.000000 82.000000 83.000000 +84.000000 85.000000 86.000000 87.000000 88.000000 89.000000 +90.000000 91.000000 92.000000 93.000000 94.000000 95.000000 +96.000000 97.000000 98.000000 99.000000 100.000000 101.000000 +102.000000 103.000000 104.000000 105.000000 106.000000 107.000000 +108.000000 109.000000 110.000000 111.000000 112.000000 113.000000 +114.000000 115.000000 116.000000 117.000000 118.000000 119.000000 +120.000000 121.000000 122.000000 123.000000 124.000000 125.000000 +126.000000 127.000000 128.000000 129.000000 130.000000 131.000000 +132.000000 133.000000 134.000000 135.000000 136.000000 137.000000 +138.000000 139.000000 140.000000 141.000000 142.000000 143.000000 +144.000000 145.000000 146.000000 147.000000 148.000000 149.000000 +150.000000 151.000000 152.000000 153.000000 154.000000 155.000000 +156.000000 157.000000 158.000000 159.000000 160.000000 161.000000 +162.000000 163.000000 164.000000 165.000000 166.000000 167.000000 +168.000000 169.000000 170.000000 171.000000 172.000000 173.000000 +174.000000 175.000000 176.000000 177.000000 178.000000 179.000000 +180.000000 181.000000 182.000000 183.000000 184.000000 185.000000 +186.000000 187.000000 188.000000 189.000000 190.000000 191.000000 +192.000000 193.000000 194.000000 195.000000 196.000000 197.000000 +198.000000 199.000000 200.000000 201.000000 202.000000 203.000000 +204.000000 205.000000 206.000000 207.000000 208.000000 209.000000 +210.000000 211.000000 212.000000 213.000000 214.000000 215.000000 +216.000000 217.000000 218.000000 219.000000 220.000000 221.000000 +222.000000 223.000000 224.000000 225.000000 226.000000 227.000000 +228.000000 229.000000 230.000000 231.000000 232.000000 233.000000 +234.000000 235.000000 236.000000 237.000000 238.000000 239.000000 +240.000000 241.000000 242.000000 243.000000 244.000000 245.000000 +246.000000 247.000000 248.000000 249.000000 250.000000 251.000000 +252.000000 253.000000 254.000000 255.000000 256.000000 257.000000 +258.000000 259.000000 260.000000 261.000000 262.000000 263.000000 +264.000000 265.000000 266.000000 267.000000 268.000000 269.000000 +270.000000 271.000000 272.000000 273.000000 274.000000 275.000000 +276.000000 277.000000 278.000000 279.000000 280.000000 281.000000 +282.000000 283.000000 284.000000 285.000000 286.000000 287.000000 +288.000000 289.000000 290.000000 291.000000 292.000000 293.000000 +294.000000 295.000000 296.000000 297.000000 298.000000 299.000000 +300.000000 301.000000 302.000000 303.000000 304.000000 305.000000 +306.000000 307.000000 308.000000 309.000000 310.000000 311.000000 +312.000000 313.000000 314.000000 315.000000 316.000000 317.000000 +318.000000 319.000000 320.000000 321.000000 322.000000 323.000000 +324.000000 325.000000 326.000000 327.000000 328.000000 329.000000 +330.000000 331.000000 332.000000 333.000000 334.000000 335.000000 +336.000000 337.000000 338.000000 339.000000 340.000000 341.000000 +342.000000 343.000000 344.000000 345.000000 346.000000 347.000000 +348.000000 349.000000 350.000000 351.000000 352.000000 353.000000 +354.000000 355.000000 356.000000 357.000000 358.000000 359.000000 +360.000000 361.000000 362.000000 363.000000 364.000000 365.000000 +366.000000 367.000000 368.000000 369.000000 370.000000 371.000000 +372.000000 373.000000 374.000000 375.000000 376.000000 377.000000 +378.000000 379.000000 380.000000 381.000000 382.000000 383.000000 +384.000000 385.000000 386.000000 387.000000 388.000000 389.000000 +390.000000 391.000000 392.000000 393.000000 394.000000 395.000000 +396.000000 397.000000 398.000000 399.000000 400.000000 401.000000 +402.000000 403.000000 404.000000 405.000000 406.000000 407.000000 +408.000000 409.000000 410.000000 411.000000 412.000000 413.000000 +414.000000 415.000000 416.000000 417.000000 418.000000 419.000000 +420.000000 421.000000 422.000000 423.000000 424.000000 425.000000 +426.000000 427.000000 428.000000 429.000000 430.000000 431.000000 +432.000000 433.000000 434.000000 435.000000 436.000000 437.000000 +438.000000 439.000000 440.000000 441.000000 442.000000 443.000000 +444.000000 445.000000 446.000000 447.000000 diff --git a/tests/regression_tests/weightwindows_fw_cadis/__init__.py b/tests/regression_tests/weightwindows_fw_cadis/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/weightwindows_fw_cadis/inputs_true.dat b/tests/regression_tests/weightwindows_fw_cadis/inputs_true.dat new file mode 100644 index 0000000000..5fa6505ddf --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis/inputs_true.dat @@ -0,0 +1,260 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 90 + 10 + 5 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + + 1 + neutron + 10 + 1 + true + fw_cadis + + + + 6 6 6 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + naive + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/weightwindows_fw_cadis/results_true.dat b/tests/regression_tests/weightwindows_fw_cadis/results_true.dat new file mode 100644 index 0000000000..e39b2e2390 --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis/results_true.dat @@ -0,0 +1,442 @@ +RegularMesh + ID = 1 + Name = + Dimensions = 3 + Voxels = [6 6 6] + Lower left = [0. 0. 0.] + Upper Right = [np.float64(30.0), np.float64(30.0), np.float64(30.0)] + Width = [5. 5. 5.] +Lower Bounds +1.50e-01 +1.54e-01 +1.43e-01 +1.30e-01 +1.30e-01 +1.97e-01 +1.46e-01 +1.25e-01 +1.34e-01 +1.32e-01 +1.27e-01 +3.40e-01 +1.36e-01 +1.13e-01 +1.09e-01 +1.19e-01 +1.25e-01 +3.08e-01 +1.38e-01 +1.18e-01 +9.49e-02 +1.19e-01 +1.35e-01 +1.96e-01 +1.12e-01 +9.60e-02 +9.33e-02 +9.74e-02 +1.37e-01 +2.76e-01 +4.53e-02 +1.31e-01 +2.47e-01 +7.36e-02 +1.72e-01 +1.38e-01 +1.55e-01 +1.54e-01 +1.48e-01 +1.36e-01 +1.29e-01 +1.88e-01 +1.40e-01 +1.51e-01 +1.28e-01 +1.18e-01 +1.29e-01 +2.86e-01 +1.42e-01 +1.26e-01 +1.28e-01 +1.16e-01 +1.22e-01 +2.81e-01 +1.38e-01 +1.22e-01 +1.04e-01 +9.81e-02 +1.08e-01 +1.97e-01 +1.34e-01 +1.32e-01 +1.10e-01 +8.69e-02 +1.03e-01 +1.33e-01 +2.51e-01 +3.39e-01 +1.13e-01 +6.92e-02 +1.54e-01 +5.71e-02 +1.60e-01 +1.52e-01 +1.34e-01 +1.32e-01 +1.28e-01 +2.53e-01 +1.33e-01 +1.35e-01 +1.15e-01 +1.32e-01 +1.37e-01 +3.13e-01 +1.27e-01 +1.36e-01 +1.35e-01 +1.10e-01 +1.08e-01 +8.81e-02 +1.51e-01 +1.32e-01 +1.13e-01 +9.89e-02 +9.25e-02 +5.04e-02 +1.40e-01 +1.56e-01 +1.40e-01 +9.19e-02 +6.54e-02 +2.05e-02 +3.35e-01 +1.88e-01 +3.16e-01 +1.80e-01 +5.78e-02 +5.36e-03 +1.43e-01 +1.35e-01 +1.47e-01 +1.33e-01 +1.47e-01 +4.95e-01 +1.42e-01 +1.45e-01 +1.27e-01 +1.31e-01 +1.26e-01 +1.43e-01 +1.49e-01 +1.23e-01 +1.24e-01 +1.13e-01 +9.69e-02 +1.07e-01 +1.65e-01 +1.50e-01 +1.24e-01 +1.07e-01 +8.90e-02 +8.81e-02 +1.48e-01 +1.58e-01 +1.20e-01 +7.93e-02 +7.19e-02 +3.94e-02 +2.29e-01 +1.95e-01 +2.66e-01 +1.51e-01 +3.52e-02 +3.68e-03 +1.48e-01 +1.18e-01 +1.47e-01 +1.41e-01 +1.37e-01 +2.71e-01 +1.70e-01 +1.70e-01 +1.29e-01 +1.33e-01 +1.28e-01 +2.79e-01 +1.84e-01 +1.42e-01 +1.18e-01 +1.25e-01 +1.26e-01 +1.64e-01 +1.73e-01 +1.37e-01 +1.12e-01 +9.01e-02 +1.13e-01 +1.68e-01 +1.60e-01 +1.52e-01 +1.30e-01 +8.71e-02 +7.72e-02 +3.76e-02 +1.57e-01 +9.31e-02 +1.63e-01 +6.80e-02 +6.22e-02 +3.37e-03 +2.47e-01 +2.17e-01 +1.48e-01 +2.01e-01 +1.54e-01 +5.69e-02 +2.89e-01 +3.59e-01 +1.17e-01 +8.64e-02 +3.34e-01 +1.50e-01 +5.00e-01 +2.16e-01 +1.22e-01 +1.66e-01 +1.44e-01 +4.98e-02 +4.28e-01 +1.17e-01 +8.73e-02 +1.11e-01 +1.09e-01 +9.69e-02 +3.26e-01 +2.28e-01 +2.49e-01 +3.55e-02 +1.69e-02 +3.95e-03 +1.03e-01 +4.89e-02 +1.90e-01 +4.61e-02 +9.75e-03 +3.00e-04 +Upper Bounds +7.49e-01 +7.71e-01 +7.13e-01 +6.51e-01 +6.48e-01 +9.85e-01 +7.31e-01 +6.26e-01 +6.69e-01 +6.59e-01 +6.33e-01 +1.70e+00 +6.82e-01 +5.67e-01 +5.45e-01 +5.94e-01 +6.24e-01 +1.54e+00 +6.92e-01 +5.91e-01 +4.75e-01 +5.96e-01 +6.77e-01 +9.82e-01 +5.59e-01 +4.80e-01 +4.67e-01 +4.87e-01 +6.84e-01 +1.38e+00 +2.26e-01 +6.55e-01 +1.24e+00 +3.68e-01 +8.59e-01 +6.88e-01 +7.73e-01 +7.70e-01 +7.42e-01 +6.78e-01 +6.46e-01 +9.40e-01 +7.00e-01 +7.57e-01 +6.39e-01 +5.92e-01 +6.43e-01 +1.43e+00 +7.12e-01 +6.29e-01 +6.40e-01 +5.78e-01 +6.09e-01 +1.40e+00 +6.90e-01 +6.10e-01 +5.21e-01 +4.91e-01 +5.42e-01 +9.87e-01 +6.71e-01 +6.60e-01 +5.52e-01 +4.35e-01 +5.14e-01 +6.65e-01 +1.26e+00 +1.70e+00 +5.63e-01 +3.46e-01 +7.72e-01 +2.86e-01 +8.00e-01 +7.58e-01 +6.70e-01 +6.59e-01 +6.41e-01 +1.26e+00 +6.65e-01 +6.76e-01 +5.76e-01 +6.60e-01 +6.86e-01 +1.57e+00 +6.35e-01 +6.81e-01 +6.75e-01 +5.51e-01 +5.39e-01 +4.40e-01 +7.56e-01 +6.62e-01 +5.67e-01 +4.95e-01 +4.63e-01 +2.52e-01 +6.98e-01 +7.78e-01 +6.99e-01 +4.59e-01 +3.27e-01 +1.02e-01 +1.67e+00 +9.38e-01 +1.58e+00 +9.00e-01 +2.89e-01 +2.68e-02 +7.16e-01 +6.75e-01 +7.35e-01 +6.63e-01 +7.34e-01 +2.47e+00 +7.11e-01 +7.23e-01 +6.34e-01 +6.54e-01 +6.32e-01 +7.14e-01 +7.45e-01 +6.15e-01 +6.19e-01 +5.64e-01 +4.85e-01 +5.37e-01 +8.26e-01 +7.51e-01 +6.21e-01 +5.36e-01 +4.45e-01 +4.40e-01 +7.40e-01 +7.92e-01 +5.99e-01 +3.96e-01 +3.60e-01 +1.97e-01 +1.14e+00 +9.76e-01 +1.33e+00 +7.55e-01 +1.76e-01 +1.84e-02 +7.42e-01 +5.90e-01 +7.37e-01 +7.07e-01 +6.84e-01 +1.36e+00 +8.52e-01 +8.49e-01 +6.46e-01 +6.63e-01 +6.42e-01 +1.40e+00 +9.19e-01 +7.08e-01 +5.89e-01 +6.24e-01 +6.28e-01 +8.21e-01 +8.64e-01 +6.87e-01 +5.58e-01 +4.50e-01 +5.64e-01 +8.40e-01 +7.99e-01 +7.62e-01 +6.51e-01 +4.36e-01 +3.86e-01 +1.88e-01 +7.85e-01 +4.66e-01 +8.13e-01 +3.40e-01 +3.11e-01 +1.68e-02 +1.24e+00 +1.09e+00 +7.40e-01 +1.00e+00 +7.72e-01 +2.85e-01 +1.44e+00 +1.79e+00 +5.85e-01 +4.32e-01 +1.67e+00 +7.50e-01 +2.50e+00 +1.08e+00 +6.10e-01 +8.32e-01 +7.19e-01 +2.49e-01 +2.14e+00 +5.87e-01 +4.36e-01 +5.57e-01 +5.47e-01 +4.84e-01 +1.63e+00 +1.14e+00 +1.25e+00 +1.78e-01 +8.43e-02 +1.97e-02 +5.17e-01 +2.44e-01 +9.49e-01 +2.31e-01 +4.87e-02 +1.50e-03 \ No newline at end of file diff --git a/tests/regression_tests/weightwindows_fw_cadis/test.py b/tests/regression_tests/weightwindows_fw_cadis/test.py new file mode 100644 index 0000000000..7a4718ed5b --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis/test.py @@ -0,0 +1,33 @@ +import os + +import openmc +from openmc.examples import random_ray_three_region_cube + +from tests.testing_harness import WeightWindowPyAPITestHarness + + +class MGXSTestHarness(WeightWindowPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +def test_random_ray_adjoint_fixed_source(): + model = random_ray_three_region_cube() + + ww_mesh = openmc.RegularMesh() + n = 6 + width = 30.0 + ww_mesh.dimension = (n, n, n) + ww_mesh.lower_left = (0.0, 0.0, 0.0) + ww_mesh.upper_right = (width, width, width) + + wwg = openmc.WeightWindowGenerator( + method="fw_cadis", mesh=ww_mesh, max_realizations=model.settings.batches) + model.settings.weight_window_generators = wwg + model.settings.random_ray['volume_estimator'] = 'naive' + + harness = MGXSTestHarness('statepoint.10.h5', model) + harness.main() diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/__init__.py b/tests/regression_tests/weightwindows_fw_cadis_mesh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat new file mode 100644 index 0000000000..ceb89e6e34 --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/inputs_true.dat @@ -0,0 +1,265 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 750 + 30 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + + 1 + neutron + 10 + 1 + true + fw_cadis + + + + 15 15 15 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + + flat + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/results_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/results_true.dat new file mode 100644 index 0000000000..0354501d15 --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/flat/results_true.dat @@ -0,0 +1,6760 @@ +RegularMesh + ID = 1 + Name = + Dimensions = 3 + Voxels = [15 15 15] + Lower left = [0. 0. 0.] + Upper Right = [np.float64(30.0), np.float64(30.0), np.float64(30.0)] + Width = [2. 2. 2.] +Lower Bounds +1.71e-01 +1.73e-01 +1.72e-01 +1.73e-01 +1.71e-01 +1.63e-01 +1.66e-01 +1.70e-01 +1.68e-01 +1.76e-01 +1.73e-01 +1.79e-01 +2.62e-01 +4.44e-01 +1.68e-01 +1.63e-01 +1.76e-01 +1.73e-01 +1.52e-01 +1.66e-01 +1.72e-01 +1.68e-01 +1.68e-01 +1.66e-01 +1.67e-01 +1.76e-01 +1.73e-01 +2.44e-01 +3.63e-01 +1.22e-01 +1.71e-01 +1.66e-01 +1.69e-01 +1.70e-01 +1.67e-01 +1.71e-01 +1.64e-01 +1.55e-01 +1.67e-01 +1.64e-01 +1.68e-01 +1.82e-01 +2.60e-01 +4.13e-01 +1.55e-01 +1.68e-01 +1.62e-01 +1.66e-01 +1.65e-01 +1.62e-01 +1.64e-01 +1.63e-01 +1.66e-01 +1.63e-01 +1.67e-01 +1.58e-01 +1.79e-01 +2.52e-01 +4.10e-01 +1.35e-01 +1.65e-01 +1.74e-01 +1.67e-01 +1.71e-01 +1.66e-01 +1.62e-01 +1.60e-01 +1.66e-01 +1.55e-01 +1.67e-01 +1.66e-01 +1.82e-01 +2.68e-01 +3.46e-01 +1.32e-01 +1.70e-01 +1.70e-01 +1.68e-01 +1.68e-01 +1.69e-01 +1.59e-01 +1.62e-01 +1.59e-01 +1.57e-01 +1.55e-01 +1.72e-01 +1.80e-01 +2.59e-01 +2.97e-01 +1.06e-01 +1.68e-01 +1.60e-01 +1.58e-01 +1.61e-01 +1.58e-01 +1.59e-01 +1.56e-01 +1.53e-01 +1.57e-01 +1.52e-01 +1.59e-01 +1.54e-01 +2.42e-01 +3.30e-01 +1.22e-01 +1.64e-01 +1.65e-01 +1.69e-01 +1.64e-01 +1.60e-01 +1.66e-01 +1.60e-01 +1.52e-01 +1.45e-01 +1.53e-01 +1.57e-01 +1.40e-01 +2.08e-01 +1.90e-01 +7.93e-02 +1.65e-01 +1.65e-01 +1.71e-01 +1.72e-01 +1.63e-01 +1.61e-01 +1.57e-01 +1.54e-01 +1.49e-01 +1.41e-01 +1.45e-01 +1.53e-01 +2.36e-01 +1.80e-01 +5.76e-02 +1.67e-01 +1.75e-01 +1.74e-01 +1.71e-01 +1.66e-01 +1.54e-01 +1.55e-01 +1.52e-01 +1.53e-01 +1.41e-01 +1.53e-01 +1.49e-01 +2.14e-01 +1.76e-01 +6.70e-02 +1.72e-01 +1.66e-01 +1.74e-01 +1.70e-01 +1.69e-01 +1.57e-01 +1.50e-01 +1.53e-01 +1.48e-01 +1.47e-01 +1.62e-01 +1.49e-01 +2.03e-01 +1.35e-01 +4.07e-02 +1.81e-01 +1.80e-01 +1.72e-01 +1.65e-01 +1.84e-01 +1.74e-01 +1.58e-01 +1.68e-01 +1.59e-01 +1.57e-01 +1.58e-01 +1.48e-01 +2.16e-01 +1.05e-01 +3.37e-02 +2.61e-01 +2.52e-01 +2.37e-01 +2.51e-01 +2.64e-01 +2.42e-01 +2.39e-01 +2.32e-01 +2.32e-01 +2.29e-01 +2.22e-01 +2.19e-01 +2.07e-01 +8.02e-02 +2.21e-02 +5.00e-01 +3.68e-01 +3.88e-01 +2.65e-01 +2.86e-01 +3.17e-01 +2.60e-01 +2.38e-01 +1.88e-01 +1.97e-01 +1.84e-01 +1.50e-01 +9.98e-02 +3.88e-02 +1.90e-02 +2.18e-01 +1.48e-01 +1.61e-01 +8.76e-02 +9.43e-02 +1.16e-01 +8.45e-02 +8.13e-02 +7.00e-02 +6.16e-02 +6.49e-02 +4.46e-02 +3.71e-02 +1.62e-02 +7.33e-03 +1.70e-01 +1.69e-01 +1.70e-01 +1.68e-01 +1.63e-01 +1.59e-01 +1.75e-01 +1.69e-01 +1.59e-01 +1.64e-01 +1.77e-01 +1.79e-01 +2.63e-01 +3.92e-01 +1.48e-01 +1.73e-01 +1.74e-01 +1.70e-01 +1.66e-01 +1.68e-01 +1.62e-01 +1.62e-01 +1.67e-01 +1.60e-01 +1.64e-01 +1.67e-01 +1.80e-01 +2.42e-01 +3.72e-01 +1.45e-01 +1.71e-01 +1.65e-01 +1.67e-01 +1.65e-01 +1.69e-01 +1.70e-01 +1.64e-01 +1.68e-01 +1.60e-01 +1.70e-01 +1.70e-01 +1.74e-01 +2.62e-01 +3.29e-01 +1.15e-01 +1.73e-01 +1.75e-01 +1.70e-01 +1.69e-01 +1.69e-01 +1.63e-01 +1.66e-01 +1.63e-01 +1.61e-01 +1.61e-01 +1.70e-01 +1.79e-01 +2.47e-01 +3.64e-01 +1.32e-01 +1.72e-01 +1.73e-01 +1.71e-01 +1.66e-01 +1.63e-01 +1.63e-01 +1.67e-01 +1.60e-01 +1.56e-01 +1.63e-01 +1.72e-01 +1.71e-01 +2.52e-01 +3.43e-01 +1.40e-01 +1.68e-01 +1.70e-01 +1.67e-01 +1.66e-01 +1.72e-01 +1.61e-01 +1.56e-01 +1.56e-01 +1.53e-01 +1.55e-01 +1.65e-01 +1.78e-01 +2.53e-01 +3.66e-01 +1.33e-01 +1.70e-01 +1.69e-01 +1.60e-01 +1.64e-01 +1.70e-01 +1.63e-01 +1.53e-01 +1.50e-01 +1.50e-01 +1.48e-01 +1.53e-01 +1.66e-01 +2.34e-01 +2.99e-01 +1.15e-01 +1.65e-01 +1.63e-01 +1.59e-01 +1.57e-01 +1.59e-01 +1.54e-01 +1.54e-01 +1.47e-01 +1.40e-01 +1.38e-01 +1.27e-01 +1.22e-01 +1.99e-01 +1.77e-01 +7.13e-02 +1.58e-01 +1.75e-01 +1.68e-01 +1.66e-01 +1.66e-01 +1.58e-01 +1.56e-01 +1.44e-01 +1.48e-01 +1.45e-01 +1.37e-01 +1.40e-01 +2.01e-01 +1.36e-01 +4.17e-02 +1.65e-01 +1.76e-01 +1.69e-01 +1.69e-01 +1.57e-01 +1.67e-01 +1.56e-01 +1.50e-01 +1.53e-01 +1.43e-01 +1.51e-01 +1.41e-01 +2.04e-01 +1.50e-01 +4.51e-02 +1.69e-01 +1.75e-01 +1.71e-01 +1.63e-01 +1.69e-01 +1.67e-01 +1.60e-01 +1.50e-01 +1.53e-01 +1.44e-01 +1.57e-01 +1.49e-01 +2.02e-01 +1.35e-01 +4.08e-02 +1.79e-01 +1.72e-01 +1.73e-01 +1.70e-01 +1.74e-01 +1.78e-01 +1.68e-01 +1.59e-01 +1.57e-01 +1.62e-01 +1.62e-01 +1.57e-01 +1.95e-01 +9.20e-02 +3.15e-02 +2.48e-01 +2.65e-01 +2.54e-01 +2.58e-01 +2.80e-01 +2.53e-01 +2.33e-01 +2.40e-01 +2.16e-01 +2.19e-01 +2.43e-01 +2.22e-01 +1.99e-01 +6.18e-02 +1.77e-02 +3.13e-01 +2.98e-01 +2.98e-01 +3.16e-01 +3.67e-01 +3.51e-01 +2.81e-01 +2.76e-01 +2.24e-01 +1.66e-01 +1.70e-01 +1.65e-01 +1.10e-01 +3.67e-02 +1.54e-02 +1.11e-01 +1.10e-01 +1.18e-01 +1.08e-01 +1.34e-01 +1.41e-01 +9.92e-02 +9.96e-02 +9.39e-02 +5.19e-02 +5.40e-02 +5.45e-02 +4.22e-02 +1.88e-02 +7.33e-03 +1.73e-01 +1.68e-01 +1.74e-01 +1.68e-01 +1.60e-01 +1.60e-01 +1.61e-01 +1.59e-01 +1.70e-01 +1.61e-01 +1.62e-01 +1.76e-01 +2.90e-01 +4.08e-01 +1.58e-01 +1.72e-01 +1.68e-01 +1.62e-01 +1.66e-01 +1.59e-01 +1.63e-01 +1.58e-01 +1.60e-01 +1.59e-01 +1.65e-01 +1.63e-01 +1.77e-01 +2.45e-01 +3.92e-01 +1.64e-01 +1.71e-01 +1.74e-01 +1.72e-01 +1.57e-01 +1.65e-01 +1.63e-01 +1.63e-01 +1.69e-01 +1.58e-01 +1.61e-01 +1.61e-01 +1.77e-01 +2.45e-01 +3.36e-01 +1.29e-01 +1.57e-01 +1.67e-01 +1.66e-01 +1.63e-01 +1.63e-01 +1.59e-01 +1.61e-01 +1.65e-01 +1.56e-01 +1.59e-01 +1.65e-01 +1.59e-01 +2.43e-01 +3.30e-01 +1.08e-01 +1.62e-01 +1.62e-01 +1.61e-01 +1.54e-01 +1.63e-01 +1.64e-01 +1.50e-01 +1.57e-01 +1.51e-01 +1.50e-01 +1.67e-01 +1.72e-01 +2.44e-01 +2.79e-01 +8.59e-02 +1.61e-01 +1.64e-01 +1.57e-01 +1.55e-01 +1.57e-01 +1.57e-01 +1.51e-01 +1.49e-01 +1.54e-01 +1.56e-01 +1.67e-01 +1.69e-01 +2.42e-01 +3.48e-01 +1.32e-01 +1.67e-01 +1.73e-01 +1.64e-01 +1.62e-01 +1.60e-01 +1.61e-01 +1.56e-01 +1.47e-01 +1.51e-01 +1.54e-01 +1.57e-01 +1.59e-01 +2.26e-01 +2.53e-01 +9.32e-02 +1.63e-01 +1.63e-01 +1.60e-01 +1.55e-01 +1.57e-01 +1.58e-01 +1.54e-01 +1.47e-01 +1.46e-01 +1.48e-01 +1.43e-01 +1.50e-01 +2.09e-01 +1.68e-01 +6.28e-02 +1.57e-01 +1.64e-01 +1.64e-01 +1.60e-01 +1.60e-01 +1.58e-01 +1.58e-01 +1.57e-01 +1.47e-01 +1.50e-01 +1.52e-01 +1.43e-01 +2.11e-01 +1.66e-01 +6.17e-02 +1.65e-01 +1.72e-01 +1.62e-01 +1.64e-01 +1.66e-01 +1.57e-01 +1.57e-01 +1.50e-01 +1.40e-01 +1.48e-01 +1.46e-01 +1.44e-01 +1.95e-01 +1.25e-01 +4.98e-02 +1.61e-01 +1.66e-01 +1.70e-01 +1.64e-01 +1.67e-01 +1.67e-01 +1.68e-01 +1.58e-01 +1.43e-01 +1.45e-01 +1.46e-01 +1.43e-01 +1.94e-01 +1.25e-01 +3.60e-02 +1.61e-01 +1.74e-01 +1.74e-01 +1.76e-01 +1.73e-01 +1.72e-01 +1.68e-01 +1.61e-01 +1.59e-01 +1.56e-01 +1.59e-01 +1.57e-01 +1.77e-01 +1.09e-01 +3.74e-02 +2.48e-01 +2.48e-01 +2.49e-01 +2.42e-01 +2.47e-01 +2.55e-01 +2.19e-01 +2.25e-01 +2.25e-01 +2.17e-01 +2.21e-01 +2.26e-01 +2.15e-01 +6.24e-02 +1.90e-02 +3.68e-01 +2.72e-01 +3.41e-01 +3.58e-01 +3.64e-01 +2.32e-01 +2.34e-01 +2.20e-01 +1.88e-01 +1.77e-01 +1.34e-01 +1.25e-01 +1.07e-01 +4.68e-02 +1.29e-02 +1.29e-01 +8.63e-02 +1.42e-01 +1.36e-01 +1.37e-01 +8.94e-02 +8.09e-02 +8.37e-02 +5.69e-02 +6.45e-02 +3.15e-02 +3.86e-02 +3.51e-02 +2.57e-02 +1.29e-02 +1.69e-01 +1.62e-01 +1.66e-01 +1.70e-01 +1.69e-01 +1.61e-01 +1.58e-01 +1.51e-01 +1.52e-01 +1.53e-01 +1.52e-01 +1.73e-01 +2.54e-01 +4.12e-01 +1.51e-01 +1.68e-01 +1.75e-01 +1.65e-01 +1.59e-01 +1.62e-01 +1.56e-01 +1.57e-01 +1.55e-01 +1.59e-01 +1.66e-01 +1.59e-01 +1.67e-01 +2.35e-01 +3.25e-01 +1.29e-01 +1.62e-01 +1.64e-01 +1.68e-01 +1.59e-01 +1.51e-01 +1.59e-01 +1.53e-01 +1.53e-01 +1.55e-01 +1.63e-01 +1.60e-01 +1.65e-01 +2.33e-01 +2.78e-01 +1.07e-01 +1.62e-01 +1.63e-01 +1.60e-01 +1.61e-01 +1.54e-01 +1.57e-01 +1.59e-01 +1.51e-01 +1.51e-01 +1.58e-01 +1.54e-01 +1.61e-01 +2.39e-01 +2.48e-01 +1.01e-01 +1.55e-01 +1.54e-01 +1.61e-01 +1.66e-01 +1.55e-01 +1.53e-01 +1.56e-01 +1.51e-01 +1.48e-01 +1.53e-01 +1.63e-01 +1.62e-01 +2.42e-01 +3.21e-01 +1.18e-01 +1.66e-01 +1.52e-01 +1.62e-01 +1.62e-01 +1.56e-01 +1.52e-01 +1.55e-01 +1.57e-01 +1.55e-01 +1.53e-01 +1.53e-01 +1.64e-01 +2.46e-01 +3.02e-01 +1.11e-01 +1.63e-01 +1.59e-01 +1.68e-01 +1.59e-01 +1.52e-01 +1.54e-01 +1.56e-01 +1.49e-01 +1.49e-01 +1.51e-01 +1.45e-01 +1.49e-01 +2.22e-01 +2.88e-01 +1.02e-01 +1.66e-01 +1.62e-01 +1.61e-01 +1.50e-01 +1.50e-01 +1.51e-01 +1.59e-01 +1.46e-01 +1.43e-01 +1.42e-01 +1.41e-01 +1.51e-01 +2.22e-01 +1.88e-01 +6.51e-02 +1.60e-01 +1.59e-01 +1.57e-01 +1.61e-01 +1.55e-01 +1.57e-01 +1.53e-01 +1.53e-01 +1.45e-01 +1.44e-01 +1.47e-01 +1.59e-01 +2.35e-01 +1.91e-01 +5.69e-02 +1.62e-01 +1.59e-01 +1.63e-01 +1.59e-01 +1.55e-01 +1.54e-01 +1.50e-01 +1.47e-01 +1.50e-01 +1.46e-01 +1.43e-01 +1.43e-01 +2.21e-01 +1.37e-01 +5.36e-02 +1.66e-01 +1.65e-01 +1.50e-01 +1.68e-01 +1.65e-01 +1.61e-01 +1.55e-01 +1.49e-01 +1.47e-01 +1.51e-01 +1.43e-01 +1.50e-01 +1.92e-01 +9.97e-02 +2.93e-02 +1.73e-01 +1.65e-01 +1.70e-01 +1.66e-01 +1.73e-01 +1.78e-01 +1.65e-01 +1.50e-01 +1.60e-01 +1.55e-01 +1.57e-01 +1.55e-01 +1.89e-01 +8.91e-02 +2.68e-02 +2.32e-01 +2.36e-01 +2.40e-01 +2.31e-01 +2.41e-01 +2.39e-01 +2.01e-01 +2.12e-01 +2.21e-01 +2.12e-01 +2.04e-01 +2.02e-01 +2.14e-01 +8.26e-02 +2.65e-02 +3.06e-01 +2.51e-01 +2.10e-01 +2.95e-01 +2.86e-01 +2.41e-01 +1.69e-01 +1.21e-01 +1.92e-01 +1.82e-01 +1.29e-01 +1.24e-01 +1.04e-01 +4.53e-02 +1.74e-02 +1.16e-01 +8.31e-02 +8.94e-02 +9.43e-02 +1.16e-01 +9.01e-02 +7.46e-02 +3.83e-02 +6.35e-02 +6.43e-02 +4.23e-02 +4.16e-02 +3.17e-02 +2.60e-02 +1.26e-02 +1.63e-01 +1.58e-01 +1.60e-01 +1.62e-01 +1.60e-01 +1.59e-01 +1.60e-01 +1.51e-01 +1.47e-01 +1.55e-01 +1.51e-01 +1.68e-01 +2.47e-01 +3.18e-01 +1.27e-01 +1.64e-01 +1.61e-01 +1.56e-01 +1.63e-01 +1.63e-01 +1.54e-01 +1.50e-01 +1.49e-01 +1.56e-01 +1.58e-01 +1.58e-01 +1.64e-01 +2.39e-01 +3.04e-01 +1.13e-01 +1.65e-01 +1.63e-01 +1.62e-01 +1.57e-01 +1.57e-01 +1.57e-01 +1.53e-01 +1.49e-01 +1.57e-01 +1.63e-01 +1.63e-01 +1.70e-01 +2.39e-01 +2.29e-01 +8.46e-02 +1.62e-01 +1.63e-01 +1.58e-01 +1.58e-01 +1.56e-01 +1.55e-01 +1.50e-01 +1.51e-01 +1.52e-01 +1.60e-01 +1.57e-01 +1.64e-01 +2.46e-01 +2.16e-01 +7.36e-02 +1.56e-01 +1.54e-01 +1.56e-01 +1.62e-01 +1.52e-01 +1.50e-01 +1.55e-01 +1.52e-01 +1.52e-01 +1.50e-01 +1.58e-01 +1.67e-01 +2.47e-01 +3.21e-01 +1.15e-01 +1.57e-01 +1.55e-01 +1.56e-01 +1.58e-01 +1.48e-01 +1.43e-01 +1.42e-01 +1.56e-01 +1.49e-01 +1.52e-01 +1.51e-01 +1.58e-01 +2.31e-01 +2.63e-01 +9.53e-02 +1.48e-01 +1.56e-01 +1.59e-01 +1.52e-01 +1.60e-01 +1.43e-01 +1.43e-01 +1.44e-01 +1.45e-01 +1.43e-01 +1.48e-01 +1.51e-01 +2.09e-01 +2.10e-01 +7.84e-02 +1.57e-01 +1.60e-01 +1.55e-01 +1.53e-01 +1.57e-01 +1.44e-01 +1.44e-01 +1.36e-01 +1.36e-01 +1.46e-01 +1.43e-01 +1.46e-01 +2.17e-01 +1.67e-01 +7.08e-02 +1.59e-01 +1.56e-01 +1.56e-01 +1.54e-01 +1.53e-01 +1.52e-01 +1.50e-01 +1.42e-01 +1.37e-01 +1.39e-01 +1.41e-01 +1.47e-01 +2.03e-01 +1.75e-01 +5.51e-02 +1.63e-01 +1.62e-01 +1.57e-01 +1.53e-01 +1.45e-01 +1.52e-01 +1.52e-01 +1.43e-01 +1.41e-01 +1.38e-01 +1.39e-01 +1.52e-01 +2.05e-01 +1.61e-01 +6.97e-02 +1.62e-01 +1.56e-01 +1.58e-01 +1.55e-01 +1.58e-01 +1.56e-01 +1.53e-01 +1.53e-01 +1.49e-01 +1.41e-01 +1.44e-01 +1.57e-01 +2.11e-01 +1.22e-01 +4.27e-02 +1.66e-01 +1.69e-01 +1.61e-01 +1.62e-01 +1.68e-01 +1.74e-01 +1.60e-01 +1.57e-01 +1.57e-01 +1.62e-01 +1.48e-01 +1.54e-01 +2.17e-01 +1.14e-01 +3.49e-02 +2.43e-01 +2.30e-01 +2.33e-01 +2.43e-01 +2.45e-01 +2.48e-01 +2.46e-01 +2.21e-01 +2.22e-01 +2.12e-01 +2.00e-01 +2.02e-01 +2.14e-01 +8.94e-02 +3.01e-02 +2.79e-01 +2.63e-01 +2.52e-01 +2.63e-01 +3.00e-01 +2.68e-01 +2.93e-01 +1.90e-01 +2.03e-01 +1.54e-01 +1.13e-01 +1.27e-01 +8.82e-02 +3.87e-02 +1.55e-02 +1.03e-01 +9.09e-02 +9.09e-02 +8.99e-02 +1.07e-01 +8.75e-02 +1.17e-01 +5.67e-02 +7.16e-02 +5.22e-02 +3.31e-02 +3.49e-02 +2.69e-02 +1.66e-02 +1.14e-02 +1.46e-01 +1.55e-01 +1.57e-01 +1.59e-01 +1.63e-01 +1.54e-01 +1.49e-01 +1.48e-01 +1.45e-01 +1.48e-01 +1.48e-01 +1.54e-01 +2.45e-01 +2.69e-01 +8.34e-02 +1.54e-01 +1.59e-01 +1.57e-01 +1.51e-01 +1.55e-01 +1.57e-01 +1.60e-01 +1.49e-01 +1.54e-01 +1.50e-01 +1.54e-01 +1.62e-01 +2.41e-01 +2.97e-01 +1.15e-01 +1.60e-01 +1.61e-01 +1.61e-01 +1.53e-01 +1.56e-01 +1.52e-01 +1.53e-01 +1.57e-01 +1.52e-01 +1.52e-01 +1.55e-01 +1.69e-01 +2.49e-01 +2.52e-01 +8.15e-02 +1.59e-01 +1.59e-01 +1.52e-01 +1.50e-01 +1.53e-01 +1.59e-01 +1.47e-01 +1.51e-01 +1.51e-01 +1.56e-01 +1.56e-01 +1.64e-01 +2.40e-01 +2.46e-01 +8.06e-02 +1.58e-01 +1.59e-01 +1.58e-01 +1.56e-01 +1.53e-01 +1.46e-01 +1.48e-01 +1.48e-01 +1.47e-01 +1.53e-01 +1.48e-01 +1.62e-01 +2.39e-01 +2.66e-01 +1.02e-01 +1.55e-01 +1.54e-01 +1.57e-01 +1.52e-01 +1.48e-01 +1.50e-01 +1.40e-01 +1.37e-01 +1.50e-01 +1.50e-01 +1.46e-01 +1.52e-01 +2.23e-01 +2.22e-01 +8.99e-02 +1.58e-01 +1.60e-01 +1.54e-01 +1.47e-01 +1.47e-01 +1.45e-01 +1.42e-01 +1.28e-01 +1.33e-01 +1.40e-01 +1.51e-01 +1.53e-01 +2.08e-01 +1.44e-01 +5.19e-02 +1.57e-01 +1.54e-01 +1.54e-01 +1.42e-01 +1.51e-01 +1.44e-01 +1.35e-01 +1.28e-01 +1.11e-01 +1.40e-01 +1.49e-01 +1.55e-01 +2.22e-01 +1.31e-01 +4.13e-02 +1.56e-01 +1.53e-01 +1.57e-01 +1.42e-01 +1.52e-01 +1.49e-01 +1.45e-01 +1.32e-01 +1.30e-01 +1.27e-01 +1.41e-01 +1.41e-01 +2.11e-01 +1.55e-01 +4.43e-02 +1.49e-01 +1.52e-01 +1.53e-01 +1.56e-01 +1.47e-01 +1.45e-01 +1.40e-01 +1.37e-01 +1.43e-01 +1.35e-01 +1.34e-01 +1.42e-01 +1.98e-01 +1.25e-01 +3.99e-02 +1.57e-01 +1.48e-01 +1.49e-01 +1.52e-01 +1.50e-01 +1.48e-01 +1.50e-01 +1.48e-01 +1.44e-01 +1.43e-01 +1.35e-01 +1.39e-01 +1.94e-01 +1.07e-01 +3.11e-02 +1.62e-01 +1.61e-01 +1.61e-01 +1.63e-01 +1.63e-01 +1.60e-01 +1.55e-01 +1.55e-01 +1.50e-01 +1.56e-01 +1.43e-01 +1.32e-01 +1.83e-01 +1.02e-01 +2.51e-02 +2.34e-01 +2.25e-01 +2.20e-01 +2.27e-01 +2.41e-01 +2.34e-01 +2.13e-01 +2.04e-01 +2.13e-01 +2.16e-01 +2.20e-01 +1.80e-01 +1.76e-01 +9.68e-02 +3.54e-02 +3.12e-01 +2.64e-01 +2.26e-01 +2.68e-01 +2.60e-01 +2.43e-01 +2.35e-01 +1.81e-01 +1.60e-01 +1.47e-01 +1.51e-01 +1.34e-01 +7.35e-02 +4.05e-02 +2.19e-02 +1.06e-01 +9.90e-02 +8.77e-02 +9.61e-02 +9.91e-02 +7.56e-02 +1.05e-01 +6.20e-02 +5.67e-02 +3.97e-02 +4.31e-02 +4.78e-02 +2.87e-02 +1.59e-02 +8.92e-03 +1.61e-01 +1.59e-01 +1.56e-01 +1.59e-01 +1.51e-01 +1.49e-01 +1.54e-01 +1.51e-01 +1.53e-01 +1.48e-01 +1.52e-01 +1.48e-01 +2.27e-01 +1.82e-01 +8.22e-02 +1.61e-01 +1.54e-01 +1.57e-01 +1.56e-01 +1.53e-01 +1.49e-01 +1.46e-01 +1.41e-01 +1.51e-01 +1.51e-01 +1.52e-01 +1.52e-01 +2.22e-01 +2.08e-01 +7.81e-02 +1.54e-01 +1.58e-01 +1.52e-01 +1.53e-01 +1.48e-01 +1.49e-01 +1.45e-01 +1.43e-01 +1.44e-01 +1.48e-01 +1.58e-01 +1.55e-01 +2.25e-01 +2.48e-01 +1.01e-01 +1.57e-01 +1.48e-01 +1.51e-01 +1.47e-01 +1.46e-01 +1.52e-01 +1.48e-01 +1.47e-01 +1.46e-01 +1.48e-01 +1.49e-01 +1.57e-01 +2.21e-01 +1.69e-01 +6.52e-02 +1.57e-01 +1.55e-01 +1.55e-01 +1.53e-01 +1.51e-01 +1.55e-01 +1.46e-01 +1.49e-01 +1.42e-01 +1.45e-01 +1.45e-01 +1.57e-01 +2.19e-01 +1.36e-01 +4.96e-02 +1.57e-01 +1.50e-01 +1.53e-01 +1.57e-01 +1.54e-01 +1.50e-01 +1.45e-01 +1.40e-01 +1.30e-01 +1.38e-01 +1.42e-01 +1.51e-01 +2.13e-01 +1.41e-01 +4.31e-02 +1.56e-01 +1.57e-01 +1.53e-01 +1.52e-01 +1.51e-01 +1.44e-01 +1.42e-01 +1.28e-01 +1.21e-01 +1.26e-01 +1.40e-01 +1.53e-01 +2.16e-01 +1.61e-01 +4.81e-02 +1.48e-01 +1.48e-01 +1.47e-01 +1.49e-01 +1.49e-01 +1.43e-01 +1.40e-01 +1.23e-01 +8.97e-02 +1.34e-01 +1.43e-01 +1.48e-01 +2.10e-01 +1.68e-01 +5.02e-02 +1.53e-01 +1.48e-01 +1.47e-01 +1.48e-01 +1.43e-01 +1.46e-01 +1.42e-01 +1.27e-01 +1.24e-01 +1.31e-01 +1.42e-01 +1.48e-01 +2.01e-01 +1.56e-01 +5.15e-02 +1.50e-01 +1.52e-01 +1.52e-01 +1.47e-01 +1.42e-01 +1.35e-01 +1.39e-01 +1.38e-01 +1.37e-01 +1.35e-01 +1.36e-01 +1.36e-01 +1.92e-01 +1.22e-01 +4.76e-02 +1.52e-01 +1.61e-01 +1.49e-01 +1.45e-01 +1.40e-01 +1.40e-01 +1.34e-01 +1.35e-01 +1.38e-01 +1.38e-01 +1.27e-01 +1.35e-01 +1.77e-01 +7.50e-02 +2.64e-02 +1.58e-01 +1.55e-01 +1.50e-01 +1.45e-01 +1.50e-01 +1.50e-01 +1.42e-01 +1.42e-01 +1.52e-01 +1.46e-01 +1.38e-01 +1.30e-01 +1.44e-01 +6.83e-02 +2.86e-02 +2.28e-01 +2.11e-01 +2.08e-01 +2.12e-01 +2.17e-01 +2.09e-01 +2.06e-01 +1.89e-01 +2.08e-01 +1.88e-01 +1.88e-01 +1.66e-01 +1.33e-01 +4.13e-02 +1.70e-02 +2.67e-01 +2.70e-01 +2.31e-01 +1.72e-01 +1.66e-01 +2.16e-01 +1.96e-01 +1.52e-01 +1.41e-01 +1.31e-01 +1.44e-01 +1.17e-01 +4.82e-02 +2.09e-02 +8.63e-03 +9.47e-02 +1.04e-01 +9.02e-02 +5.55e-02 +5.39e-02 +6.95e-02 +7.10e-02 +4.92e-02 +4.73e-02 +3.52e-02 +4.69e-02 +4.03e-02 +1.69e-02 +9.25e-03 +5.98e-03 +1.46e-01 +1.50e-01 +1.48e-01 +1.53e-01 +1.56e-01 +1.57e-01 +1.56e-01 +1.54e-01 +1.44e-01 +1.42e-01 +1.44e-01 +1.52e-01 +2.10e-01 +1.56e-01 +5.53e-02 +1.50e-01 +1.57e-01 +1.57e-01 +1.57e-01 +1.54e-01 +1.51e-01 +1.52e-01 +1.42e-01 +1.42e-01 +1.39e-01 +1.45e-01 +1.50e-01 +2.11e-01 +1.18e-01 +3.92e-02 +1.49e-01 +1.48e-01 +1.55e-01 +1.54e-01 +1.51e-01 +1.50e-01 +1.48e-01 +1.48e-01 +1.41e-01 +1.38e-01 +1.45e-01 +1.54e-01 +2.08e-01 +1.62e-01 +5.89e-02 +1.48e-01 +1.49e-01 +1.51e-01 +1.50e-01 +1.51e-01 +1.49e-01 +1.48e-01 +1.47e-01 +1.34e-01 +1.33e-01 +1.53e-01 +1.54e-01 +1.96e-01 +1.55e-01 +6.15e-02 +1.51e-01 +1.53e-01 +1.59e-01 +1.48e-01 +1.51e-01 +1.48e-01 +1.45e-01 +1.41e-01 +1.38e-01 +1.37e-01 +1.49e-01 +1.46e-01 +2.06e-01 +1.25e-01 +3.47e-02 +1.51e-01 +1.47e-01 +1.52e-01 +1.53e-01 +1.44e-01 +1.40e-01 +1.45e-01 +1.38e-01 +1.39e-01 +1.34e-01 +1.36e-01 +1.50e-01 +1.95e-01 +1.29e-01 +4.48e-02 +1.49e-01 +1.43e-01 +1.49e-01 +1.46e-01 +1.48e-01 +1.42e-01 +1.35e-01 +1.27e-01 +1.24e-01 +1.26e-01 +1.29e-01 +1.38e-01 +1.87e-01 +1.53e-01 +5.84e-02 +1.46e-01 +1.51e-01 +1.44e-01 +1.45e-01 +1.45e-01 +1.41e-01 +1.31e-01 +1.24e-01 +1.29e-01 +1.30e-01 +1.43e-01 +1.42e-01 +1.91e-01 +1.36e-01 +5.08e-02 +1.39e-01 +1.40e-01 +1.43e-01 +1.39e-01 +1.43e-01 +1.39e-01 +1.35e-01 +1.27e-01 +1.23e-01 +1.22e-01 +1.41e-01 +1.37e-01 +1.86e-01 +1.09e-01 +3.41e-02 +1.44e-01 +1.41e-01 +1.44e-01 +1.37e-01 +1.37e-01 +1.39e-01 +1.29e-01 +1.33e-01 +1.20e-01 +1.18e-01 +1.29e-01 +1.36e-01 +1.94e-01 +1.23e-01 +3.96e-02 +1.50e-01 +1.47e-01 +1.48e-01 +1.39e-01 +1.35e-01 +1.36e-01 +1.34e-01 +1.40e-01 +1.32e-01 +1.27e-01 +1.23e-01 +1.26e-01 +1.61e-01 +8.34e-02 +2.18e-02 +1.58e-01 +1.51e-01 +1.50e-01 +1.46e-01 +1.45e-01 +1.35e-01 +1.30e-01 +1.41e-01 +1.39e-01 +1.36e-01 +1.32e-01 +1.22e-01 +1.45e-01 +5.48e-02 +1.56e-02 +2.28e-01 +2.05e-01 +2.09e-01 +1.89e-01 +2.03e-01 +1.97e-01 +1.63e-01 +1.67e-01 +1.68e-01 +1.92e-01 +1.83e-01 +1.55e-01 +1.14e-01 +3.43e-02 +1.15e-02 +2.24e-01 +2.31e-01 +1.67e-01 +1.43e-01 +1.53e-01 +1.76e-01 +1.13e-01 +9.76e-02 +8.62e-02 +1.49e-01 +1.29e-01 +8.35e-02 +4.44e-02 +1.21e-02 +4.84e-03 +7.75e-02 +7.83e-02 +5.37e-02 +5.23e-02 +4.63e-02 +6.06e-02 +4.19e-02 +3.16e-02 +2.72e-02 +5.09e-02 +4.06e-02 +2.76e-02 +1.94e-02 +8.81e-03 +2.34e-03 +1.54e-01 +1.58e-01 +1.62e-01 +1.48e-01 +1.57e-01 +1.52e-01 +1.49e-01 +1.54e-01 +1.42e-01 +1.36e-01 +1.36e-01 +1.50e-01 +2.08e-01 +1.23e-01 +3.56e-02 +1.51e-01 +1.53e-01 +1.59e-01 +1.57e-01 +1.46e-01 +1.55e-01 +1.50e-01 +1.49e-01 +1.41e-01 +1.41e-01 +1.42e-01 +1.48e-01 +2.00e-01 +1.32e-01 +4.13e-02 +1.48e-01 +1.50e-01 +1.53e-01 +1.52e-01 +1.54e-01 +1.42e-01 +1.48e-01 +1.50e-01 +1.40e-01 +1.37e-01 +1.38e-01 +1.53e-01 +2.20e-01 +1.65e-01 +5.51e-02 +1.52e-01 +1.44e-01 +1.49e-01 +1.46e-01 +1.48e-01 +1.48e-01 +1.45e-01 +1.46e-01 +1.38e-01 +1.40e-01 +1.43e-01 +1.39e-01 +1.95e-01 +1.78e-01 +7.38e-02 +1.48e-01 +1.45e-01 +1.44e-01 +1.47e-01 +1.46e-01 +1.44e-01 +1.42e-01 +1.45e-01 +1.43e-01 +1.37e-01 +1.43e-01 +1.49e-01 +1.94e-01 +1.37e-01 +4.11e-02 +1.52e-01 +1.48e-01 +1.45e-01 +1.38e-01 +1.38e-01 +1.40e-01 +1.41e-01 +1.32e-01 +1.36e-01 +1.36e-01 +1.37e-01 +1.48e-01 +2.02e-01 +1.53e-01 +5.25e-02 +1.45e-01 +1.44e-01 +1.44e-01 +1.36e-01 +1.36e-01 +1.36e-01 +1.33e-01 +1.32e-01 +1.26e-01 +1.32e-01 +1.37e-01 +1.38e-01 +1.71e-01 +1.19e-01 +4.26e-02 +1.40e-01 +1.43e-01 +1.46e-01 +1.39e-01 +1.38e-01 +1.34e-01 +1.29e-01 +1.26e-01 +1.28e-01 +1.28e-01 +1.34e-01 +1.39e-01 +1.70e-01 +7.71e-02 +3.01e-02 +1.38e-01 +1.35e-01 +1.38e-01 +1.41e-01 +1.44e-01 +1.46e-01 +1.34e-01 +1.28e-01 +1.24e-01 +1.20e-01 +1.22e-01 +1.33e-01 +1.84e-01 +8.77e-02 +2.26e-02 +1.45e-01 +1.37e-01 +1.37e-01 +1.29e-01 +1.34e-01 +1.37e-01 +1.34e-01 +1.29e-01 +1.23e-01 +1.19e-01 +1.21e-01 +1.25e-01 +1.84e-01 +9.18e-02 +2.31e-02 +1.48e-01 +1.32e-01 +1.36e-01 +1.34e-01 +1.41e-01 +1.34e-01 +1.32e-01 +1.27e-01 +1.27e-01 +1.20e-01 +1.10e-01 +1.17e-01 +1.65e-01 +9.68e-02 +2.83e-02 +1.48e-01 +1.35e-01 +1.35e-01 +1.43e-01 +1.44e-01 +1.40e-01 +1.43e-01 +1.35e-01 +1.34e-01 +1.29e-01 +1.17e-01 +9.99e-02 +9.90e-02 +5.49e-02 +1.66e-02 +2.22e-01 +2.00e-01 +2.03e-01 +2.10e-01 +2.09e-01 +1.99e-01 +1.86e-01 +1.93e-01 +1.79e-01 +1.90e-01 +1.70e-01 +1.45e-01 +1.22e-01 +4.35e-02 +1.22e-02 +2.30e-01 +1.84e-01 +1.79e-01 +1.67e-01 +1.72e-01 +1.61e-01 +1.13e-01 +1.16e-01 +1.06e-01 +1.13e-01 +1.20e-01 +8.32e-02 +4.37e-02 +1.70e-02 +5.87e-03 +7.01e-02 +5.48e-02 +4.65e-02 +4.76e-02 +5.00e-02 +5.48e-02 +3.77e-02 +3.45e-02 +3.12e-02 +3.14e-02 +3.88e-02 +2.77e-02 +1.27e-02 +6.38e-03 +1.50e-03 +1.48e-01 +1.54e-01 +1.58e-01 +1.56e-01 +1.55e-01 +1.46e-01 +1.44e-01 +1.47e-01 +1.42e-01 +1.44e-01 +1.40e-01 +1.54e-01 +1.94e-01 +1.38e-01 +5.04e-02 +1.51e-01 +1.53e-01 +1.49e-01 +1.49e-01 +1.49e-01 +1.53e-01 +1.51e-01 +1.50e-01 +1.45e-01 +1.36e-01 +1.42e-01 +1.57e-01 +2.09e-01 +1.55e-01 +4.56e-02 +1.55e-01 +1.48e-01 +1.43e-01 +1.44e-01 +1.46e-01 +1.53e-01 +1.50e-01 +1.51e-01 +1.54e-01 +1.38e-01 +1.36e-01 +1.52e-01 +2.10e-01 +1.66e-01 +5.44e-02 +1.48e-01 +1.47e-01 +1.46e-01 +1.43e-01 +1.49e-01 +1.48e-01 +1.51e-01 +1.49e-01 +1.42e-01 +1.38e-01 +1.39e-01 +1.49e-01 +1.94e-01 +1.42e-01 +5.42e-02 +1.43e-01 +1.39e-01 +1.46e-01 +1.48e-01 +1.48e-01 +1.42e-01 +1.41e-01 +1.46e-01 +1.40e-01 +1.38e-01 +1.43e-01 +1.43e-01 +1.95e-01 +1.20e-01 +3.81e-02 +1.49e-01 +1.42e-01 +1.47e-01 +1.42e-01 +1.43e-01 +1.43e-01 +1.42e-01 +1.41e-01 +1.36e-01 +1.33e-01 +1.38e-01 +1.42e-01 +1.87e-01 +1.10e-01 +4.00e-02 +1.46e-01 +1.44e-01 +1.42e-01 +1.42e-01 +1.42e-01 +1.43e-01 +1.36e-01 +1.37e-01 +1.32e-01 +1.37e-01 +1.30e-01 +1.42e-01 +1.97e-01 +1.41e-01 +4.74e-02 +1.42e-01 +1.46e-01 +1.45e-01 +1.44e-01 +1.40e-01 +1.37e-01 +1.29e-01 +1.32e-01 +1.28e-01 +1.22e-01 +1.29e-01 +1.44e-01 +1.79e-01 +8.11e-02 +2.49e-02 +1.36e-01 +1.40e-01 +1.42e-01 +1.45e-01 +1.42e-01 +1.35e-01 +1.28e-01 +1.25e-01 +1.21e-01 +1.20e-01 +1.24e-01 +1.37e-01 +1.76e-01 +8.67e-02 +2.55e-02 +1.41e-01 +1.39e-01 +1.39e-01 +1.42e-01 +1.37e-01 +1.33e-01 +1.30e-01 +1.23e-01 +1.22e-01 +1.24e-01 +1.16e-01 +1.28e-01 +1.68e-01 +1.03e-01 +3.30e-02 +1.41e-01 +1.35e-01 +1.34e-01 +1.33e-01 +1.33e-01 +1.34e-01 +1.37e-01 +1.31e-01 +1.21e-01 +1.17e-01 +1.11e-01 +1.13e-01 +1.57e-01 +8.80e-02 +2.97e-02 +1.53e-01 +1.39e-01 +1.45e-01 +1.45e-01 +1.38e-01 +1.49e-01 +1.41e-01 +1.36e-01 +1.24e-01 +1.23e-01 +1.14e-01 +9.70e-02 +1.01e-01 +5.74e-02 +1.83e-02 +2.39e-01 +1.91e-01 +1.80e-01 +2.07e-01 +1.97e-01 +2.03e-01 +1.92e-01 +2.07e-01 +1.99e-01 +2.04e-01 +1.76e-01 +1.35e-01 +1.07e-01 +3.67e-02 +1.30e-02 +1.91e-01 +1.54e-01 +1.49e-01 +1.68e-01 +1.82e-01 +1.72e-01 +1.48e-01 +1.49e-01 +1.30e-01 +1.06e-01 +9.60e-02 +7.06e-02 +4.00e-02 +1.71e-02 +7.45e-03 +7.31e-02 +6.14e-02 +6.36e-02 +5.75e-02 +6.28e-02 +5.80e-02 +4.73e-02 +4.31e-02 +4.49e-02 +3.41e-02 +3.27e-02 +2.30e-02 +1.26e-02 +7.67e-03 +3.56e-03 +1.42e-01 +1.47e-01 +1.56e-01 +1.59e-01 +1.58e-01 +1.56e-01 +1.58e-01 +1.63e-01 +1.48e-01 +1.51e-01 +1.49e-01 +1.44e-01 +1.75e-01 +9.73e-02 +3.77e-02 +1.45e-01 +1.50e-01 +1.49e-01 +1.50e-01 +1.53e-01 +1.52e-01 +1.51e-01 +1.54e-01 +1.52e-01 +1.51e-01 +1.48e-01 +1.46e-01 +1.93e-01 +1.30e-01 +4.16e-02 +1.46e-01 +1.48e-01 +1.46e-01 +1.53e-01 +1.47e-01 +1.54e-01 +1.58e-01 +1.45e-01 +1.47e-01 +1.46e-01 +1.45e-01 +1.50e-01 +1.99e-01 +1.42e-01 +4.85e-02 +1.46e-01 +1.48e-01 +1.56e-01 +1.44e-01 +1.54e-01 +1.56e-01 +1.52e-01 +1.46e-01 +1.44e-01 +1.37e-01 +1.38e-01 +1.46e-01 +1.94e-01 +1.11e-01 +3.61e-02 +1.45e-01 +1.48e-01 +1.51e-01 +1.46e-01 +1.54e-01 +1.54e-01 +1.45e-01 +1.44e-01 +1.48e-01 +1.40e-01 +1.46e-01 +1.49e-01 +1.93e-01 +1.06e-01 +3.16e-02 +1.45e-01 +1.48e-01 +1.50e-01 +1.56e-01 +1.46e-01 +1.51e-01 +1.44e-01 +1.41e-01 +1.32e-01 +1.41e-01 +1.41e-01 +1.39e-01 +2.04e-01 +1.15e-01 +3.10e-02 +1.41e-01 +1.46e-01 +1.41e-01 +1.48e-01 +1.45e-01 +1.45e-01 +1.44e-01 +1.38e-01 +1.34e-01 +1.34e-01 +1.38e-01 +1.42e-01 +1.85e-01 +1.26e-01 +3.53e-02 +1.38e-01 +1.45e-01 +1.45e-01 +1.49e-01 +1.41e-01 +1.40e-01 +1.33e-01 +1.34e-01 +1.33e-01 +1.36e-01 +1.35e-01 +1.37e-01 +1.76e-01 +1.16e-01 +4.10e-02 +1.35e-01 +1.39e-01 +1.44e-01 +1.43e-01 +1.44e-01 +1.40e-01 +1.27e-01 +1.29e-01 +1.24e-01 +1.24e-01 +1.21e-01 +1.25e-01 +1.74e-01 +9.17e-02 +2.60e-02 +1.39e-01 +1.35e-01 +1.40e-01 +1.43e-01 +1.45e-01 +1.32e-01 +1.32e-01 +1.29e-01 +1.20e-01 +1.22e-01 +1.14e-01 +1.15e-01 +1.43e-01 +8.26e-02 +2.82e-02 +1.37e-01 +1.36e-01 +1.37e-01 +1.38e-01 +1.39e-01 +1.32e-01 +1.34e-01 +1.31e-01 +1.19e-01 +1.18e-01 +1.10e-01 +1.13e-01 +1.51e-01 +5.50e-02 +1.63e-02 +1.36e-01 +1.38e-01 +1.40e-01 +1.44e-01 +1.37e-01 +1.43e-01 +1.44e-01 +1.39e-01 +1.26e-01 +1.21e-01 +1.14e-01 +1.05e-01 +1.24e-01 +4.77e-02 +1.43e-02 +1.96e-01 +1.79e-01 +1.80e-01 +1.95e-01 +1.99e-01 +1.94e-01 +1.81e-01 +1.92e-01 +1.80e-01 +1.60e-01 +1.61e-01 +1.34e-01 +7.13e-02 +2.07e-02 +7.42e-03 +1.76e-01 +9.49e-02 +1.22e-01 +1.31e-01 +1.35e-01 +1.34e-01 +1.06e-01 +1.42e-01 +1.15e-01 +7.49e-02 +8.42e-02 +5.97e-02 +2.86e-02 +1.13e-02 +3.88e-03 +5.74e-02 +3.06e-02 +3.68e-02 +4.09e-02 +5.26e-02 +4.71e-02 +3.30e-02 +4.67e-02 +3.85e-02 +2.55e-02 +2.40e-02 +1.51e-02 +1.04e-02 +6.23e-03 +3.39e-03 +1.51e-01 +1.61e-01 +1.68e-01 +1.62e-01 +1.62e-01 +1.64e-01 +1.64e-01 +1.55e-01 +1.53e-01 +1.51e-01 +1.54e-01 +1.44e-01 +1.43e-01 +3.50e-02 +1.38e-02 +1.65e-01 +1.68e-01 +1.64e-01 +1.54e-01 +1.51e-01 +1.54e-01 +1.57e-01 +1.50e-01 +1.50e-01 +1.49e-01 +1.55e-01 +1.49e-01 +1.60e-01 +6.68e-02 +2.43e-02 +1.59e-01 +1.68e-01 +1.59e-01 +1.54e-01 +1.55e-01 +1.66e-01 +1.64e-01 +1.60e-01 +1.47e-01 +1.49e-01 +1.42e-01 +1.60e-01 +2.06e-01 +1.03e-01 +2.77e-02 +1.56e-01 +1.59e-01 +1.54e-01 +1.54e-01 +1.59e-01 +1.65e-01 +1.63e-01 +1.43e-01 +1.52e-01 +1.45e-01 +1.35e-01 +1.44e-01 +1.79e-01 +9.16e-02 +3.06e-02 +1.50e-01 +1.51e-01 +1.54e-01 +1.55e-01 +1.59e-01 +1.59e-01 +1.54e-01 +1.47e-01 +1.53e-01 +1.51e-01 +1.40e-01 +1.45e-01 +1.90e-01 +1.07e-01 +2.94e-02 +1.50e-01 +1.52e-01 +1.48e-01 +1.54e-01 +1.52e-01 +1.51e-01 +1.50e-01 +1.43e-01 +1.47e-01 +1.46e-01 +1.38e-01 +1.35e-01 +1.78e-01 +9.46e-02 +3.09e-02 +1.41e-01 +1.42e-01 +1.53e-01 +1.56e-01 +1.53e-01 +1.48e-01 +1.48e-01 +1.41e-01 +1.38e-01 +1.40e-01 +1.40e-01 +1.30e-01 +1.49e-01 +9.60e-02 +3.33e-02 +1.42e-01 +1.46e-01 +1.40e-01 +1.50e-01 +1.45e-01 +1.45e-01 +1.50e-01 +1.45e-01 +1.41e-01 +1.33e-01 +1.36e-01 +1.37e-01 +1.71e-01 +7.74e-02 +2.54e-02 +1.45e-01 +1.43e-01 +1.47e-01 +1.51e-01 +1.49e-01 +1.54e-01 +1.42e-01 +1.37e-01 +1.34e-01 +1.33e-01 +1.27e-01 +1.27e-01 +1.66e-01 +7.29e-02 +2.38e-02 +1.44e-01 +1.39e-01 +1.51e-01 +1.49e-01 +1.48e-01 +1.46e-01 +1.38e-01 +1.29e-01 +1.23e-01 +1.18e-01 +1.23e-01 +1.14e-01 +1.60e-01 +7.60e-02 +2.69e-02 +1.34e-01 +1.33e-01 +1.39e-01 +1.41e-01 +1.50e-01 +1.43e-01 +1.31e-01 +1.28e-01 +1.23e-01 +1.19e-01 +1.12e-01 +1.08e-01 +1.37e-01 +5.02e-02 +1.48e-02 +1.33e-01 +1.31e-01 +1.34e-01 +1.36e-01 +1.35e-01 +1.30e-01 +1.37e-01 +1.40e-01 +1.27e-01 +1.22e-01 +1.08e-01 +1.01e-01 +1.15e-01 +4.47e-02 +1.28e-02 +1.73e-01 +1.60e-01 +1.60e-01 +1.70e-01 +1.65e-01 +1.66e-01 +1.67e-01 +1.80e-01 +1.81e-01 +1.46e-01 +1.35e-01 +1.18e-01 +6.95e-02 +2.21e-02 +7.42e-03 +1.30e-01 +7.02e-02 +8.47e-02 +1.05e-01 +8.11e-02 +8.16e-02 +7.32e-02 +7.94e-02 +1.08e-01 +7.59e-02 +5.62e-02 +4.38e-02 +2.63e-02 +6.47e-03 +2.09e-03 +5.13e-02 +2.67e-02 +3.48e-02 +3.62e-02 +2.93e-02 +2.83e-02 +2.47e-02 +2.60e-02 +3.29e-02 +2.55e-02 +1.63e-02 +1.59e-02 +1.02e-02 +3.46e-03 +1.50e-03 +2.17e-01 +2.25e-01 +2.43e-01 +2.43e-01 +2.31e-01 +2.22e-01 +2.22e-01 +2.12e-01 +2.21e-01 +1.98e-01 +1.99e-01 +2.04e-01 +1.57e-01 +3.11e-02 +7.85e-03 +2.30e-01 +2.10e-01 +2.47e-01 +2.20e-01 +2.20e-01 +2.29e-01 +2.22e-01 +2.06e-01 +1.98e-01 +2.04e-01 +1.90e-01 +1.83e-01 +1.78e-01 +5.75e-02 +1.61e-02 +2.24e-01 +2.08e-01 +2.36e-01 +2.21e-01 +2.12e-01 +2.36e-01 +2.08e-01 +2.30e-01 +2.06e-01 +2.08e-01 +1.93e-01 +1.84e-01 +2.10e-01 +9.89e-02 +3.10e-02 +2.25e-01 +2.06e-01 +2.18e-01 +2.25e-01 +2.19e-01 +2.39e-01 +2.22e-01 +2.20e-01 +2.12e-01 +2.02e-01 +1.75e-01 +1.62e-01 +1.73e-01 +6.25e-02 +1.80e-02 +2.16e-01 +2.06e-01 +2.12e-01 +2.21e-01 +2.09e-01 +2.25e-01 +2.54e-01 +2.18e-01 +2.16e-01 +2.23e-01 +1.99e-01 +1.77e-01 +1.57e-01 +7.90e-02 +2.92e-02 +2.13e-01 +1.96e-01 +2.07e-01 +2.04e-01 +2.00e-01 +2.17e-01 +2.17e-01 +2.01e-01 +2.06e-01 +2.15e-01 +1.92e-01 +1.66e-01 +1.68e-01 +8.03e-02 +2.44e-02 +1.90e-01 +1.93e-01 +2.12e-01 +2.07e-01 +2.00e-01 +2.08e-01 +2.04e-01 +1.97e-01 +1.68e-01 +1.90e-01 +1.82e-01 +1.49e-01 +1.55e-01 +6.10e-02 +1.96e-02 +1.94e-01 +1.89e-01 +2.00e-01 +2.16e-01 +2.22e-01 +2.06e-01 +2.02e-01 +1.99e-01 +1.77e-01 +1.77e-01 +1.83e-01 +1.68e-01 +1.29e-01 +3.50e-02 +1.16e-02 +1.93e-01 +2.00e-01 +2.02e-01 +2.11e-01 +2.16e-01 +1.93e-01 +2.15e-01 +2.05e-01 +1.80e-01 +1.82e-01 +1.71e-01 +1.65e-01 +1.61e-01 +4.47e-02 +1.50e-02 +1.94e-01 +2.06e-01 +2.19e-01 +2.03e-01 +2.12e-01 +1.96e-01 +2.02e-01 +1.81e-01 +1.66e-01 +1.60e-01 +1.62e-01 +1.62e-01 +1.56e-01 +4.92e-02 +1.53e-02 +1.78e-01 +1.86e-01 +2.05e-01 +2.04e-01 +1.97e-01 +2.15e-01 +1.88e-01 +1.63e-01 +1.57e-01 +1.38e-01 +1.47e-01 +1.39e-01 +1.01e-01 +4.20e-02 +1.54e-02 +1.47e-01 +1.52e-01 +1.77e-01 +1.74e-01 +1.84e-01 +1.91e-01 +2.00e-01 +1.85e-01 +1.68e-01 +1.42e-01 +1.34e-01 +1.28e-01 +9.77e-02 +2.94e-02 +1.17e-02 +1.28e-01 +1.39e-01 +1.56e-01 +1.44e-01 +1.64e-01 +1.66e-01 +1.39e-01 +1.87e-01 +1.87e-01 +1.57e-01 +1.18e-01 +9.64e-02 +6.30e-02 +1.82e-02 +8.08e-03 +7.18e-02 +4.39e-02 +5.20e-02 +6.11e-02 +4.96e-02 +4.82e-02 +4.41e-02 +5.38e-02 +8.79e-02 +5.77e-02 +2.70e-02 +2.61e-02 +1.49e-02 +4.93e-03 +2.78e-03 +2.74e-02 +1.41e-02 +1.81e-02 +1.93e-02 +1.48e-02 +1.51e-02 +1.60e-02 +1.57e-02 +2.80e-02 +2.17e-02 +1.10e-02 +8.28e-03 +4.76e-03 +2.53e-03 +9.95e-04 +2.15e-01 +2.13e-01 +2.69e-01 +1.97e-01 +2.97e-01 +2.44e-01 +2.16e-01 +2.27e-01 +1.95e-01 +1.69e-01 +1.21e-01 +1.27e-01 +1.07e-01 +2.85e-02 +7.97e-03 +2.71e-01 +1.94e-01 +3.20e-01 +3.60e-01 +2.90e-01 +2.34e-01 +1.99e-01 +2.04e-01 +1.73e-01 +1.40e-01 +1.29e-01 +1.08e-01 +8.99e-02 +4.08e-02 +1.48e-02 +2.36e-01 +2.27e-01 +2.95e-01 +2.38e-01 +1.88e-01 +2.38e-01 +1.95e-01 +2.03e-01 +1.85e-01 +1.65e-01 +1.78e-01 +1.40e-01 +7.70e-02 +4.33e-02 +2.87e-02 +2.64e-01 +2.27e-01 +2.72e-01 +3.07e-01 +2.33e-01 +2.58e-01 +2.71e-01 +2.36e-01 +2.17e-01 +1.73e-01 +1.26e-01 +9.14e-02 +6.26e-02 +3.03e-02 +1.37e-02 +2.60e-01 +1.95e-01 +2.57e-01 +3.08e-01 +2.65e-01 +2.75e-01 +2.70e-01 +2.25e-01 +1.83e-01 +1.71e-01 +1.54e-01 +8.13e-02 +5.27e-02 +2.61e-02 +1.31e-02 +2.09e-01 +1.59e-01 +2.23e-01 +2.30e-01 +1.63e-01 +1.84e-01 +2.17e-01 +1.62e-01 +1.39e-01 +1.79e-01 +1.60e-01 +1.05e-01 +5.74e-02 +2.47e-02 +1.55e-02 +1.50e-01 +1.80e-01 +2.09e-01 +2.09e-01 +1.82e-01 +2.00e-01 +1.82e-01 +1.57e-01 +1.32e-01 +1.22e-01 +9.76e-02 +6.91e-02 +4.61e-02 +2.65e-02 +1.49e-02 +1.12e-01 +1.42e-01 +1.70e-01 +1.73e-01 +1.86e-01 +1.93e-01 +1.34e-01 +1.27e-01 +9.53e-02 +8.75e-02 +9.91e-02 +7.87e-02 +4.52e-02 +2.11e-02 +7.92e-03 +1.22e-01 +1.39e-01 +1.69e-01 +2.22e-01 +1.74e-01 +1.60e-01 +1.21e-01 +1.47e-01 +1.19e-01 +1.07e-01 +9.89e-02 +8.43e-02 +5.88e-02 +2.01e-02 +8.74e-03 +9.13e-02 +1.61e-01 +2.14e-01 +1.61e-01 +1.43e-01 +1.01e-01 +1.25e-01 +1.42e-01 +1.15e-01 +9.15e-02 +8.87e-02 +6.91e-02 +5.19e-02 +2.72e-02 +8.44e-03 +9.87e-02 +1.23e-01 +1.61e-01 +1.11e-01 +1.42e-01 +1.45e-01 +1.19e-01 +1.11e-01 +8.68e-02 +5.91e-02 +7.60e-02 +6.84e-02 +4.18e-02 +2.17e-02 +9.67e-03 +8.56e-02 +7.21e-02 +1.04e-01 +9.72e-02 +9.63e-02 +1.38e-01 +1.19e-01 +7.48e-02 +6.52e-02 +5.51e-02 +5.18e-02 +5.14e-02 +3.71e-02 +9.91e-03 +3.65e-03 +4.31e-02 +5.32e-02 +6.28e-02 +4.91e-02 +5.90e-02 +8.59e-02 +7.51e-02 +7.34e-02 +6.10e-02 +6.19e-02 +4.42e-02 +3.60e-02 +2.68e-02 +1.01e-02 +4.00e-03 +1.92e-02 +2.02e-02 +2.30e-02 +1.98e-02 +2.54e-02 +3.35e-02 +2.56e-02 +3.19e-02 +3.49e-02 +2.85e-02 +1.72e-02 +1.18e-02 +8.37e-03 +4.19e-03 +2.02e-03 +1.38e-02 +7.42e-03 +8.36e-03 +8.57e-03 +9.08e-03 +1.23e-02 +8.17e-03 +1.02e-02 +1.45e-02 +1.35e-02 +6.28e-03 +3.59e-03 +3.28e-03 +1.49e-03 +5.93e-04 +7.32e-02 +7.34e-02 +9.95e-02 +6.68e-02 +1.09e-01 +8.20e-02 +7.49e-02 +7.93e-02 +7.32e-02 +5.71e-02 +3.93e-02 +3.97e-02 +3.57e-02 +2.10e-02 +7.29e-03 +1.10e-01 +5.23e-02 +1.07e-01 +1.46e-01 +1.24e-01 +8.85e-02 +7.13e-02 +6.52e-02 +6.37e-02 +5.06e-02 +3.96e-02 +3.23e-02 +2.70e-02 +1.79e-02 +8.68e-03 +8.76e-02 +6.93e-02 +1.01e-01 +9.79e-02 +5.28e-02 +8.50e-02 +7.16e-02 +6.22e-02 +6.61e-02 +4.64e-02 +6.19e-02 +5.31e-02 +2.85e-02 +1.22e-02 +1.28e-02 +8.68e-02 +7.27e-02 +1.06e-01 +1.20e-01 +8.06e-02 +8.67e-02 +9.61e-02 +8.95e-02 +8.71e-02 +6.56e-02 +4.39e-02 +3.10e-02 +2.08e-02 +1.24e-02 +5.85e-03 +1.06e-01 +7.84e-02 +9.79e-02 +1.14e-01 +1.02e-01 +9.64e-02 +1.00e-01 +7.46e-02 +7.16e-02 +5.01e-02 +5.24e-02 +2.38e-02 +1.47e-02 +8.69e-03 +5.50e-03 +6.48e-02 +5.48e-02 +7.05e-02 +7.64e-02 +6.18e-02 +5.28e-02 +6.84e-02 +5.17e-02 +4.47e-02 +5.44e-02 +5.78e-02 +4.35e-02 +2.02e-02 +8.44e-03 +4.29e-03 +6.15e-02 +5.80e-02 +7.42e-02 +8.23e-02 +4.82e-02 +5.80e-02 +5.99e-02 +5.52e-02 +5.07e-02 +4.11e-02 +3.25e-02 +2.74e-02 +1.58e-02 +7.93e-03 +7.33e-03 +3.07e-02 +5.33e-02 +5.17e-02 +5.32e-02 +5.86e-02 +6.36e-02 +3.92e-02 +4.20e-02 +3.02e-02 +2.37e-02 +2.60e-02 +2.49e-02 +1.38e-02 +1.14e-02 +8.22e-03 +3.87e-02 +4.20e-02 +5.55e-02 +6.88e-02 +6.66e-02 +6.23e-02 +3.77e-02 +4.86e-02 +4.16e-02 +3.47e-02 +2.92e-02 +2.68e-02 +2.21e-02 +9.30e-03 +3.87e-03 +2.33e-02 +4.93e-02 +6.49e-02 +6.24e-02 +4.58e-02 +2.96e-02 +4.02e-02 +4.66e-02 +4.25e-02 +2.79e-02 +2.67e-02 +2.26e-02 +1.37e-02 +1.36e-02 +7.38e-03 +3.11e-02 +5.02e-02 +5.87e-02 +3.06e-02 +4.64e-02 +4.05e-02 +2.71e-02 +3.77e-02 +3.09e-02 +2.18e-02 +2.34e-02 +1.91e-02 +1.39e-02 +1.02e-02 +7.91e-03 +2.26e-02 +2.44e-02 +3.23e-02 +2.90e-02 +2.99e-02 +4.72e-02 +4.07e-02 +3.01e-02 +2.01e-02 +1.70e-02 +1.57e-02 +1.51e-02 +1.50e-02 +4.17e-03 +1.63e-03 +1.86e-02 +1.84e-02 +2.18e-02 +2.46e-02 +1.84e-02 +3.15e-02 +2.72e-02 +2.27e-02 +1.73e-02 +2.03e-02 +1.19e-02 +1.20e-02 +1.14e-02 +4.19e-03 +1.45e-03 +6.21e-03 +9.69e-03 +9.74e-03 +7.36e-03 +1.15e-02 +1.57e-02 +1.41e-02 +1.64e-02 +1.88e-02 +1.03e-02 +1.09e-02 +6.84e-03 +4.29e-03 +2.69e-03 +2.28e-03 +4.54e-03 +5.03e-03 +5.40e-03 +5.12e-03 +6.04e-03 +1.01e-02 +6.83e-03 +6.36e-03 +1.16e-02 +7.66e-03 +5.81e-03 +3.71e-03 +1.48e-03 +1.00e-03 +1.45e-03 +Upper Bounds +8.55e-01 +8.64e-01 +8.59e-01 +8.67e-01 +8.53e-01 +8.16e-01 +8.31e-01 +8.50e-01 +8.39e-01 +8.80e-01 +8.66e-01 +8.96e-01 +1.31e+00 +2.22e+00 +8.38e-01 +8.15e-01 +8.82e-01 +8.67e-01 +7.61e-01 +8.32e-01 +8.60e-01 +8.41e-01 +8.42e-01 +8.32e-01 +8.34e-01 +8.80e-01 +8.65e-01 +1.22e+00 +1.82e+00 +6.12e-01 +8.56e-01 +8.31e-01 +8.43e-01 +8.52e-01 +8.33e-01 +8.57e-01 +8.19e-01 +7.77e-01 +8.33e-01 +8.19e-01 +8.41e-01 +9.08e-01 +1.30e+00 +2.06e+00 +7.76e-01 +8.38e-01 +8.08e-01 +8.28e-01 +8.25e-01 +8.10e-01 +8.21e-01 +8.13e-01 +8.31e-01 +8.17e-01 +8.36e-01 +7.91e-01 +8.95e-01 +1.26e+00 +2.05e+00 +6.73e-01 +8.25e-01 +8.69e-01 +8.34e-01 +8.56e-01 +8.29e-01 +8.11e-01 +8.01e-01 +8.32e-01 +7.75e-01 +8.33e-01 +8.31e-01 +9.11e-01 +1.34e+00 +1.73e+00 +6.60e-01 +8.48e-01 +8.49e-01 +8.41e-01 +8.41e-01 +8.43e-01 +7.95e-01 +8.08e-01 +7.96e-01 +7.85e-01 +7.74e-01 +8.58e-01 +8.98e-01 +1.29e+00 +1.49e+00 +5.32e-01 +8.39e-01 +7.98e-01 +7.91e-01 +8.03e-01 +7.88e-01 +7.96e-01 +7.81e-01 +7.66e-01 +7.83e-01 +7.62e-01 +7.93e-01 +7.72e-01 +1.21e+00 +1.65e+00 +6.11e-01 +8.18e-01 +8.25e-01 +8.44e-01 +8.18e-01 +8.00e-01 +8.28e-01 +8.00e-01 +7.60e-01 +7.23e-01 +7.65e-01 +7.83e-01 +7.01e-01 +1.04e+00 +9.49e-01 +3.97e-01 +8.24e-01 +8.27e-01 +8.57e-01 +8.58e-01 +8.13e-01 +8.03e-01 +7.83e-01 +7.70e-01 +7.46e-01 +7.07e-01 +7.24e-01 +7.65e-01 +1.18e+00 +9.01e-01 +2.88e-01 +8.34e-01 +8.73e-01 +8.70e-01 +8.56e-01 +8.29e-01 +7.71e-01 +7.75e-01 +7.58e-01 +7.63e-01 +7.06e-01 +7.64e-01 +7.43e-01 +1.07e+00 +8.82e-01 +3.35e-01 +8.58e-01 +8.29e-01 +8.72e-01 +8.48e-01 +8.46e-01 +7.87e-01 +7.50e-01 +7.65e-01 +7.38e-01 +7.34e-01 +8.10e-01 +7.43e-01 +1.02e+00 +6.73e-01 +2.03e-01 +9.03e-01 +9.01e-01 +8.60e-01 +8.26e-01 +9.18e-01 +8.72e-01 +7.91e-01 +8.39e-01 +7.94e-01 +7.87e-01 +7.88e-01 +7.38e-01 +1.08e+00 +5.23e-01 +1.69e-01 +1.31e+00 +1.26e+00 +1.18e+00 +1.25e+00 +1.32e+00 +1.21e+00 +1.20e+00 +1.16e+00 +1.16e+00 +1.14e+00 +1.11e+00 +1.10e+00 +1.03e+00 +4.01e-01 +1.10e-01 +2.50e+00 +1.84e+00 +1.94e+00 +1.32e+00 +1.43e+00 +1.59e+00 +1.30e+00 +1.19e+00 +9.40e-01 +9.83e-01 +9.19e-01 +7.51e-01 +4.99e-01 +1.94e-01 +9.48e-02 +1.09e+00 +7.40e-01 +8.05e-01 +4.38e-01 +4.72e-01 +5.81e-01 +4.22e-01 +4.06e-01 +3.50e-01 +3.08e-01 +3.25e-01 +2.23e-01 +1.85e-01 +8.09e-02 +3.66e-02 +8.48e-01 +8.45e-01 +8.50e-01 +8.39e-01 +8.13e-01 +7.97e-01 +8.76e-01 +8.44e-01 +7.95e-01 +8.18e-01 +8.84e-01 +8.93e-01 +1.31e+00 +1.96e+00 +7.41e-01 +8.63e-01 +8.69e-01 +8.49e-01 +8.32e-01 +8.42e-01 +8.08e-01 +8.09e-01 +8.36e-01 +7.99e-01 +8.20e-01 +8.33e-01 +9.00e-01 +1.21e+00 +1.86e+00 +7.23e-01 +8.57e-01 +8.27e-01 +8.35e-01 +8.27e-01 +8.44e-01 +8.48e-01 +8.19e-01 +8.39e-01 +8.01e-01 +8.48e-01 +8.52e-01 +8.70e-01 +1.31e+00 +1.65e+00 +5.73e-01 +8.64e-01 +8.75e-01 +8.50e-01 +8.46e-01 +8.43e-01 +8.17e-01 +8.28e-01 +8.15e-01 +8.07e-01 +8.04e-01 +8.51e-01 +8.94e-01 +1.24e+00 +1.82e+00 +6.58e-01 +8.61e-01 +8.65e-01 +8.57e-01 +8.28e-01 +8.15e-01 +8.13e-01 +8.36e-01 +8.01e-01 +7.82e-01 +8.17e-01 +8.58e-01 +8.55e-01 +1.26e+00 +1.71e+00 +6.99e-01 +8.39e-01 +8.48e-01 +8.35e-01 +8.30e-01 +8.61e-01 +8.03e-01 +7.79e-01 +7.79e-01 +7.65e-01 +7.73e-01 +8.23e-01 +8.92e-01 +1.26e+00 +1.83e+00 +6.65e-01 +8.50e-01 +8.43e-01 +8.01e-01 +8.18e-01 +8.51e-01 +8.15e-01 +7.63e-01 +7.50e-01 +7.52e-01 +7.40e-01 +7.66e-01 +8.29e-01 +1.17e+00 +1.50e+00 +5.74e-01 +8.23e-01 +8.15e-01 +7.95e-01 +7.87e-01 +7.97e-01 +7.70e-01 +7.69e-01 +7.35e-01 +6.98e-01 +6.91e-01 +6.35e-01 +6.08e-01 +9.93e-01 +8.87e-01 +3.56e-01 +7.90e-01 +8.73e-01 +8.38e-01 +8.31e-01 +8.29e-01 +7.89e-01 +7.80e-01 +7.18e-01 +7.42e-01 +7.23e-01 +6.87e-01 +7.01e-01 +1.01e+00 +6.82e-01 +2.08e-01 +8.24e-01 +8.78e-01 +8.46e-01 +8.43e-01 +7.84e-01 +8.35e-01 +7.79e-01 +7.48e-01 +7.63e-01 +7.14e-01 +7.56e-01 +7.03e-01 +1.02e+00 +7.48e-01 +2.25e-01 +8.45e-01 +8.73e-01 +8.56e-01 +8.15e-01 +8.44e-01 +8.36e-01 +7.99e-01 +7.51e-01 +7.67e-01 +7.20e-01 +7.86e-01 +7.45e-01 +1.01e+00 +6.73e-01 +2.04e-01 +8.93e-01 +8.60e-01 +8.64e-01 +8.51e-01 +8.70e-01 +8.90e-01 +8.41e-01 +7.94e-01 +7.85e-01 +8.08e-01 +8.08e-01 +7.83e-01 +9.73e-01 +4.60e-01 +1.58e-01 +1.24e+00 +1.32e+00 +1.27e+00 +1.29e+00 +1.40e+00 +1.26e+00 +1.17e+00 +1.20e+00 +1.08e+00 +1.09e+00 +1.21e+00 +1.11e+00 +9.96e-01 +3.09e-01 +8.83e-02 +1.56e+00 +1.49e+00 +1.49e+00 +1.58e+00 +1.83e+00 +1.75e+00 +1.41e+00 +1.38e+00 +1.12e+00 +8.28e-01 +8.50e-01 +8.27e-01 +5.48e-01 +1.84e-01 +7.71e-02 +5.56e-01 +5.50e-01 +5.89e-01 +5.41e-01 +6.72e-01 +7.04e-01 +4.96e-01 +4.98e-01 +4.70e-01 +2.59e-01 +2.70e-01 +2.72e-01 +2.11e-01 +9.38e-02 +3.66e-02 +8.67e-01 +8.42e-01 +8.70e-01 +8.39e-01 +7.98e-01 +8.02e-01 +8.06e-01 +7.94e-01 +8.48e-01 +8.04e-01 +8.10e-01 +8.81e-01 +1.45e+00 +2.04e+00 +7.89e-01 +8.58e-01 +8.42e-01 +8.10e-01 +8.30e-01 +7.96e-01 +8.14e-01 +7.91e-01 +8.00e-01 +7.94e-01 +8.24e-01 +8.16e-01 +8.86e-01 +1.22e+00 +1.96e+00 +8.18e-01 +8.56e-01 +8.69e-01 +8.58e-01 +7.87e-01 +8.24e-01 +8.16e-01 +8.16e-01 +8.43e-01 +7.92e-01 +8.05e-01 +8.03e-01 +8.83e-01 +1.22e+00 +1.68e+00 +6.47e-01 +7.87e-01 +8.37e-01 +8.29e-01 +8.15e-01 +8.16e-01 +7.97e-01 +8.04e-01 +8.23e-01 +7.82e-01 +7.95e-01 +8.24e-01 +7.97e-01 +1.22e+00 +1.65e+00 +5.42e-01 +8.09e-01 +8.09e-01 +8.04e-01 +7.72e-01 +8.14e-01 +8.20e-01 +7.50e-01 +7.87e-01 +7.53e-01 +7.48e-01 +8.36e-01 +8.59e-01 +1.22e+00 +1.39e+00 +4.29e-01 +8.07e-01 +8.21e-01 +7.86e-01 +7.76e-01 +7.84e-01 +7.85e-01 +7.56e-01 +7.44e-01 +7.72e-01 +7.80e-01 +8.33e-01 +8.47e-01 +1.21e+00 +1.74e+00 +6.61e-01 +8.36e-01 +8.64e-01 +8.19e-01 +8.11e-01 +8.00e-01 +8.07e-01 +7.80e-01 +7.34e-01 +7.56e-01 +7.70e-01 +7.83e-01 +7.96e-01 +1.13e+00 +1.26e+00 +4.66e-01 +8.16e-01 +8.15e-01 +7.98e-01 +7.75e-01 +7.87e-01 +7.91e-01 +7.70e-01 +7.35e-01 +7.31e-01 +7.42e-01 +7.14e-01 +7.50e-01 +1.05e+00 +8.42e-01 +3.14e-01 +7.86e-01 +8.18e-01 +8.21e-01 +8.01e-01 +7.99e-01 +7.90e-01 +7.88e-01 +7.83e-01 +7.33e-01 +7.48e-01 +7.58e-01 +7.15e-01 +1.06e+00 +8.32e-01 +3.09e-01 +8.27e-01 +8.58e-01 +8.09e-01 +8.21e-01 +8.32e-01 +7.85e-01 +7.86e-01 +7.50e-01 +7.01e-01 +7.38e-01 +7.30e-01 +7.22e-01 +9.77e-01 +6.23e-01 +2.49e-01 +8.03e-01 +8.31e-01 +8.48e-01 +8.19e-01 +8.34e-01 +8.33e-01 +8.42e-01 +7.89e-01 +7.16e-01 +7.24e-01 +7.28e-01 +7.16e-01 +9.71e-01 +6.27e-01 +1.80e-01 +8.04e-01 +8.69e-01 +8.69e-01 +8.82e-01 +8.64e-01 +8.59e-01 +8.38e-01 +8.03e-01 +7.94e-01 +7.80e-01 +7.95e-01 +7.83e-01 +8.87e-01 +5.44e-01 +1.87e-01 +1.24e+00 +1.24e+00 +1.24e+00 +1.21e+00 +1.24e+00 +1.28e+00 +1.10e+00 +1.13e+00 +1.12e+00 +1.09e+00 +1.11e+00 +1.13e+00 +1.07e+00 +3.12e-01 +9.52e-02 +1.84e+00 +1.36e+00 +1.70e+00 +1.79e+00 +1.82e+00 +1.16e+00 +1.17e+00 +1.10e+00 +9.39e-01 +8.87e-01 +6.70e-01 +6.23e-01 +5.35e-01 +2.34e-01 +6.47e-02 +6.44e-01 +4.31e-01 +7.11e-01 +6.80e-01 +6.87e-01 +4.47e-01 +4.04e-01 +4.19e-01 +2.85e-01 +3.23e-01 +1.57e-01 +1.93e-01 +1.76e-01 +1.29e-01 +6.44e-02 +8.45e-01 +8.09e-01 +8.31e-01 +8.52e-01 +8.43e-01 +8.05e-01 +7.88e-01 +7.57e-01 +7.58e-01 +7.66e-01 +7.61e-01 +8.67e-01 +1.27e+00 +2.06e+00 +7.56e-01 +8.40e-01 +8.74e-01 +8.23e-01 +7.97e-01 +8.08e-01 +7.78e-01 +7.85e-01 +7.76e-01 +7.95e-01 +8.30e-01 +7.95e-01 +8.37e-01 +1.18e+00 +1.62e+00 +6.44e-01 +8.11e-01 +8.19e-01 +8.40e-01 +7.94e-01 +7.57e-01 +7.93e-01 +7.66e-01 +7.66e-01 +7.76e-01 +8.15e-01 +7.99e-01 +8.24e-01 +1.16e+00 +1.39e+00 +5.36e-01 +8.08e-01 +8.15e-01 +7.99e-01 +8.03e-01 +7.68e-01 +7.86e-01 +7.93e-01 +7.55e-01 +7.56e-01 +7.89e-01 +7.68e-01 +8.06e-01 +1.20e+00 +1.24e+00 +5.05e-01 +7.77e-01 +7.72e-01 +8.04e-01 +8.28e-01 +7.74e-01 +7.64e-01 +7.80e-01 +7.54e-01 +7.38e-01 +7.65e-01 +8.13e-01 +8.11e-01 +1.21e+00 +1.61e+00 +5.92e-01 +8.28e-01 +7.61e-01 +8.10e-01 +8.12e-01 +7.80e-01 +7.60e-01 +7.76e-01 +7.84e-01 +7.75e-01 +7.67e-01 +7.63e-01 +8.21e-01 +1.23e+00 +1.51e+00 +5.55e-01 +8.13e-01 +7.95e-01 +8.40e-01 +7.97e-01 +7.59e-01 +7.72e-01 +7.80e-01 +7.46e-01 +7.44e-01 +7.55e-01 +7.27e-01 +7.44e-01 +1.11e+00 +1.44e+00 +5.11e-01 +8.31e-01 +8.12e-01 +8.03e-01 +7.49e-01 +7.48e-01 +7.53e-01 +7.93e-01 +7.32e-01 +7.17e-01 +7.10e-01 +7.05e-01 +7.55e-01 +1.11e+00 +9.42e-01 +3.25e-01 +8.00e-01 +7.96e-01 +7.87e-01 +8.07e-01 +7.73e-01 +7.86e-01 +7.64e-01 +7.65e-01 +7.27e-01 +7.21e-01 +7.35e-01 +7.93e-01 +1.18e+00 +9.54e-01 +2.85e-01 +8.11e-01 +7.95e-01 +8.15e-01 +7.97e-01 +7.74e-01 +7.69e-01 +7.52e-01 +7.33e-01 +7.51e-01 +7.28e-01 +7.13e-01 +7.16e-01 +1.10e+00 +6.86e-01 +2.68e-01 +8.31e-01 +8.25e-01 +7.50e-01 +8.42e-01 +8.24e-01 +8.05e-01 +7.74e-01 +7.47e-01 +7.33e-01 +7.57e-01 +7.16e-01 +7.50e-01 +9.60e-01 +4.98e-01 +1.46e-01 +8.65e-01 +8.27e-01 +8.48e-01 +8.31e-01 +8.65e-01 +8.92e-01 +8.23e-01 +7.50e-01 +8.02e-01 +7.77e-01 +7.85e-01 +7.73e-01 +9.44e-01 +4.45e-01 +1.34e-01 +1.16e+00 +1.18e+00 +1.20e+00 +1.16e+00 +1.20e+00 +1.19e+00 +1.01e+00 +1.06e+00 +1.10e+00 +1.06e+00 +1.02e+00 +1.01e+00 +1.07e+00 +4.13e-01 +1.32e-01 +1.53e+00 +1.26e+00 +1.05e+00 +1.47e+00 +1.43e+00 +1.20e+00 +8.45e-01 +6.05e-01 +9.60e-01 +9.09e-01 +6.43e-01 +6.18e-01 +5.18e-01 +2.26e-01 +8.71e-02 +5.78e-01 +4.15e-01 +4.47e-01 +4.72e-01 +5.79e-01 +4.50e-01 +3.73e-01 +1.91e-01 +3.18e-01 +3.21e-01 +2.12e-01 +2.08e-01 +1.59e-01 +1.30e-01 +6.29e-02 +8.13e-01 +7.92e-01 +7.98e-01 +8.10e-01 +8.01e-01 +7.94e-01 +7.98e-01 +7.55e-01 +7.35e-01 +7.76e-01 +7.54e-01 +8.38e-01 +1.23e+00 +1.59e+00 +6.35e-01 +8.22e-01 +8.05e-01 +7.80e-01 +8.16e-01 +8.13e-01 +7.71e-01 +7.48e-01 +7.45e-01 +7.82e-01 +7.91e-01 +7.88e-01 +8.20e-01 +1.19e+00 +1.52e+00 +5.64e-01 +8.23e-01 +8.14e-01 +8.08e-01 +7.83e-01 +7.86e-01 +7.83e-01 +7.67e-01 +7.43e-01 +7.86e-01 +8.17e-01 +8.13e-01 +8.52e-01 +1.19e+00 +1.15e+00 +4.23e-01 +8.08e-01 +8.13e-01 +7.88e-01 +7.92e-01 +7.82e-01 +7.73e-01 +7.50e-01 +7.57e-01 +7.60e-01 +8.01e-01 +7.85e-01 +8.21e-01 +1.23e+00 +1.08e+00 +3.68e-01 +7.79e-01 +7.72e-01 +7.80e-01 +8.09e-01 +7.59e-01 +7.50e-01 +7.76e-01 +7.62e-01 +7.59e-01 +7.48e-01 +7.90e-01 +8.33e-01 +1.23e+00 +1.60e+00 +5.75e-01 +7.85e-01 +7.73e-01 +7.79e-01 +7.88e-01 +7.39e-01 +7.13e-01 +7.08e-01 +7.78e-01 +7.45e-01 +7.61e-01 +7.55e-01 +7.92e-01 +1.16e+00 +1.31e+00 +4.76e-01 +7.40e-01 +7.79e-01 +7.94e-01 +7.59e-01 +7.99e-01 +7.16e-01 +7.14e-01 +7.21e-01 +7.25e-01 +7.16e-01 +7.41e-01 +7.53e-01 +1.04e+00 +1.05e+00 +3.92e-01 +7.83e-01 +8.02e-01 +7.76e-01 +7.66e-01 +7.84e-01 +7.21e-01 +7.22e-01 +6.78e-01 +6.82e-01 +7.30e-01 +7.15e-01 +7.28e-01 +1.08e+00 +8.33e-01 +3.54e-01 +7.96e-01 +7.79e-01 +7.80e-01 +7.68e-01 +7.67e-01 +7.60e-01 +7.52e-01 +7.12e-01 +6.86e-01 +6.95e-01 +7.05e-01 +7.37e-01 +1.02e+00 +8.76e-01 +2.76e-01 +8.13e-01 +8.09e-01 +7.86e-01 +7.66e-01 +7.26e-01 +7.62e-01 +7.59e-01 +7.17e-01 +7.04e-01 +6.89e-01 +6.94e-01 +7.59e-01 +1.02e+00 +8.04e-01 +3.49e-01 +8.12e-01 +7.81e-01 +7.92e-01 +7.74e-01 +7.90e-01 +7.78e-01 +7.64e-01 +7.66e-01 +7.45e-01 +7.07e-01 +7.19e-01 +7.86e-01 +1.05e+00 +6.12e-01 +2.13e-01 +8.28e-01 +8.45e-01 +8.05e-01 +8.12e-01 +8.40e-01 +8.71e-01 +8.00e-01 +7.85e-01 +7.85e-01 +8.08e-01 +7.39e-01 +7.70e-01 +1.09e+00 +5.71e-01 +1.75e-01 +1.22e+00 +1.15e+00 +1.17e+00 +1.21e+00 +1.23e+00 +1.24e+00 +1.23e+00 +1.10e+00 +1.11e+00 +1.06e+00 +1.00e+00 +1.01e+00 +1.07e+00 +4.47e-01 +1.50e-01 +1.39e+00 +1.31e+00 +1.26e+00 +1.31e+00 +1.50e+00 +1.34e+00 +1.46e+00 +9.51e-01 +1.01e+00 +7.68e-01 +5.65e-01 +6.36e-01 +4.41e-01 +1.94e-01 +7.74e-02 +5.17e-01 +4.55e-01 +4.54e-01 +4.50e-01 +5.33e-01 +4.37e-01 +5.86e-01 +2.84e-01 +3.58e-01 +2.61e-01 +1.66e-01 +1.75e-01 +1.34e-01 +8.28e-02 +5.70e-02 +7.29e-01 +7.75e-01 +7.84e-01 +7.97e-01 +8.15e-01 +7.72e-01 +7.47e-01 +7.39e-01 +7.27e-01 +7.42e-01 +7.41e-01 +7.72e-01 +1.22e+00 +1.35e+00 +4.17e-01 +7.68e-01 +7.93e-01 +7.83e-01 +7.57e-01 +7.76e-01 +7.86e-01 +7.98e-01 +7.43e-01 +7.68e-01 +7.51e-01 +7.70e-01 +8.08e-01 +1.20e+00 +1.48e+00 +5.74e-01 +7.98e-01 +8.06e-01 +8.03e-01 +7.63e-01 +7.79e-01 +7.61e-01 +7.64e-01 +7.83e-01 +7.61e-01 +7.60e-01 +7.76e-01 +8.43e-01 +1.25e+00 +1.26e+00 +4.08e-01 +7.94e-01 +7.94e-01 +7.59e-01 +7.50e-01 +7.63e-01 +7.94e-01 +7.36e-01 +7.53e-01 +7.57e-01 +7.78e-01 +7.78e-01 +8.22e-01 +1.20e+00 +1.23e+00 +4.03e-01 +7.88e-01 +7.93e-01 +7.89e-01 +7.79e-01 +7.63e-01 +7.30e-01 +7.40e-01 +7.42e-01 +7.36e-01 +7.64e-01 +7.39e-01 +8.10e-01 +1.19e+00 +1.33e+00 +5.12e-01 +7.75e-01 +7.68e-01 +7.84e-01 +7.62e-01 +7.39e-01 +7.49e-01 +7.00e-01 +6.85e-01 +7.48e-01 +7.48e-01 +7.30e-01 +7.60e-01 +1.12e+00 +1.11e+00 +4.49e-01 +7.89e-01 +8.02e-01 +7.72e-01 +7.35e-01 +7.33e-01 +7.24e-01 +7.11e-01 +6.42e-01 +6.64e-01 +7.00e-01 +7.57e-01 +7.64e-01 +1.04e+00 +7.18e-01 +2.59e-01 +7.83e-01 +7.69e-01 +7.68e-01 +7.09e-01 +7.54e-01 +7.20e-01 +6.74e-01 +6.39e-01 +5.53e-01 +6.99e-01 +7.47e-01 +7.76e-01 +1.11e+00 +6.53e-01 +2.06e-01 +7.78e-01 +7.65e-01 +7.86e-01 +7.09e-01 +7.60e-01 +7.43e-01 +7.27e-01 +6.62e-01 +6.51e-01 +6.35e-01 +7.03e-01 +7.07e-01 +1.06e+00 +7.73e-01 +2.21e-01 +7.46e-01 +7.60e-01 +7.63e-01 +7.80e-01 +7.34e-01 +7.24e-01 +7.02e-01 +6.87e-01 +7.13e-01 +6.75e-01 +6.69e-01 +7.11e-01 +9.92e-01 +6.25e-01 +1.99e-01 +7.86e-01 +7.39e-01 +7.47e-01 +7.61e-01 +7.48e-01 +7.41e-01 +7.50e-01 +7.38e-01 +7.18e-01 +7.15e-01 +6.76e-01 +6.94e-01 +9.71e-01 +5.36e-01 +1.55e-01 +8.10e-01 +8.03e-01 +8.04e-01 +8.15e-01 +8.13e-01 +8.02e-01 +7.77e-01 +7.76e-01 +7.50e-01 +7.78e-01 +7.13e-01 +6.60e-01 +9.13e-01 +5.08e-01 +1.25e-01 +1.17e+00 +1.12e+00 +1.10e+00 +1.13e+00 +1.21e+00 +1.17e+00 +1.07e+00 +1.02e+00 +1.07e+00 +1.08e+00 +1.10e+00 +8.99e-01 +8.82e-01 +4.84e-01 +1.77e-01 +1.56e+00 +1.32e+00 +1.13e+00 +1.34e+00 +1.30e+00 +1.22e+00 +1.17e+00 +9.07e-01 +7.99e-01 +7.33e-01 +7.55e-01 +6.69e-01 +3.68e-01 +2.03e-01 +1.10e-01 +5.31e-01 +4.95e-01 +4.39e-01 +4.81e-01 +4.96e-01 +3.78e-01 +5.25e-01 +3.10e-01 +2.83e-01 +1.99e-01 +2.15e-01 +2.39e-01 +1.44e-01 +7.93e-02 +4.46e-02 +8.04e-01 +7.95e-01 +7.82e-01 +7.95e-01 +7.54e-01 +7.47e-01 +7.72e-01 +7.56e-01 +7.67e-01 +7.42e-01 +7.58e-01 +7.40e-01 +1.14e+00 +9.11e-01 +4.11e-01 +8.04e-01 +7.68e-01 +7.85e-01 +7.81e-01 +7.65e-01 +7.45e-01 +7.31e-01 +7.07e-01 +7.56e-01 +7.56e-01 +7.59e-01 +7.59e-01 +1.11e+00 +1.04e+00 +3.91e-01 +7.71e-01 +7.89e-01 +7.60e-01 +7.67e-01 +7.41e-01 +7.47e-01 +7.26e-01 +7.15e-01 +7.19e-01 +7.39e-01 +7.88e-01 +7.73e-01 +1.12e+00 +1.24e+00 +5.06e-01 +7.87e-01 +7.40e-01 +7.55e-01 +7.36e-01 +7.30e-01 +7.59e-01 +7.40e-01 +7.37e-01 +7.32e-01 +7.38e-01 +7.44e-01 +7.85e-01 +1.11e+00 +8.43e-01 +3.26e-01 +7.83e-01 +7.76e-01 +7.73e-01 +7.64e-01 +7.54e-01 +7.73e-01 +7.28e-01 +7.45e-01 +7.11e-01 +7.24e-01 +7.27e-01 +7.83e-01 +1.10e+00 +6.80e-01 +2.48e-01 +7.83e-01 +7.52e-01 +7.66e-01 +7.85e-01 +7.69e-01 +7.48e-01 +7.23e-01 +6.99e-01 +6.49e-01 +6.92e-01 +7.08e-01 +7.57e-01 +1.07e+00 +7.05e-01 +2.16e-01 +7.82e-01 +7.85e-01 +7.66e-01 +7.58e-01 +7.56e-01 +7.22e-01 +7.09e-01 +6.42e-01 +6.03e-01 +6.32e-01 +6.99e-01 +7.64e-01 +1.08e+00 +8.03e-01 +2.40e-01 +7.40e-01 +7.41e-01 +7.33e-01 +7.45e-01 +7.43e-01 +7.15e-01 +6.99e-01 +6.15e-01 +4.48e-01 +6.70e-01 +7.13e-01 +7.40e-01 +1.05e+00 +8.40e-01 +2.51e-01 +7.67e-01 +7.41e-01 +7.36e-01 +7.41e-01 +7.16e-01 +7.28e-01 +7.11e-01 +6.33e-01 +6.18e-01 +6.57e-01 +7.08e-01 +7.42e-01 +1.00e+00 +7.79e-01 +2.57e-01 +7.51e-01 +7.62e-01 +7.59e-01 +7.36e-01 +7.09e-01 +6.77e-01 +6.94e-01 +6.91e-01 +6.86e-01 +6.77e-01 +6.81e-01 +6.82e-01 +9.60e-01 +6.08e-01 +2.38e-01 +7.58e-01 +8.07e-01 +7.45e-01 +7.27e-01 +7.02e-01 +6.98e-01 +6.68e-01 +6.74e-01 +6.92e-01 +6.88e-01 +6.37e-01 +6.76e-01 +8.84e-01 +3.75e-01 +1.32e-01 +7.90e-01 +7.77e-01 +7.49e-01 +7.25e-01 +7.49e-01 +7.50e-01 +7.08e-01 +7.12e-01 +7.61e-01 +7.28e-01 +6.88e-01 +6.51e-01 +7.19e-01 +3.41e-01 +1.43e-01 +1.14e+00 +1.06e+00 +1.04e+00 +1.06e+00 +1.09e+00 +1.05e+00 +1.03e+00 +9.47e-01 +1.04e+00 +9.40e-01 +9.42e-01 +8.31e-01 +6.65e-01 +2.06e-01 +8.49e-02 +1.33e+00 +1.35e+00 +1.16e+00 +8.61e-01 +8.28e-01 +1.08e+00 +9.81e-01 +7.58e-01 +7.04e-01 +6.54e-01 +7.20e-01 +5.86e-01 +2.41e-01 +1.04e-01 +4.32e-02 +4.74e-01 +5.21e-01 +4.51e-01 +2.77e-01 +2.70e-01 +3.48e-01 +3.55e-01 +2.46e-01 +2.37e-01 +1.76e-01 +2.35e-01 +2.02e-01 +8.43e-02 +4.62e-02 +2.99e-02 +7.31e-01 +7.48e-01 +7.39e-01 +7.66e-01 +7.81e-01 +7.83e-01 +7.79e-01 +7.68e-01 +7.20e-01 +7.09e-01 +7.19e-01 +7.61e-01 +1.05e+00 +7.78e-01 +2.76e-01 +7.52e-01 +7.85e-01 +7.84e-01 +7.87e-01 +7.71e-01 +7.57e-01 +7.62e-01 +7.09e-01 +7.08e-01 +6.96e-01 +7.23e-01 +7.49e-01 +1.06e+00 +5.92e-01 +1.96e-01 +7.43e-01 +7.38e-01 +7.76e-01 +7.70e-01 +7.56e-01 +7.48e-01 +7.41e-01 +7.42e-01 +7.06e-01 +6.90e-01 +7.25e-01 +7.69e-01 +1.04e+00 +8.10e-01 +2.94e-01 +7.40e-01 +7.47e-01 +7.57e-01 +7.48e-01 +7.55e-01 +7.44e-01 +7.42e-01 +7.35e-01 +6.70e-01 +6.64e-01 +7.63e-01 +7.71e-01 +9.80e-01 +7.74e-01 +3.07e-01 +7.55e-01 +7.66e-01 +7.97e-01 +7.40e-01 +7.53e-01 +7.39e-01 +7.26e-01 +7.07e-01 +6.89e-01 +6.87e-01 +7.44e-01 +7.30e-01 +1.03e+00 +6.26e-01 +1.73e-01 +7.55e-01 +7.36e-01 +7.61e-01 +7.64e-01 +7.21e-01 +7.01e-01 +7.26e-01 +6.89e-01 +6.95e-01 +6.71e-01 +6.81e-01 +7.49e-01 +9.77e-01 +6.44e-01 +2.24e-01 +7.44e-01 +7.16e-01 +7.45e-01 +7.32e-01 +7.41e-01 +7.10e-01 +6.77e-01 +6.35e-01 +6.21e-01 +6.30e-01 +6.43e-01 +6.90e-01 +9.34e-01 +7.67e-01 +2.92e-01 +7.32e-01 +7.55e-01 +7.18e-01 +7.24e-01 +7.26e-01 +7.05e-01 +6.53e-01 +6.21e-01 +6.43e-01 +6.52e-01 +7.13e-01 +7.09e-01 +9.53e-01 +6.82e-01 +2.54e-01 +6.97e-01 +7.00e-01 +7.14e-01 +6.97e-01 +7.15e-01 +6.93e-01 +6.77e-01 +6.33e-01 +6.17e-01 +6.11e-01 +7.05e-01 +6.87e-01 +9.29e-01 +5.46e-01 +1.70e-01 +7.20e-01 +7.06e-01 +7.18e-01 +6.87e-01 +6.85e-01 +6.94e-01 +6.44e-01 +6.64e-01 +5.99e-01 +5.89e-01 +6.44e-01 +6.80e-01 +9.68e-01 +6.14e-01 +1.98e-01 +7.51e-01 +7.34e-01 +7.39e-01 +6.97e-01 +6.75e-01 +6.82e-01 +6.70e-01 +7.02e-01 +6.60e-01 +6.35e-01 +6.16e-01 +6.32e-01 +8.05e-01 +4.17e-01 +1.09e-01 +7.89e-01 +7.57e-01 +7.50e-01 +7.29e-01 +7.26e-01 +6.77e-01 +6.48e-01 +7.04e-01 +6.97e-01 +6.79e-01 +6.60e-01 +6.12e-01 +7.24e-01 +2.74e-01 +7.82e-02 +1.14e+00 +1.03e+00 +1.04e+00 +9.43e-01 +1.02e+00 +9.87e-01 +8.16e-01 +8.35e-01 +8.39e-01 +9.59e-01 +9.15e-01 +7.74e-01 +5.71e-01 +1.72e-01 +5.75e-02 +1.12e+00 +1.16e+00 +8.35e-01 +7.13e-01 +7.64e-01 +8.80e-01 +5.67e-01 +4.88e-01 +4.31e-01 +7.46e-01 +6.47e-01 +4.18e-01 +2.22e-01 +6.04e-02 +2.42e-02 +3.88e-01 +3.92e-01 +2.68e-01 +2.61e-01 +2.31e-01 +3.03e-01 +2.09e-01 +1.58e-01 +1.36e-01 +2.55e-01 +2.03e-01 +1.38e-01 +9.69e-02 +4.41e-02 +1.17e-02 +7.71e-01 +7.88e-01 +8.12e-01 +7.42e-01 +7.85e-01 +7.60e-01 +7.46e-01 +7.71e-01 +7.11e-01 +6.80e-01 +6.82e-01 +7.51e-01 +1.04e+00 +6.16e-01 +1.78e-01 +7.57e-01 +7.66e-01 +7.96e-01 +7.87e-01 +7.31e-01 +7.75e-01 +7.52e-01 +7.45e-01 +7.07e-01 +7.05e-01 +7.08e-01 +7.39e-01 +9.99e-01 +6.60e-01 +2.06e-01 +7.40e-01 +7.48e-01 +7.67e-01 +7.62e-01 +7.72e-01 +7.12e-01 +7.41e-01 +7.52e-01 +6.99e-01 +6.87e-01 +6.89e-01 +7.63e-01 +1.10e+00 +8.27e-01 +2.75e-01 +7.59e-01 +7.19e-01 +7.47e-01 +7.32e-01 +7.40e-01 +7.41e-01 +7.25e-01 +7.31e-01 +6.89e-01 +7.01e-01 +7.16e-01 +6.95e-01 +9.76e-01 +8.88e-01 +3.69e-01 +7.39e-01 +7.23e-01 +7.22e-01 +7.34e-01 +7.32e-01 +7.19e-01 +7.08e-01 +7.23e-01 +7.13e-01 +6.86e-01 +7.13e-01 +7.47e-01 +9.68e-01 +6.85e-01 +2.05e-01 +7.59e-01 +7.40e-01 +7.23e-01 +6.89e-01 +6.89e-01 +6.98e-01 +7.04e-01 +6.61e-01 +6.79e-01 +6.79e-01 +6.86e-01 +7.42e-01 +1.01e+00 +7.65e-01 +2.63e-01 +7.27e-01 +7.22e-01 +7.21e-01 +6.82e-01 +6.82e-01 +6.79e-01 +6.66e-01 +6.58e-01 +6.31e-01 +6.61e-01 +6.87e-01 +6.88e-01 +8.56e-01 +5.97e-01 +2.13e-01 +7.02e-01 +7.13e-01 +7.28e-01 +6.93e-01 +6.91e-01 +6.69e-01 +6.46e-01 +6.28e-01 +6.40e-01 +6.38e-01 +6.71e-01 +6.96e-01 +8.50e-01 +3.86e-01 +1.50e-01 +6.91e-01 +6.77e-01 +6.91e-01 +7.07e-01 +7.18e-01 +7.31e-01 +6.70e-01 +6.39e-01 +6.18e-01 +6.02e-01 +6.11e-01 +6.66e-01 +9.21e-01 +4.38e-01 +1.13e-01 +7.24e-01 +6.84e-01 +6.84e-01 +6.47e-01 +6.69e-01 +6.83e-01 +6.69e-01 +6.47e-01 +6.14e-01 +5.97e-01 +6.05e-01 +6.26e-01 +9.22e-01 +4.59e-01 +1.15e-01 +7.38e-01 +6.58e-01 +6.82e-01 +6.72e-01 +7.05e-01 +6.71e-01 +6.60e-01 +6.37e-01 +6.35e-01 +6.02e-01 +5.50e-01 +5.85e-01 +8.26e-01 +4.84e-01 +1.41e-01 +7.40e-01 +6.73e-01 +6.74e-01 +7.15e-01 +7.22e-01 +6.98e-01 +7.17e-01 +6.74e-01 +6.70e-01 +6.46e-01 +5.85e-01 +5.00e-01 +4.95e-01 +2.74e-01 +8.29e-02 +1.11e+00 +9.99e-01 +1.02e+00 +1.05e+00 +1.05e+00 +9.96e-01 +9.29e-01 +9.64e-01 +8.97e-01 +9.51e-01 +8.48e-01 +7.23e-01 +6.11e-01 +2.17e-01 +6.09e-02 +1.15e+00 +9.21e-01 +8.94e-01 +8.34e-01 +8.60e-01 +8.05e-01 +5.65e-01 +5.80e-01 +5.28e-01 +5.64e-01 +6.00e-01 +4.16e-01 +2.18e-01 +8.48e-02 +2.93e-02 +3.50e-01 +2.74e-01 +2.33e-01 +2.38e-01 +2.50e-01 +2.74e-01 +1.88e-01 +1.72e-01 +1.56e-01 +1.57e-01 +1.94e-01 +1.39e-01 +6.35e-02 +3.19e-02 +7.49e-03 +7.41e-01 +7.68e-01 +7.90e-01 +7.78e-01 +7.76e-01 +7.30e-01 +7.18e-01 +7.37e-01 +7.09e-01 +7.20e-01 +7.00e-01 +7.72e-01 +9.68e-01 +6.90e-01 +2.52e-01 +7.57e-01 +7.64e-01 +7.45e-01 +7.47e-01 +7.46e-01 +7.65e-01 +7.54e-01 +7.52e-01 +7.26e-01 +6.82e-01 +7.09e-01 +7.86e-01 +1.04e+00 +7.77e-01 +2.28e-01 +7.74e-01 +7.42e-01 +7.17e-01 +7.21e-01 +7.29e-01 +7.67e-01 +7.52e-01 +7.57e-01 +7.68e-01 +6.89e-01 +6.78e-01 +7.59e-01 +1.05e+00 +8.28e-01 +2.72e-01 +7.40e-01 +7.34e-01 +7.31e-01 +7.16e-01 +7.44e-01 +7.39e-01 +7.56e-01 +7.44e-01 +7.10e-01 +6.88e-01 +6.97e-01 +7.44e-01 +9.71e-01 +7.11e-01 +2.71e-01 +7.13e-01 +6.97e-01 +7.29e-01 +7.42e-01 +7.39e-01 +7.08e-01 +7.05e-01 +7.31e-01 +6.98e-01 +6.88e-01 +7.17e-01 +7.13e-01 +9.73e-01 +6.01e-01 +1.91e-01 +7.45e-01 +7.09e-01 +7.35e-01 +7.10e-01 +7.14e-01 +7.15e-01 +7.08e-01 +7.06e-01 +6.78e-01 +6.67e-01 +6.90e-01 +7.12e-01 +9.35e-01 +5.48e-01 +2.00e-01 +7.32e-01 +7.22e-01 +7.10e-01 +7.08e-01 +7.09e-01 +7.14e-01 +6.79e-01 +6.85e-01 +6.58e-01 +6.83e-01 +6.50e-01 +7.09e-01 +9.87e-01 +7.05e-01 +2.37e-01 +7.08e-01 +7.29e-01 +7.25e-01 +7.21e-01 +7.00e-01 +6.83e-01 +6.43e-01 +6.62e-01 +6.42e-01 +6.10e-01 +6.47e-01 +7.20e-01 +8.94e-01 +4.05e-01 +1.25e-01 +6.82e-01 +6.98e-01 +7.11e-01 +7.24e-01 +7.09e-01 +6.74e-01 +6.40e-01 +6.25e-01 +6.06e-01 +6.01e-01 +6.22e-01 +6.85e-01 +8.81e-01 +4.33e-01 +1.27e-01 +7.04e-01 +6.95e-01 +6.94e-01 +7.08e-01 +6.83e-01 +6.66e-01 +6.52e-01 +6.14e-01 +6.08e-01 +6.22e-01 +5.79e-01 +6.40e-01 +8.39e-01 +5.15e-01 +1.65e-01 +7.06e-01 +6.74e-01 +6.69e-01 +6.66e-01 +6.63e-01 +6.70e-01 +6.87e-01 +6.53e-01 +6.03e-01 +5.84e-01 +5.54e-01 +5.65e-01 +7.87e-01 +4.40e-01 +1.48e-01 +7.67e-01 +6.97e-01 +7.26e-01 +7.26e-01 +6.91e-01 +7.44e-01 +7.07e-01 +6.80e-01 +6.22e-01 +6.16e-01 +5.70e-01 +4.85e-01 +5.06e-01 +2.87e-01 +9.16e-02 +1.20e+00 +9.57e-01 +8.98e-01 +1.03e+00 +9.83e-01 +1.01e+00 +9.58e-01 +1.03e+00 +9.95e-01 +1.02e+00 +8.79e-01 +6.76e-01 +5.34e-01 +1.83e-01 +6.50e-02 +9.53e-01 +7.68e-01 +7.45e-01 +8.42e-01 +9.10e-01 +8.62e-01 +7.39e-01 +7.46e-01 +6.49e-01 +5.31e-01 +4.80e-01 +3.53e-01 +2.00e-01 +8.55e-02 +3.73e-02 +3.66e-01 +3.07e-01 +3.18e-01 +2.87e-01 +3.14e-01 +2.90e-01 +2.36e-01 +2.15e-01 +2.24e-01 +1.70e-01 +1.63e-01 +1.15e-01 +6.32e-02 +3.84e-02 +1.78e-02 +7.11e-01 +7.35e-01 +7.78e-01 +7.95e-01 +7.89e-01 +7.78e-01 +7.90e-01 +8.15e-01 +7.38e-01 +7.56e-01 +7.46e-01 +7.19e-01 +8.73e-01 +4.87e-01 +1.89e-01 +7.24e-01 +7.51e-01 +7.47e-01 +7.52e-01 +7.63e-01 +7.61e-01 +7.53e-01 +7.69e-01 +7.59e-01 +7.54e-01 +7.39e-01 +7.28e-01 +9.65e-01 +6.51e-01 +2.08e-01 +7.30e-01 +7.39e-01 +7.32e-01 +7.64e-01 +7.35e-01 +7.69e-01 +7.88e-01 +7.27e-01 +7.35e-01 +7.32e-01 +7.27e-01 +7.49e-01 +9.97e-01 +7.08e-01 +2.42e-01 +7.30e-01 +7.41e-01 +7.80e-01 +7.20e-01 +7.72e-01 +7.80e-01 +7.58e-01 +7.29e-01 +7.22e-01 +6.85e-01 +6.91e-01 +7.32e-01 +9.70e-01 +5.55e-01 +1.80e-01 +7.25e-01 +7.42e-01 +7.54e-01 +7.29e-01 +7.69e-01 +7.68e-01 +7.23e-01 +7.19e-01 +7.39e-01 +6.99e-01 +7.30e-01 +7.43e-01 +9.67e-01 +5.29e-01 +1.58e-01 +7.27e-01 +7.38e-01 +7.50e-01 +7.78e-01 +7.29e-01 +7.53e-01 +7.20e-01 +7.07e-01 +6.58e-01 +7.03e-01 +7.03e-01 +6.97e-01 +1.02e+00 +5.77e-01 +1.55e-01 +7.04e-01 +7.29e-01 +7.04e-01 +7.41e-01 +7.23e-01 +7.27e-01 +7.22e-01 +6.89e-01 +6.68e-01 +6.72e-01 +6.90e-01 +7.11e-01 +9.24e-01 +6.32e-01 +1.76e-01 +6.92e-01 +7.24e-01 +7.27e-01 +7.47e-01 +7.06e-01 +7.02e-01 +6.65e-01 +6.69e-01 +6.63e-01 +6.78e-01 +6.76e-01 +6.84e-01 +8.81e-01 +5.82e-01 +2.05e-01 +6.77e-01 +6.97e-01 +7.18e-01 +7.16e-01 +7.22e-01 +6.99e-01 +6.33e-01 +6.46e-01 +6.22e-01 +6.20e-01 +6.04e-01 +6.27e-01 +8.72e-01 +4.58e-01 +1.30e-01 +6.94e-01 +6.76e-01 +6.98e-01 +7.16e-01 +7.24e-01 +6.61e-01 +6.58e-01 +6.43e-01 +5.98e-01 +6.12e-01 +5.72e-01 +5.75e-01 +7.13e-01 +4.13e-01 +1.41e-01 +6.83e-01 +6.80e-01 +6.85e-01 +6.92e-01 +6.94e-01 +6.60e-01 +6.70e-01 +6.56e-01 +5.95e-01 +5.92e-01 +5.52e-01 +5.66e-01 +7.57e-01 +2.75e-01 +8.14e-02 +6.80e-01 +6.88e-01 +6.98e-01 +7.21e-01 +6.86e-01 +7.14e-01 +7.21e-01 +6.93e-01 +6.28e-01 +6.06e-01 +5.69e-01 +5.25e-01 +6.18e-01 +2.39e-01 +7.13e-02 +9.78e-01 +8.94e-01 +9.02e-01 +9.75e-01 +9.93e-01 +9.72e-01 +9.04e-01 +9.60e-01 +9.01e-01 +8.02e-01 +8.03e-01 +6.71e-01 +3.57e-01 +1.04e-01 +3.71e-02 +8.79e-01 +4.74e-01 +6.09e-01 +6.57e-01 +6.73e-01 +6.70e-01 +5.29e-01 +7.08e-01 +5.73e-01 +3.74e-01 +4.21e-01 +2.98e-01 +1.43e-01 +5.64e-02 +1.94e-02 +2.87e-01 +1.53e-01 +1.84e-01 +2.04e-01 +2.63e-01 +2.36e-01 +1.65e-01 +2.34e-01 +1.92e-01 +1.28e-01 +1.20e-01 +7.57e-02 +5.20e-02 +3.12e-02 +1.69e-02 +7.55e-01 +8.03e-01 +8.38e-01 +8.11e-01 +8.12e-01 +8.21e-01 +8.19e-01 +7.76e-01 +7.65e-01 +7.57e-01 +7.69e-01 +7.22e-01 +7.14e-01 +1.75e-01 +6.92e-02 +8.24e-01 +8.38e-01 +8.21e-01 +7.68e-01 +7.55e-01 +7.68e-01 +7.83e-01 +7.52e-01 +7.51e-01 +7.46e-01 +7.74e-01 +7.44e-01 +8.02e-01 +3.34e-01 +1.21e-01 +7.93e-01 +8.41e-01 +7.94e-01 +7.70e-01 +7.76e-01 +8.31e-01 +8.21e-01 +7.99e-01 +7.37e-01 +7.45e-01 +7.12e-01 +8.00e-01 +1.03e+00 +5.16e-01 +1.38e-01 +7.80e-01 +7.93e-01 +7.72e-01 +7.70e-01 +7.94e-01 +8.27e-01 +8.15e-01 +7.14e-01 +7.62e-01 +7.24e-01 +6.76e-01 +7.22e-01 +8.97e-01 +4.58e-01 +1.53e-01 +7.51e-01 +7.55e-01 +7.71e-01 +7.75e-01 +7.94e-01 +7.94e-01 +7.68e-01 +7.35e-01 +7.64e-01 +7.55e-01 +7.00e-01 +7.25e-01 +9.50e-01 +5.35e-01 +1.47e-01 +7.48e-01 +7.62e-01 +7.42e-01 +7.69e-01 +7.62e-01 +7.55e-01 +7.49e-01 +7.15e-01 +7.33e-01 +7.29e-01 +6.88e-01 +6.77e-01 +8.92e-01 +4.73e-01 +1.54e-01 +7.07e-01 +7.12e-01 +7.63e-01 +7.78e-01 +7.67e-01 +7.38e-01 +7.39e-01 +7.04e-01 +6.90e-01 +7.02e-01 +7.00e-01 +6.50e-01 +7.45e-01 +4.80e-01 +1.67e-01 +7.10e-01 +7.31e-01 +7.02e-01 +7.48e-01 +7.25e-01 +7.23e-01 +7.48e-01 +7.26e-01 +7.06e-01 +6.63e-01 +6.82e-01 +6.87e-01 +8.55e-01 +3.87e-01 +1.27e-01 +7.27e-01 +7.16e-01 +7.34e-01 +7.54e-01 +7.46e-01 +7.70e-01 +7.11e-01 +6.87e-01 +6.70e-01 +6.64e-01 +6.35e-01 +6.35e-01 +8.28e-01 +3.65e-01 +1.19e-01 +7.19e-01 +6.95e-01 +7.56e-01 +7.46e-01 +7.40e-01 +7.30e-01 +6.89e-01 +6.43e-01 +6.17e-01 +5.90e-01 +6.17e-01 +5.70e-01 +7.99e-01 +3.80e-01 +1.34e-01 +6.68e-01 +6.64e-01 +6.96e-01 +7.05e-01 +7.48e-01 +7.14e-01 +6.55e-01 +6.42e-01 +6.17e-01 +5.96e-01 +5.59e-01 +5.39e-01 +6.83e-01 +2.51e-01 +7.41e-02 +6.65e-01 +6.54e-01 +6.72e-01 +6.79e-01 +6.77e-01 +6.50e-01 +6.85e-01 +6.99e-01 +6.33e-01 +6.12e-01 +5.40e-01 +5.07e-01 +5.77e-01 +2.24e-01 +6.42e-02 +8.66e-01 +8.00e-01 +8.01e-01 +8.48e-01 +8.27e-01 +8.32e-01 +8.36e-01 +9.02e-01 +9.05e-01 +7.29e-01 +6.75e-01 +5.88e-01 +3.48e-01 +1.10e-01 +3.71e-02 +6.49e-01 +3.51e-01 +4.24e-01 +5.24e-01 +4.05e-01 +4.08e-01 +3.66e-01 +3.97e-01 +5.38e-01 +3.80e-01 +2.81e-01 +2.19e-01 +1.32e-01 +3.23e-02 +1.05e-02 +2.57e-01 +1.34e-01 +1.74e-01 +1.81e-01 +1.46e-01 +1.42e-01 +1.23e-01 +1.30e-01 +1.65e-01 +1.27e-01 +8.17e-02 +7.95e-02 +5.09e-02 +1.73e-02 +7.50e-03 +1.08e+00 +1.13e+00 +1.22e+00 +1.22e+00 +1.16e+00 +1.11e+00 +1.11e+00 +1.06e+00 +1.11e+00 +9.89e-01 +9.97e-01 +1.02e+00 +7.84e-01 +1.55e-01 +3.93e-02 +1.15e+00 +1.05e+00 +1.23e+00 +1.10e+00 +1.10e+00 +1.14e+00 +1.11e+00 +1.03e+00 +9.91e-01 +1.02e+00 +9.48e-01 +9.15e-01 +8.91e-01 +2.88e-01 +8.05e-02 +1.12e+00 +1.04e+00 +1.18e+00 +1.10e+00 +1.06e+00 +1.18e+00 +1.04e+00 +1.15e+00 +1.03e+00 +1.04e+00 +9.63e-01 +9.19e-01 +1.05e+00 +4.94e-01 +1.55e-01 +1.12e+00 +1.03e+00 +1.09e+00 +1.12e+00 +1.10e+00 +1.19e+00 +1.11e+00 +1.10e+00 +1.06e+00 +1.01e+00 +8.73e-01 +8.08e-01 +8.67e-01 +3.12e-01 +9.00e-02 +1.08e+00 +1.03e+00 +1.06e+00 +1.10e+00 +1.05e+00 +1.13e+00 +1.27e+00 +1.09e+00 +1.08e+00 +1.12e+00 +9.93e-01 +8.86e-01 +7.84e-01 +3.95e-01 +1.46e-01 +1.07e+00 +9.78e-01 +1.03e+00 +1.02e+00 +1.00e+00 +1.09e+00 +1.08e+00 +1.01e+00 +1.03e+00 +1.07e+00 +9.59e-01 +8.28e-01 +8.42e-01 +4.01e-01 +1.22e-01 +9.48e-01 +9.63e-01 +1.06e+00 +1.04e+00 +9.98e-01 +1.04e+00 +1.02e+00 +9.83e-01 +8.41e-01 +9.51e-01 +9.08e-01 +7.45e-01 +7.77e-01 +3.05e-01 +9.78e-02 +9.70e-01 +9.47e-01 +9.99e-01 +1.08e+00 +1.11e+00 +1.03e+00 +1.01e+00 +9.93e-01 +8.83e-01 +8.87e-01 +9.14e-01 +8.38e-01 +6.43e-01 +1.75e-01 +5.80e-02 +9.67e-01 +9.99e-01 +1.01e+00 +1.06e+00 +1.08e+00 +9.67e-01 +1.08e+00 +1.02e+00 +8.98e-01 +9.10e-01 +8.57e-01 +8.25e-01 +8.06e-01 +2.24e-01 +7.49e-02 +9.69e-01 +1.03e+00 +1.10e+00 +1.01e+00 +1.06e+00 +9.81e-01 +1.01e+00 +9.04e-01 +8.30e-01 +8.02e-01 +8.09e-01 +8.11e-01 +7.80e-01 +2.46e-01 +7.63e-02 +8.90e-01 +9.28e-01 +1.02e+00 +1.02e+00 +9.83e-01 +1.07e+00 +9.40e-01 +8.16e-01 +7.84e-01 +6.91e-01 +7.33e-01 +6.96e-01 +5.05e-01 +2.10e-01 +7.72e-02 +7.36e-01 +7.62e-01 +8.86e-01 +8.71e-01 +9.21e-01 +9.57e-01 +1.00e+00 +9.26e-01 +8.38e-01 +7.10e-01 +6.68e-01 +6.38e-01 +4.88e-01 +1.47e-01 +5.84e-02 +6.38e-01 +6.97e-01 +7.81e-01 +7.21e-01 +8.22e-01 +8.29e-01 +6.94e-01 +9.33e-01 +9.36e-01 +7.85e-01 +5.91e-01 +4.82e-01 +3.15e-01 +9.11e-02 +4.04e-02 +3.59e-01 +2.19e-01 +2.60e-01 +3.06e-01 +2.48e-01 +2.41e-01 +2.20e-01 +2.69e-01 +4.39e-01 +2.88e-01 +1.35e-01 +1.30e-01 +7.47e-02 +2.46e-02 +1.39e-02 +1.37e-01 +7.05e-02 +9.05e-02 +9.63e-02 +7.38e-02 +7.57e-02 +7.98e-02 +7.83e-02 +1.40e-01 +1.09e-01 +5.52e-02 +4.14e-02 +2.38e-02 +1.27e-02 +4.98e-03 +1.08e+00 +1.06e+00 +1.34e+00 +9.87e-01 +1.49e+00 +1.22e+00 +1.08e+00 +1.13e+00 +9.74e-01 +8.45e-01 +6.07e-01 +6.33e-01 +5.34e-01 +1.43e-01 +3.98e-02 +1.35e+00 +9.68e-01 +1.60e+00 +1.80e+00 +1.45e+00 +1.17e+00 +9.94e-01 +1.02e+00 +8.65e-01 +7.01e-01 +6.46e-01 +5.40e-01 +4.49e-01 +2.04e-01 +7.38e-02 +1.18e+00 +1.14e+00 +1.48e+00 +1.19e+00 +9.38e-01 +1.19e+00 +9.74e-01 +1.01e+00 +9.23e-01 +8.23e-01 +8.89e-01 +7.01e-01 +3.85e-01 +2.16e-01 +1.44e-01 +1.32e+00 +1.14e+00 +1.36e+00 +1.53e+00 +1.16e+00 +1.29e+00 +1.36e+00 +1.18e+00 +1.09e+00 +8.66e-01 +6.32e-01 +4.57e-01 +3.13e-01 +1.52e-01 +6.87e-02 +1.30e+00 +9.74e-01 +1.29e+00 +1.54e+00 +1.33e+00 +1.38e+00 +1.35e+00 +1.13e+00 +9.14e-01 +8.56e-01 +7.71e-01 +4.06e-01 +2.63e-01 +1.30e-01 +6.54e-02 +1.05e+00 +7.94e-01 +1.12e+00 +1.15e+00 +8.13e-01 +9.20e-01 +1.09e+00 +8.11e-01 +6.93e-01 +8.96e-01 +8.00e-01 +5.26e-01 +2.87e-01 +1.23e-01 +7.75e-02 +7.48e-01 +9.01e-01 +1.04e+00 +1.05e+00 +9.12e-01 +1.00e+00 +9.12e-01 +7.83e-01 +6.62e-01 +6.12e-01 +4.88e-01 +3.46e-01 +2.31e-01 +1.33e-01 +7.44e-02 +5.62e-01 +7.11e-01 +8.51e-01 +8.66e-01 +9.29e-01 +9.65e-01 +6.70e-01 +6.37e-01 +4.77e-01 +4.37e-01 +4.96e-01 +3.94e-01 +2.26e-01 +1.06e-01 +3.96e-02 +6.10e-01 +6.97e-01 +8.44e-01 +1.11e+00 +8.69e-01 +7.98e-01 +6.03e-01 +7.36e-01 +5.96e-01 +5.37e-01 +4.95e-01 +4.22e-01 +2.94e-01 +1.01e-01 +4.37e-02 +4.56e-01 +8.07e-01 +1.07e+00 +8.05e-01 +7.16e-01 +5.03e-01 +6.26e-01 +7.10e-01 +5.73e-01 +4.58e-01 +4.44e-01 +3.45e-01 +2.60e-01 +1.36e-01 +4.22e-02 +4.93e-01 +6.13e-01 +8.04e-01 +5.55e-01 +7.11e-01 +7.23e-01 +5.94e-01 +5.56e-01 +4.34e-01 +2.96e-01 +3.80e-01 +3.42e-01 +2.09e-01 +1.08e-01 +4.84e-02 +4.28e-01 +3.61e-01 +5.20e-01 +4.86e-01 +4.81e-01 +6.92e-01 +5.95e-01 +3.74e-01 +3.26e-01 +2.76e-01 +2.59e-01 +2.57e-01 +1.85e-01 +4.96e-02 +1.83e-02 +2.15e-01 +2.66e-01 +3.14e-01 +2.45e-01 +2.95e-01 +4.30e-01 +3.75e-01 +3.67e-01 +3.05e-01 +3.09e-01 +2.21e-01 +1.80e-01 +1.34e-01 +5.03e-02 +2.00e-02 +9.60e-02 +1.01e-01 +1.15e-01 +9.88e-02 +1.27e-01 +1.67e-01 +1.28e-01 +1.60e-01 +1.75e-01 +1.43e-01 +8.60e-02 +5.91e-02 +4.18e-02 +2.09e-02 +1.01e-02 +6.92e-02 +3.71e-02 +4.18e-02 +4.29e-02 +4.54e-02 +6.16e-02 +4.09e-02 +5.12e-02 +7.23e-02 +6.74e-02 +3.14e-02 +1.80e-02 +1.64e-02 +7.47e-03 +2.97e-03 +3.66e-01 +3.67e-01 +4.98e-01 +3.34e-01 +5.46e-01 +4.10e-01 +3.75e-01 +3.97e-01 +3.66e-01 +2.85e-01 +1.96e-01 +1.98e-01 +1.79e-01 +1.05e-01 +3.65e-02 +5.51e-01 +2.62e-01 +5.35e-01 +7.29e-01 +6.22e-01 +4.43e-01 +3.56e-01 +3.26e-01 +3.18e-01 +2.53e-01 +1.98e-01 +1.61e-01 +1.35e-01 +8.96e-02 +4.34e-02 +4.38e-01 +3.47e-01 +5.06e-01 +4.90e-01 +2.64e-01 +4.25e-01 +3.58e-01 +3.11e-01 +3.30e-01 +2.32e-01 +3.09e-01 +2.65e-01 +1.43e-01 +6.12e-02 +6.39e-02 +4.34e-01 +3.64e-01 +5.30e-01 +6.02e-01 +4.03e-01 +4.34e-01 +4.80e-01 +4.48e-01 +4.35e-01 +3.28e-01 +2.19e-01 +1.55e-01 +1.04e-01 +6.22e-02 +2.93e-02 +5.28e-01 +3.92e-01 +4.89e-01 +5.69e-01 +5.08e-01 +4.82e-01 +5.01e-01 +3.73e-01 +3.58e-01 +2.50e-01 +2.62e-01 +1.19e-01 +7.33e-02 +4.35e-02 +2.75e-02 +3.24e-01 +2.74e-01 +3.53e-01 +3.82e-01 +3.09e-01 +2.64e-01 +3.42e-01 +2.59e-01 +2.24e-01 +2.72e-01 +2.89e-01 +2.17e-01 +1.01e-01 +4.22e-02 +2.14e-02 +3.08e-01 +2.90e-01 +3.71e-01 +4.11e-01 +2.41e-01 +2.90e-01 +2.99e-01 +2.76e-01 +2.53e-01 +2.06e-01 +1.62e-01 +1.37e-01 +7.91e-02 +3.97e-02 +3.67e-02 +1.54e-01 +2.66e-01 +2.58e-01 +2.66e-01 +2.93e-01 +3.18e-01 +1.96e-01 +2.10e-01 +1.51e-01 +1.19e-01 +1.30e-01 +1.25e-01 +6.88e-02 +5.68e-02 +4.11e-02 +1.94e-01 +2.10e-01 +2.78e-01 +3.44e-01 +3.33e-01 +3.12e-01 +1.89e-01 +2.43e-01 +2.08e-01 +1.74e-01 +1.46e-01 +1.34e-01 +1.11e-01 +4.65e-02 +1.94e-02 +1.16e-01 +2.46e-01 +3.24e-01 +3.12e-01 +2.29e-01 +1.48e-01 +2.01e-01 +2.33e-01 +2.12e-01 +1.40e-01 +1.33e-01 +1.13e-01 +6.83e-02 +6.78e-02 +3.69e-02 +1.55e-01 +2.51e-01 +2.94e-01 +1.53e-01 +2.32e-01 +2.02e-01 +1.35e-01 +1.89e-01 +1.55e-01 +1.09e-01 +1.17e-01 +9.56e-02 +6.95e-02 +5.08e-02 +3.96e-02 +1.13e-01 +1.22e-01 +1.62e-01 +1.45e-01 +1.49e-01 +2.36e-01 +2.03e-01 +1.50e-01 +1.01e-01 +8.51e-02 +7.83e-02 +7.56e-02 +7.49e-02 +2.08e-02 +8.15e-03 +9.29e-02 +9.19e-02 +1.09e-01 +1.23e-01 +9.19e-02 +1.58e-01 +1.36e-01 +1.13e-01 +8.64e-02 +1.01e-01 +5.95e-02 +6.00e-02 +5.68e-02 +2.10e-02 +7.24e-03 +3.10e-02 +4.84e-02 +4.87e-02 +3.68e-02 +5.75e-02 +7.85e-02 +7.07e-02 +8.18e-02 +9.39e-02 +5.13e-02 +5.46e-02 +3.42e-02 +2.14e-02 +1.34e-02 +1.14e-02 +2.27e-02 +2.51e-02 +2.70e-02 +2.56e-02 +3.02e-02 +5.07e-02 +3.42e-02 +3.18e-02 +5.82e-02 +3.83e-02 +2.90e-02 +1.85e-02 +7.38e-03 +5.00e-03 +7.27e-03 \ No newline at end of file diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat new file mode 100644 index 0000000000..c7691e950c --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/inputs_true.dat @@ -0,0 +1,265 @@ + + + + mgxs.h5 + + + + + + + + + + + + + + + + + + + + + 2.5 2.5 2.5 + 12 12 12 + 0.0 0.0 0.0 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 +1 1 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 +2 2 2 2 2 2 2 2 2 2 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 +3 3 3 3 3 3 3 3 3 3 3 3 + + + + + + + + + + fixed source + 750 + 30 + 20 + + + 100.0 1.0 + + + universe + 1 + + + multi-group + + + 1 + neutron + 10 + 1 + true + fw_cadis + + + + 15 15 15 + 0.0 0.0 0.0 + 30.0 30.0 30.0 + + + 500.0 + 100.0 + + + 0.0 0.0 0.0 30.0 30.0 30.0 + + + true + + + + + + linear + + + + + 1 + + + 2 + + + 3 + + + 3 + flux + tracklength + + + 2 + flux + tracklength + + + 1 + flux + tracklength + + + diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/results_true.dat b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/results_true.dat new file mode 100644 index 0000000000..c1066ae828 --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/linear/results_true.dat @@ -0,0 +1,6760 @@ +RegularMesh + ID = 1 + Name = + Dimensions = 3 + Voxels = [15 15 15] + Lower left = [0. 0. 0.] + Upper Right = [np.float64(30.0), np.float64(30.0), np.float64(30.0)] + Width = [2. 2. 2.] +Lower Bounds +1.77e-01 +1.81e-01 +1.81e-01 +1.81e-01 +1.78e-01 +1.74e-01 +1.70e-01 +1.76e-01 +1.80e-01 +1.88e-01 +1.75e-01 +1.93e-01 +2.76e-01 +4.28e-01 +1.58e-01 +1.74e-01 +1.82e-01 +1.81e-01 +1.63e-01 +1.78e-01 +1.81e-01 +1.77e-01 +1.77e-01 +1.74e-01 +1.72e-01 +1.81e-01 +1.82e-01 +2.58e-01 +3.27e-01 +1.07e-01 +1.82e-01 +1.75e-01 +1.81e-01 +1.82e-01 +1.76e-01 +1.77e-01 +1.74e-01 +1.66e-01 +1.66e-01 +1.69e-01 +1.76e-01 +1.87e-01 +2.67e-01 +4.16e-01 +1.57e-01 +1.76e-01 +1.74e-01 +1.72e-01 +1.76e-01 +1.67e-01 +1.68e-01 +1.70e-01 +1.75e-01 +1.72e-01 +1.73e-01 +1.65e-01 +1.84e-01 +2.67e-01 +3.99e-01 +1.28e-01 +1.75e-01 +1.82e-01 +1.82e-01 +1.84e-01 +1.72e-01 +1.72e-01 +1.67e-01 +1.67e-01 +1.66e-01 +1.74e-01 +1.69e-01 +1.90e-01 +2.82e-01 +3.31e-01 +1.29e-01 +1.80e-01 +1.79e-01 +1.79e-01 +1.78e-01 +1.76e-01 +1.69e-01 +1.68e-01 +1.63e-01 +1.70e-01 +1.66e-01 +1.71e-01 +1.92e-01 +2.70e-01 +2.88e-01 +1.02e-01 +1.76e-01 +1.72e-01 +1.68e-01 +1.71e-01 +1.66e-01 +1.68e-01 +1.65e-01 +1.66e-01 +1.62e-01 +1.60e-01 +1.61e-01 +1.66e-01 +2.39e-01 +3.32e-01 +1.23e-01 +1.82e-01 +1.81e-01 +1.79e-01 +1.70e-01 +1.67e-01 +1.72e-01 +1.65e-01 +1.62e-01 +1.47e-01 +1.56e-01 +1.56e-01 +1.44e-01 +2.01e-01 +1.76e-01 +7.55e-02 +1.76e-01 +1.76e-01 +1.80e-01 +1.82e-01 +1.71e-01 +1.67e-01 +1.65e-01 +1.62e-01 +1.54e-01 +1.55e-01 +1.54e-01 +1.55e-01 +2.28e-01 +1.67e-01 +5.36e-02 +1.77e-01 +1.85e-01 +1.83e-01 +1.81e-01 +1.79e-01 +1.61e-01 +1.64e-01 +1.58e-01 +1.61e-01 +1.60e-01 +1.55e-01 +1.53e-01 +2.33e-01 +1.74e-01 +6.51e-02 +1.81e-01 +1.79e-01 +1.83e-01 +1.79e-01 +1.80e-01 +1.69e-01 +1.58e-01 +1.66e-01 +1.59e-01 +1.59e-01 +1.70e-01 +1.56e-01 +2.11e-01 +1.28e-01 +3.74e-02 +1.90e-01 +1.90e-01 +1.82e-01 +1.71e-01 +1.90e-01 +1.82e-01 +1.72e-01 +1.79e-01 +1.69e-01 +1.69e-01 +1.66e-01 +1.51e-01 +2.22e-01 +9.62e-02 +3.14e-02 +2.75e-01 +2.73e-01 +2.51e-01 +2.62e-01 +2.73e-01 +2.67e-01 +2.53e-01 +2.43e-01 +2.41e-01 +2.42e-01 +2.39e-01 +2.23e-01 +2.12e-01 +7.28e-02 +2.10e-02 +5.00e-01 +3.50e-01 +3.91e-01 +2.55e-01 +2.81e-01 +3.18e-01 +2.54e-01 +2.37e-01 +1.86e-01 +1.97e-01 +1.81e-01 +1.50e-01 +9.74e-02 +3.76e-02 +1.86e-02 +2.17e-01 +1.42e-01 +1.63e-01 +8.18e-02 +9.18e-02 +1.14e-01 +8.12e-02 +7.94e-02 +6.97e-02 +6.23e-02 +6.28e-02 +4.48e-02 +3.69e-02 +1.59e-02 +7.16e-03 +1.71e-01 +1.76e-01 +1.78e-01 +1.74e-01 +1.77e-01 +1.76e-01 +1.81e-01 +1.77e-01 +1.69e-01 +1.76e-01 +1.83e-01 +1.84e-01 +2.71e-01 +3.99e-01 +1.54e-01 +1.79e-01 +1.79e-01 +1.80e-01 +1.75e-01 +1.76e-01 +1.71e-01 +1.74e-01 +1.74e-01 +1.72e-01 +1.73e-01 +1.77e-01 +1.85e-01 +2.54e-01 +3.67e-01 +1.34e-01 +1.80e-01 +1.76e-01 +1.74e-01 +1.77e-01 +1.76e-01 +1.77e-01 +1.74e-01 +1.78e-01 +1.72e-01 +1.80e-01 +1.81e-01 +1.81e-01 +2.66e-01 +3.32e-01 +1.15e-01 +1.82e-01 +1.86e-01 +1.81e-01 +1.83e-01 +1.74e-01 +1.67e-01 +1.72e-01 +1.75e-01 +1.70e-01 +1.69e-01 +1.75e-01 +1.84e-01 +2.58e-01 +3.55e-01 +1.26e-01 +1.80e-01 +1.82e-01 +1.79e-01 +1.73e-01 +1.66e-01 +1.70e-01 +1.77e-01 +1.69e-01 +1.68e-01 +1.69e-01 +1.78e-01 +1.83e-01 +2.75e-01 +3.02e-01 +1.32e-01 +1.75e-01 +1.78e-01 +1.77e-01 +1.69e-01 +1.79e-01 +1.69e-01 +1.65e-01 +1.63e-01 +1.63e-01 +1.67e-01 +1.74e-01 +1.87e-01 +2.69e-01 +3.43e-01 +1.20e-01 +1.80e-01 +1.82e-01 +1.68e-01 +1.67e-01 +1.77e-01 +1.70e-01 +1.59e-01 +1.60e-01 +1.58e-01 +1.61e-01 +1.65e-01 +1.67e-01 +2.34e-01 +2.93e-01 +1.13e-01 +1.71e-01 +1.72e-01 +1.65e-01 +1.65e-01 +1.65e-01 +1.60e-01 +1.57e-01 +1.46e-01 +1.43e-01 +1.50e-01 +1.48e-01 +1.06e-01 +1.93e-01 +1.68e-01 +7.02e-02 +1.69e-01 +1.86e-01 +1.75e-01 +1.74e-01 +1.77e-01 +1.65e-01 +1.61e-01 +1.54e-01 +1.53e-01 +1.55e-01 +1.47e-01 +1.41e-01 +1.98e-01 +1.23e-01 +3.84e-02 +1.78e-01 +1.86e-01 +1.76e-01 +1.77e-01 +1.65e-01 +1.75e-01 +1.66e-01 +1.54e-01 +1.59e-01 +1.54e-01 +1.63e-01 +1.52e-01 +2.17e-01 +1.45e-01 +4.33e-02 +1.80e-01 +1.87e-01 +1.79e-01 +1.73e-01 +1.81e-01 +1.76e-01 +1.67e-01 +1.60e-01 +1.63e-01 +1.60e-01 +1.69e-01 +1.55e-01 +2.16e-01 +1.34e-01 +4.11e-02 +1.88e-01 +1.80e-01 +1.84e-01 +1.81e-01 +1.84e-01 +1.87e-01 +1.74e-01 +1.69e-01 +1.66e-01 +1.73e-01 +1.70e-01 +1.62e-01 +2.01e-01 +8.29e-02 +2.86e-02 +2.58e-01 +2.81e-01 +2.59e-01 +2.69e-01 +2.95e-01 +2.69e-01 +2.46e-01 +2.56e-01 +2.33e-01 +2.31e-01 +2.59e-01 +2.41e-01 +2.08e-01 +5.36e-02 +1.40e-02 +3.12e-01 +2.97e-01 +2.71e-01 +3.04e-01 +3.70e-01 +3.54e-01 +2.77e-01 +2.79e-01 +2.19e-01 +1.62e-01 +1.62e-01 +1.63e-01 +1.08e-01 +3.47e-02 +1.48e-02 +1.09e-01 +1.08e-01 +1.05e-01 +1.01e-01 +1.35e-01 +1.42e-01 +9.52e-02 +9.99e-02 +9.32e-02 +5.18e-02 +5.18e-02 +5.34e-02 +4.23e-02 +1.87e-02 +7.19e-03 +1.80e-01 +1.75e-01 +1.82e-01 +1.75e-01 +1.71e-01 +1.70e-01 +1.69e-01 +1.65e-01 +1.75e-01 +1.71e-01 +1.68e-01 +1.86e-01 +3.12e-01 +4.27e-01 +1.66e-01 +1.81e-01 +1.77e-01 +1.70e-01 +1.73e-01 +1.73e-01 +1.72e-01 +1.64e-01 +1.66e-01 +1.66e-01 +1.73e-01 +1.70e-01 +1.89e-01 +2.63e-01 +4.02e-01 +1.64e-01 +1.79e-01 +1.80e-01 +1.83e-01 +1.71e-01 +1.73e-01 +1.71e-01 +1.70e-01 +1.75e-01 +1.69e-01 +1.68e-01 +1.68e-01 +1.80e-01 +2.49e-01 +3.42e-01 +1.29e-01 +1.63e-01 +1.73e-01 +1.74e-01 +1.71e-01 +1.68e-01 +1.65e-01 +1.68e-01 +1.73e-01 +1.72e-01 +1.65e-01 +1.72e-01 +1.69e-01 +2.52e-01 +3.19e-01 +1.06e-01 +1.67e-01 +1.70e-01 +1.72e-01 +1.64e-01 +1.71e-01 +1.71e-01 +1.57e-01 +1.64e-01 +1.61e-01 +1.62e-01 +1.73e-01 +1.79e-01 +2.63e-01 +2.66e-01 +7.74e-02 +1.68e-01 +1.72e-01 +1.64e-01 +1.66e-01 +1.65e-01 +1.66e-01 +1.58e-01 +1.59e-01 +1.63e-01 +1.63e-01 +1.70e-01 +1.75e-01 +2.51e-01 +3.47e-01 +1.31e-01 +1.72e-01 +1.79e-01 +1.70e-01 +1.70e-01 +1.69e-01 +1.71e-01 +1.70e-01 +1.55e-01 +1.59e-01 +1.65e-01 +1.70e-01 +1.71e-01 +2.43e-01 +2.46e-01 +9.20e-02 +1.70e-01 +1.70e-01 +1.66e-01 +1.63e-01 +1.66e-01 +1.63e-01 +1.59e-01 +1.58e-01 +1.57e-01 +1.60e-01 +1.55e-01 +1.53e-01 +2.20e-01 +1.63e-01 +6.37e-02 +1.70e-01 +1.70e-01 +1.73e-01 +1.69e-01 +1.66e-01 +1.64e-01 +1.63e-01 +1.64e-01 +1.52e-01 +1.54e-01 +1.53e-01 +1.49e-01 +2.12e-01 +1.64e-01 +6.14e-02 +1.74e-01 +1.78e-01 +1.73e-01 +1.75e-01 +1.74e-01 +1.65e-01 +1.67e-01 +1.59e-01 +1.48e-01 +1.57e-01 +1.55e-01 +1.59e-01 +2.06e-01 +1.23e-01 +4.80e-02 +1.69e-01 +1.78e-01 +1.77e-01 +1.74e-01 +1.77e-01 +1.73e-01 +1.74e-01 +1.68e-01 +1.52e-01 +1.58e-01 +1.58e-01 +1.55e-01 +2.12e-01 +1.22e-01 +3.34e-02 +1.75e-01 +1.85e-01 +1.86e-01 +1.90e-01 +1.80e-01 +1.79e-01 +1.76e-01 +1.71e-01 +1.67e-01 +1.71e-01 +1.69e-01 +1.69e-01 +1.86e-01 +1.02e-01 +3.54e-02 +2.66e-01 +2.66e-01 +2.64e-01 +2.50e-01 +2.60e-01 +2.58e-01 +2.34e-01 +2.42e-01 +2.35e-01 +2.29e-01 +2.33e-01 +2.42e-01 +2.28e-01 +5.44e-02 +1.71e-02 +3.77e-01 +2.75e-01 +3.41e-01 +3.51e-01 +3.64e-01 +2.31e-01 +2.32e-01 +2.18e-01 +1.82e-01 +1.77e-01 +1.28e-01 +1.16e-01 +1.03e-01 +4.16e-02 +1.06e-02 +1.33e-01 +8.71e-02 +1.41e-01 +1.34e-01 +1.37e-01 +8.83e-02 +7.82e-02 +8.36e-02 +5.61e-02 +6.39e-02 +2.90e-02 +3.69e-02 +3.46e-02 +2.60e-02 +1.24e-02 +1.77e-01 +1.71e-01 +1.75e-01 +1.79e-01 +1.73e-01 +1.71e-01 +1.64e-01 +1.66e-01 +1.59e-01 +1.61e-01 +1.55e-01 +1.79e-01 +2.67e-01 +4.26e-01 +1.56e-01 +1.75e-01 +1.82e-01 +1.71e-01 +1.65e-01 +1.69e-01 +1.66e-01 +1.69e-01 +1.63e-01 +1.65e-01 +1.74e-01 +1.69e-01 +1.76e-01 +2.47e-01 +3.31e-01 +1.30e-01 +1.69e-01 +1.73e-01 +1.77e-01 +1.67e-01 +1.55e-01 +1.67e-01 +1.59e-01 +1.63e-01 +1.58e-01 +1.70e-01 +1.69e-01 +1.84e-01 +2.44e-01 +2.71e-01 +1.02e-01 +1.69e-01 +1.70e-01 +1.73e-01 +1.69e-01 +1.66e-01 +1.67e-01 +1.64e-01 +1.60e-01 +1.62e-01 +1.63e-01 +1.63e-01 +1.67e-01 +2.48e-01 +2.40e-01 +9.84e-02 +1.62e-01 +1.64e-01 +1.69e-01 +1.71e-01 +1.62e-01 +1.58e-01 +1.62e-01 +1.58e-01 +1.62e-01 +1.61e-01 +1.71e-01 +1.67e-01 +2.50e-01 +3.22e-01 +1.17e-01 +1.74e-01 +1.60e-01 +1.69e-01 +1.72e-01 +1.67e-01 +1.60e-01 +1.65e-01 +1.68e-01 +1.62e-01 +1.60e-01 +1.63e-01 +1.76e-01 +2.53e-01 +2.99e-01 +1.10e-01 +1.69e-01 +1.67e-01 +1.76e-01 +1.64e-01 +1.59e-01 +1.58e-01 +1.64e-01 +1.59e-01 +1.55e-01 +1.59e-01 +1.51e-01 +1.60e-01 +2.50e-01 +2.88e-01 +1.01e-01 +1.72e-01 +1.71e-01 +1.68e-01 +1.63e-01 +1.59e-01 +1.61e-01 +1.66e-01 +1.56e-01 +1.54e-01 +1.51e-01 +1.49e-01 +1.58e-01 +2.33e-01 +1.85e-01 +6.62e-02 +1.69e-01 +1.65e-01 +1.63e-01 +1.67e-01 +1.62e-01 +1.63e-01 +1.63e-01 +1.61e-01 +1.55e-01 +1.53e-01 +1.59e-01 +1.69e-01 +2.48e-01 +1.87e-01 +5.29e-02 +1.71e-01 +1.66e-01 +1.73e-01 +1.68e-01 +1.65e-01 +1.64e-01 +1.59e-01 +1.52e-01 +1.60e-01 +1.52e-01 +1.48e-01 +1.54e-01 +2.32e-01 +1.35e-01 +5.41e-02 +1.76e-01 +1.78e-01 +1.68e-01 +1.79e-01 +1.73e-01 +1.69e-01 +1.62e-01 +1.61e-01 +1.59e-01 +1.61e-01 +1.58e-01 +1.60e-01 +2.06e-01 +9.41e-02 +2.78e-02 +1.88e-01 +1.77e-01 +1.78e-01 +1.76e-01 +1.82e-01 +1.90e-01 +1.76e-01 +1.62e-01 +1.69e-01 +1.69e-01 +1.69e-01 +1.68e-01 +1.97e-01 +7.83e-02 +2.27e-02 +2.47e-01 +2.50e-01 +2.59e-01 +2.51e-01 +2.57e-01 +2.55e-01 +2.15e-01 +2.28e-01 +2.42e-01 +2.25e-01 +2.12e-01 +2.14e-01 +2.28e-01 +7.82e-02 +2.46e-02 +3.10e-01 +2.49e-01 +2.07e-01 +2.92e-01 +2.88e-01 +2.33e-01 +1.62e-01 +1.13e-01 +1.90e-01 +1.80e-01 +1.16e-01 +1.07e-01 +9.41e-02 +4.21e-02 +1.63e-02 +1.17e-01 +8.58e-02 +8.78e-02 +9.13e-02 +1.15e-01 +8.57e-02 +6.78e-02 +3.63e-02 +6.34e-02 +6.38e-02 +4.06e-02 +3.60e-02 +2.77e-02 +2.56e-02 +1.21e-02 +1.71e-01 +1.68e-01 +1.63e-01 +1.68e-01 +1.72e-01 +1.66e-01 +1.65e-01 +1.51e-01 +1.57e-01 +1.63e-01 +1.57e-01 +1.78e-01 +2.58e-01 +3.15e-01 +1.24e-01 +1.72e-01 +1.71e-01 +1.66e-01 +1.69e-01 +1.71e-01 +1.63e-01 +1.58e-01 +1.51e-01 +1.62e-01 +1.68e-01 +1.67e-01 +1.76e-01 +2.54e-01 +3.04e-01 +1.12e-01 +1.75e-01 +1.72e-01 +1.70e-01 +1.68e-01 +1.63e-01 +1.64e-01 +1.63e-01 +1.54e-01 +1.65e-01 +1.69e-01 +1.68e-01 +1.76e-01 +2.52e-01 +2.21e-01 +8.02e-02 +1.68e-01 +1.71e-01 +1.65e-01 +1.66e-01 +1.63e-01 +1.64e-01 +1.61e-01 +1.62e-01 +1.61e-01 +1.66e-01 +1.64e-01 +1.72e-01 +2.60e-01 +2.09e-01 +7.14e-02 +1.63e-01 +1.61e-01 +1.63e-01 +1.69e-01 +1.61e-01 +1.62e-01 +1.62e-01 +1.61e-01 +1.58e-01 +1.54e-01 +1.64e-01 +1.76e-01 +2.59e-01 +3.27e-01 +1.16e-01 +1.60e-01 +1.60e-01 +1.63e-01 +1.68e-01 +1.62e-01 +1.54e-01 +1.50e-01 +1.64e-01 +1.63e-01 +1.58e-01 +1.58e-01 +1.68e-01 +2.43e-01 +2.59e-01 +9.33e-02 +1.61e-01 +1.63e-01 +1.68e-01 +1.62e-01 +1.69e-01 +1.53e-01 +1.53e-01 +1.57e-01 +1.50e-01 +1.54e-01 +1.57e-01 +1.59e-01 +2.17e-01 +2.06e-01 +7.58e-02 +1.71e-01 +1.70e-01 +1.63e-01 +1.57e-01 +1.61e-01 +1.53e-01 +1.56e-01 +1.49e-01 +1.49e-01 +1.59e-01 +1.49e-01 +1.55e-01 +2.27e-01 +1.62e-01 +6.88e-02 +1.71e-01 +1.67e-01 +1.66e-01 +1.60e-01 +1.61e-01 +1.62e-01 +1.63e-01 +1.54e-01 +1.50e-01 +1.48e-01 +1.51e-01 +1.55e-01 +2.15e-01 +1.65e-01 +4.95e-02 +1.71e-01 +1.73e-01 +1.68e-01 +1.62e-01 +1.57e-01 +1.62e-01 +1.59e-01 +1.56e-01 +1.53e-01 +1.47e-01 +1.49e-01 +1.62e-01 +2.14e-01 +1.58e-01 +6.86e-02 +1.70e-01 +1.66e-01 +1.69e-01 +1.66e-01 +1.67e-01 +1.62e-01 +1.63e-01 +1.63e-01 +1.58e-01 +1.52e-01 +1.54e-01 +1.67e-01 +2.26e-01 +1.15e-01 +4.03e-02 +1.74e-01 +1.82e-01 +1.70e-01 +1.72e-01 +1.77e-01 +1.85e-01 +1.68e-01 +1.65e-01 +1.63e-01 +1.74e-01 +1.63e-01 +1.66e-01 +2.29e-01 +1.07e-01 +3.27e-02 +2.58e-01 +2.47e-01 +2.50e-01 +2.57e-01 +2.60e-01 +2.66e-01 +2.59e-01 +2.39e-01 +2.35e-01 +2.29e-01 +2.16e-01 +2.25e-01 +2.33e-01 +8.96e-02 +3.01e-02 +2.73e-01 +2.51e-01 +2.52e-01 +2.63e-01 +3.05e-01 +2.69e-01 +2.91e-01 +1.85e-01 +2.00e-01 +1.47e-01 +1.01e-01 +1.17e-01 +8.80e-02 +3.85e-02 +1.53e-02 +1.00e-01 +8.71e-02 +9.09e-02 +9.00e-02 +1.08e-01 +8.70e-02 +1.17e-01 +5.47e-02 +7.04e-02 +5.07e-02 +3.08e-02 +3.14e-02 +2.64e-02 +1.69e-02 +1.16e-02 +1.60e-01 +1.64e-01 +1.63e-01 +1.67e-01 +1.70e-01 +1.62e-01 +1.54e-01 +1.52e-01 +1.52e-01 +1.55e-01 +1.57e-01 +1.63e-01 +2.61e-01 +2.58e-01 +7.58e-02 +1.59e-01 +1.68e-01 +1.63e-01 +1.58e-01 +1.65e-01 +1.65e-01 +1.67e-01 +1.52e-01 +1.58e-01 +1.56e-01 +1.60e-01 +1.69e-01 +2.49e-01 +2.88e-01 +1.11e-01 +1.66e-01 +1.69e-01 +1.70e-01 +1.63e-01 +1.63e-01 +1.59e-01 +1.63e-01 +1.65e-01 +1.58e-01 +1.58e-01 +1.60e-01 +1.75e-01 +2.57e-01 +2.53e-01 +8.19e-02 +1.67e-01 +1.68e-01 +1.58e-01 +1.58e-01 +1.61e-01 +1.67e-01 +1.54e-01 +1.55e-01 +1.61e-01 +1.61e-01 +1.63e-01 +1.70e-01 +2.53e-01 +2.35e-01 +7.59e-02 +1.63e-01 +1.66e-01 +1.68e-01 +1.64e-01 +1.59e-01 +1.59e-01 +1.57e-01 +1.58e-01 +1.58e-01 +1.56e-01 +1.58e-01 +1.66e-01 +2.50e-01 +2.58e-01 +9.81e-02 +1.60e-01 +1.61e-01 +1.64e-01 +1.61e-01 +1.60e-01 +1.56e-01 +1.51e-01 +1.50e-01 +1.63e-01 +1.64e-01 +1.57e-01 +1.64e-01 +2.37e-01 +2.18e-01 +9.03e-02 +1.68e-01 +1.69e-01 +1.64e-01 +1.56e-01 +1.58e-01 +1.53e-01 +1.51e-01 +1.43e-01 +1.51e-01 +1.53e-01 +1.55e-01 +1.63e-01 +2.19e-01 +1.33e-01 +4.89e-02 +1.67e-01 +1.60e-01 +1.61e-01 +1.55e-01 +1.64e-01 +1.56e-01 +1.49e-01 +1.45e-01 +1.44e-01 +1.51e-01 +1.57e-01 +1.64e-01 +2.32e-01 +1.18e-01 +3.65e-02 +1.62e-01 +1.67e-01 +1.68e-01 +1.55e-01 +1.59e-01 +1.57e-01 +1.58e-01 +1.46e-01 +1.43e-01 +1.39e-01 +1.48e-01 +1.54e-01 +2.26e-01 +1.45e-01 +4.05e-02 +1.55e-01 +1.63e-01 +1.63e-01 +1.67e-01 +1.56e-01 +1.53e-01 +1.50e-01 +1.49e-01 +1.49e-01 +1.45e-01 +1.47e-01 +1.53e-01 +2.10e-01 +1.20e-01 +3.82e-02 +1.65e-01 +1.59e-01 +1.61e-01 +1.61e-01 +1.56e-01 +1.56e-01 +1.60e-01 +1.59e-01 +1.54e-01 +1.53e-01 +1.48e-01 +1.52e-01 +2.07e-01 +1.01e-01 +3.00e-02 +1.77e-01 +1.71e-01 +1.68e-01 +1.71e-01 +1.75e-01 +1.69e-01 +1.64e-01 +1.68e-01 +1.61e-01 +1.61e-01 +1.53e-01 +1.46e-01 +1.95e-01 +9.40e-02 +2.21e-02 +2.46e-01 +2.38e-01 +2.31e-01 +2.41e-01 +2.54e-01 +2.50e-01 +2.28e-01 +2.20e-01 +2.21e-01 +2.26e-01 +2.35e-01 +2.02e-01 +1.88e-01 +9.58e-02 +3.52e-02 +3.06e-01 +2.59e-01 +2.23e-01 +2.61e-01 +2.57e-01 +2.36e-01 +2.32e-01 +1.78e-01 +1.53e-01 +1.37e-01 +1.47e-01 +1.36e-01 +7.24e-02 +4.03e-02 +2.23e-02 +1.04e-01 +9.82e-02 +8.79e-02 +9.20e-02 +9.75e-02 +7.29e-02 +1.04e-01 +6.06e-02 +5.37e-02 +3.67e-02 +4.29e-02 +4.85e-02 +2.87e-02 +1.59e-02 +9.17e-03 +1.71e-01 +1.67e-01 +1.65e-01 +1.69e-01 +1.59e-01 +1.60e-01 +1.62e-01 +1.62e-01 +1.57e-01 +1.49e-01 +1.52e-01 +1.53e-01 +2.42e-01 +1.80e-01 +8.19e-02 +1.68e-01 +1.63e-01 +1.67e-01 +1.65e-01 +1.63e-01 +1.54e-01 +1.59e-01 +1.52e-01 +1.59e-01 +1.52e-01 +1.48e-01 +1.55e-01 +2.25e-01 +2.01e-01 +7.57e-02 +1.63e-01 +1.59e-01 +1.62e-01 +1.61e-01 +1.57e-01 +1.56e-01 +1.50e-01 +1.51e-01 +1.48e-01 +1.53e-01 +1.61e-01 +1.58e-01 +2.32e-01 +2.49e-01 +1.01e-01 +1.67e-01 +1.54e-01 +1.55e-01 +1.52e-01 +1.55e-01 +1.61e-01 +1.57e-01 +1.54e-01 +1.54e-01 +1.54e-01 +1.56e-01 +1.64e-01 +2.25e-01 +1.56e-01 +6.14e-02 +1.67e-01 +1.65e-01 +1.61e-01 +1.57e-01 +1.58e-01 +1.62e-01 +1.55e-01 +1.57e-01 +1.50e-01 +1.54e-01 +1.55e-01 +1.68e-01 +2.32e-01 +1.28e-01 +4.68e-02 +1.63e-01 +1.57e-01 +1.60e-01 +1.63e-01 +1.61e-01 +1.58e-01 +1.50e-01 +1.46e-01 +1.43e-01 +1.52e-01 +1.55e-01 +1.61e-01 +2.25e-01 +1.35e-01 +4.13e-02 +1.64e-01 +1.62e-01 +1.60e-01 +1.62e-01 +1.61e-01 +1.51e-01 +1.50e-01 +1.41e-01 +1.46e-01 +1.49e-01 +1.51e-01 +1.64e-01 +2.31e-01 +1.50e-01 +4.56e-02 +1.53e-01 +1.55e-01 +1.54e-01 +1.57e-01 +1.55e-01 +1.49e-01 +1.50e-01 +1.44e-01 +1.43e-01 +1.47e-01 +1.51e-01 +1.57e-01 +2.19e-01 +1.63e-01 +4.89e-02 +1.64e-01 +1.55e-01 +1.57e-01 +1.53e-01 +1.48e-01 +1.53e-01 +1.52e-01 +1.40e-01 +1.38e-01 +1.40e-01 +1.50e-01 +1.57e-01 +2.10e-01 +1.54e-01 +5.14e-02 +1.56e-01 +1.58e-01 +1.61e-01 +1.57e-01 +1.52e-01 +1.47e-01 +1.51e-01 +1.48e-01 +1.44e-01 +1.48e-01 +1.44e-01 +1.43e-01 +2.07e-01 +1.20e-01 +4.74e-02 +1.62e-01 +1.71e-01 +1.62e-01 +1.57e-01 +1.52e-01 +1.51e-01 +1.44e-01 +1.43e-01 +1.46e-01 +1.46e-01 +1.36e-01 +1.45e-01 +1.86e-01 +7.07e-02 +2.51e-02 +1.70e-01 +1.71e-01 +1.63e-01 +1.56e-01 +1.60e-01 +1.61e-01 +1.56e-01 +1.55e-01 +1.59e-01 +1.53e-01 +1.48e-01 +1.41e-01 +1.55e-01 +6.13e-02 +2.68e-02 +2.40e-01 +2.25e-01 +2.22e-01 +2.27e-01 +2.31e-01 +2.20e-01 +2.21e-01 +1.99e-01 +2.15e-01 +1.98e-01 +2.02e-01 +1.80e-01 +1.35e-01 +3.74e-02 +1.62e-02 +2.64e-01 +2.69e-01 +2.32e-01 +1.55e-01 +1.50e-01 +1.99e-01 +1.79e-01 +1.44e-01 +1.30e-01 +1.25e-01 +1.44e-01 +1.15e-01 +4.22e-02 +1.96e-02 +8.51e-03 +9.50e-02 +1.04e-01 +8.97e-02 +4.80e-02 +4.87e-02 +6.43e-02 +6.38e-02 +4.70e-02 +4.40e-02 +3.28e-02 +4.72e-02 +4.03e-02 +1.52e-02 +8.66e-03 +6.14e-03 +1.56e-01 +1.57e-01 +1.54e-01 +1.60e-01 +1.65e-01 +1.66e-01 +1.65e-01 +1.58e-01 +1.47e-01 +1.40e-01 +1.36e-01 +1.59e-01 +2.22e-01 +1.52e-01 +5.31e-02 +1.61e-01 +1.62e-01 +1.69e-01 +1.63e-01 +1.58e-01 +1.58e-01 +1.59e-01 +1.47e-01 +1.45e-01 +1.39e-01 +1.30e-01 +1.48e-01 +2.15e-01 +1.09e-01 +3.46e-02 +1.58e-01 +1.57e-01 +1.62e-01 +1.63e-01 +1.59e-01 +1.57e-01 +1.56e-01 +1.58e-01 +1.52e-01 +1.45e-01 +1.50e-01 +1.60e-01 +2.17e-01 +1.56e-01 +5.66e-02 +1.60e-01 +1.57e-01 +1.60e-01 +1.57e-01 +1.60e-01 +1.58e-01 +1.54e-01 +1.53e-01 +1.47e-01 +1.42e-01 +1.56e-01 +1.66e-01 +2.10e-01 +1.46e-01 +6.02e-02 +1.62e-01 +1.61e-01 +1.68e-01 +1.54e-01 +1.54e-01 +1.59e-01 +1.50e-01 +1.49e-01 +1.50e-01 +1.46e-01 +1.58e-01 +1.59e-01 +2.16e-01 +1.18e-01 +3.24e-02 +1.61e-01 +1.55e-01 +1.60e-01 +1.59e-01 +1.50e-01 +1.45e-01 +1.50e-01 +1.45e-01 +1.47e-01 +1.46e-01 +1.48e-01 +1.58e-01 +2.13e-01 +1.24e-01 +4.33e-02 +1.58e-01 +1.55e-01 +1.58e-01 +1.51e-01 +1.57e-01 +1.54e-01 +1.50e-01 +1.39e-01 +1.40e-01 +1.40e-01 +1.39e-01 +1.49e-01 +2.03e-01 +1.50e-01 +5.78e-02 +1.51e-01 +1.59e-01 +1.53e-01 +1.53e-01 +1.52e-01 +1.52e-01 +1.46e-01 +1.43e-01 +1.42e-01 +1.41e-01 +1.52e-01 +1.52e-01 +2.06e-01 +1.37e-01 +5.13e-02 +1.55e-01 +1.52e-01 +1.56e-01 +1.49e-01 +1.53e-01 +1.49e-01 +1.45e-01 +1.39e-01 +1.35e-01 +1.33e-01 +1.48e-01 +1.46e-01 +1.98e-01 +1.08e-01 +3.36e-02 +1.53e-01 +1.53e-01 +1.49e-01 +1.43e-01 +1.45e-01 +1.48e-01 +1.38e-01 +1.42e-01 +1.29e-01 +1.29e-01 +1.38e-01 +1.45e-01 +2.11e-01 +1.20e-01 +3.89e-02 +1.60e-01 +1.55e-01 +1.59e-01 +1.49e-01 +1.48e-01 +1.48e-01 +1.47e-01 +1.50e-01 +1.41e-01 +1.37e-01 +1.31e-01 +1.36e-01 +1.77e-01 +7.63e-02 +1.94e-02 +1.69e-01 +1.64e-01 +1.65e-01 +1.57e-01 +1.52e-01 +1.48e-01 +1.44e-01 +1.54e-01 +1.48e-01 +1.49e-01 +1.40e-01 +1.31e-01 +1.53e-01 +4.85e-02 +1.42e-02 +2.42e-01 +2.23e-01 +2.24e-01 +2.03e-01 +2.18e-01 +2.15e-01 +1.75e-01 +1.74e-01 +1.73e-01 +2.02e-01 +2.01e-01 +1.69e-01 +1.22e-01 +3.16e-02 +1.08e-02 +2.16e-01 +2.25e-01 +1.60e-01 +1.34e-01 +1.44e-01 +1.68e-01 +1.02e-01 +8.69e-02 +7.20e-02 +1.43e-01 +1.29e-01 +8.21e-02 +4.29e-02 +1.08e-02 +4.27e-03 +7.45e-02 +7.65e-02 +5.07e-02 +4.76e-02 +4.31e-02 +5.72e-02 +3.77e-02 +2.92e-02 +2.28e-02 +4.79e-02 +4.01e-02 +2.76e-02 +1.93e-02 +8.26e-03 +2.13e-03 +1.63e-01 +1.66e-01 +1.71e-01 +1.63e-01 +1.67e-01 +1.59e-01 +1.56e-01 +1.61e-01 +1.49e-01 +1.45e-01 +1.38e-01 +1.54e-01 +2.17e-01 +1.17e-01 +3.57e-02 +1.59e-01 +1.61e-01 +1.68e-01 +1.63e-01 +1.62e-01 +1.62e-01 +1.60e-01 +1.56e-01 +1.49e-01 +1.47e-01 +1.44e-01 +1.52e-01 +2.09e-01 +1.26e-01 +3.94e-02 +1.57e-01 +1.61e-01 +1.58e-01 +1.59e-01 +1.60e-01 +1.51e-01 +1.56e-01 +1.60e-01 +1.49e-01 +1.45e-01 +1.44e-01 +1.61e-01 +2.41e-01 +1.63e-01 +5.31e-02 +1.60e-01 +1.54e-01 +1.60e-01 +1.53e-01 +1.56e-01 +1.57e-01 +1.55e-01 +1.53e-01 +1.46e-01 +1.47e-01 +1.49e-01 +1.47e-01 +2.08e-01 +1.76e-01 +7.44e-02 +1.55e-01 +1.52e-01 +1.53e-01 +1.52e-01 +1.52e-01 +1.51e-01 +1.51e-01 +1.53e-01 +1.48e-01 +1.40e-01 +1.50e-01 +1.61e-01 +2.05e-01 +1.29e-01 +4.01e-02 +1.60e-01 +1.58e-01 +1.50e-01 +1.47e-01 +1.42e-01 +1.46e-01 +1.50e-01 +1.46e-01 +1.45e-01 +1.42e-01 +1.48e-01 +1.60e-01 +2.17e-01 +1.50e-01 +5.21e-02 +1.53e-01 +1.53e-01 +1.52e-01 +1.45e-01 +1.45e-01 +1.43e-01 +1.40e-01 +1.38e-01 +1.37e-01 +1.36e-01 +1.46e-01 +1.49e-01 +1.88e-01 +1.14e-01 +4.21e-02 +1.48e-01 +1.50e-01 +1.52e-01 +1.43e-01 +1.48e-01 +1.44e-01 +1.36e-01 +1.34e-01 +1.37e-01 +1.37e-01 +1.46e-01 +1.47e-01 +1.84e-01 +7.19e-02 +2.90e-02 +1.48e-01 +1.44e-01 +1.48e-01 +1.48e-01 +1.49e-01 +1.54e-01 +1.45e-01 +1.38e-01 +1.29e-01 +1.30e-01 +1.31e-01 +1.39e-01 +1.93e-01 +7.96e-02 +2.03e-02 +1.53e-01 +1.46e-01 +1.46e-01 +1.41e-01 +1.47e-01 +1.45e-01 +1.42e-01 +1.37e-01 +1.29e-01 +1.27e-01 +1.30e-01 +1.33e-01 +1.95e-01 +8.81e-02 +2.19e-02 +1.56e-01 +1.42e-01 +1.45e-01 +1.43e-01 +1.52e-01 +1.48e-01 +1.44e-01 +1.40e-01 +1.41e-01 +1.31e-01 +1.18e-01 +1.31e-01 +1.83e-01 +9.55e-02 +2.75e-02 +1.63e-01 +1.47e-01 +1.48e-01 +1.57e-01 +1.55e-01 +1.50e-01 +1.56e-01 +1.42e-01 +1.44e-01 +1.39e-01 +1.29e-01 +1.20e-01 +1.52e-01 +5.41e-02 +1.62e-02 +2.32e-01 +2.08e-01 +2.20e-01 +2.22e-01 +2.23e-01 +2.17e-01 +1.99e-01 +2.05e-01 +1.92e-01 +2.03e-01 +1.97e-01 +1.60e-01 +1.37e-01 +4.00e-02 +1.07e-02 +2.25e-01 +1.79e-01 +1.71e-01 +1.67e-01 +1.69e-01 +1.54e-01 +1.02e-01 +1.09e-01 +9.88e-02 +1.08e-01 +1.18e-01 +8.29e-02 +4.15e-02 +1.49e-02 +5.20e-03 +6.83e-02 +5.09e-02 +4.28e-02 +4.83e-02 +4.92e-02 +5.19e-02 +3.56e-02 +3.22e-02 +2.88e-02 +3.04e-02 +3.83e-02 +2.79e-02 +1.21e-02 +5.81e-03 +1.30e-03 +1.57e-01 +1.62e-01 +1.67e-01 +1.62e-01 +1.61e-01 +1.50e-01 +1.52e-01 +1.57e-01 +1.53e-01 +1.51e-01 +1.43e-01 +1.61e-01 +2.04e-01 +1.43e-01 +5.22e-02 +1.59e-01 +1.63e-01 +1.58e-01 +1.58e-01 +1.58e-01 +1.62e-01 +1.60e-01 +1.58e-01 +1.58e-01 +1.43e-01 +1.49e-01 +1.59e-01 +2.21e-01 +1.56e-01 +4.57e-02 +1.63e-01 +1.57e-01 +1.51e-01 +1.54e-01 +1.53e-01 +1.60e-01 +1.55e-01 +1.60e-01 +1.60e-01 +1.45e-01 +1.40e-01 +1.59e-01 +2.23e-01 +1.66e-01 +5.44e-02 +1.53e-01 +1.54e-01 +1.54e-01 +1.50e-01 +1.57e-01 +1.53e-01 +1.59e-01 +1.56e-01 +1.48e-01 +1.45e-01 +1.45e-01 +1.58e-01 +2.07e-01 +1.44e-01 +5.45e-02 +1.50e-01 +1.46e-01 +1.56e-01 +1.56e-01 +1.54e-01 +1.48e-01 +1.50e-01 +1.54e-01 +1.46e-01 +1.46e-01 +1.50e-01 +1.52e-01 +2.09e-01 +1.16e-01 +3.68e-02 +1.56e-01 +1.50e-01 +1.56e-01 +1.48e-01 +1.50e-01 +1.51e-01 +1.48e-01 +1.49e-01 +1.43e-01 +1.41e-01 +1.46e-01 +1.53e-01 +2.02e-01 +1.07e-01 +3.92e-02 +1.55e-01 +1.55e-01 +1.53e-01 +1.49e-01 +1.47e-01 +1.48e-01 +1.41e-01 +1.44e-01 +1.41e-01 +1.41e-01 +1.37e-01 +1.51e-01 +2.06e-01 +1.37e-01 +4.68e-02 +1.52e-01 +1.53e-01 +1.54e-01 +1.53e-01 +1.48e-01 +1.43e-01 +1.32e-01 +1.41e-01 +1.35e-01 +1.27e-01 +1.35e-01 +1.52e-01 +1.90e-01 +7.26e-02 +2.22e-02 +1.47e-01 +1.47e-01 +1.49e-01 +1.50e-01 +1.49e-01 +1.42e-01 +1.39e-01 +1.32e-01 +1.28e-01 +1.26e-01 +1.34e-01 +1.45e-01 +1.87e-01 +8.17e-02 +2.42e-02 +1.49e-01 +1.48e-01 +1.47e-01 +1.50e-01 +1.45e-01 +1.43e-01 +1.38e-01 +1.30e-01 +1.29e-01 +1.31e-01 +1.22e-01 +1.36e-01 +1.78e-01 +1.04e-01 +3.33e-02 +1.48e-01 +1.45e-01 +1.44e-01 +1.42e-01 +1.41e-01 +1.43e-01 +1.46e-01 +1.38e-01 +1.29e-01 +1.25e-01 +1.18e-01 +1.25e-01 +1.79e-01 +8.54e-02 +2.89e-02 +1.64e-01 +1.47e-01 +1.55e-01 +1.56e-01 +1.47e-01 +1.59e-01 +1.48e-01 +1.44e-01 +1.36e-01 +1.33e-01 +1.26e-01 +1.11e-01 +1.31e-01 +5.72e-02 +1.80e-02 +2.46e-01 +2.05e-01 +1.90e-01 +2.18e-01 +2.16e-01 +2.14e-01 +2.02e-01 +2.24e-01 +2.13e-01 +2.23e-01 +1.86e-01 +1.51e-01 +1.18e-01 +3.41e-02 +1.21e-02 +1.87e-01 +1.53e-01 +1.50e-01 +1.67e-01 +1.83e-01 +1.69e-01 +1.39e-01 +1.42e-01 +1.29e-01 +1.05e-01 +9.27e-02 +6.73e-02 +3.93e-02 +1.65e-02 +7.05e-03 +7.21e-02 +6.08e-02 +6.40e-02 +5.53e-02 +6.28e-02 +5.59e-02 +4.39e-02 +4.02e-02 +4.46e-02 +3.36e-02 +3.13e-02 +2.12e-02 +1.23e-02 +7.79e-03 +3.56e-03 +1.51e-01 +1.56e-01 +1.63e-01 +1.66e-01 +1.67e-01 +1.65e-01 +1.66e-01 +1.70e-01 +1.53e-01 +1.56e-01 +1.51e-01 +1.51e-01 +1.83e-01 +9.73e-02 +3.81e-02 +1.53e-01 +1.59e-01 +1.54e-01 +1.57e-01 +1.61e-01 +1.59e-01 +1.57e-01 +1.58e-01 +1.58e-01 +1.58e-01 +1.52e-01 +1.52e-01 +2.00e-01 +1.30e-01 +4.17e-02 +1.54e-01 +1.56e-01 +1.53e-01 +1.62e-01 +1.55e-01 +1.59e-01 +1.66e-01 +1.53e-01 +1.56e-01 +1.56e-01 +1.52e-01 +1.57e-01 +2.13e-01 +1.42e-01 +4.79e-02 +1.53e-01 +1.59e-01 +1.67e-01 +1.55e-01 +1.64e-01 +1.66e-01 +1.61e-01 +1.55e-01 +1.54e-01 +1.50e-01 +1.45e-01 +1.54e-01 +2.09e-01 +1.08e-01 +3.44e-02 +1.54e-01 +1.57e-01 +1.60e-01 +1.54e-01 +1.62e-01 +1.59e-01 +1.56e-01 +1.54e-01 +1.57e-01 +1.52e-01 +1.52e-01 +1.56e-01 +2.03e-01 +9.74e-02 +2.86e-02 +1.52e-01 +1.57e-01 +1.58e-01 +1.62e-01 +1.56e-01 +1.57e-01 +1.52e-01 +1.48e-01 +1.44e-01 +1.47e-01 +1.50e-01 +1.51e-01 +2.12e-01 +1.09e-01 +2.91e-02 +1.51e-01 +1.54e-01 +1.48e-01 +1.58e-01 +1.54e-01 +1.52e-01 +1.51e-01 +1.44e-01 +1.44e-01 +1.43e-01 +1.45e-01 +1.51e-01 +1.99e-01 +1.23e-01 +3.39e-02 +1.48e-01 +1.50e-01 +1.55e-01 +1.59e-01 +1.47e-01 +1.46e-01 +1.37e-01 +1.40e-01 +1.41e-01 +1.47e-01 +1.42e-01 +1.45e-01 +1.87e-01 +1.10e-01 +3.91e-02 +1.43e-01 +1.48e-01 +1.52e-01 +1.52e-01 +1.56e-01 +1.49e-01 +1.38e-01 +1.38e-01 +1.34e-01 +1.30e-01 +1.26e-01 +1.34e-01 +1.85e-01 +8.58e-02 +2.44e-02 +1.48e-01 +1.45e-01 +1.47e-01 +1.50e-01 +1.53e-01 +1.41e-01 +1.37e-01 +1.35e-01 +1.27e-01 +1.27e-01 +1.23e-01 +1.24e-01 +1.55e-01 +8.15e-02 +2.87e-02 +1.44e-01 +1.47e-01 +1.45e-01 +1.44e-01 +1.45e-01 +1.40e-01 +1.42e-01 +1.37e-01 +1.27e-01 +1.29e-01 +1.17e-01 +1.21e-01 +1.63e-01 +5.00e-02 +1.53e-02 +1.47e-01 +1.48e-01 +1.47e-01 +1.53e-01 +1.45e-01 +1.50e-01 +1.52e-01 +1.47e-01 +1.34e-01 +1.29e-01 +1.22e-01 +1.13e-01 +1.35e-01 +4.55e-02 +1.41e-02 +2.05e-01 +1.89e-01 +1.85e-01 +2.07e-01 +2.14e-01 +2.10e-01 +1.91e-01 +1.99e-01 +1.97e-01 +1.79e-01 +1.74e-01 +1.47e-01 +7.71e-02 +1.94e-02 +6.96e-03 +1.71e-01 +9.07e-02 +1.18e-01 +1.23e-01 +1.31e-01 +1.29e-01 +1.02e-01 +1.39e-01 +1.15e-01 +7.59e-02 +8.17e-02 +5.28e-02 +2.69e-02 +1.04e-02 +3.19e-03 +5.55e-02 +2.88e-02 +3.51e-02 +3.72e-02 +5.09e-02 +4.45e-02 +3.21e-02 +4.62e-02 +3.95e-02 +2.61e-02 +2.32e-02 +1.25e-02 +9.62e-03 +6.12e-03 +3.33e-03 +1.57e-01 +1.68e-01 +1.76e-01 +1.71e-01 +1.70e-01 +1.76e-01 +1.72e-01 +1.62e-01 +1.62e-01 +1.58e-01 +1.58e-01 +1.48e-01 +1.49e-01 +3.17e-02 +1.30e-02 +1.73e-01 +1.78e-01 +1.74e-01 +1.61e-01 +1.60e-01 +1.64e-01 +1.67e-01 +1.62e-01 +1.58e-01 +1.56e-01 +1.59e-01 +1.53e-01 +1.62e-01 +5.38e-02 +2.31e-02 +1.66e-01 +1.78e-01 +1.66e-01 +1.64e-01 +1.63e-01 +1.71e-01 +1.77e-01 +1.69e-01 +1.55e-01 +1.57e-01 +1.50e-01 +1.66e-01 +2.15e-01 +9.77e-02 +2.59e-02 +1.65e-01 +1.70e-01 +1.68e-01 +1.65e-01 +1.69e-01 +1.74e-01 +1.78e-01 +1.61e-01 +1.64e-01 +1.57e-01 +1.44e-01 +1.50e-01 +1.92e-01 +8.51e-02 +2.93e-02 +1.60e-01 +1.58e-01 +1.61e-01 +1.66e-01 +1.68e-01 +1.71e-01 +1.64e-01 +1.61e-01 +1.62e-01 +1.60e-01 +1.44e-01 +1.42e-01 +1.97e-01 +1.03e-01 +2.80e-02 +1.55e-01 +1.62e-01 +1.58e-01 +1.62e-01 +1.63e-01 +1.64e-01 +1.60e-01 +1.52e-01 +1.55e-01 +1.57e-01 +1.44e-01 +1.41e-01 +1.88e-01 +9.04e-02 +2.91e-02 +1.49e-01 +1.51e-01 +1.62e-01 +1.64e-01 +1.61e-01 +1.56e-01 +1.57e-01 +1.49e-01 +1.46e-01 +1.48e-01 +1.45e-01 +1.41e-01 +1.63e-01 +9.25e-02 +3.25e-02 +1.52e-01 +1.51e-01 +1.52e-01 +1.59e-01 +1.57e-01 +1.54e-01 +1.55e-01 +1.52e-01 +1.48e-01 +1.42e-01 +1.43e-01 +1.45e-01 +1.82e-01 +6.97e-02 +2.31e-02 +1.55e-01 +1.54e-01 +1.58e-01 +1.59e-01 +1.58e-01 +1.62e-01 +1.50e-01 +1.47e-01 +1.42e-01 +1.42e-01 +1.33e-01 +1.37e-01 +1.74e-01 +6.53e-02 +2.18e-02 +1.52e-01 +1.45e-01 +1.60e-01 +1.58e-01 +1.58e-01 +1.54e-01 +1.50e-01 +1.37e-01 +1.30e-01 +1.26e-01 +1.30e-01 +1.23e-01 +1.71e-01 +7.09e-02 +2.53e-02 +1.42e-01 +1.41e-01 +1.47e-01 +1.47e-01 +1.58e-01 +1.48e-01 +1.36e-01 +1.35e-01 +1.33e-01 +1.29e-01 +1.19e-01 +1.14e-01 +1.45e-01 +4.24e-02 +1.27e-02 +1.43e-01 +1.40e-01 +1.44e-01 +1.45e-01 +1.42e-01 +1.37e-01 +1.42e-01 +1.47e-01 +1.33e-01 +1.30e-01 +1.15e-01 +1.07e-01 +1.24e-01 +3.91e-02 +1.19e-02 +1.85e-01 +1.70e-01 +1.73e-01 +1.81e-01 +1.77e-01 +1.76e-01 +1.75e-01 +1.93e-01 +1.94e-01 +1.58e-01 +1.45e-01 +1.24e-01 +7.16e-02 +2.05e-02 +7.17e-03 +1.27e-01 +6.46e-02 +8.27e-02 +1.04e-01 +7.86e-02 +7.86e-02 +7.04e-02 +6.74e-02 +1.02e-01 +7.60e-02 +5.37e-02 +4.16e-02 +2.54e-02 +6.01e-03 +1.97e-03 +5.06e-02 +2.49e-02 +3.45e-02 +3.64e-02 +2.84e-02 +2.70e-02 +2.34e-02 +2.43e-02 +3.12e-02 +2.55e-02 +1.56e-02 +1.52e-02 +9.91e-03 +3.26e-03 +1.47e-03 +2.30e-01 +2.43e-01 +2.55e-01 +2.55e-01 +2.51e-01 +2.39e-01 +2.30e-01 +2.27e-01 +2.29e-01 +2.07e-01 +2.02e-01 +2.14e-01 +1.61e-01 +2.95e-02 +7.43e-03 +2.45e-01 +2.22e-01 +2.61e-01 +2.31e-01 +2.30e-01 +2.40e-01 +2.34e-01 +2.18e-01 +2.03e-01 +2.11e-01 +1.97e-01 +1.92e-01 +1.80e-01 +4.88e-02 +1.49e-02 +2.38e-01 +2.17e-01 +2.52e-01 +2.29e-01 +2.25e-01 +2.44e-01 +2.15e-01 +2.39e-01 +2.19e-01 +2.19e-01 +2.01e-01 +1.92e-01 +2.20e-01 +9.45e-02 +2.94e-02 +2.36e-01 +2.27e-01 +2.34e-01 +2.40e-01 +2.35e-01 +2.56e-01 +2.38e-01 +2.37e-01 +2.25e-01 +2.08e-01 +1.82e-01 +1.60e-01 +1.81e-01 +5.67e-02 +1.61e-02 +2.29e-01 +2.18e-01 +2.26e-01 +2.37e-01 +2.21e-01 +2.35e-01 +2.69e-01 +2.31e-01 +2.30e-01 +2.32e-01 +2.01e-01 +1.47e-01 +1.58e-01 +7.81e-02 +2.93e-02 +2.28e-01 +2.05e-01 +2.20e-01 +2.25e-01 +2.12e-01 +2.31e-01 +2.33e-01 +2.13e-01 +2.09e-01 +2.24e-01 +1.97e-01 +1.71e-01 +1.79e-01 +7.52e-02 +2.31e-02 +2.03e-01 +2.07e-01 +2.27e-01 +2.19e-01 +2.11e-01 +2.22e-01 +2.17e-01 +2.08e-01 +1.77e-01 +2.01e-01 +1.89e-01 +1.57e-01 +1.63e-01 +5.48e-02 +1.75e-02 +2.07e-01 +2.13e-01 +2.09e-01 +2.25e-01 +2.36e-01 +2.27e-01 +2.11e-01 +2.10e-01 +1.87e-01 +1.89e-01 +1.88e-01 +1.79e-01 +1.33e-01 +3.03e-02 +1.06e-02 +2.08e-01 +2.14e-01 +2.12e-01 +2.27e-01 +2.27e-01 +2.05e-01 +2.29e-01 +2.21e-01 +1.93e-01 +2.02e-01 +1.81e-01 +1.73e-01 +1.67e-01 +4.08e-02 +1.39e-02 +2.00e-01 +2.19e-01 +2.28e-01 +2.14e-01 +2.28e-01 +2.06e-01 +2.17e-01 +1.92e-01 +1.79e-01 +1.74e-01 +1.72e-01 +1.71e-01 +1.60e-01 +4.62e-02 +1.43e-02 +1.82e-01 +1.92e-01 +2.16e-01 +2.14e-01 +2.12e-01 +2.26e-01 +1.94e-01 +1.68e-01 +1.64e-01 +1.50e-01 +1.55e-01 +1.48e-01 +1.04e-01 +4.02e-02 +1.44e-02 +1.56e-01 +1.60e-01 +1.93e-01 +1.87e-01 +1.94e-01 +2.01e-01 +2.07e-01 +1.85e-01 +1.76e-01 +1.49e-01 +1.39e-01 +1.38e-01 +1.04e-01 +2.84e-02 +1.17e-02 +1.33e-01 +1.46e-01 +1.65e-01 +1.58e-01 +1.74e-01 +1.70e-01 +1.37e-01 +1.94e-01 +1.93e-01 +1.64e-01 +1.20e-01 +1.02e-01 +6.63e-02 +1.85e-02 +8.29e-03 +6.84e-02 +4.09e-02 +5.11e-02 +6.13e-02 +4.88e-02 +4.65e-02 +3.93e-02 +4.74e-02 +8.51e-02 +5.59e-02 +2.40e-02 +2.44e-02 +1.30e-02 +4.68e-03 +2.65e-03 +2.59e-02 +1.30e-02 +1.81e-02 +1.96e-02 +1.49e-02 +1.49e-02 +1.50e-02 +1.42e-02 +2.71e-02 +2.16e-02 +1.05e-02 +7.72e-03 +4.11e-03 +2.35e-03 +9.64e-04 +2.21e-01 +2.15e-01 +2.72e-01 +1.92e-01 +3.01e-01 +2.43e-01 +2.13e-01 +2.18e-01 +1.88e-01 +1.66e-01 +1.13e-01 +1.21e-01 +1.08e-01 +2.92e-02 +8.37e-03 +2.75e-01 +1.89e-01 +3.28e-01 +3.64e-01 +2.91e-01 +2.34e-01 +1.86e-01 +1.90e-01 +1.53e-01 +1.35e-01 +1.21e-01 +1.01e-01 +8.79e-02 +3.91e-02 +1.37e-02 +2.37e-01 +2.28e-01 +2.92e-01 +2.21e-01 +1.70e-01 +2.29e-01 +1.86e-01 +1.95e-01 +1.65e-01 +1.42e-01 +1.73e-01 +1.38e-01 +7.39e-02 +4.16e-02 +2.91e-02 +2.73e-01 +2.33e-01 +2.78e-01 +3.06e-01 +2.31e-01 +2.52e-01 +2.69e-01 +2.39e-01 +2.13e-01 +1.63e-01 +1.20e-01 +8.39e-02 +5.83e-02 +2.86e-02 +1.32e-02 +2.63e-01 +1.92e-01 +2.54e-01 +3.11e-01 +2.65e-01 +2.75e-01 +2.71e-01 +2.21e-01 +1.81e-01 +1.68e-01 +1.49e-01 +7.05e-02 +5.03e-02 +2.54e-02 +1.28e-02 +2.09e-01 +1.61e-01 +2.21e-01 +2.28e-01 +1.55e-01 +1.78e-01 +2.16e-01 +1.50e-01 +1.32e-01 +1.76e-01 +1.57e-01 +1.01e-01 +5.61e-02 +2.32e-02 +1.53e-02 +1.50e-01 +1.81e-01 +2.03e-01 +2.04e-01 +1.79e-01 +1.94e-01 +1.81e-01 +1.52e-01 +1.26e-01 +1.13e-01 +9.16e-02 +6.72e-02 +3.94e-02 +2.35e-02 +1.45e-02 +1.06e-01 +1.43e-01 +1.61e-01 +1.57e-01 +1.80e-01 +1.80e-01 +1.23e-01 +1.22e-01 +8.18e-02 +7.30e-02 +9.33e-02 +7.39e-02 +3.89e-02 +2.07e-02 +8.17e-03 +1.13e-01 +1.37e-01 +1.61e-01 +2.20e-01 +1.71e-01 +1.51e-01 +1.16e-01 +1.45e-01 +1.13e-01 +1.05e-01 +9.41e-02 +7.14e-02 +5.35e-02 +1.91e-02 +7.31e-03 +8.55e-02 +1.58e-01 +2.08e-01 +1.60e-01 +1.37e-01 +8.33e-02 +1.16e-01 +1.39e-01 +1.15e-01 +9.22e-02 +8.69e-02 +6.24e-02 +4.30e-02 +2.48e-02 +7.57e-03 +9.22e-02 +1.13e-01 +1.60e-01 +1.04e-01 +1.40e-01 +1.35e-01 +1.08e-01 +1.02e-01 +8.56e-02 +5.91e-02 +7.46e-02 +6.28e-02 +3.81e-02 +2.04e-02 +9.21e-03 +7.85e-02 +6.83e-02 +1.01e-01 +9.42e-02 +9.47e-02 +1.38e-01 +1.13e-01 +6.40e-02 +6.03e-02 +5.21e-02 +4.63e-02 +4.67e-02 +3.57e-02 +9.24e-03 +3.46e-03 +3.82e-02 +4.99e-02 +6.21e-02 +4.80e-02 +5.79e-02 +8.37e-02 +6.86e-02 +6.66e-02 +5.19e-02 +5.77e-02 +4.00e-02 +3.46e-02 +2.69e-02 +9.16e-03 +3.73e-03 +1.66e-02 +1.94e-02 +2.26e-02 +1.79e-02 +2.45e-02 +3.08e-02 +2.00e-02 +2.89e-02 +3.35e-02 +2.83e-02 +1.58e-02 +1.10e-02 +7.32e-03 +3.83e-03 +1.85e-03 +1.35e-02 +7.32e-03 +8.41e-03 +8.10e-03 +9.00e-03 +1.12e-02 +5.57e-03 +9.30e-03 +1.48e-02 +1.39e-02 +6.30e-03 +3.42e-03 +2.99e-03 +1.40e-03 +5.26e-04 +7.56e-02 +7.51e-02 +1.02e-01 +6.63e-02 +1.09e-01 +8.00e-02 +7.46e-02 +7.69e-02 +7.23e-02 +5.75e-02 +3.77e-02 +3.71e-02 +3.59e-02 +2.14e-02 +7.54e-03 +1.13e-01 +5.14e-02 +1.11e-01 +1.48e-01 +1.23e-01 +8.67e-02 +6.58e-02 +5.93e-02 +5.79e-02 +5.00e-02 +3.78e-02 +2.92e-02 +2.64e-02 +1.76e-02 +8.34e-03 +8.71e-02 +6.82e-02 +1.03e-01 +9.93e-02 +4.68e-02 +8.00e-02 +6.54e-02 +5.73e-02 +5.88e-02 +4.17e-02 +6.05e-02 +5.26e-02 +2.82e-02 +1.18e-02 +1.25e-02 +8.93e-02 +6.93e-02 +1.05e-01 +1.22e-01 +7.82e-02 +8.44e-02 +9.53e-02 +9.08e-02 +8.61e-02 +6.38e-02 +4.26e-02 +2.94e-02 +1.95e-02 +1.16e-02 +5.85e-03 +1.07e-01 +7.48e-02 +9.61e-02 +1.16e-01 +1.00e-01 +9.56e-02 +1.01e-01 +7.22e-02 +7.15e-02 +4.92e-02 +5.10e-02 +2.14e-02 +1.39e-02 +8.55e-03 +5.42e-03 +6.30e-02 +5.46e-02 +6.94e-02 +7.56e-02 +5.76e-02 +4.99e-02 +6.77e-02 +4.79e-02 +4.29e-02 +5.31e-02 +5.73e-02 +4.32e-02 +2.01e-02 +8.20e-03 +4.18e-03 +6.21e-02 +5.98e-02 +7.34e-02 +7.91e-02 +4.60e-02 +5.72e-02 +6.01e-02 +5.26e-02 +5.01e-02 +3.98e-02 +3.12e-02 +2.62e-02 +1.44e-02 +6.44e-03 +6.95e-03 +2.95e-02 +5.38e-02 +4.94e-02 +4.81e-02 +5.65e-02 +6.07e-02 +3.70e-02 +3.88e-02 +2.59e-02 +2.05e-02 +2.51e-02 +2.29e-02 +1.19e-02 +1.08e-02 +8.34e-03 +3.62e-02 +4.00e-02 +5.20e-02 +6.63e-02 +6.42e-02 +5.90e-02 +3.69e-02 +4.87e-02 +4.00e-02 +3.31e-02 +2.85e-02 +2.46e-02 +2.05e-02 +8.72e-03 +3.45e-03 +2.17e-02 +4.81e-02 +6.28e-02 +6.24e-02 +4.43e-02 +2.41e-02 +3.73e-02 +4.41e-02 +4.25e-02 +2.80e-02 +2.62e-02 +2.10e-02 +1.12e-02 +1.28e-02 +6.90e-03 +2.96e-02 +4.88e-02 +5.84e-02 +2.83e-02 +4.58e-02 +3.78e-02 +2.43e-02 +3.47e-02 +3.08e-02 +2.18e-02 +2.32e-02 +1.70e-02 +1.26e-02 +9.95e-03 +8.03e-03 +2.02e-02 +2.21e-02 +3.09e-02 +2.79e-02 +2.92e-02 +4.73e-02 +3.85e-02 +2.69e-02 +1.89e-02 +1.70e-02 +1.44e-02 +1.42e-02 +1.51e-02 +4.21e-03 +1.55e-03 +1.72e-02 +1.70e-02 +2.18e-02 +2.50e-02 +1.84e-02 +3.18e-02 +2.53e-02 +2.01e-02 +1.37e-02 +1.89e-02 +1.02e-02 +1.15e-02 +1.16e-02 +3.85e-03 +1.22e-03 +5.45e-03 +9.53e-03 +9.49e-03 +6.68e-03 +1.08e-02 +1.59e-02 +1.34e-02 +1.53e-02 +1.86e-02 +1.03e-02 +1.07e-02 +6.50e-03 +3.98e-03 +2.38e-03 +2.29e-03 +4.46e-03 +5.06e-03 +5.22e-03 +4.55e-03 +5.72e-03 +1.00e-02 +6.07e-03 +5.95e-03 +1.20e-02 +8.03e-03 +5.95e-03 +3.61e-03 +1.35e-03 +8.82e-04 +1.47e-03 +Upper Bounds +8.83e-01 +9.04e-01 +9.04e-01 +9.06e-01 +8.90e-01 +8.71e-01 +8.51e-01 +8.82e-01 +8.99e-01 +9.42e-01 +8.75e-01 +9.67e-01 +1.38e+00 +2.14e+00 +7.89e-01 +8.70e-01 +9.11e-01 +9.06e-01 +8.15e-01 +8.92e-01 +9.06e-01 +8.84e-01 +8.83e-01 +8.68e-01 +8.58e-01 +9.07e-01 +9.12e-01 +1.29e+00 +1.63e+00 +5.33e-01 +9.10e-01 +8.73e-01 +9.05e-01 +9.08e-01 +8.81e-01 +8.85e-01 +8.70e-01 +8.31e-01 +8.30e-01 +8.46e-01 +8.79e-01 +9.36e-01 +1.33e+00 +2.08e+00 +7.86e-01 +8.80e-01 +8.72e-01 +8.61e-01 +8.81e-01 +8.35e-01 +8.38e-01 +8.52e-01 +8.74e-01 +8.62e-01 +8.63e-01 +8.27e-01 +9.20e-01 +1.33e+00 +2.00e+00 +6.39e-01 +8.76e-01 +9.10e-01 +9.11e-01 +9.20e-01 +8.60e-01 +8.61e-01 +8.34e-01 +8.36e-01 +8.28e-01 +8.72e-01 +8.44e-01 +9.49e-01 +1.41e+00 +1.65e+00 +6.46e-01 +9.01e-01 +8.97e-01 +8.94e-01 +8.90e-01 +8.81e-01 +8.46e-01 +8.38e-01 +8.15e-01 +8.49e-01 +8.29e-01 +8.56e-01 +9.61e-01 +1.35e+00 +1.44e+00 +5.12e-01 +8.79e-01 +8.59e-01 +8.42e-01 +8.55e-01 +8.31e-01 +8.41e-01 +8.24e-01 +8.31e-01 +8.11e-01 +7.99e-01 +8.06e-01 +8.32e-01 +1.19e+00 +1.66e+00 +6.17e-01 +9.09e-01 +9.03e-01 +8.95e-01 +8.51e-01 +8.33e-01 +8.62e-01 +8.26e-01 +8.10e-01 +7.35e-01 +7.80e-01 +7.82e-01 +7.20e-01 +1.00e+00 +8.79e-01 +3.78e-01 +8.81e-01 +8.79e-01 +8.98e-01 +9.09e-01 +8.56e-01 +8.36e-01 +8.26e-01 +8.10e-01 +7.69e-01 +7.73e-01 +7.71e-01 +7.76e-01 +1.14e+00 +8.33e-01 +2.68e-01 +8.83e-01 +9.23e-01 +9.16e-01 +9.05e-01 +8.94e-01 +8.03e-01 +8.19e-01 +7.89e-01 +8.04e-01 +7.99e-01 +7.77e-01 +7.67e-01 +1.16e+00 +8.69e-01 +3.26e-01 +9.04e-01 +8.93e-01 +9.15e-01 +8.96e-01 +8.99e-01 +8.47e-01 +7.92e-01 +8.32e-01 +7.94e-01 +7.96e-01 +8.49e-01 +7.81e-01 +1.06e+00 +6.40e-01 +1.87e-01 +9.49e-01 +9.48e-01 +9.08e-01 +8.57e-01 +9.51e-01 +9.10e-01 +8.62e-01 +8.93e-01 +8.43e-01 +8.47e-01 +8.29e-01 +7.55e-01 +1.11e+00 +4.81e-01 +1.57e-01 +1.38e+00 +1.37e+00 +1.26e+00 +1.31e+00 +1.37e+00 +1.34e+00 +1.26e+00 +1.21e+00 +1.21e+00 +1.21e+00 +1.20e+00 +1.12e+00 +1.06e+00 +3.64e-01 +1.05e-01 +2.50e+00 +1.75e+00 +1.96e+00 +1.28e+00 +1.40e+00 +1.59e+00 +1.27e+00 +1.18e+00 +9.31e-01 +9.87e-01 +9.06e-01 +7.50e-01 +4.87e-01 +1.88e-01 +9.31e-02 +1.08e+00 +7.08e-01 +8.14e-01 +4.09e-01 +4.59e-01 +5.72e-01 +4.06e-01 +3.97e-01 +3.49e-01 +3.11e-01 +3.14e-01 +2.24e-01 +1.84e-01 +7.93e-02 +3.58e-02 +8.56e-01 +8.80e-01 +8.90e-01 +8.72e-01 +8.83e-01 +8.79e-01 +9.07e-01 +8.87e-01 +8.46e-01 +8.81e-01 +9.13e-01 +9.22e-01 +1.35e+00 +1.99e+00 +7.68e-01 +8.95e-01 +8.96e-01 +9.01e-01 +8.77e-01 +8.78e-01 +8.57e-01 +8.71e-01 +8.72e-01 +8.61e-01 +8.65e-01 +8.84e-01 +9.25e-01 +1.27e+00 +1.84e+00 +6.71e-01 +9.00e-01 +8.80e-01 +8.68e-01 +8.83e-01 +8.82e-01 +8.83e-01 +8.68e-01 +8.89e-01 +8.61e-01 +9.00e-01 +9.04e-01 +9.06e-01 +1.33e+00 +1.66e+00 +5.75e-01 +9.10e-01 +9.28e-01 +9.04e-01 +9.15e-01 +8.68e-01 +8.37e-01 +8.58e-01 +8.74e-01 +8.49e-01 +8.43e-01 +8.73e-01 +9.20e-01 +1.29e+00 +1.77e+00 +6.31e-01 +8.99e-01 +9.11e-01 +8.97e-01 +8.66e-01 +8.29e-01 +8.48e-01 +8.85e-01 +8.47e-01 +8.39e-01 +8.47e-01 +8.91e-01 +9.17e-01 +1.38e+00 +1.51e+00 +6.62e-01 +8.74e-01 +8.88e-01 +8.84e-01 +8.46e-01 +8.97e-01 +8.43e-01 +8.27e-01 +8.17e-01 +8.15e-01 +8.35e-01 +8.69e-01 +9.33e-01 +1.35e+00 +1.72e+00 +6.01e-01 +9.01e-01 +9.12e-01 +8.41e-01 +8.34e-01 +8.86e-01 +8.49e-01 +7.94e-01 +7.99e-01 +7.91e-01 +8.03e-01 +8.26e-01 +8.34e-01 +1.17e+00 +1.46e+00 +5.66e-01 +8.53e-01 +8.62e-01 +8.25e-01 +8.26e-01 +8.26e-01 +7.99e-01 +7.85e-01 +7.29e-01 +7.14e-01 +7.51e-01 +7.41e-01 +5.28e-01 +9.65e-01 +8.41e-01 +3.51e-01 +8.43e-01 +9.30e-01 +8.75e-01 +8.72e-01 +8.86e-01 +8.25e-01 +8.05e-01 +7.70e-01 +7.66e-01 +7.77e-01 +7.36e-01 +7.06e-01 +9.91e-01 +6.14e-01 +1.92e-01 +8.89e-01 +9.28e-01 +8.82e-01 +8.85e-01 +8.26e-01 +8.77e-01 +8.28e-01 +7.72e-01 +7.97e-01 +7.70e-01 +8.14e-01 +7.60e-01 +1.08e+00 +7.25e-01 +2.17e-01 +8.99e-01 +9.33e-01 +8.95e-01 +8.67e-01 +9.07e-01 +8.80e-01 +8.33e-01 +7.98e-01 +8.16e-01 +7.99e-01 +8.47e-01 +7.75e-01 +1.08e+00 +6.72e-01 +2.06e-01 +9.39e-01 +8.99e-01 +9.19e-01 +9.05e-01 +9.21e-01 +9.37e-01 +8.70e-01 +8.47e-01 +8.32e-01 +8.66e-01 +8.48e-01 +8.12e-01 +1.00e+00 +4.14e-01 +1.43e-01 +1.29e+00 +1.41e+00 +1.29e+00 +1.35e+00 +1.47e+00 +1.34e+00 +1.23e+00 +1.28e+00 +1.17e+00 +1.16e+00 +1.29e+00 +1.20e+00 +1.04e+00 +2.68e-01 +6.98e-02 +1.56e+00 +1.49e+00 +1.36e+00 +1.52e+00 +1.85e+00 +1.77e+00 +1.39e+00 +1.40e+00 +1.09e+00 +8.12e-01 +8.12e-01 +8.15e-01 +5.39e-01 +1.74e-01 +7.42e-02 +5.46e-01 +5.41e-01 +5.27e-01 +5.05e-01 +6.76e-01 +7.08e-01 +4.76e-01 +5.00e-01 +4.66e-01 +2.59e-01 +2.59e-01 +2.67e-01 +2.12e-01 +9.34e-02 +3.59e-02 +8.98e-01 +8.76e-01 +9.11e-01 +8.74e-01 +8.57e-01 +8.49e-01 +8.45e-01 +8.23e-01 +8.73e-01 +8.56e-01 +8.41e-01 +9.32e-01 +1.56e+00 +2.14e+00 +8.32e-01 +9.04e-01 +8.85e-01 +8.48e-01 +8.66e-01 +8.66e-01 +8.58e-01 +8.22e-01 +8.28e-01 +8.32e-01 +8.65e-01 +8.49e-01 +9.47e-01 +1.32e+00 +2.01e+00 +8.20e-01 +8.96e-01 +9.02e-01 +9.15e-01 +8.55e-01 +8.63e-01 +8.56e-01 +8.51e-01 +8.73e-01 +8.47e-01 +8.41e-01 +8.38e-01 +8.99e-01 +1.24e+00 +1.71e+00 +6.45e-01 +8.13e-01 +8.67e-01 +8.68e-01 +8.57e-01 +8.41e-01 +8.27e-01 +8.41e-01 +8.63e-01 +8.60e-01 +8.27e-01 +8.62e-01 +8.45e-01 +1.26e+00 +1.59e+00 +5.28e-01 +8.37e-01 +8.48e-01 +8.61e-01 +8.18e-01 +8.55e-01 +8.57e-01 +7.87e-01 +8.18e-01 +8.05e-01 +8.09e-01 +8.66e-01 +8.95e-01 +1.31e+00 +1.33e+00 +3.87e-01 +8.40e-01 +8.59e-01 +8.21e-01 +8.32e-01 +8.27e-01 +8.30e-01 +7.89e-01 +7.95e-01 +8.16e-01 +8.14e-01 +8.48e-01 +8.74e-01 +1.25e+00 +1.74e+00 +6.56e-01 +8.62e-01 +8.97e-01 +8.49e-01 +8.48e-01 +8.43e-01 +8.56e-01 +8.49e-01 +7.73e-01 +7.97e-01 +8.23e-01 +8.50e-01 +8.53e-01 +1.22e+00 +1.23e+00 +4.60e-01 +8.51e-01 +8.51e-01 +8.30e-01 +8.17e-01 +8.30e-01 +8.17e-01 +7.95e-01 +7.91e-01 +7.83e-01 +7.99e-01 +7.74e-01 +7.63e-01 +1.10e+00 +8.15e-01 +3.18e-01 +8.51e-01 +8.51e-01 +8.64e-01 +8.45e-01 +8.30e-01 +8.19e-01 +8.14e-01 +8.18e-01 +7.60e-01 +7.72e-01 +7.64e-01 +7.45e-01 +1.06e+00 +8.19e-01 +3.07e-01 +8.70e-01 +8.92e-01 +8.63e-01 +8.73e-01 +8.70e-01 +8.23e-01 +8.34e-01 +7.96e-01 +7.41e-01 +7.85e-01 +7.76e-01 +7.97e-01 +1.03e+00 +6.15e-01 +2.40e-01 +8.46e-01 +8.88e-01 +8.83e-01 +8.69e-01 +8.83e-01 +8.65e-01 +8.68e-01 +8.40e-01 +7.58e-01 +7.91e-01 +7.91e-01 +7.77e-01 +1.06e+00 +6.09e-01 +1.67e-01 +8.76e-01 +9.27e-01 +9.32e-01 +9.51e-01 +9.02e-01 +8.97e-01 +8.78e-01 +8.56e-01 +8.35e-01 +8.55e-01 +8.47e-01 +8.43e-01 +9.29e-01 +5.11e-01 +1.77e-01 +1.33e+00 +1.33e+00 +1.32e+00 +1.25e+00 +1.30e+00 +1.29e+00 +1.17e+00 +1.21e+00 +1.18e+00 +1.15e+00 +1.16e+00 +1.21e+00 +1.14e+00 +2.72e-01 +8.55e-02 +1.89e+00 +1.37e+00 +1.71e+00 +1.75e+00 +1.82e+00 +1.16e+00 +1.16e+00 +1.09e+00 +9.10e-01 +8.87e-01 +6.38e-01 +5.79e-01 +5.13e-01 +2.08e-01 +5.28e-02 +6.66e-01 +4.35e-01 +7.04e-01 +6.72e-01 +6.85e-01 +4.41e-01 +3.91e-01 +4.18e-01 +2.80e-01 +3.20e-01 +1.45e-01 +1.85e-01 +1.73e-01 +1.30e-01 +6.20e-02 +8.83e-01 +8.54e-01 +8.74e-01 +8.97e-01 +8.66e-01 +8.53e-01 +8.20e-01 +8.29e-01 +7.95e-01 +8.07e-01 +7.74e-01 +8.95e-01 +1.34e+00 +2.13e+00 +7.78e-01 +8.75e-01 +9.11e-01 +8.53e-01 +8.24e-01 +8.43e-01 +8.30e-01 +8.44e-01 +8.16e-01 +8.23e-01 +8.70e-01 +8.45e-01 +8.80e-01 +1.24e+00 +1.66e+00 +6.51e-01 +8.43e-01 +8.65e-01 +8.86e-01 +8.33e-01 +7.75e-01 +8.33e-01 +7.93e-01 +8.14e-01 +7.91e-01 +8.49e-01 +8.47e-01 +9.21e-01 +1.22e+00 +1.35e+00 +5.11e-01 +8.45e-01 +8.50e-01 +8.67e-01 +8.43e-01 +8.32e-01 +8.35e-01 +8.18e-01 +8.02e-01 +8.09e-01 +8.16e-01 +8.13e-01 +8.33e-01 +1.24e+00 +1.20e+00 +4.92e-01 +8.11e-01 +8.21e-01 +8.46e-01 +8.56e-01 +8.12e-01 +7.90e-01 +8.09e-01 +7.88e-01 +8.08e-01 +8.07e-01 +8.57e-01 +8.34e-01 +1.25e+00 +1.61e+00 +5.85e-01 +8.68e-01 +7.99e-01 +8.45e-01 +8.59e-01 +8.33e-01 +7.98e-01 +8.23e-01 +8.40e-01 +8.11e-01 +8.01e-01 +8.15e-01 +8.78e-01 +1.27e+00 +1.49e+00 +5.48e-01 +8.43e-01 +8.36e-01 +8.81e-01 +8.18e-01 +7.95e-01 +7.90e-01 +8.20e-01 +7.94e-01 +7.74e-01 +7.96e-01 +7.56e-01 +8.00e-01 +1.25e+00 +1.44e+00 +5.06e-01 +8.60e-01 +8.53e-01 +8.41e-01 +8.13e-01 +7.94e-01 +8.04e-01 +8.30e-01 +7.78e-01 +7.68e-01 +7.54e-01 +7.46e-01 +7.89e-01 +1.17e+00 +9.24e-01 +3.31e-01 +8.47e-01 +8.25e-01 +8.14e-01 +8.34e-01 +8.09e-01 +8.13e-01 +8.14e-01 +8.04e-01 +7.77e-01 +7.65e-01 +7.94e-01 +8.47e-01 +1.24e+00 +9.33e-01 +2.64e-01 +8.55e-01 +8.28e-01 +8.67e-01 +8.38e-01 +8.25e-01 +8.22e-01 +7.96e-01 +7.62e-01 +7.99e-01 +7.61e-01 +7.38e-01 +7.68e-01 +1.16e+00 +6.75e-01 +2.70e-01 +8.80e-01 +8.88e-01 +8.39e-01 +8.93e-01 +8.67e-01 +8.43e-01 +8.12e-01 +8.07e-01 +7.97e-01 +8.03e-01 +7.88e-01 +8.01e-01 +1.03e+00 +4.71e-01 +1.39e-01 +9.42e-01 +8.84e-01 +8.89e-01 +8.81e-01 +9.12e-01 +9.52e-01 +8.80e-01 +8.12e-01 +8.44e-01 +8.47e-01 +8.47e-01 +8.42e-01 +9.87e-01 +3.92e-01 +1.14e-01 +1.24e+00 +1.25e+00 +1.29e+00 +1.25e+00 +1.29e+00 +1.28e+00 +1.08e+00 +1.14e+00 +1.21e+00 +1.12e+00 +1.06e+00 +1.07e+00 +1.14e+00 +3.91e-01 +1.23e-01 +1.55e+00 +1.24e+00 +1.03e+00 +1.46e+00 +1.44e+00 +1.17e+00 +8.10e-01 +5.67e-01 +9.52e-01 +9.00e-01 +5.78e-01 +5.33e-01 +4.71e-01 +2.10e-01 +8.15e-02 +5.84e-01 +4.29e-01 +4.39e-01 +4.57e-01 +5.73e-01 +4.28e-01 +3.39e-01 +1.82e-01 +3.17e-01 +3.19e-01 +2.03e-01 +1.80e-01 +1.39e-01 +1.28e-01 +6.06e-02 +8.53e-01 +8.41e-01 +8.16e-01 +8.39e-01 +8.62e-01 +8.30e-01 +8.25e-01 +7.57e-01 +7.84e-01 +8.17e-01 +7.85e-01 +8.91e-01 +1.29e+00 +1.58e+00 +6.19e-01 +8.62e-01 +8.53e-01 +8.28e-01 +8.47e-01 +8.54e-01 +8.13e-01 +7.89e-01 +7.56e-01 +8.10e-01 +8.39e-01 +8.37e-01 +8.80e-01 +1.27e+00 +1.52e+00 +5.61e-01 +8.76e-01 +8.58e-01 +8.51e-01 +8.40e-01 +8.16e-01 +8.22e-01 +8.17e-01 +7.68e-01 +8.23e-01 +8.47e-01 +8.38e-01 +8.80e-01 +1.26e+00 +1.11e+00 +4.01e-01 +8.41e-01 +8.55e-01 +8.26e-01 +8.31e-01 +8.17e-01 +8.22e-01 +8.05e-01 +8.08e-01 +8.05e-01 +8.32e-01 +8.20e-01 +8.61e-01 +1.30e+00 +1.05e+00 +3.57e-01 +8.15e-01 +8.06e-01 +8.14e-01 +8.44e-01 +8.05e-01 +8.12e-01 +8.12e-01 +8.06e-01 +7.90e-01 +7.71e-01 +8.19e-01 +8.79e-01 +1.29e+00 +1.63e+00 +5.79e-01 +7.99e-01 +7.98e-01 +8.14e-01 +8.39e-01 +8.09e-01 +7.70e-01 +7.51e-01 +8.22e-01 +8.15e-01 +7.92e-01 +7.89e-01 +8.42e-01 +1.22e+00 +1.30e+00 +4.67e-01 +8.04e-01 +8.15e-01 +8.41e-01 +8.12e-01 +8.44e-01 +7.64e-01 +7.65e-01 +7.83e-01 +7.51e-01 +7.69e-01 +7.83e-01 +7.94e-01 +1.09e+00 +1.03e+00 +3.79e-01 +8.53e-01 +8.48e-01 +8.17e-01 +7.87e-01 +8.03e-01 +7.66e-01 +7.80e-01 +7.47e-01 +7.47e-01 +7.94e-01 +7.46e-01 +7.73e-01 +1.13e+00 +8.10e-01 +3.44e-01 +8.55e-01 +8.35e-01 +8.31e-01 +8.01e-01 +8.04e-01 +8.11e-01 +8.17e-01 +7.71e-01 +7.52e-01 +7.41e-01 +7.55e-01 +7.73e-01 +1.08e+00 +8.26e-01 +2.48e-01 +8.55e-01 +8.65e-01 +8.39e-01 +8.10e-01 +7.84e-01 +8.10e-01 +7.97e-01 +7.80e-01 +7.63e-01 +7.36e-01 +7.45e-01 +8.12e-01 +1.07e+00 +7.90e-01 +3.43e-01 +8.52e-01 +8.31e-01 +8.47e-01 +8.29e-01 +8.34e-01 +8.10e-01 +8.15e-01 +8.13e-01 +7.91e-01 +7.58e-01 +7.69e-01 +8.34e-01 +1.13e+00 +5.77e-01 +2.01e-01 +8.71e-01 +9.08e-01 +8.51e-01 +8.61e-01 +8.87e-01 +9.24e-01 +8.39e-01 +8.24e-01 +8.13e-01 +8.68e-01 +8.13e-01 +8.31e-01 +1.14e+00 +5.34e-01 +1.63e-01 +1.29e+00 +1.23e+00 +1.25e+00 +1.29e+00 +1.30e+00 +1.33e+00 +1.29e+00 +1.19e+00 +1.17e+00 +1.14e+00 +1.08e+00 +1.12e+00 +1.16e+00 +4.48e-01 +1.51e-01 +1.37e+00 +1.25e+00 +1.26e+00 +1.31e+00 +1.52e+00 +1.34e+00 +1.46e+00 +9.26e-01 +1.00e+00 +7.33e-01 +5.03e-01 +5.86e-01 +4.40e-01 +1.93e-01 +7.63e-02 +5.00e-01 +4.36e-01 +4.55e-01 +4.50e-01 +5.38e-01 +4.35e-01 +5.87e-01 +2.73e-01 +3.52e-01 +2.53e-01 +1.54e-01 +1.57e-01 +1.32e-01 +8.46e-02 +5.80e-02 +7.99e-01 +8.20e-01 +8.15e-01 +8.33e-01 +8.51e-01 +8.10e-01 +7.68e-01 +7.62e-01 +7.58e-01 +7.77e-01 +7.87e-01 +8.14e-01 +1.30e+00 +1.29e+00 +3.79e-01 +7.97e-01 +8.41e-01 +8.13e-01 +7.89e-01 +8.26e-01 +8.24e-01 +8.37e-01 +7.59e-01 +7.92e-01 +7.78e-01 +7.98e-01 +8.46e-01 +1.24e+00 +1.44e+00 +5.54e-01 +8.32e-01 +8.44e-01 +8.48e-01 +8.16e-01 +8.13e-01 +7.97e-01 +8.17e-01 +8.24e-01 +7.90e-01 +7.89e-01 +7.98e-01 +8.75e-01 +1.29e+00 +1.26e+00 +4.10e-01 +8.34e-01 +8.40e-01 +7.91e-01 +7.91e-01 +8.06e-01 +8.33e-01 +7.71e-01 +7.76e-01 +8.05e-01 +8.07e-01 +8.15e-01 +8.50e-01 +1.26e+00 +1.18e+00 +3.80e-01 +8.13e-01 +8.32e-01 +8.39e-01 +8.18e-01 +7.93e-01 +7.93e-01 +7.84e-01 +7.91e-01 +7.91e-01 +7.82e-01 +7.90e-01 +8.30e-01 +1.25e+00 +1.29e+00 +4.90e-01 +8.01e-01 +8.04e-01 +8.21e-01 +8.03e-01 +7.98e-01 +7.82e-01 +7.55e-01 +7.51e-01 +8.14e-01 +8.19e-01 +7.83e-01 +8.19e-01 +1.18e+00 +1.09e+00 +4.51e-01 +8.40e-01 +8.47e-01 +8.18e-01 +7.81e-01 +7.89e-01 +7.65e-01 +7.53e-01 +7.17e-01 +7.54e-01 +7.65e-01 +7.76e-01 +8.17e-01 +1.09e+00 +6.64e-01 +2.45e-01 +8.36e-01 +7.99e-01 +8.04e-01 +7.74e-01 +8.20e-01 +7.78e-01 +7.45e-01 +7.25e-01 +7.18e-01 +7.55e-01 +7.86e-01 +8.22e-01 +1.16e+00 +5.88e-01 +1.82e-01 +8.12e-01 +8.33e-01 +8.42e-01 +7.73e-01 +7.95e-01 +7.85e-01 +7.91e-01 +7.28e-01 +7.16e-01 +6.95e-01 +7.39e-01 +7.71e-01 +1.13e+00 +7.23e-01 +2.02e-01 +7.76e-01 +8.13e-01 +8.17e-01 +8.37e-01 +7.81e-01 +7.65e-01 +7.51e-01 +7.47e-01 +7.44e-01 +7.26e-01 +7.33e-01 +7.63e-01 +1.05e+00 +6.00e-01 +1.91e-01 +8.26e-01 +7.94e-01 +8.07e-01 +8.05e-01 +7.80e-01 +7.80e-01 +8.00e-01 +7.93e-01 +7.69e-01 +7.64e-01 +7.39e-01 +7.58e-01 +1.04e+00 +5.07e-01 +1.50e-01 +8.87e-01 +8.55e-01 +8.41e-01 +8.56e-01 +8.73e-01 +8.47e-01 +8.19e-01 +8.38e-01 +8.05e-01 +8.07e-01 +7.65e-01 +7.30e-01 +9.77e-01 +4.70e-01 +1.10e-01 +1.23e+00 +1.19e+00 +1.16e+00 +1.21e+00 +1.27e+00 +1.25e+00 +1.14e+00 +1.10e+00 +1.11e+00 +1.13e+00 +1.17e+00 +1.01e+00 +9.39e-01 +4.79e-01 +1.76e-01 +1.53e+00 +1.30e+00 +1.11e+00 +1.31e+00 +1.28e+00 +1.18e+00 +1.16e+00 +8.91e-01 +7.64e-01 +6.86e-01 +7.35e-01 +6.78e-01 +3.62e-01 +2.02e-01 +1.12e-01 +5.19e-01 +4.91e-01 +4.40e-01 +4.60e-01 +4.87e-01 +3.65e-01 +5.18e-01 +3.03e-01 +2.69e-01 +1.84e-01 +2.14e-01 +2.43e-01 +1.44e-01 +7.93e-02 +4.59e-02 +8.53e-01 +8.34e-01 +8.24e-01 +8.44e-01 +7.93e-01 +7.98e-01 +8.11e-01 +8.08e-01 +7.84e-01 +7.45e-01 +7.60e-01 +7.66e-01 +1.21e+00 +9.01e-01 +4.10e-01 +8.39e-01 +8.13e-01 +8.33e-01 +8.25e-01 +8.17e-01 +7.68e-01 +7.94e-01 +7.61e-01 +7.94e-01 +7.58e-01 +7.42e-01 +7.76e-01 +1.12e+00 +1.01e+00 +3.78e-01 +8.15e-01 +7.96e-01 +8.12e-01 +8.03e-01 +7.86e-01 +7.79e-01 +7.51e-01 +7.57e-01 +7.42e-01 +7.66e-01 +8.04e-01 +7.92e-01 +1.16e+00 +1.25e+00 +5.04e-01 +8.34e-01 +7.68e-01 +7.75e-01 +7.60e-01 +7.73e-01 +8.04e-01 +7.86e-01 +7.68e-01 +7.69e-01 +7.72e-01 +7.79e-01 +8.19e-01 +1.12e+00 +7.80e-01 +3.07e-01 +8.35e-01 +8.24e-01 +8.03e-01 +7.87e-01 +7.88e-01 +8.12e-01 +7.74e-01 +7.87e-01 +7.48e-01 +7.70e-01 +7.77e-01 +8.41e-01 +1.16e+00 +6.39e-01 +2.34e-01 +8.17e-01 +7.87e-01 +7.98e-01 +8.15e-01 +8.06e-01 +7.88e-01 +7.52e-01 +7.32e-01 +7.17e-01 +7.60e-01 +7.74e-01 +8.04e-01 +1.13e+00 +6.75e-01 +2.06e-01 +8.22e-01 +8.10e-01 +8.02e-01 +8.08e-01 +8.05e-01 +7.56e-01 +7.48e-01 +7.03e-01 +7.31e-01 +7.43e-01 +7.54e-01 +8.22e-01 +1.16e+00 +7.52e-01 +2.28e-01 +7.64e-01 +7.75e-01 +7.68e-01 +7.87e-01 +7.73e-01 +7.45e-01 +7.48e-01 +7.22e-01 +7.13e-01 +7.34e-01 +7.57e-01 +7.86e-01 +1.10e+00 +8.15e-01 +2.44e-01 +8.21e-01 +7.75e-01 +7.87e-01 +7.66e-01 +7.39e-01 +7.65e-01 +7.62e-01 +6.99e-01 +6.88e-01 +6.98e-01 +7.52e-01 +7.85e-01 +1.05e+00 +7.70e-01 +2.57e-01 +7.79e-01 +7.89e-01 +8.06e-01 +7.85e-01 +7.59e-01 +7.34e-01 +7.57e-01 +7.39e-01 +7.18e-01 +7.39e-01 +7.21e-01 +7.16e-01 +1.03e+00 +6.02e-01 +2.37e-01 +8.09e-01 +8.54e-01 +8.12e-01 +7.85e-01 +7.60e-01 +7.56e-01 +7.18e-01 +7.13e-01 +7.31e-01 +7.31e-01 +6.81e-01 +7.25e-01 +9.31e-01 +3.54e-01 +1.26e-01 +8.50e-01 +8.54e-01 +8.14e-01 +7.80e-01 +8.02e-01 +8.05e-01 +7.81e-01 +7.77e-01 +7.97e-01 +7.65e-01 +7.40e-01 +7.05e-01 +7.73e-01 +3.06e-01 +1.34e-01 +1.20e+00 +1.12e+00 +1.11e+00 +1.13e+00 +1.15e+00 +1.10e+00 +1.10e+00 +9.95e-01 +1.08e+00 +9.88e-01 +1.01e+00 +9.01e-01 +6.74e-01 +1.87e-01 +8.09e-02 +1.32e+00 +1.34e+00 +1.16e+00 +7.75e-01 +7.51e-01 +9.97e-01 +8.93e-01 +7.22e-01 +6.51e-01 +6.25e-01 +7.18e-01 +5.73e-01 +2.11e-01 +9.82e-02 +4.26e-02 +4.75e-01 +5.18e-01 +4.49e-01 +2.40e-01 +2.44e-01 +3.22e-01 +3.19e-01 +2.35e-01 +2.20e-01 +1.64e-01 +2.36e-01 +2.02e-01 +7.60e-02 +4.33e-02 +3.07e-02 +7.81e-01 +7.87e-01 +7.72e-01 +8.00e-01 +8.24e-01 +8.30e-01 +8.27e-01 +7.88e-01 +7.35e-01 +7.02e-01 +6.79e-01 +7.96e-01 +1.11e+00 +7.59e-01 +2.66e-01 +8.05e-01 +8.08e-01 +8.45e-01 +8.16e-01 +7.90e-01 +7.90e-01 +7.97e-01 +7.35e-01 +7.24e-01 +6.97e-01 +6.50e-01 +7.41e-01 +1.08e+00 +5.46e-01 +1.73e-01 +7.91e-01 +7.84e-01 +8.12e-01 +8.13e-01 +7.93e-01 +7.86e-01 +7.80e-01 +7.88e-01 +7.62e-01 +7.25e-01 +7.48e-01 +8.00e-01 +1.08e+00 +7.80e-01 +2.83e-01 +7.99e-01 +7.86e-01 +8.00e-01 +7.83e-01 +8.01e-01 +7.89e-01 +7.68e-01 +7.67e-01 +7.34e-01 +7.08e-01 +7.82e-01 +8.28e-01 +1.05e+00 +7.28e-01 +3.01e-01 +8.10e-01 +8.05e-01 +8.41e-01 +7.70e-01 +7.70e-01 +7.93e-01 +7.51e-01 +7.44e-01 +7.48e-01 +7.29e-01 +7.92e-01 +7.97e-01 +1.08e+00 +5.90e-01 +1.62e-01 +8.06e-01 +7.76e-01 +8.01e-01 +7.96e-01 +7.50e-01 +7.27e-01 +7.51e-01 +7.26e-01 +7.36e-01 +7.31e-01 +7.40e-01 +7.92e-01 +1.07e+00 +6.18e-01 +2.17e-01 +7.91e-01 +7.77e-01 +7.90e-01 +7.54e-01 +7.85e-01 +7.70e-01 +7.48e-01 +6.96e-01 +6.99e-01 +7.02e-01 +6.96e-01 +7.43e-01 +1.02e+00 +7.52e-01 +2.89e-01 +7.56e-01 +7.94e-01 +7.67e-01 +7.64e-01 +7.61e-01 +7.60e-01 +7.28e-01 +7.14e-01 +7.09e-01 +7.03e-01 +7.62e-01 +7.59e-01 +1.03e+00 +6.86e-01 +2.56e-01 +7.75e-01 +7.59e-01 +7.81e-01 +7.45e-01 +7.66e-01 +7.46e-01 +7.25e-01 +6.93e-01 +6.75e-01 +6.66e-01 +7.38e-01 +7.31e-01 +9.88e-01 +5.41e-01 +1.68e-01 +7.63e-01 +7.64e-01 +7.46e-01 +7.17e-01 +7.23e-01 +7.40e-01 +6.88e-01 +7.11e-01 +6.45e-01 +6.43e-01 +6.92e-01 +7.27e-01 +1.06e+00 +5.99e-01 +1.94e-01 +7.99e-01 +7.77e-01 +7.94e-01 +7.47e-01 +7.40e-01 +7.39e-01 +7.34e-01 +7.52e-01 +7.06e-01 +6.86e-01 +6.55e-01 +6.81e-01 +8.86e-01 +3.82e-01 +9.69e-02 +8.46e-01 +8.18e-01 +8.26e-01 +7.84e-01 +7.60e-01 +7.38e-01 +7.20e-01 +7.68e-01 +7.41e-01 +7.47e-01 +7.02e-01 +6.54e-01 +7.66e-01 +2.43e-01 +7.12e-02 +1.21e+00 +1.12e+00 +1.12e+00 +1.01e+00 +1.09e+00 +1.07e+00 +8.74e-01 +8.72e-01 +8.67e-01 +1.01e+00 +1.01e+00 +8.47e-01 +6.09e-01 +1.58e-01 +5.39e-02 +1.08e+00 +1.12e+00 +8.01e-01 +6.68e-01 +7.20e-01 +8.39e-01 +5.11e-01 +4.35e-01 +3.60e-01 +7.14e-01 +6.43e-01 +4.11e-01 +2.15e-01 +5.39e-02 +2.14e-02 +3.73e-01 +3.83e-01 +2.54e-01 +2.38e-01 +2.15e-01 +2.86e-01 +1.89e-01 +1.46e-01 +1.14e-01 +2.39e-01 +2.00e-01 +1.38e-01 +9.65e-02 +4.13e-02 +1.06e-02 +8.16e-01 +8.31e-01 +8.55e-01 +8.17e-01 +8.37e-01 +7.96e-01 +7.81e-01 +8.05e-01 +7.46e-01 +7.27e-01 +6.91e-01 +7.68e-01 +1.08e+00 +5.87e-01 +1.78e-01 +7.93e-01 +8.07e-01 +8.40e-01 +8.16e-01 +8.12e-01 +8.09e-01 +7.98e-01 +7.79e-01 +7.43e-01 +7.35e-01 +7.18e-01 +7.62e-01 +1.05e+00 +6.30e-01 +1.97e-01 +7.86e-01 +8.04e-01 +7.90e-01 +7.96e-01 +8.02e-01 +7.57e-01 +7.79e-01 +7.99e-01 +7.43e-01 +7.26e-01 +7.20e-01 +8.04e-01 +1.21e+00 +8.14e-01 +2.65e-01 +8.01e-01 +7.70e-01 +8.02e-01 +7.66e-01 +7.78e-01 +7.86e-01 +7.74e-01 +7.63e-01 +7.30e-01 +7.36e-01 +7.45e-01 +7.37e-01 +1.04e+00 +8.80e-01 +3.72e-01 +7.76e-01 +7.61e-01 +7.65e-01 +7.59e-01 +7.59e-01 +7.54e-01 +7.53e-01 +7.66e-01 +7.40e-01 +7.01e-01 +7.50e-01 +8.07e-01 +1.03e+00 +6.47e-01 +2.01e-01 +8.00e-01 +7.91e-01 +7.50e-01 +7.33e-01 +7.09e-01 +7.28e-01 +7.48e-01 +7.31e-01 +7.26e-01 +7.09e-01 +7.38e-01 +8.00e-01 +1.08e+00 +7.50e-01 +2.61e-01 +7.64e-01 +7.65e-01 +7.60e-01 +7.25e-01 +7.24e-01 +7.17e-01 +6.99e-01 +6.92e-01 +6.87e-01 +6.82e-01 +7.32e-01 +7.47e-01 +9.42e-01 +5.70e-01 +2.11e-01 +7.38e-01 +7.50e-01 +7.58e-01 +7.14e-01 +7.38e-01 +7.21e-01 +6.80e-01 +6.69e-01 +6.84e-01 +6.84e-01 +7.32e-01 +7.33e-01 +9.18e-01 +3.59e-01 +1.45e-01 +7.38e-01 +7.18e-01 +7.38e-01 +7.39e-01 +7.46e-01 +7.70e-01 +7.25e-01 +6.90e-01 +6.46e-01 +6.49e-01 +6.56e-01 +6.97e-01 +9.65e-01 +3.98e-01 +1.02e-01 +7.64e-01 +7.31e-01 +7.32e-01 +7.05e-01 +7.34e-01 +7.26e-01 +7.09e-01 +6.85e-01 +6.43e-01 +6.34e-01 +6.51e-01 +6.67e-01 +9.76e-01 +4.41e-01 +1.09e-01 +7.80e-01 +7.12e-01 +7.26e-01 +7.17e-01 +7.58e-01 +7.39e-01 +7.19e-01 +7.02e-01 +7.03e-01 +6.55e-01 +5.91e-01 +6.53e-01 +9.17e-01 +4.78e-01 +1.37e-01 +8.17e-01 +7.34e-01 +7.39e-01 +7.85e-01 +7.75e-01 +7.52e-01 +7.79e-01 +7.11e-01 +7.19e-01 +6.96e-01 +6.43e-01 +6.00e-01 +7.61e-01 +2.70e-01 +8.10e-02 +1.16e+00 +1.04e+00 +1.10e+00 +1.11e+00 +1.11e+00 +1.08e+00 +9.95e-01 +1.02e+00 +9.59e-01 +1.01e+00 +9.84e-01 +7.98e-01 +6.84e-01 +2.00e-01 +5.36e-02 +1.12e+00 +8.97e-01 +8.53e-01 +8.36e-01 +8.45e-01 +7.72e-01 +5.11e-01 +5.45e-01 +4.94e-01 +5.38e-01 +5.92e-01 +4.14e-01 +2.08e-01 +7.44e-02 +2.60e-02 +3.41e-01 +2.55e-01 +2.14e-01 +2.42e-01 +2.46e-01 +2.60e-01 +1.78e-01 +1.61e-01 +1.44e-01 +1.52e-01 +1.91e-01 +1.39e-01 +6.05e-02 +2.90e-02 +6.48e-03 +7.87e-01 +8.11e-01 +8.37e-01 +8.09e-01 +8.06e-01 +7.52e-01 +7.59e-01 +7.86e-01 +7.65e-01 +7.53e-01 +7.16e-01 +8.07e-01 +1.02e+00 +7.17e-01 +2.61e-01 +7.93e-01 +8.13e-01 +7.88e-01 +7.89e-01 +7.88e-01 +8.08e-01 +8.00e-01 +7.90e-01 +7.89e-01 +7.16e-01 +7.47e-01 +7.97e-01 +1.11e+00 +7.82e-01 +2.29e-01 +8.16e-01 +7.85e-01 +7.57e-01 +7.70e-01 +7.67e-01 +7.99e-01 +7.75e-01 +7.99e-01 +8.00e-01 +7.26e-01 +7.00e-01 +7.95e-01 +1.12e+00 +8.32e-01 +2.72e-01 +7.66e-01 +7.71e-01 +7.72e-01 +7.50e-01 +7.83e-01 +7.66e-01 +7.97e-01 +7.79e-01 +7.41e-01 +7.26e-01 +7.24e-01 +7.90e-01 +1.04e+00 +7.19e-01 +2.72e-01 +7.50e-01 +7.28e-01 +7.78e-01 +7.78e-01 +7.70e-01 +7.41e-01 +7.52e-01 +7.68e-01 +7.29e-01 +7.30e-01 +7.52e-01 +7.62e-01 +1.04e+00 +5.78e-01 +1.84e-01 +7.79e-01 +7.50e-01 +7.82e-01 +7.38e-01 +7.48e-01 +7.53e-01 +7.41e-01 +7.47e-01 +7.17e-01 +7.06e-01 +7.29e-01 +7.63e-01 +1.01e+00 +5.33e-01 +1.96e-01 +7.73e-01 +7.74e-01 +7.65e-01 +7.47e-01 +7.35e-01 +7.39e-01 +7.07e-01 +7.21e-01 +7.07e-01 +7.06e-01 +6.86e-01 +7.53e-01 +1.03e+00 +6.87e-01 +2.34e-01 +7.58e-01 +7.65e-01 +7.72e-01 +7.64e-01 +7.41e-01 +7.16e-01 +6.58e-01 +7.03e-01 +6.75e-01 +6.34e-01 +6.76e-01 +7.61e-01 +9.52e-01 +3.63e-01 +1.11e-01 +7.33e-01 +7.35e-01 +7.46e-01 +7.50e-01 +7.43e-01 +7.09e-01 +6.97e-01 +6.61e-01 +6.39e-01 +6.31e-01 +6.70e-01 +7.26e-01 +9.35e-01 +4.08e-01 +1.21e-01 +7.44e-01 +7.40e-01 +7.34e-01 +7.49e-01 +7.23e-01 +7.13e-01 +6.92e-01 +6.51e-01 +6.43e-01 +6.54e-01 +6.11e-01 +6.81e-01 +8.92e-01 +5.18e-01 +1.67e-01 +7.39e-01 +7.24e-01 +7.18e-01 +7.10e-01 +7.07e-01 +7.17e-01 +7.28e-01 +6.89e-01 +6.45e-01 +6.27e-01 +5.89e-01 +6.27e-01 +8.94e-01 +4.27e-01 +1.44e-01 +8.20e-01 +7.35e-01 +7.73e-01 +7.79e-01 +7.33e-01 +7.94e-01 +7.38e-01 +7.21e-01 +6.82e-01 +6.66e-01 +6.30e-01 +5.53e-01 +6.56e-01 +2.86e-01 +9.02e-02 +1.23e+00 +1.02e+00 +9.49e-01 +1.09e+00 +1.08e+00 +1.07e+00 +1.01e+00 +1.12e+00 +1.07e+00 +1.12e+00 +9.32e-01 +7.55e-01 +5.90e-01 +1.70e-01 +6.04e-02 +9.34e-01 +7.67e-01 +7.48e-01 +8.36e-01 +9.13e-01 +8.43e-01 +6.94e-01 +7.11e-01 +6.45e-01 +5.23e-01 +4.64e-01 +3.36e-01 +1.97e-01 +8.24e-02 +3.52e-02 +3.61e-01 +3.04e-01 +3.20e-01 +2.77e-01 +3.14e-01 +2.79e-01 +2.19e-01 +2.01e-01 +2.23e-01 +1.68e-01 +1.56e-01 +1.06e-01 +6.15e-02 +3.90e-02 +1.78e-02 +7.55e-01 +7.78e-01 +8.17e-01 +8.31e-01 +8.37e-01 +8.23e-01 +8.31e-01 +8.49e-01 +7.67e-01 +7.79e-01 +7.53e-01 +7.56e-01 +9.13e-01 +4.87e-01 +1.90e-01 +7.63e-01 +7.96e-01 +7.72e-01 +7.84e-01 +8.07e-01 +7.97e-01 +7.85e-01 +7.92e-01 +7.90e-01 +7.89e-01 +7.62e-01 +7.60e-01 +9.99e-01 +6.50e-01 +2.08e-01 +7.70e-01 +7.78e-01 +7.67e-01 +8.10e-01 +7.76e-01 +7.97e-01 +8.29e-01 +7.63e-01 +7.81e-01 +7.78e-01 +7.59e-01 +7.86e-01 +1.06e+00 +7.09e-01 +2.39e-01 +7.64e-01 +7.94e-01 +8.36e-01 +7.73e-01 +8.21e-01 +8.29e-01 +8.06e-01 +7.74e-01 +7.69e-01 +7.48e-01 +7.26e-01 +7.70e-01 +1.04e+00 +5.38e-01 +1.72e-01 +7.68e-01 +7.86e-01 +7.98e-01 +7.72e-01 +8.10e-01 +7.96e-01 +7.80e-01 +7.69e-01 +7.86e-01 +7.59e-01 +7.61e-01 +7.80e-01 +1.02e+00 +4.87e-01 +1.43e-01 +7.60e-01 +7.84e-01 +7.88e-01 +8.10e-01 +7.80e-01 +7.87e-01 +7.60e-01 +7.40e-01 +7.18e-01 +7.35e-01 +7.48e-01 +7.57e-01 +1.06e+00 +5.45e-01 +1.46e-01 +7.56e-01 +7.71e-01 +7.41e-01 +7.88e-01 +7.68e-01 +7.62e-01 +7.57e-01 +7.22e-01 +7.18e-01 +7.13e-01 +7.27e-01 +7.54e-01 +9.95e-01 +6.13e-01 +1.70e-01 +7.39e-01 +7.50e-01 +7.74e-01 +7.93e-01 +7.34e-01 +7.32e-01 +6.83e-01 +7.01e-01 +7.06e-01 +7.36e-01 +7.12e-01 +7.25e-01 +9.34e-01 +5.51e-01 +1.95e-01 +7.17e-01 +7.39e-01 +7.59e-01 +7.62e-01 +7.78e-01 +7.43e-01 +6.89e-01 +6.89e-01 +6.69e-01 +6.52e-01 +6.30e-01 +6.68e-01 +9.23e-01 +4.29e-01 +1.22e-01 +7.39e-01 +7.23e-01 +7.35e-01 +7.50e-01 +7.65e-01 +7.07e-01 +6.87e-01 +6.74e-01 +6.35e-01 +6.34e-01 +6.14e-01 +6.22e-01 +7.74e-01 +4.07e-01 +1.43e-01 +7.20e-01 +7.36e-01 +7.25e-01 +7.20e-01 +7.27e-01 +7.02e-01 +7.08e-01 +6.86e-01 +6.37e-01 +6.43e-01 +5.84e-01 +6.05e-01 +8.14e-01 +2.50e-01 +7.65e-02 +7.35e-01 +7.38e-01 +7.34e-01 +7.63e-01 +7.24e-01 +7.52e-01 +7.60e-01 +7.35e-01 +6.72e-01 +6.44e-01 +6.12e-01 +5.65e-01 +6.76e-01 +2.27e-01 +7.04e-02 +1.03e+00 +9.44e-01 +9.23e-01 +1.03e+00 +1.07e+00 +1.05e+00 +9.55e-01 +9.95e-01 +9.86e-01 +8.94e-01 +8.68e-01 +7.36e-01 +3.85e-01 +9.70e-02 +3.48e-02 +8.54e-01 +4.54e-01 +5.88e-01 +6.14e-01 +6.53e-01 +6.43e-01 +5.10e-01 +6.95e-01 +5.75e-01 +3.80e-01 +4.09e-01 +2.64e-01 +1.34e-01 +5.20e-02 +1.59e-02 +2.77e-01 +1.44e-01 +1.76e-01 +1.86e-01 +2.54e-01 +2.23e-01 +1.61e-01 +2.31e-01 +1.97e-01 +1.30e-01 +1.16e-01 +6.26e-02 +4.81e-02 +3.06e-02 +1.67e-02 +7.86e-01 +8.39e-01 +8.82e-01 +8.55e-01 +8.50e-01 +8.79e-01 +8.60e-01 +8.12e-01 +8.09e-01 +7.89e-01 +7.90e-01 +7.42e-01 +7.44e-01 +1.58e-01 +6.48e-02 +8.67e-01 +8.89e-01 +8.72e-01 +8.03e-01 +7.98e-01 +8.19e-01 +8.35e-01 +8.10e-01 +7.91e-01 +7.79e-01 +7.97e-01 +7.65e-01 +8.12e-01 +2.69e-01 +1.15e-01 +8.28e-01 +8.91e-01 +8.28e-01 +8.22e-01 +8.14e-01 +8.56e-01 +8.86e-01 +8.46e-01 +7.76e-01 +7.84e-01 +7.49e-01 +8.31e-01 +1.07e+00 +4.88e-01 +1.30e-01 +8.26e-01 +8.49e-01 +8.39e-01 +8.27e-01 +8.44e-01 +8.70e-01 +8.88e-01 +8.04e-01 +8.19e-01 +7.85e-01 +7.20e-01 +7.52e-01 +9.62e-01 +4.26e-01 +1.46e-01 +8.01e-01 +7.89e-01 +8.03e-01 +8.28e-01 +8.39e-01 +8.53e-01 +8.18e-01 +8.07e-01 +8.12e-01 +8.02e-01 +7.19e-01 +7.09e-01 +9.85e-01 +5.13e-01 +1.40e-01 +7.74e-01 +8.11e-01 +7.91e-01 +8.10e-01 +8.13e-01 +8.20e-01 +7.99e-01 +7.62e-01 +7.73e-01 +7.84e-01 +7.21e-01 +7.04e-01 +9.41e-01 +4.52e-01 +1.46e-01 +7.47e-01 +7.56e-01 +8.11e-01 +8.22e-01 +8.04e-01 +7.79e-01 +7.83e-01 +7.46e-01 +7.28e-01 +7.40e-01 +7.25e-01 +7.07e-01 +8.17e-01 +4.63e-01 +1.62e-01 +7.58e-01 +7.56e-01 +7.60e-01 +7.96e-01 +7.87e-01 +7.69e-01 +7.75e-01 +7.58e-01 +7.39e-01 +7.10e-01 +7.14e-01 +7.27e-01 +9.10e-01 +3.49e-01 +1.16e-01 +7.76e-01 +7.69e-01 +7.90e-01 +7.93e-01 +7.91e-01 +8.11e-01 +7.48e-01 +7.33e-01 +7.10e-01 +7.10e-01 +6.65e-01 +6.83e-01 +8.72e-01 +3.27e-01 +1.09e-01 +7.58e-01 +7.27e-01 +8.00e-01 +7.89e-01 +7.90e-01 +7.69e-01 +7.48e-01 +6.87e-01 +6.49e-01 +6.32e-01 +6.50e-01 +6.16e-01 +8.53e-01 +3.55e-01 +1.26e-01 +7.12e-01 +7.03e-01 +7.35e-01 +7.34e-01 +7.90e-01 +7.42e-01 +6.78e-01 +6.75e-01 +6.64e-01 +6.43e-01 +5.93e-01 +5.68e-01 +7.24e-01 +2.12e-01 +6.37e-02 +7.13e-01 +6.99e-01 +7.21e-01 +7.24e-01 +7.11e-01 +6.84e-01 +7.09e-01 +7.37e-01 +6.65e-01 +6.48e-01 +5.74e-01 +5.37e-01 +6.18e-01 +1.95e-01 +5.93e-02 +9.23e-01 +8.49e-01 +8.63e-01 +9.07e-01 +8.87e-01 +8.82e-01 +8.74e-01 +9.65e-01 +9.71e-01 +7.90e-01 +7.23e-01 +6.21e-01 +3.58e-01 +1.02e-01 +3.58e-02 +6.33e-01 +3.23e-01 +4.14e-01 +5.18e-01 +3.93e-01 +3.93e-01 +3.52e-01 +3.37e-01 +5.09e-01 +3.80e-01 +2.69e-01 +2.08e-01 +1.27e-01 +3.00e-02 +9.83e-03 +2.53e-01 +1.24e-01 +1.72e-01 +1.82e-01 +1.42e-01 +1.35e-01 +1.17e-01 +1.22e-01 +1.56e-01 +1.28e-01 +7.82e-02 +7.58e-02 +4.95e-02 +1.63e-02 +7.34e-03 +1.15e+00 +1.21e+00 +1.27e+00 +1.27e+00 +1.26e+00 +1.19e+00 +1.15e+00 +1.14e+00 +1.15e+00 +1.04e+00 +1.01e+00 +1.07e+00 +8.04e-01 +1.47e-01 +3.71e-02 +1.23e+00 +1.11e+00 +1.30e+00 +1.15e+00 +1.15e+00 +1.20e+00 +1.17e+00 +1.09e+00 +1.02e+00 +1.06e+00 +9.84e-01 +9.61e-01 +9.02e-01 +2.44e-01 +7.45e-02 +1.19e+00 +1.09e+00 +1.26e+00 +1.15e+00 +1.13e+00 +1.22e+00 +1.07e+00 +1.19e+00 +1.10e+00 +1.09e+00 +1.01e+00 +9.62e-01 +1.10e+00 +4.73e-01 +1.47e-01 +1.18e+00 +1.14e+00 +1.17e+00 +1.20e+00 +1.17e+00 +1.28e+00 +1.19e+00 +1.18e+00 +1.12e+00 +1.04e+00 +9.10e-01 +8.02e-01 +9.06e-01 +2.84e-01 +8.05e-02 +1.14e+00 +1.09e+00 +1.13e+00 +1.18e+00 +1.11e+00 +1.18e+00 +1.35e+00 +1.16e+00 +1.15e+00 +1.16e+00 +1.00e+00 +7.35e-01 +7.88e-01 +3.90e-01 +1.47e-01 +1.14e+00 +1.03e+00 +1.10e+00 +1.12e+00 +1.06e+00 +1.15e+00 +1.16e+00 +1.07e+00 +1.05e+00 +1.12e+00 +9.87e-01 +8.55e-01 +8.93e-01 +3.76e-01 +1.15e-01 +1.02e+00 +1.04e+00 +1.14e+00 +1.09e+00 +1.05e+00 +1.11e+00 +1.08e+00 +1.04e+00 +8.85e-01 +1.00e+00 +9.45e-01 +7.84e-01 +8.14e-01 +2.74e-01 +8.75e-02 +1.04e+00 +1.06e+00 +1.04e+00 +1.12e+00 +1.18e+00 +1.13e+00 +1.06e+00 +1.05e+00 +9.34e-01 +9.47e-01 +9.38e-01 +8.93e-01 +6.67e-01 +1.52e-01 +5.28e-02 +1.04e+00 +1.07e+00 +1.06e+00 +1.13e+00 +1.13e+00 +1.03e+00 +1.15e+00 +1.11e+00 +9.63e-01 +1.01e+00 +9.05e-01 +8.67e-01 +8.37e-01 +2.04e-01 +6.93e-02 +1.00e+00 +1.10e+00 +1.14e+00 +1.07e+00 +1.14e+00 +1.03e+00 +1.08e+00 +9.61e-01 +8.93e-01 +8.70e-01 +8.58e-01 +8.57e-01 +8.01e-01 +2.31e-01 +7.13e-02 +9.12e-01 +9.60e-01 +1.08e+00 +1.07e+00 +1.06e+00 +1.13e+00 +9.68e-01 +8.42e-01 +8.21e-01 +7.51e-01 +7.76e-01 +7.41e-01 +5.22e-01 +2.01e-01 +7.22e-02 +7.80e-01 +7.99e-01 +9.64e-01 +9.33e-01 +9.70e-01 +1.00e+00 +1.03e+00 +9.27e-01 +8.81e-01 +7.47e-01 +6.96e-01 +6.90e-01 +5.22e-01 +1.42e-01 +5.84e-02 +6.66e-01 +7.30e-01 +8.24e-01 +7.89e-01 +8.68e-01 +8.50e-01 +6.87e-01 +9.72e-01 +9.67e-01 +8.19e-01 +6.02e-01 +5.08e-01 +3.32e-01 +9.24e-02 +4.14e-02 +3.42e-01 +2.04e-01 +2.56e-01 +3.06e-01 +2.44e-01 +2.32e-01 +1.97e-01 +2.37e-01 +4.25e-01 +2.80e-01 +1.20e-01 +1.22e-01 +6.52e-02 +2.34e-02 +1.33e-02 +1.30e-01 +6.51e-02 +9.03e-02 +9.81e-02 +7.43e-02 +7.44e-02 +7.50e-02 +7.08e-02 +1.36e-01 +1.08e-01 +5.23e-02 +3.86e-02 +2.05e-02 +1.18e-02 +4.82e-03 +1.10e+00 +1.08e+00 +1.36e+00 +9.59e-01 +1.50e+00 +1.22e+00 +1.06e+00 +1.09e+00 +9.39e-01 +8.29e-01 +5.63e-01 +6.05e-01 +5.41e-01 +1.46e-01 +4.18e-02 +1.38e+00 +9.47e-01 +1.64e+00 +1.82e+00 +1.45e+00 +1.17e+00 +9.32e-01 +9.52e-01 +7.63e-01 +6.75e-01 +6.06e-01 +5.05e-01 +4.40e-01 +1.95e-01 +6.83e-02 +1.18e+00 +1.14e+00 +1.46e+00 +1.11e+00 +8.51e-01 +1.15e+00 +9.29e-01 +9.75e-01 +8.23e-01 +7.12e-01 +8.66e-01 +6.90e-01 +3.69e-01 +2.08e-01 +1.46e-01 +1.36e+00 +1.17e+00 +1.39e+00 +1.53e+00 +1.15e+00 +1.26e+00 +1.34e+00 +1.20e+00 +1.06e+00 +8.13e-01 +6.01e-01 +4.19e-01 +2.92e-01 +1.43e-01 +6.62e-02 +1.31e+00 +9.58e-01 +1.27e+00 +1.56e+00 +1.32e+00 +1.38e+00 +1.36e+00 +1.10e+00 +9.06e-01 +8.42e-01 +7.44e-01 +3.53e-01 +2.51e-01 +1.27e-01 +6.42e-02 +1.05e+00 +8.06e-01 +1.10e+00 +1.14e+00 +7.77e-01 +8.88e-01 +1.08e+00 +7.49e-01 +6.61e-01 +8.78e-01 +7.85e-01 +5.07e-01 +2.81e-01 +1.16e-01 +7.65e-02 +7.49e-01 +9.07e-01 +1.01e+00 +1.02e+00 +8.96e-01 +9.71e-01 +9.06e-01 +7.59e-01 +6.31e-01 +5.64e-01 +4.58e-01 +3.36e-01 +1.97e-01 +1.17e-01 +7.24e-02 +5.31e-01 +7.16e-01 +8.03e-01 +7.87e-01 +9.01e-01 +9.00e-01 +6.17e-01 +6.08e-01 +4.09e-01 +3.65e-01 +4.67e-01 +3.69e-01 +1.95e-01 +1.03e-01 +4.09e-02 +5.66e-01 +6.84e-01 +8.03e-01 +1.10e+00 +8.56e-01 +7.53e-01 +5.80e-01 +7.25e-01 +5.65e-01 +5.23e-01 +4.71e-01 +3.57e-01 +2.67e-01 +9.56e-02 +3.66e-02 +4.28e-01 +7.89e-01 +1.04e+00 +7.98e-01 +6.86e-01 +4.17e-01 +5.80e-01 +6.93e-01 +5.76e-01 +4.61e-01 +4.35e-01 +3.12e-01 +2.15e-01 +1.24e-01 +3.78e-02 +4.61e-01 +5.64e-01 +7.98e-01 +5.21e-01 +7.00e-01 +6.75e-01 +5.39e-01 +5.12e-01 +4.28e-01 +2.95e-01 +3.73e-01 +3.14e-01 +1.91e-01 +1.02e-01 +4.60e-02 +3.92e-01 +3.42e-01 +5.06e-01 +4.71e-01 +4.73e-01 +6.89e-01 +5.67e-01 +3.20e-01 +3.01e-01 +2.61e-01 +2.32e-01 +2.34e-01 +1.79e-01 +4.62e-02 +1.73e-02 +1.91e-01 +2.50e-01 +3.10e-01 +2.40e-01 +2.89e-01 +4.18e-01 +3.43e-01 +3.33e-01 +2.60e-01 +2.88e-01 +2.00e-01 +1.73e-01 +1.35e-01 +4.58e-02 +1.87e-02 +8.30e-02 +9.70e-02 +1.13e-01 +8.94e-02 +1.22e-01 +1.54e-01 +1.00e-01 +1.45e-01 +1.68e-01 +1.41e-01 +7.91e-02 +5.48e-02 +3.66e-02 +1.92e-02 +9.23e-03 +6.77e-02 +3.66e-02 +4.21e-02 +4.05e-02 +4.50e-02 +5.61e-02 +2.78e-02 +4.65e-02 +7.38e-02 +6.93e-02 +3.15e-02 +1.71e-02 +1.50e-02 +7.02e-03 +2.63e-03 +3.78e-01 +3.76e-01 +5.09e-01 +3.31e-01 +5.46e-01 +4.00e-01 +3.73e-01 +3.85e-01 +3.61e-01 +2.87e-01 +1.88e-01 +1.85e-01 +1.79e-01 +1.07e-01 +3.77e-02 +5.65e-01 +2.57e-01 +5.55e-01 +7.39e-01 +6.13e-01 +4.33e-01 +3.29e-01 +2.97e-01 +2.89e-01 +2.50e-01 +1.89e-01 +1.46e-01 +1.32e-01 +8.81e-02 +4.17e-02 +4.36e-01 +3.41e-01 +5.13e-01 +4.96e-01 +2.34e-01 +4.00e-01 +3.27e-01 +2.87e-01 +2.94e-01 +2.08e-01 +3.02e-01 +2.63e-01 +1.41e-01 +5.88e-02 +6.26e-02 +4.46e-01 +3.47e-01 +5.27e-01 +6.11e-01 +3.91e-01 +4.22e-01 +4.76e-01 +4.54e-01 +4.31e-01 +3.19e-01 +2.13e-01 +1.47e-01 +9.76e-02 +5.82e-02 +2.92e-02 +5.35e-01 +3.74e-01 +4.80e-01 +5.78e-01 +5.02e-01 +4.78e-01 +5.06e-01 +3.61e-01 +3.58e-01 +2.46e-01 +2.55e-01 +1.07e-01 +6.95e-02 +4.27e-02 +2.71e-02 +3.15e-01 +2.73e-01 +3.47e-01 +3.78e-01 +2.88e-01 +2.50e-01 +3.39e-01 +2.40e-01 +2.15e-01 +2.65e-01 +2.87e-01 +2.16e-01 +1.00e-01 +4.10e-02 +2.09e-02 +3.10e-01 +2.99e-01 +3.67e-01 +3.96e-01 +2.30e-01 +2.86e-01 +3.00e-01 +2.63e-01 +2.50e-01 +1.99e-01 +1.56e-01 +1.31e-01 +7.20e-02 +3.22e-02 +3.48e-02 +1.47e-01 +2.69e-01 +2.47e-01 +2.40e-01 +2.82e-01 +3.03e-01 +1.85e-01 +1.94e-01 +1.29e-01 +1.02e-01 +1.25e-01 +1.15e-01 +5.95e-02 +5.42e-02 +4.17e-02 +1.81e-01 +2.00e-01 +2.60e-01 +3.31e-01 +3.21e-01 +2.95e-01 +1.85e-01 +2.44e-01 +2.00e-01 +1.66e-01 +1.43e-01 +1.23e-01 +1.02e-01 +4.36e-02 +1.72e-02 +1.08e-01 +2.40e-01 +3.14e-01 +3.12e-01 +2.21e-01 +1.20e-01 +1.86e-01 +2.21e-01 +2.13e-01 +1.40e-01 +1.31e-01 +1.05e-01 +5.58e-02 +6.38e-02 +3.45e-02 +1.48e-01 +2.44e-01 +2.92e-01 +1.42e-01 +2.29e-01 +1.89e-01 +1.21e-01 +1.73e-01 +1.54e-01 +1.09e-01 +1.16e-01 +8.49e-02 +6.30e-02 +4.98e-02 +4.02e-02 +1.01e-01 +1.10e-01 +1.55e-01 +1.40e-01 +1.46e-01 +2.37e-01 +1.93e-01 +1.34e-01 +9.47e-02 +8.48e-02 +7.21e-02 +7.09e-02 +7.53e-02 +2.10e-02 +7.74e-03 +8.60e-02 +8.49e-02 +1.09e-01 +1.25e-01 +9.19e-02 +1.59e-01 +1.27e-01 +1.01e-01 +6.87e-02 +9.44e-02 +5.12e-02 +5.73e-02 +5.81e-02 +1.92e-02 +6.09e-03 +2.72e-02 +4.77e-02 +4.74e-02 +3.34e-02 +5.41e-02 +7.93e-02 +6.69e-02 +7.67e-02 +9.31e-02 +5.17e-02 +5.34e-02 +3.25e-02 +1.99e-02 +1.19e-02 +1.14e-02 +2.23e-02 +2.53e-02 +2.61e-02 +2.28e-02 +2.86e-02 +5.00e-02 +3.04e-02 +2.98e-02 +5.98e-02 +4.02e-02 +2.97e-02 +1.80e-02 +6.73e-03 +4.41e-03 +7.35e-03 \ No newline at end of file diff --git a/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py b/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py new file mode 100644 index 0000000000..680e9dc6df --- /dev/null +++ b/tests/regression_tests/weightwindows_fw_cadis_mesh/test.py @@ -0,0 +1,49 @@ +import os + +import openmc +from openmc.utility_funcs import change_directory +from openmc.examples import random_ray_three_region_cube +import pytest + +from tests.testing_harness import WeightWindowPyAPITestHarness + + +class MGXSTestHarness(WeightWindowPyAPITestHarness): + def _cleanup(self): + super()._cleanup() + f = 'mgxs.h5' + if os.path.exists(f): + os.remove(f) + + +@pytest.mark.parametrize("shape", ["flat", "linear"]) +def test_weight_windows_fw_cadis_mesh(shape): + with change_directory(shape): + openmc.reset_auto_ids() + + model = random_ray_three_region_cube() + + # The base model has a resolution of 12, so we overlay + # something else for FW-CADIS + n = 15 + width = 30.0 + ww_mesh = openmc.RegularMesh() + ww_mesh.dimension = (n, n, n) + ww_mesh.lower_left = (0.0, 0.0, 0.0) + ww_mesh.upper_right = (width, width, width) + + wwg = openmc.WeightWindowGenerator( + method="fw_cadis", mesh=ww_mesh, max_realizations=model.settings.batches) + model.settings.weight_window_generators = wwg + + root = model.geometry.root_universe + model.settings.random_ray['source_region_meshes'] = [(ww_mesh, [root])] + + model.settings.particles = 750 + model.settings.batches = 30 + model.settings.inactive = 20 + + model.settings.random_ray['source_shape'] = shape + + harness = MGXSTestHarness('statepoint.30.h5', model) + harness.main() diff --git a/tests/regression_tests/white_plane/results_true.dat b/tests/regression_tests/white_plane/results_true.dat index dea3692059..ffb19491dd 100644 --- a/tests/regression_tests/white_plane/results_true.dat +++ b/tests/regression_tests/white_plane/results_true.dat @@ -1,2 +1,2 @@ k-combined: -2.279902E+00 3.262078E-03 +2.274312E+00 4.223342E-03 diff --git a/tests/test_matplotlib_import.py b/tests/test_matplotlib_import.py new file mode 100644 index 0000000000..d319976c45 --- /dev/null +++ b/tests/test_matplotlib_import.py @@ -0,0 +1,6 @@ +import sys +import openmc + +def test_matplotlib_presence(): + """Checks that matplotlib remains a deferred import""" + assert 'matplotlib' not in sys.modules diff --git a/tests/testing_harness.py b/tests/testing_harness.py index ba65ba5a69..11ced5b38a 100644 --- a/tests/testing_harness.py +++ b/tests/testing_harness.py @@ -1,6 +1,7 @@ from difflib import unified_diff import filecmp import glob +import h5py import hashlib import os import shutil @@ -92,7 +93,7 @@ class TestHarness: # Write out k-combined. outstr += 'k-combined:\n' form = '{0:12.6E} {1:12.6E}\n' - outstr += form.format(sp.k_combined.n, sp.k_combined.s) + outstr += form.format(sp.keff.n, sp.keff.s) # Write out tally data. for i, tally_ind in enumerate(sp.tallies): @@ -179,7 +180,7 @@ class CMFDTestHarness(TestHarness): outstr += '\ncmfd openmc source comparison\n' outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfd_run.src_cmp]) outstr += '\ncmfd source\n' - cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.product(cmfd_run.indices), + cmfdsrc = np.reshape(cmfd_run.cmfd_src, np.prod(cmfd_run.indices), order='F') outstr += '\n'.join(['{:.6E}'.format(x) for x in cmfdsrc]) outstr += '\n' @@ -276,6 +277,13 @@ class ParticleRestartTestHarness(TestHarness): return outstr + def _cleanup(self): + """Delete particle restart files.""" + super()._cleanup() + output = glob.glob('particle*.h5') + for f in output: + os.remove(f) + class PyAPITestHarness(TestHarness): def __init__(self, statepoint_name, model=None, inputs_true=None): @@ -329,12 +337,11 @@ class PyAPITestHarness(TestHarness): def _build_inputs(self): """Write input XML files.""" - self._model.export_to_xml() + self._model.export_to_model_xml() def _get_inputs(self): """Return a hash digest of the input XML files.""" - xmls = ['geometry.xml', 'materials.xml', 'settings.xml', - 'tallies.xml', 'plots.xml'] + xmls = ['model.xml', 'plots.xml'] return ''.join([open(fname).read() for fname in xmls if os.path.exists(fname)]) @@ -364,7 +371,7 @@ class PyAPITestHarness(TestHarness): """Delete XMLs, statepoints, tally, and test files.""" super()._cleanup() output = ['materials.xml', 'geometry.xml', 'settings.xml', - 'tallies.xml', 'plots.xml', 'inputs_test.dat'] + 'tallies.xml', 'plots.xml', 'inputs_test.dat', 'model.xml'] for f in output: if os.path.exists(f): os.remove(f) @@ -374,3 +381,145 @@ class HashedPyAPITestHarness(PyAPITestHarness): def _get_results(self): """Digest info in the statepoint and return as a string.""" return super()._get_results(True) + + +class TolerantPyAPITestHarness(PyAPITestHarness): + """Specialized harness for running tests that involve significant levels + of floating point non-associativity when using shared memory parallelism + due to single precision usage (e.g., as in the random ray solver). + + """ + def _are_files_equal(self, actual_path, expected_path, tolerance): + def isfloat(value): + try: + float(value) + return True + except ValueError: + return False + + def tokenize(line): + return line.strip().split() + + def compare_tokens(token1, token2): + if isfloat(token1) and isfloat(token2): + float1, float2 = float(token1), float(token2) + return abs(float1 - float2) <= tolerance * max(abs(float1), abs(float2)) + else: + return token1 == token2 + + expected = open(expected_path).readlines() + actual = open(actual_path).readlines() + + if len(expected) != len(actual): + return False + + for line1, line2 in zip(expected, actual): + tokens1 = tokenize(line1) + tokens2 = tokenize(line2) + + if len(tokens1) != len(tokens2): + return False + + for token1, token2 in zip(tokens1, tokens2): + if not compare_tokens(token1, token2): + return False + + return True + + def _compare_results(self): + """Make sure the current results agree with the reference.""" + compare = self._are_files_equal('results_test.dat', 'results_true.dat', 1e-6) + if not compare: + expected = open('results_true.dat').readlines() + actual = open('results_test.dat').readlines() + diff = unified_diff(expected, actual, 'results_true.dat', + 'results_test.dat') + print('Result differences:') + print(''.join(colorize(diff))) + os.rename('results_test.dat', 'results_error.dat') + assert compare, 'Results do not agree' + + +class WeightWindowPyAPITestHarness(PyAPITestHarness): + def _get_results(self): + """Digest info in the weight window file and return as a string.""" + ww = openmc.WeightWindowsList.from_hdf5()[0] + + # Access the weight window bounds + lower_bound = ww.lower_ww_bounds + upper_bound = ww.upper_ww_bounds + + # Flatten both arrays + flattened_lower_bound = lower_bound.flatten() + flattened_upper_bound = upper_bound.flatten() + + # Convert each element to a string in scientific notation with 2 decimal places + formatted_lower_bound = [f'{x:.2e}' for x in flattened_lower_bound] + formatted_upper_bound = [f'{x:.2e}' for x in flattened_upper_bound] + + # Concatenate the formatted arrays + concatenated_strings = ["Lower Bounds"] + formatted_lower_bound + \ + ["Upper Bounds"] + formatted_upper_bound + + # Join the concatenated strings into a single string with newline characters + final_string = '\n'.join(concatenated_strings) + + # Prepend the mesh text description and return final string + return str(ww.mesh) + final_string + + def _cleanup(self): + super()._cleanup() + f = 'weight_windows.h5' + if os.path.exists(f): + os.remove(f) + + +class PlotTestHarness(TestHarness): + """Specialized TestHarness for running OpenMC plotting tests.""" + def __init__(self, plot_names, voxel_convert_checks=[]): + super().__init__(None) + self._plot_names = plot_names + self._voxel_convert_checks = voxel_convert_checks + + def _run_openmc(self): + openmc.plot_geometry(openmc_exec=config['exe']) + + # Check that voxel h5 can be converted to vtk + for voxel_h5_filename in self._voxel_convert_checks: + openmc.voxel_to_vtk(voxel_h5_filename) + + def _test_output_created(self): + """Make sure *.png has been created.""" + for fname in self._plot_names: + assert os.path.exists(fname), 'Plot output file does not exist.' + + def _cleanup(self): + super()._cleanup() + for fname in self._plot_names: + if os.path.exists(fname): + os.remove(fname) + + def _get_results(self): + """Return a string hash of the plot files.""" + outstr = bytes() + + for fname in self._plot_names: + if fname.endswith('.png'): + # Add PNG output to results + with open(fname, 'rb') as fh: + outstr += fh.read() + elif fname.endswith('.h5'): + # Add voxel data to results + with h5py.File(fname, 'r') as fh: + outstr += fh.attrs['filetype'] + outstr += fh.attrs['num_voxels'].tobytes() + outstr += fh.attrs['lower_left'].tobytes() + outstr += fh.attrs['voxel_width'].tobytes() + outstr += fh['data'][()].tobytes() + + # Hash the information and return. + sha512 = hashlib.sha512() + sha512.update(outstr) + outstr = sha512.hexdigest() + + return outstr diff --git a/tests/unit_tests/cell_instances/test_hex_multilattice.py b/tests/unit_tests/cell_instances/test_hex_multilattice.py new file mode 100644 index 0000000000..3f503fe9b8 --- /dev/null +++ b/tests/unit_tests/cell_instances/test_hex_multilattice.py @@ -0,0 +1,119 @@ +from math import sqrt + +import pytest +import numpy as np +import openmc +import openmc.lib + +from tests import cdtemp + + +@pytest.fixture(scope='module', autouse=True) +def double_hex_lattice_model(): + openmc.reset_auto_ids() + radius = 0.9 + pin_lattice_pitch = 2.0 + # make the hex prism a little larger to make sure test + # locations are definitively in the model + hex_prism_edge = 1.2 * pin_lattice_pitch + + model = openmc.Model() + + # materials + nat_u = openmc.Material() + nat_u.set_density('g/cm3', 12.0) + nat_u.add_element('U', 1.0) + + graphite = openmc.Material() + graphite.set_density('g/cm3', 1.1995) + graphite.add_element('C', 1.0) + + # zplanes to define lower and upper region + z_low = openmc.ZPlane(-10, boundary_type='vacuum') + z_mid = openmc.ZPlane(0) + z_high = openmc.ZPlane(10, boundary_type='vacuum') + hex_prism = openmc.model.HexagonalPrism( + edge_length=hex_prism_edge, boundary_type='reflective') + + # geometry + cyl = openmc.ZCylinder(r=radius) + univ = openmc.model.pin([cyl], [nat_u, graphite]) + + # create a hexagonal lattice of compacts + hex_lattice = openmc.HexLattice() + hex_lattice.orientation = 'y' + hex_lattice.pitch = (pin_lattice_pitch,) + hex_lattice.center = (0., 0.) + center = [univ] + ring = [univ, univ, univ, univ, univ, univ] + hex_lattice.universes = [ring, center] + lower_hex_cell = openmc.Cell(fill=hex_lattice, region=-hex_prism & +z_low & -z_mid) + upper_hex_cell = openmc.Cell(fill=hex_lattice, region=-hex_prism & +z_mid & -z_high) + hex_cells = [lower_hex_cell, upper_hex_cell] + model.geometry = openmc.Geometry(hex_cells) + + # moderator + cell = next(iter(univ.get_all_cells().values())) + tally = openmc.Tally(tally_id=1) + filter = openmc.DistribcellFilter(cell) + tally.filters = [filter] + tally.scores = ['flux'] + model.tallies = [tally] + + # settings + # source definition. fission source given bounding box of graphite active region + system_LL = (-pin_lattice_pitch*sqrt(3)/2, -pin_lattice_pitch, -5) + system_UR = (pin_lattice_pitch*sqrt(3)/2, pin_lattice_pitch, 5) + source_dist = openmc.stats.Box(system_LL, system_UR) + model.settings.source = openmc.IndependentSource(space=source_dist) + model.settings.particles = 100 + model.settings.inactive = 2 + model.settings.batches = 10 + + with cdtemp(): + model.export_to_xml() + openmc.lib.init() + yield + openmc.lib.finalize() + + +# Lower cell instances +# 6 +# 5 4 +# 3 +# 2 1 +# 0 +# Upper cell instances +# 13 +# 12 11 +# 10 +# 9 8 +# 7 +hex_expected_results = [ + ((0.0, -2.0, -5.0), 0), + ((1.732, -1.0, -5.0), 1), + ((-1.732, -1.0, -5.0), 2), + ((0.0, 0.0, -0.1), 3), + ((1.732, 1.0, -5.0), 4), + ((-1.732, 1.0, -5.0), 5), + ((0.0, 2.0, -0.1), 6), + ((0.0, -2.0, 5.0), 7), + ((1.732, -1.0, 5.0), 8), + ((-1.732, -1.0, 5.0), 9), + ((0.0, 0.0, 5.0), 10), + ((1.732, 1.0, 5.0), 11), + ((-1.732, 1.0, 5.0), 12), + ((0.0, 2.0, 5.0), 13), +] + + +@pytest.mark.parametrize("r,expected_cell_instance", hex_expected_results, ids=str) +def test_cell_instance_hex_multilattice(r, expected_cell_instance): + _, cell_instance = openmc.lib.find_cell(r) + assert cell_instance == expected_cell_instance + + +def test_cell_instance_multilattice_results(): + openmc.lib.run() + tally_results = openmc.lib.tallies[1].mean + assert (tally_results != 0.0).all() diff --git a/tests/unit_tests/cell_instances/test_rect_multilattice.py b/tests/unit_tests/cell_instances/test_rect_multilattice.py new file mode 100644 index 0000000000..aaecb3bdac --- /dev/null +++ b/tests/unit_tests/cell_instances/test_rect_multilattice.py @@ -0,0 +1,96 @@ +import pytest +import numpy as np + +import openmc +import openmc.lib + +from tests import cdtemp + + +@pytest.fixture(scope='module', autouse=True) +def double_rect_lattice_model(): + openmc.reset_auto_ids() + model = openmc.Model() + + # Create a single material + m = openmc.Material() + m.add_nuclide('U235', 1.0) + m.set_density('g/cm3', 10.0) + model.materials.append(m) + + # Create a universe with a single infinite cell + c = openmc.Cell(fill=m) + u = openmc.Universe(cells=[c]) + + # Create a 2x2 lattice filled with above universe + lattice = openmc.RectLattice() + lattice.lower_left = (0.0, 0.0) + lattice.pitch = (1.0, 1.0) + lattice.universes = np.full((2, 2), u) + + # Create two cells each filled with the same lattice, one from x=0..2 and + # y=0..2 and the other from x=2..4 and y=0..2 + x0 = openmc.XPlane(0.0, boundary_type='vacuum') + x2 = openmc.XPlane(2.0) + x4 = openmc.XPlane(4.0, boundary_type='vacuum') + y0 = openmc.YPlane(0.0, boundary_type='vacuum') + y2 = openmc.YPlane(2.0, boundary_type='vacuum') + cell_with_lattice1 = openmc.Cell(fill=lattice, region=+x0 & -x2 & +y0 & -y2) + cell_with_lattice2 = openmc.Cell(fill=lattice, region=+x2 & -x4 & +y0 & -y2) + cell_with_lattice2.translation = (2., 0., 0.) + model.geometry = openmc.Geometry([cell_with_lattice1, cell_with_lattice2]) + + tally = openmc.Tally(tally_id=1) + dcell_filter = openmc.DistribcellFilter(c) + tally.filters = [dcell_filter] + tally.scores = ['flux'] + model.tallies = [tally] + + # Add box source that covers the model space well + bbox = model.geometry.bounding_box + bbox[0][2] = -0.5 + bbox[1][2] = 0.5 + space = openmc.stats.Box(*bbox) + source = openmc.IndependentSource(space=space) + model.settings.source = source + + # Add necessary settings and export + model.settings.batches = 10 + model.settings.inactive = 0 + model.settings.particles = 100 + + with cdtemp(): + model.export_to_xml() + openmc.lib.init() + yield + openmc.lib.finalize() + +# This shows the expected cell instance numbers for each lattice position: +# ┌─┬─┬─┬─┐ +# │2│3│6│7│ +# ├─┼─┼─┼─┤ +# │0│1│4│5│ +# └─┴─┴─┴─┘ +rect_expected_results = [ + ((0.5, 0.5, 0.0), 0), + ((1.5, 0.5, 0.0), 1), + ((0.5, 1.5, 0.0), 2), + ((1.5, 1.5, 0.0), 3), + ((2.5, 0.5, 0.0), 4), + ((3.5, 0.5, 0.0), 5), + ((2.5, 1.5, 0.0), 6), + ((3.5, 1.5, 0.0), 7), +] + + +@pytest.mark.parametrize("r,expected_cell_instance", rect_expected_results, ids=lambda p : f'{p}') +def test_cell_instance_rect_multilattice(r, expected_cell_instance): + _, cell_instance = openmc.lib.find_cell(r) + assert cell_instance == expected_cell_instance + + +def test_cell_instance_multilattice_results(): + openmc.run() + openmc.lib.run() + tally_results = openmc.lib.tallies[1].mean + assert (tally_results != 0.0).all() diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 51d1b19a33..6041d89820 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -48,7 +48,7 @@ def sphere_model(): model.settings.particles = 100 model.settings.batches = 10 model.settings.run_mode = 'fixed source' - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) return model diff --git a/tests/unit_tests/dagmc/broken_model.h5m b/tests/unit_tests/dagmc/broken_model.h5m new file mode 100644 index 0000000000..4bc6c7d425 Binary files /dev/null and b/tests/unit_tests/dagmc/broken_model.h5m differ diff --git a/tests/unit_tests/dagmc/dagmc_tetrahedral_no_graveyard.h5m b/tests/unit_tests/dagmc/dagmc_tetrahedral_no_graveyard.h5m new file mode 100644 index 0000000000..2aa72956b8 Binary files /dev/null and b/tests/unit_tests/dagmc/dagmc_tetrahedral_no_graveyard.h5m differ diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index b3e2c390f4..e84b5317ed 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -1,6 +1,7 @@ import shutil import numpy as np +from pathlib import Path import pytest import openmc @@ -26,13 +27,22 @@ def dagmc_model(request): model.settings.verbosity = 1 source_box = openmc.stats.Box([ -4, -4, -4 ], [ 4, 4, 4 ]) - source = openmc.Source(space=source_box) + source = openmc.IndependentSource(space=source_box) model.settings.source = source # geometry - dagmc_universe = openmc.DAGMCUniverse('dagmc.h5m') + dagmc_file = Path(request.fspath).parent / 'dagmc.h5m' + dagmc_universe = openmc.DAGMCUniverse(dagmc_file) model.geometry = openmc.Geometry(dagmc_universe) + # check number of surfaces and volumes for this pincell model there should + # be 5 volumes: two fuel regions, water, graveyard, implicit complement (the + # implicit complement cell is created automatically at runtime) + # and 21 surfaces: 3 cylinders (9 surfaces) and a bounding cubic shell + # (12 surfaces) + assert dagmc_universe.n_cells == 5 + assert dagmc_universe.n_surfaces == 21 + # tally tally = openmc.Tally() tally.scores = ['total'] diff --git a/tests/unit_tests/dagmc/test_bounds.py b/tests/unit_tests/dagmc/test_bounds.py new file mode 100644 index 0000000000..35e1dd3403 --- /dev/null +++ b/tests/unit_tests/dagmc/test_bounds.py @@ -0,0 +1,97 @@ +import openmc +import pytest +from pathlib import Path + + +def test_bounding_box(request): + """Checks that the DAGMCUniverse.bounding_box returns the correct values""" + + u = openmc.DAGMCUniverse(Path(request.fspath).parent / "dagmc.h5m") + + ll, ur = u.bounding_box + assert ll == pytest.approx((-25.0, -25.0, -25)) + assert ur == pytest.approx((25.0, 25.0, 25)) + + +def test_bounding_region(request): + """Checks that the DAGMCUniverse.bounding_region() returns a region with + correct surfaces and boundary types""" + + u = openmc.DAGMCUniverse(Path(request.fspath).parent / "dagmc.h5m") + + region = u.bounding_region() # should default to bounded_type='box' + assert isinstance(region, openmc.Region) + assert len(region) == 6 + assert region[0].surface.type == "x-plane" + assert region[0].surface.x0 == -25. + assert region[1].surface.type == "x-plane" + assert region[1].surface.x0 == 25. + assert region[2].surface.type == "y-plane" + assert region[2].surface.y0 == -25. + assert region[3].surface.type == "y-plane" + assert region[3].surface.y0 == 25. + assert region[4].surface.type == "z-plane" + assert region[4].surface.z0 == -25. + assert region[5].surface.type == "z-plane" + assert region[5].surface.z0 == 25. + assert region[0].surface.boundary_type == "vacuum" + assert region[1].surface.boundary_type == "vacuum" + assert region[2].surface.boundary_type == "vacuum" + assert region[3].surface.boundary_type == "vacuum" + assert region[4].surface.boundary_type == "vacuum" + assert region[5].surface.boundary_type == "vacuum" + region = u.bounding_region(padding_distance=5) + assert region[0].surface.x0 == -30. + assert region[1].surface.x0 == 30. + assert region[2].surface.y0 == -30. + assert region[3].surface.y0 == 30. + assert region[4].surface.z0 == -30. + assert region[5].surface.z0 == 30. + + region = u.bounding_region(bounded_type="sphere", boundary_type="reflective") + assert isinstance(region, openmc.Region) + assert isinstance(region, openmc.Halfspace) + assert region.surface.type == "sphere" + assert region.surface.boundary_type == "reflective" + larger_region = u.bounding_region(bounded_type="sphere", padding_distance=10) + assert larger_region.surface.r > region.surface.r + + +def test_bounded_universe(request): + """Checks that the DAGMCUniverse.bounded_universe() returns a + openmc.Universe with correct surface ids and cell ids""" + + u = openmc.DAGMCUniverse(Path(request.fspath).parent / "dagmc.h5m") + + # bounded with defaults + bu = u.bounded_universe() + + cells = list(bu.get_all_cells().items()) + assert isinstance(bu, openmc.Universe) + assert len(cells) == 1 + assert cells[0][0] == 10000 # default bounding_cell_id is 10000 + assert cells[0][1].id == 10000 # default bounding_cell_id is 10000 + surfaces = list(cells[0][1].region.get_surfaces().items()) + assert len(surfaces) == 6 + assert surfaces[0][1].id == 10000 + + # bounded with non defaults + bu = u.bounded_universe(bounding_cell_id=42, bounded_type="sphere", starting_id=43) + + cells = list(bu.get_all_cells().items()) + assert isinstance(bu, openmc.Universe) + assert len(cells) == 1 + assert cells[0][0] == 42 # default bounding_cell_id is 10000 + assert cells[0][1].id == 42 # default bounding_cell_id is 10000 + surfaces = list(cells[0][1].region.get_surfaces().items()) + assert surfaces[0][1].type == "sphere" + assert surfaces[0][1].id == 43 + + +def test_material_names(request): + """Checks that the DAGMCUniverse.material_names() returns a list of the + name present in the dagmc.h5m file in the expected order""" + + u = openmc.DAGMCUniverse(Path(request.fspath).parent / "dagmc.h5m") + + assert u.material_names == ['41', 'Graveyard', 'no-void fuel'] diff --git a/tests/unit_tests/dagmc/test_h5m_subdir.py b/tests/unit_tests/dagmc/test_h5m_subdir.py new file mode 100644 index 0000000000..dd9c6b043b --- /dev/null +++ b/tests/unit_tests/dagmc/test_h5m_subdir.py @@ -0,0 +1,40 @@ +import shutil +from pathlib import Path + +import openmc +import openmc.lib +import pytest + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) + + +@pytest.mark.parametrize("absolute", [True, False]) +def test_model_h5m_in_subdirectory(run_in_tmpdir, request, absolute): + # Create new subdirectory and copy h5m file there + h5m = Path(request.fspath).parent / "dagmc.h5m" + subdir = Path("h5m") + subdir.mkdir() + shutil.copy(h5m, subdir) + + # Create simple model with h5m file in subdirectory + if absolute: + dag_univ = openmc.DAGMCUniverse((subdir / "dagmc.h5m").absolute()) + else: + dag_univ = openmc.DAGMCUniverse(subdir / "dagmc.h5m") + model = openmc.Model() + model.geometry = openmc.Geometry(dag_univ.bounded_universe()) + mat1 = openmc.Material(name="41") + mat1.add_nuclide("H1", 1.0) + mat2 = openmc.Material(name="no-void fuel") + mat2.add_nuclide("U235", 1.0) + model.materials = [mat1, mat2] + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 1000 + + # Make sure model can load + model.export_to_model_xml() + openmc.lib.init(["model.xml"]) + openmc.lib.finalize() diff --git a/tests/unit_tests/dagmc/test_lost_particles.py b/tests/unit_tests/dagmc/test_lost_particles.py new file mode 100644 index 0000000000..502bd795e8 --- /dev/null +++ b/tests/unit_tests/dagmc/test_lost_particles.py @@ -0,0 +1,81 @@ +import numpy as np +from pathlib import Path + +import openmc +import openmc.lib + +import pytest + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), + reason="DAGMC CAD geometry is not enabled.") + + +@pytest.fixture +def broken_dagmc_model(request): + openmc.reset_auto_ids() + model = openmc.Model() + + ### MATERIALS ### + fuel = openmc.Material(name='no-void fuel') + fuel.set_density('g/cc', 10.29769) + fuel.add_nuclide('U233', 1.0) + + cladding = openmc.Material(name='clad') + cladding.set_density('g/cc', 6.55) + cladding.add_nuclide('Zr90', 1.0) + + h1 = openmc.Material(name='water') + h1.set_density('g/cc', 0.75) + h1.add_nuclide('H1', 1.0) + + model.materials = openmc.Materials([fuel, cladding, h1]) + + ### GEOMETRY ### + # create the DAGMC universe using a model that has many triangles + # removed + dagmc_file = Path(request.fspath).parent / "broken_model.h5m" + pincell_univ = openmc.DAGMCUniverse(filename=dagmc_file, auto_geom_ids=True) + + # create a 2 x 2 lattice using the DAGMC pincell + pitch = np.asarray((24.0, 24.0)) + lattice = openmc.RectLattice() + lattice.pitch = pitch + lattice.universes = [[pincell_univ] * 2] * 2 + lattice.lower_left = -pitch + + # clip the DAGMC geometry at +/- 10 cm w/ CSG planes + rpp = openmc.model.RectangularParallelepiped( + -pitch[0], pitch[0], -pitch[1], pitch[1], -10.0, 10.0, boundary_type='reflective') + bounding_cell = openmc.Cell(fill=lattice, region=-rpp) + + model.geometry = openmc.Geometry(root=[bounding_cell]) + + # settings + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 2 + model.settings.output = {'summary': False} + + model.export_to_xml() + + return model + + +def test_lost_particles(run_in_tmpdir, broken_dagmc_model): + broken_dagmc_model.export_to_xml() + # ensure that particles will be lost when cell intersections can't be found + # due to the removed triangles in this model + with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + openmc.run() + + # run this again, but with the dagmc universe as the root unvierse + for univ in broken_dagmc_model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + broken_dagmc_model.geometry.root_unvierse = univ + break + + broken_dagmc_model.export_to_xml() + with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + openmc.run() + diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py new file mode 100644 index 0000000000..0de4f6092d --- /dev/null +++ b/tests/unit_tests/dagmc/test_model.py @@ -0,0 +1,257 @@ +from pathlib import Path + +import lxml.etree as ET +import numpy as np +import pytest +import openmc +import openmc.lib +from openmc.utility_funcs import change_directory + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), + reason="DAGMC CAD geometry is not enabled.") + + +@pytest.fixture() +def model(request): + pitch = 1.26 + + mats = {} + mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") + mats["no-void fuel"].add_nuclide("U235", 0.03) + mats["no-void fuel"].add_nuclide("U238", 0.97) + mats["no-void fuel"].add_nuclide("O16", 2.0) + mats["no-void fuel"].set_density("g/cm3", 10.0) + + mats["41"] = openmc.Material(name="41") + mats["41"].add_nuclide("H1", 2.0) + mats["41"].add_element("O", 1.0) + mats["41"].set_density("g/cm3", 1.0) + mats["41"].add_s_alpha_beta("c_H_in_H2O") + + p = Path(request.fspath).parent / "dagmc.h5m" + + daguniv = openmc.DAGMCUniverse(p, auto_geom_ids=True) + + lattice = openmc.RectLattice() + lattice.dimension = [2, 2] + lattice.lower_left = [-pitch, -pitch] + lattice.pitch = [pitch, pitch] + lattice.universes = [ + [daguniv, daguniv], + [daguniv, daguniv]] + + box = openmc.model.RectangularParallelepiped(-pitch, pitch, -pitch, pitch, -5, 5) + + root = openmc.Universe(cells=[openmc.Cell(region=-box, fill=lattice)]) + + settings = openmc.Settings() + settings.batches = 100 + settings.inactive = 10 + settings.particles = 1000 + + ll, ur = root.bounding_box + mat_vol = openmc.VolumeCalculation([mats["no-void fuel"]], 1000000, ll, ur) + cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) + settings.volume_calculations = [mat_vol, cell_vol] + + model = openmc.Model() + model.materials = openmc.Materials(mats.values()) + model.geometry = openmc.Geometry(root=root) + model.settings = settings + + with change_directory(tmpdir=True): + try: + model.init_lib() + model.sync_dagmc_universes() + yield model + finally: + model.finalize_lib() + openmc.reset_auto_ids() + + +def test_dagmc_replace_material_assignment(model): + mats = {} + + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.replace_material_assignment("41", mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + + +def test_dagmc_add_material_override_with_id(model): + mats = {} + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell.id, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + + +def test_dagmc_add_material_override_with_cell(model): + mats = {} + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + + +def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): + model.calculate_volumes() + + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate the depletable materials + model.differentiate_depletable_mats(diff_volume_method="divide equally") + # Get the volume of the no-void fuel material after differentiation + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 +1 + + +def test_model_differentiate_with_dagmc(model): + root = model.geometry.root_universe + ll, ur = root.bounding_box + model.calculate_volumes() + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate all the materials + model.differentiate_mats(depletable_only=False) + + # Get the volume of the no-void fuel material after differentiation + mat_vol = openmc.VolumeCalculation(model.materials, 1000000, ll, ur) + model.settings.volume_calculations = [mat_vol] + model.init_lib() # need to reinitialize the lib after differentiating the materials + model.calculate_volumes() + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 + 4 + + +def test_bad_override_cell_id(model): + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): + univ.material_overrides = {1: model.materials[0]} + + +def test_bad_override_type(model): + not_a_dag_cell = openmc.Cell() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Unrecognized key type. Must be an integer or openmc.DAGMCCell object"): + univ.material_overrides = {not_a_dag_cell: model.materials[0]} + + +def test_bad_replacement_mat_name(model): + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="No material with name 'not_a_mat' found in the DAGMC universe"): + univ.replace_material_assignment("not_a_mat", model.materials[0]) + + +def test_dagmc_xml(model): + # Set the environment + mats = {} + mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") + mats["no-void fuel"].add_nuclide("U235", 0.03) + mats["no-void fuel"].add_nuclide("U238", 0.97) + mats["no-void fuel"].add_nuclide("O16", 2.0) + mats["no-void fuel"].set_density("g/cm3", 10.0) + + mats[5] = openmc.Material(name="41") + mats[5].add_nuclide("H1", 2.0) + mats[5].add_element("O", 1.0) + mats[5].set_density("g/cm3", 1.0) + mats[5].add_s_alpha_beta("c_H_in_H2O") + + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + dag_univ = univ + break + + for k, v in mats.items(): + if isinstance(k, int): + dag_univ.add_material_override(k, v) + model.materials.append(v) + elif isinstance(k, str): + dag_univ.replace_material_assignment(k, v) + + # Tesing the XML subelement generation + root = ET.Element('dagmc_universe') + dag_univ.create_xml_subelement(root) + dagmc_ele = root.find('dagmc_universe') + + assert dagmc_ele.get('id') == str(dag_univ.id) + assert dagmc_ele.get('filename') == str(dag_univ.filename) + assert dagmc_ele.get('auto_geom_ids') == str(dag_univ.auto_geom_ids).lower() + + override_eles = dagmc_ele.find('material_overrides').findall('cell_override') + assert len(override_eles) == 4 + + for i, override_ele in enumerate(override_eles): + cell_id = override_ele.get('id') + assert dag_univ.material_overrides[int(cell_id)][0].id == int(override_ele.find('material_ids').text) + + model.export_to_model_xml() + + xml_model = openmc.Model.from_model_xml() + + for univ in xml_model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + xml_dagmc_univ = univ + break + + assert xml_dagmc_univ._material_overrides.keys() == dag_univ._material_overrides.keys() + + for xml_mats, model_mats in zip(xml_dagmc_univ._material_overrides.values(), dag_univ._material_overrides.values()): + assert all([xml_mat.id == orig_mat.id for xml_mat, orig_mat in zip(xml_mats, model_mats)]) diff --git a/tests/unit_tests/dagmc/test_plot.py b/tests/unit_tests/dagmc/test_plot.py new file mode 100644 index 0000000000..6ce1d79a22 --- /dev/null +++ b/tests/unit_tests/dagmc/test_plot.py @@ -0,0 +1,71 @@ +import pytest +import openmc +import openmc.lib + + +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) + +def test_plotting_dagmc_model(request): + """Test plotting a DAGMC model with OpenMC. This is different to CSG + model plotting as the path to the DAGMC file needs handling.""" + + dag_universe = openmc.DAGMCUniverse(request.path.parent / 'dagmc.h5m') + csg_with_dag_inside = dag_universe.bounded_universe() + model = openmc.Model() + model.geometry = openmc.Geometry(csg_with_dag_inside) + + for mat_name in dag_universe.material_names: + material = openmc.Material(name=mat_name) + material.add_nuclide("Fe56", 1.0) + material.set_density("g/cm3", 7.0) + model.materials.append(material) + + # putting the source at the center of the bounding box of the DAGMC + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(dag_universe.bounding_box.center) + ) + model.settings.batches = 10 + model.settings.particles = 50 + + model.plot() + + +def test_plotting_dagmc_universe(request): + """Test plotting a DAGMCUniverse with OpenMC. This is different to plotting + UniverseBase as the materials are not defined withing the DAGMCUniverse.""" + + dag_universe = openmc.DAGMCUniverse(request.path.parent / 'dagmc.h5m') + dag_universe.plot() + + +def test_plotting_geometry_filled_with_dagmc_universe(request): + """Test plotting a geometry with OpenMC. This is an edge case when plotting + geometry as often geometry objects don't include a DAGMCUniverse. The + inclusion of a DAGMCUniverse requires special handling for the materials.""" + + dag_universe = openmc.DAGMCUniverse(request.path.parent / 'dagmc.h5m', auto_geom_ids=True) + + sphere1 = openmc.Sphere(r=50.0) + sphere2 = openmc.Sphere(r=60.0) + sphere2 = openmc.Sphere(r=70.0, boundary_type='vacuum') + + # Adding a material to the CSG Universe to check universe materials are accounted for + csg_material = openmc.Material(name='csg_material') + csg_material.add_nuclide("H1", 1.0) + + # Adding a material with the same name as a dagmc material to check that + # the plot can handel two materials with the same name from different universes + csg_material = openmc.Material(name=dag_universe.material_names[0]) + csg_material.add_nuclide("H1", 1.0) + + cell1 = openmc.Cell(fill=dag_universe, region=-sphere1) + cell2 = openmc.Cell(fill=csg_material, region=+sphere1 & -sphere2) + + geometry = openmc.Geometry([cell1, cell2]) + geometry.plot() + + # Close plot to avoid warning + import matplotlib.pyplot as plt + plt.close() diff --git a/tests/unit_tests/mesh_to_vtk/__init__.py b/tests/unit_tests/mesh_to_vtk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit_tests/mesh_to_vtk/cyl-data-actual.vtk b/tests/unit_tests/mesh_to_vtk/cyl-data-actual.vtk new file mode 100644 index 0000000000..801a02b3ea --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/cyl-data-actual.vtk @@ -0,0 +1,41 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 5 4 4 +POINTS 80 double +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -10 +9.375 11.082531755 -10 8.75 12.165063509 -10 8.125 13.247595264 -10 +7.5 14.330127019 -10 10 10 -10 9.375 8.9174682453 -10 +8.75 7.8349364905 -10 8.125 6.7524047358 -10 7.5 5.6698729811 -10 +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -9.3333333333 9.375 11.082531755 -9.3333333333 +8.75 12.165063509 -9.3333333333 8.125 13.247595264 -9.3333333333 7.5 14.330127019 -9.3333333333 +10 10 -9.3333333333 9.375 8.9174682453 -9.3333333333 8.75 7.8349364905 -9.3333333333 +8.125 6.7524047358 -9.3333333333 7.5 5.6698729811 -9.3333333333 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8.6666666667 9.375 11.082531755 -8.6666666667 8.75 12.165063509 -8.6666666667 +8.125 13.247595264 -8.6666666667 7.5 14.330127019 -8.6666666667 10 10 -8.6666666667 +9.375 8.9174682453 -8.6666666667 8.75 7.8349364905 -8.6666666667 8.125 6.7524047358 -8.6666666667 +7.5 5.6698729811 -8.6666666667 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 10 10 -8 +9.375 11.082531755 -8 8.75 12.165063509 -8 8.125 13.247595264 -8 +7.5 14.330127019 -8 10 10 -8 9.375 8.9174682453 -8 +8.75 7.8349364905 -8 8.125 6.7524047358 -8 7.5 5.6698729811 -8 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 +CELL_DATA 36 +FIELD FieldData 1 +ascending_data 1 36 double +0 100 200 300 400 500 600 700 800 +900 1000 1100 1200 1300 1400 1500 1600 1700 +1800 1900 2000 2100 2200 2300 2400 2500 2600 +2700 2800 2900 3000 3100 3200 3300 3400 3500 + diff --git a/tests/unit_tests/mesh_to_vtk/cyl-data-curvilinear.vtk b/tests/unit_tests/mesh_to_vtk/cyl-data-curvilinear.vtk new file mode 100644 index 0000000000..332d37db07 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/cyl-data-curvilinear.vtk @@ -0,0 +1,329 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 323 double +-4.0450849719 -2.9389262615 0 -4.0450849719 -2.9389262615 0.66666666667 -4.0450849719 -2.9389262615 1.3333333333 +-4.0450849719 -2.9389262615 2 -4.0450849719 2.9389262615 0 -4.0450849719 2.9389262615 0.66666666667 +-4.0450849719 2.9389262615 1.3333333333 -4.0450849719 2.9389262615 2 -3.0338137289 -2.2041946961 0 +-3.0338137289 -2.2041946961 0.66666666667 -3.0338137289 -2.2041946961 1.3333333333 -3.0338137289 -2.2041946961 2 +-3.0338137289 2.2041946961 0 -3.0338137289 2.2041946961 0.66666666667 -3.0338137289 2.2041946961 1.3333333333 +-3.0338137289 2.2041946961 2 -2.0225424859 -1.4694631307 0 -2.0225424859 -1.4694631307 0.66666666667 +-2.0225424859 -1.4694631307 1.3333333333 -2.0225424859 -1.4694631307 2 -2.0225424859 1.4694631307 0 +-2.0225424859 1.4694631307 0.66666666667 -2.0225424859 1.4694631307 1.3333333333 -2.0225424859 1.4694631307 2 +-1.011271243 -0.73473156537 0 -1.011271243 -0.73473156537 0.66666666667 -1.011271243 -0.73473156537 1.3333333333 +-1.011271243 -0.73473156537 2 -1.011271243 0.73473156537 0 -1.011271243 0.73473156537 0.66666666667 +-1.011271243 0.73473156537 1.3333333333 -1.011271243 0.73473156537 2 0 0 0 +0 0 0.66666666667 0 0 1.3333333333 0 0 2 +0.38627124297 -1.1888206454 0 0.38627124297 -1.1888206454 0.66666666667 0.38627124297 -1.1888206454 1.3333333333 +0.38627124297 -1.1888206454 2 0.38627124297 1.1888206454 0 0.38627124297 1.1888206454 0.66666666667 +0.38627124297 1.1888206454 1.3333333333 0.38627124297 1.1888206454 2 0.77254248594 -2.3776412907 0 +0.77254248594 -2.3776412907 0.66666666667 0.77254248594 -2.3776412907 1.3333333333 0.77254248594 -2.3776412907 2 +0.77254248594 2.3776412907 0 0.77254248594 2.3776412907 0.66666666667 0.77254248594 2.3776412907 1.3333333333 +0.77254248594 2.3776412907 2 1.1588137289 -3.5664619361 0 1.1588137289 -3.5664619361 0.66666666667 +1.1588137289 -3.5664619361 1.3333333333 1.1588137289 -3.5664619361 2 1.1588137289 3.5664619361 0 +1.1588137289 3.5664619361 0.66666666667 1.1588137289 3.5664619361 1.3333333333 1.1588137289 3.5664619361 2 +1.25 -3.0616169979e-16 0 1.25 -3.0616169979e-16 0.66666666667 1.25 -3.0616169979e-16 1.3333333333 +1.25 -3.0616169979e-16 2 1.25 0 0 1.25 0 0.66666666667 +1.25 0 1.3333333333 1.25 0 2 1.5450849719 -4.7552825815 0 +1.5450849719 -4.7552825815 0.66666666667 1.5450849719 -4.7552825815 1.3333333333 1.5450849719 -4.7552825815 2 +1.5450849719 4.7552825815 0 1.5450849719 4.7552825815 0.66666666667 1.5450849719 4.7552825815 1.3333333333 +1.5450849719 4.7552825815 2 2.5 -6.1232339957e-16 0 2.5 -6.1232339957e-16 0.66666666667 +2.5 -6.1232339957e-16 1.3333333333 2.5 -6.1232339957e-16 2 2.5 0 0 +2.5 0 0.66666666667 2.5 0 1.3333333333 2.5 0 2 +3.75 -9.1848509936e-16 0 3.75 -9.1848509936e-16 0.66666666667 3.75 -9.1848509936e-16 1.3333333333 +3.75 -9.1848509936e-16 2 3.75 0 0 3.75 0 0.66666666667 +3.75 0 1.3333333333 3.75 0 2 5 -1.2246467991e-15 0 +5 -1.2246467991e-15 0.66666666667 5 -1.2246467991e-15 1.3333333333 5 -1.2246467991e-15 2 +5 0 0 5 0 0.66666666667 5 0 1.3333333333 +5 0 2 0.625 0 0 1.875 0 0 +3.125 0 0 4.375 0 0 0.19313562148 0.59441032268 0 +0.57940686445 1.7832309681 0 0.96567810742 2.9720516134 0 1.3519493504 4.1608722588 0 +-0.50563562148 0.36736578268 0 -1.5169068645 1.102097348 0 -2.5281781074 1.8368289134 0 +-3.5394493504 2.5715604788 0 -0.50563562148 -0.36736578268 0 -1.5169068645 -1.102097348 0 +-2.5281781074 -1.8368289134 0 -3.5394493504 -2.5715604788 0 0.19313562148 -0.59441032268 0 +0.57940686445 -1.7832309681 0 0.96567810742 -2.9720516134 0 1.3519493504 -4.1608722588 0 +0.625 0 0.66666666667 1.875 0 0.66666666667 3.125 0 0.66666666667 +4.375 0 0.66666666667 0.19313562148 0.59441032268 0.66666666667 0.57940686445 1.7832309681 0.66666666667 +0.96567810742 2.9720516134 0.66666666667 1.3519493504 4.1608722588 0.66666666667 -0.50563562148 0.36736578268 0.66666666667 +-1.5169068645 1.102097348 0.66666666667 -2.5281781074 1.8368289134 0.66666666667 -3.5394493504 2.5715604788 0.66666666667 +-0.50563562148 -0.36736578268 0.66666666667 -1.5169068645 -1.102097348 0.66666666667 -2.5281781074 -1.8368289134 0.66666666667 +-3.5394493504 -2.5715604788 0.66666666667 0.19313562148 -0.59441032268 0.66666666667 0.57940686445 -1.7832309681 0.66666666667 +0.96567810742 -2.9720516134 0.66666666667 1.3519493504 -4.1608722588 0.66666666667 0.625 0 1.3333333333 +1.875 0 1.3333333333 3.125 0 1.3333333333 4.375 0 1.3333333333 +0.19313562148 0.59441032268 1.3333333333 0.57940686445 1.7832309681 1.3333333333 0.96567810742 2.9720516134 1.3333333333 +1.3519493504 4.1608722588 1.3333333333 -0.50563562148 0.36736578268 1.3333333333 -1.5169068645 1.102097348 1.3333333333 +-2.5281781074 1.8368289134 1.3333333333 -3.5394493504 2.5715604788 1.3333333333 -0.50563562148 -0.36736578268 1.3333333333 +-1.5169068645 -1.102097348 1.3333333333 -2.5281781074 -1.8368289134 1.3333333333 -3.5394493504 -2.5715604788 1.3333333333 +0.19313562148 -0.59441032268 1.3333333333 0.57940686445 -1.7832309681 1.3333333333 0.96567810742 -2.9720516134 1.3333333333 +1.3519493504 -4.1608722588 1.3333333333 0.625 0 2 1.875 0 2 +3.125 0 2 4.375 0 2 0.19313562148 0.59441032268 2 +0.57940686445 1.7832309681 2 0.96567810742 2.9720516134 2 1.3519493504 4.1608722588 2 +-0.50563562148 0.36736578268 2 -1.5169068645 1.102097348 2 -2.5281781074 1.8368289134 2 +-3.5394493504 2.5715604788 2 -0.50563562148 -0.36736578268 2 -1.5169068645 -1.102097348 2 +-2.5281781074 -1.8368289134 2 -3.5394493504 -2.5715604788 2 0.19313562148 -0.59441032268 2 +0.57940686445 -1.7832309681 2 0.96567810742 -2.9720516134 2 1.3519493504 -4.1608722588 2 +1.011271243 0.73473156537 0 2.0225424859 1.4694631307 0 3.0338137289 2.2041946961 0 +4.0450849719 2.9389262615 0 -0.38627124297 1.1888206454 0 -0.77254248594 2.3776412907 0 +-1.1588137289 3.5664619361 0 -1.5450849719 4.7552825815 0 -1.25 1.5308084989e-16 0 +-2.5 3.0616169979e-16 0 -3.75 4.5924254968e-16 0 -5 6.1232339957e-16 0 +-0.38627124297 -1.1888206454 0 -0.77254248594 -2.3776412907 0 -1.1588137289 -3.5664619361 0 +-1.5450849719 -4.7552825815 0 1.011271243 -0.73473156537 0 2.0225424859 -1.4694631307 0 +3.0338137289 -2.2041946961 0 4.0450849719 -2.9389262615 0 1.011271243 0.73473156537 0.66666666667 +2.0225424859 1.4694631307 0.66666666667 3.0338137289 2.2041946961 0.66666666667 4.0450849719 2.9389262615 0.66666666667 +-0.38627124297 1.1888206454 0.66666666667 -0.77254248594 2.3776412907 0.66666666667 -1.1588137289 3.5664619361 0.66666666667 +-1.5450849719 4.7552825815 0.66666666667 -1.25 1.5308084989e-16 0.66666666667 -2.5 3.0616169979e-16 0.66666666667 +-3.75 4.5924254968e-16 0.66666666667 -5 6.1232339957e-16 0.66666666667 -0.38627124297 -1.1888206454 0.66666666667 +-0.77254248594 -2.3776412907 0.66666666667 -1.1588137289 -3.5664619361 0.66666666667 -1.5450849719 -4.7552825815 0.66666666667 +1.011271243 -0.73473156537 0.66666666667 2.0225424859 -1.4694631307 0.66666666667 3.0338137289 -2.2041946961 0.66666666667 +4.0450849719 -2.9389262615 0.66666666667 1.011271243 0.73473156537 1.3333333333 2.0225424859 1.4694631307 1.3333333333 +3.0338137289 2.2041946961 1.3333333333 4.0450849719 2.9389262615 1.3333333333 -0.38627124297 1.1888206454 1.3333333333 +-0.77254248594 2.3776412907 1.3333333333 -1.1588137289 3.5664619361 1.3333333333 -1.5450849719 4.7552825815 1.3333333333 +-1.25 1.5308084989e-16 1.3333333333 -2.5 3.0616169979e-16 1.3333333333 -3.75 4.5924254968e-16 1.3333333333 +-5 6.1232339957e-16 1.3333333333 -0.38627124297 -1.1888206454 1.3333333333 -0.77254248594 -2.3776412907 1.3333333333 +-1.1588137289 -3.5664619361 1.3333333333 -1.5450849719 -4.7552825815 1.3333333333 1.011271243 -0.73473156537 1.3333333333 +2.0225424859 -1.4694631307 1.3333333333 3.0338137289 -2.2041946961 1.3333333333 4.0450849719 -2.9389262615 1.3333333333 +1.011271243 0.73473156537 2 2.0225424859 1.4694631307 2 3.0338137289 2.2041946961 2 +4.0450849719 2.9389262615 2 -0.38627124297 1.1888206454 2 -0.77254248594 2.3776412907 2 +-1.1588137289 3.5664619361 2 -1.5450849719 4.7552825815 2 -1.25 1.5308084989e-16 2 +-2.5 3.0616169979e-16 2 -3.75 4.5924254968e-16 2 -5 6.1232339957e-16 2 +-0.38627124297 -1.1888206454 2 -0.77254248594 -2.3776412907 2 -1.1588137289 -3.5664619361 2 +-1.5450849719 -4.7552825815 2 1.011271243 -0.73473156537 2 2.0225424859 -1.4694631307 2 +3.0338137289 -2.2041946961 2 4.0450849719 -2.9389262615 2 0 0 0.33333333333 +1.25 0 0.33333333333 2.5 0 0.33333333333 3.75 0 0.33333333333 +5 0 0.33333333333 0.38627124297 1.1888206454 0.33333333333 0.77254248594 2.3776412907 0.33333333333 +1.1588137289 3.5664619361 0.33333333333 1.5450849719 4.7552825815 0.33333333333 -1.011271243 0.73473156537 0.33333333333 +-2.0225424859 1.4694631307 0.33333333333 -3.0338137289 2.2041946961 0.33333333333 -4.0450849719 2.9389262615 0.33333333333 +-1.011271243 -0.73473156537 0.33333333333 -2.0225424859 -1.4694631307 0.33333333333 -3.0338137289 -2.2041946961 0.33333333333 +-4.0450849719 -2.9389262615 0.33333333333 0.38627124297 -1.1888206454 0.33333333333 0.77254248594 -2.3776412907 0.33333333333 +1.1588137289 -3.5664619361 0.33333333333 1.5450849719 -4.7552825815 0.33333333333 0 0 1 +1.25 0 1 2.5 0 1 3.75 0 1 +5 0 1 0.38627124297 1.1888206454 1 0.77254248594 2.3776412907 1 +1.1588137289 3.5664619361 1 1.5450849719 4.7552825815 1 -1.011271243 0.73473156537 1 +-2.0225424859 1.4694631307 1 -3.0338137289 2.2041946961 1 -4.0450849719 2.9389262615 1 +-1.011271243 -0.73473156537 1 -2.0225424859 -1.4694631307 1 -3.0338137289 -2.2041946961 1 +-4.0450849719 -2.9389262615 1 0.38627124297 -1.1888206454 1 0.77254248594 -2.3776412907 1 +1.1588137289 -3.5664619361 1 1.5450849719 -4.7552825815 1 0 0 1.6666666667 +1.25 0 1.6666666667 2.5 0 1.6666666667 3.75 0 1.6666666667 +5 0 1.6666666667 0.38627124297 1.1888206454 1.6666666667 0.77254248594 2.3776412907 1.6666666667 +1.1588137289 3.5664619361 1.6666666667 1.5450849719 4.7552825815 1.6666666667 -1.011271243 0.73473156537 1.6666666667 +-2.0225424859 1.4694631307 1.6666666667 -3.0338137289 2.2041946961 1.6666666667 -4.0450849719 2.9389262615 1.6666666667 +-1.011271243 -0.73473156537 1.6666666667 -2.0225424859 -1.4694631307 1.6666666667 -3.0338137289 -2.2041946961 1.6666666667 +-4.0450849719 -2.9389262615 1.6666666667 0.38627124297 -1.1888206454 1.6666666667 0.77254248594 -2.3776412907 1.6666666667 +1.1588137289 -3.5664619361 1.6666666667 1.5450849719 -4.7552825815 1.6666666667 +CELLS 61 1200 +OFFSETS vtktypeint64 +0 20 40 60 80 100 120 140 160 +180 200 220 240 260 280 300 320 340 +360 380 400 420 440 460 480 500 520 +540 560 580 600 620 640 660 680 700 +720 740 760 780 800 820 840 860 880 +900 920 940 960 980 1000 1020 1040 1060 +1080 1100 1120 1140 1160 1180 1200 +CONNECTIVITY vtktypeint64 +32 60 40 32 33 61 41 33 100 +180 104 32 120 200 124 33 260 261 +265 260 60 76 48 40 61 77 49 +41 101 181 105 180 121 201 125 200 +261 262 266 265 76 84 56 48 77 +85 57 49 102 182 106 181 122 202 +126 201 262 263 267 266 84 92 72 +56 85 93 73 57 103 183 107 182 +123 203 127 202 263 264 268 267 32 +40 28 32 33 41 29 33 104 184 +108 32 124 204 128 33 260 265 269 +260 40 48 20 28 41 49 21 29 +105 185 109 184 125 205 129 204 265 +266 270 269 48 56 12 20 49 57 +13 21 106 186 110 185 126 206 130 +205 266 267 271 270 56 72 4 12 +57 73 5 13 107 187 111 186 127 +207 131 206 267 268 272 271 32 28 +24 32 33 29 25 33 108 188 112 +32 128 208 132 33 260 269 273 260 +28 20 16 24 29 21 17 25 109 +189 113 188 129 209 133 208 269 270 +274 273 20 12 8 16 21 13 9 +17 110 190 114 189 130 210 134 209 +270 271 275 274 12 4 0 8 13 +5 1 9 111 191 115 190 131 211 +135 210 271 272 276 275 32 24 36 +32 33 25 37 33 112 192 116 32 +132 212 136 33 260 273 277 260 24 +16 44 36 25 17 45 37 113 193 +117 192 133 213 137 212 273 274 278 +277 16 8 52 44 17 9 53 45 +114 194 118 193 134 214 138 213 274 +275 279 278 8 0 68 52 9 1 +69 53 115 195 119 194 135 215 139 +214 275 276 280 279 32 36 60 32 +33 37 61 33 116 196 100 32 136 +216 120 33 260 277 261 260 36 44 +76 60 37 45 77 61 117 197 101 +196 137 217 121 216 277 278 262 261 +44 52 84 76 45 53 85 77 118 +198 102 197 138 218 122 217 278 279 +263 262 52 68 92 84 53 69 93 +85 119 199 103 198 139 219 123 218 +279 280 264 263 33 61 41 33 34 +62 42 34 120 200 124 33 140 220 +144 34 281 282 286 281 61 77 49 +41 62 78 50 42 121 201 125 200 +141 221 145 220 282 283 287 286 77 +85 57 49 78 86 58 50 122 202 +126 201 142 222 146 221 283 284 288 +287 85 93 73 57 86 94 74 58 +123 203 127 202 143 223 147 222 284 +285 289 288 33 41 29 33 34 42 +30 34 124 204 128 33 144 224 148 +34 281 286 290 281 41 49 21 29 +42 50 22 30 125 205 129 204 145 +225 149 224 286 287 291 290 49 57 +13 21 50 58 14 22 126 206 130 +205 146 226 150 225 287 288 292 291 +57 73 5 13 58 74 6 14 127 +207 131 206 147 227 151 226 288 289 +293 292 33 29 25 33 34 30 26 +34 128 208 132 33 148 228 152 34 +281 290 294 281 29 21 17 25 30 +22 18 26 129 209 133 208 149 229 +153 228 290 291 295 294 21 13 9 +17 22 14 10 18 130 210 134 209 +150 230 154 229 291 292 296 295 13 +5 1 9 14 6 2 10 131 211 +135 210 151 231 155 230 292 293 297 +296 33 25 37 33 34 26 38 34 +132 212 136 33 152 232 156 34 281 +294 298 281 25 17 45 37 26 18 +46 38 133 213 137 212 153 233 157 +232 294 295 299 298 17 9 53 45 +18 10 54 46 134 214 138 213 154 +234 158 233 295 296 300 299 9 1 +69 53 10 2 70 54 135 215 139 +214 155 235 159 234 296 297 301 300 +33 37 61 33 34 38 62 34 136 +216 120 33 156 236 140 34 281 298 +282 281 37 45 77 61 38 46 78 +62 137 217 121 216 157 237 141 236 +298 299 283 282 45 53 85 77 46 +54 86 78 138 218 122 217 158 238 +142 237 299 300 284 283 53 69 93 +85 54 70 94 86 139 219 123 218 +159 239 143 238 300 301 285 284 34 +62 42 34 35 63 43 35 140 220 +144 34 160 240 164 35 302 303 307 +302 62 78 50 42 63 79 51 43 +141 221 145 220 161 241 165 240 303 +304 308 307 78 86 58 50 79 87 +59 51 142 222 146 221 162 242 166 +241 304 305 309 308 86 94 74 58 +87 95 75 59 143 223 147 222 163 +243 167 242 305 306 310 309 34 42 +30 34 35 43 31 35 144 224 148 +34 164 244 168 35 302 307 311 302 +42 50 22 30 43 51 23 31 145 +225 149 224 165 245 169 244 307 308 +312 311 50 58 14 22 51 59 15 +23 146 226 150 225 166 246 170 245 +308 309 313 312 58 74 6 14 59 +75 7 15 147 227 151 226 167 247 +171 246 309 310 314 313 34 30 26 +34 35 31 27 35 148 228 152 34 +168 248 172 35 302 311 315 302 30 +22 18 26 31 23 19 27 149 229 +153 228 169 249 173 248 311 312 316 +315 22 14 10 18 23 15 11 19 +150 230 154 229 170 250 174 249 312 +313 317 316 14 6 2 10 15 7 +3 11 151 231 155 230 171 251 175 +250 313 314 318 317 34 26 38 34 +35 27 39 35 152 232 156 34 172 +252 176 35 302 315 319 302 26 18 +46 38 27 19 47 39 153 233 157 +232 173 253 177 252 315 316 320 319 +18 10 54 46 19 11 55 47 154 +234 158 233 174 254 178 253 316 317 +321 320 10 2 70 54 11 3 71 +55 155 235 159 234 175 255 179 254 +317 318 322 321 34 38 62 34 35 +39 63 35 156 236 140 34 176 256 +160 35 302 319 303 302 38 46 78 +62 39 47 79 63 157 237 141 236 +177 257 161 256 319 320 304 303 46 +54 86 78 47 55 87 79 158 238 +142 237 178 258 162 257 320 321 305 +304 54 70 94 86 55 71 95 87 +159 239 143 238 179 259 163 258 321 +322 306 305 +CELL_TYPES 60 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 + +CELL_DATA 60 +FIELD FieldData 1 +ascending_data 1 60 double +0 100 200 300 400 500 600 700 800 +900 1000 1100 1200 1300 1400 1500 1600 1700 +1800 1900 2000 2100 2200 2300 2400 2500 2600 +2700 2800 2900 3000 3100 3200 3300 3400 3500 +3600 3700 3800 3900 4000 4100 4200 4300 4400 +4500 4600 4700 4800 4900 5000 5100 5200 5300 +5400 5500 5600 5700 5800 5900 diff --git a/tests/unit_tests/mesh_to_vtk/cyl-data.vtk b/tests/unit_tests/mesh_to_vtk/cyl-data.vtk new file mode 100644 index 0000000000..801a02b3ea --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/cyl-data.vtk @@ -0,0 +1,41 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 5 4 4 +POINTS 80 double +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -10 +9.375 11.082531755 -10 8.75 12.165063509 -10 8.125 13.247595264 -10 +7.5 14.330127019 -10 10 10 -10 9.375 8.9174682453 -10 +8.75 7.8349364905 -10 8.125 6.7524047358 -10 7.5 5.6698729811 -10 +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -9.3333333333 9.375 11.082531755 -9.3333333333 +8.75 12.165063509 -9.3333333333 8.125 13.247595264 -9.3333333333 7.5 14.330127019 -9.3333333333 +10 10 -9.3333333333 9.375 8.9174682453 -9.3333333333 8.75 7.8349364905 -9.3333333333 +8.125 6.7524047358 -9.3333333333 7.5 5.6698729811 -9.3333333333 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8.6666666667 9.375 11.082531755 -8.6666666667 8.75 12.165063509 -8.6666666667 +8.125 13.247595264 -8.6666666667 7.5 14.330127019 -8.6666666667 10 10 -8.6666666667 +9.375 8.9174682453 -8.6666666667 8.75 7.8349364905 -8.6666666667 8.125 6.7524047358 -8.6666666667 +7.5 5.6698729811 -8.6666666667 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 10 10 -8 +9.375 11.082531755 -8 8.75 12.165063509 -8 8.125 13.247595264 -8 +7.5 14.330127019 -8 10 10 -8 9.375 8.9174682453 -8 +8.75 7.8349364905 -8 8.125 6.7524047358 -8 7.5 5.6698729811 -8 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 +CELL_DATA 36 +FIELD FieldData 1 +ascending_data 1 36 double +0 100 200 300 400 500 600 700 800 +900 1000 1100 1200 1300 1400 1500 1600 1700 +1800 1900 2000 2100 2200 2300 2400 2500 2600 +2700 2800 2900 3000 3100 3200 3300 3400 3500 + diff --git a/tests/unit_tests/mesh_to_vtk/cylindrical-curvilinear.vtk b/tests/unit_tests/mesh_to_vtk/cylindrical-curvilinear.vtk new file mode 100644 index 0000000000..24bdd7f14e --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/cylindrical-curvilinear.vtk @@ -0,0 +1,198 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 195 double +7.5 5.6698729811 -10 7.5 5.6698729811 -9.3333333333 7.5 5.6698729811 -8.6666666667 +7.5 5.6698729811 -8 7.5 14.330127019 -10 7.5 14.330127019 -9.3333333333 +7.5 14.330127019 -8.6666666667 7.5 14.330127019 -8 8.125 6.7524047358 -10 +8.125 6.7524047358 -9.3333333333 8.125 6.7524047358 -8.6666666667 8.125 6.7524047358 -8 +8.125 13.247595264 -10 8.125 13.247595264 -9.3333333333 8.125 13.247595264 -8.6666666667 +8.125 13.247595264 -8 8.75 7.8349364905 -10 8.75 7.8349364905 -9.3333333333 +8.75 7.8349364905 -8.6666666667 8.75 7.8349364905 -8 8.75 12.165063509 -10 +8.75 12.165063509 -9.3333333333 8.75 12.165063509 -8.6666666667 8.75 12.165063509 -8 +9.375 8.9174682453 -10 9.375 8.9174682453 -9.3333333333 9.375 8.9174682453 -8.6666666667 +9.375 8.9174682453 -8 9.375 11.082531755 -10 9.375 11.082531755 -9.3333333333 +9.375 11.082531755 -8.6666666667 9.375 11.082531755 -8 10 10 -10 +10 10 -9.3333333333 10 10 -8.6666666667 10 10 -8 +11.25 10 -10 11.25 10 -9.3333333333 11.25 10 -8.6666666667 +11.25 10 -8 12.5 10 -10 12.5 10 -9.3333333333 +12.5 10 -8.6666666667 12.5 10 -8 13.75 10 -10 +13.75 10 -9.3333333333 13.75 10 -8.6666666667 13.75 10 -8 +13.75 10 -10 13.75 10 -9.3333333333 13.75 10 -8.6666666667 +13.75 10 -8 15 10 -10 15 10 -9.3333333333 +15 10 -8.6666666667 15 10 -8 15 10 -10 +15 10 -9.3333333333 15 10 -8.6666666667 15 10 -8 +10.625 10 -10 11.875 10 -10 13.125 10 -10 +14.375 10 -10 9.6875 10.541265877 -10 9.0625 11.623797632 -10 +8.4375 12.706329387 -10 7.8125 13.788861142 -10 9.6875 9.4587341226 -10 +9.0625 8.3762023679 -10 8.4375 7.2936706132 -10 7.8125 6.2111388584 -10 +10.625 10 -9.3333333333 11.875 10 -9.3333333333 13.125 10 -9.3333333333 +14.375 10 -9.3333333333 9.6875 10.541265877 -9.3333333333 9.0625 11.623797632 -9.3333333333 +8.4375 12.706329387 -9.3333333333 7.8125 13.788861142 -9.3333333333 9.6875 9.4587341226 -9.3333333333 +9.0625 8.3762023679 -9.3333333333 8.4375 7.2936706132 -9.3333333333 7.8125 6.2111388584 -9.3333333333 +10.625 10 -8.6666666667 11.875 10 -8.6666666667 13.125 10 -8.6666666667 +14.375 10 -8.6666666667 9.6875 10.541265877 -8.6666666667 9.0625 11.623797632 -8.6666666667 +8.4375 12.706329387 -8.6666666667 7.8125 13.788861142 -8.6666666667 9.6875 9.4587341226 -8.6666666667 +9.0625 8.3762023679 -8.6666666667 8.4375 7.2936706132 -8.6666666667 7.8125 6.2111388584 -8.6666666667 +10.625 10 -8 11.875 10 -8 13.125 10 -8 +14.375 10 -8 9.6875 10.541265877 -8 9.0625 11.623797632 -8 +8.4375 12.706329387 -8 7.8125 13.788861142 -8 9.6875 9.4587341226 -8 +9.0625 8.3762023679 -8 8.4375 7.2936706132 -8 7.8125 6.2111388584 -8 +10.625 11.082531755 -10 11.25 12.165063509 -10 11.875 13.247595264 -10 +12.5 14.330127019 -10 8.75 10 -10 7.5 10 -10 +6.25 10 -10 5 10 -10 10.625 8.9174682453 -10 +11.25 7.8349364905 -10 11.875 6.7524047358 -10 12.5 5.6698729811 -10 +10.625 11.082531755 -9.3333333333 11.25 12.165063509 -9.3333333333 11.875 13.247595264 -9.3333333333 +12.5 14.330127019 -9.3333333333 8.75 10 -9.3333333333 7.5 10 -9.3333333333 +6.25 10 -9.3333333333 5 10 -9.3333333333 10.625 8.9174682453 -9.3333333333 +11.25 7.8349364905 -9.3333333333 11.875 6.7524047358 -9.3333333333 12.5 5.6698729811 -9.3333333333 +10.625 11.082531755 -8.6666666667 11.25 12.165063509 -8.6666666667 11.875 13.247595264 -8.6666666667 +12.5 14.330127019 -8.6666666667 8.75 10 -8.6666666667 7.5 10 -8.6666666667 +6.25 10 -8.6666666667 5 10 -8.6666666667 10.625 8.9174682453 -8.6666666667 +11.25 7.8349364905 -8.6666666667 11.875 6.7524047358 -8.6666666667 12.5 5.6698729811 -8.6666666667 +10.625 11.082531755 -8 11.25 12.165063509 -8 11.875 13.247595264 -8 +12.5 14.330127019 -8 8.75 10 -8 7.5 10 -8 +6.25 10 -8 5 10 -8 10.625 8.9174682453 -8 +11.25 7.8349364905 -8 11.875 6.7524047358 -8 12.5 5.6698729811 -8 +10 10 -9.6666666667 11.25 10 -9.6666666667 12.5 10 -9.6666666667 +13.75 10 -9.6666666667 15 10 -9.6666666667 9.375 11.082531755 -9.6666666667 +8.75 12.165063509 -9.6666666667 8.125 13.247595264 -9.6666666667 7.5 14.330127019 -9.6666666667 +9.375 8.9174682453 -9.6666666667 8.75 7.8349364905 -9.6666666667 8.125 6.7524047358 -9.6666666667 +7.5 5.6698729811 -9.6666666667 10 10 -9 11.25 10 -9 +12.5 10 -9 13.75 10 -9 15 10 -9 +9.375 11.082531755 -9 8.75 12.165063509 -9 8.125 13.247595264 -9 +7.5 14.330127019 -9 9.375 8.9174682453 -9 8.75 7.8349364905 -9 +8.125 6.7524047358 -9 7.5 5.6698729811 -9 10 10 -8.3333333333 +11.25 10 -8.3333333333 12.5 10 -8.3333333333 13.75 10 -8.3333333333 +15 10 -8.3333333333 9.375 11.082531755 -8.3333333333 8.75 12.165063509 -8.3333333333 +8.125 13.247595264 -8.3333333333 7.5 14.330127019 -8.3333333333 9.375 8.9174682453 -8.3333333333 +8.75 7.8349364905 -8.3333333333 8.125 6.7524047358 -8.3333333333 7.5 5.6698729811 -8.3333333333 + +CELLS 37 720 +OFFSETS vtktypeint64 +0 20 40 60 80 100 120 140 160 +180 200 220 240 260 280 300 320 340 +360 380 400 420 440 460 480 500 520 +540 560 580 600 620 640 660 680 700 +720 +CONNECTIVITY vtktypeint64 +32 36 28 32 33 37 29 33 60 +108 64 32 72 120 76 33 156 157 +161 156 36 40 20 28 37 41 21 +29 61 109 65 108 73 121 77 120 +157 158 162 161 40 44 12 20 41 +45 13 21 62 110 66 109 74 122 +78 121 158 159 163 162 44 52 4 +12 45 53 5 13 63 111 67 110 +75 123 79 122 159 160 164 163 32 +28 24 32 33 29 25 33 64 112 +68 32 76 124 80 33 156 161 165 +156 28 20 16 24 29 21 17 25 +65 113 69 112 77 125 81 124 161 +162 166 165 20 12 8 16 21 13 +9 17 66 114 70 113 78 126 82 +125 162 163 167 166 12 4 0 8 +13 5 1 9 67 115 71 114 79 +127 83 126 163 164 168 167 32 24 +36 32 33 25 37 33 68 116 60 +32 80 128 72 33 156 165 157 156 +24 16 40 36 25 17 41 37 69 +117 61 116 81 129 73 128 165 166 +158 157 16 8 44 40 17 9 45 +41 70 118 62 117 82 130 74 129 +166 167 159 158 8 0 52 44 9 +1 53 45 71 119 63 118 83 131 +75 130 167 168 160 159 33 37 29 +33 34 38 30 34 72 120 76 33 +84 132 88 34 169 170 174 169 37 +41 21 29 38 42 22 30 73 121 +77 120 85 133 89 132 170 171 175 +174 41 45 13 21 42 46 14 22 +74 122 78 121 86 134 90 133 171 +172 176 175 45 53 5 13 46 54 +6 14 75 123 79 122 87 135 91 +134 172 173 177 176 33 29 25 33 +34 30 26 34 76 124 80 33 88 +136 92 34 169 174 178 169 29 21 +17 25 30 22 18 26 77 125 81 +124 89 137 93 136 174 175 179 178 +21 13 9 17 22 14 10 18 78 +126 82 125 90 138 94 137 175 176 +180 179 13 5 1 9 14 6 2 +10 79 127 83 126 91 139 95 138 +176 177 181 180 33 25 37 33 34 +26 38 34 80 128 72 33 92 140 +84 34 169 178 170 169 25 17 41 +37 26 18 42 38 81 129 73 128 +93 141 85 140 178 179 171 170 17 +9 45 41 18 10 46 42 82 130 +74 129 94 142 86 141 179 180 172 +171 9 1 53 45 10 2 54 46 +83 131 75 130 95 143 87 142 180 +181 173 172 34 38 30 34 35 39 +31 35 84 132 88 34 96 144 100 +35 182 183 187 182 38 42 22 30 +39 43 23 31 85 133 89 132 97 +145 101 144 183 184 188 187 42 46 +14 22 43 47 15 23 86 134 90 +133 98 146 102 145 184 185 189 188 +46 54 6 14 47 55 7 15 87 +135 91 134 99 147 103 146 185 186 +190 189 34 30 26 34 35 31 27 +35 88 136 92 34 100 148 104 35 +182 187 191 182 30 22 18 26 31 +23 19 27 89 137 93 136 101 149 +105 148 187 188 192 191 22 14 10 +18 23 15 11 19 90 138 94 137 +102 150 106 149 188 189 193 192 14 +6 2 10 15 7 3 11 91 139 +95 138 103 151 107 150 189 190 194 +193 34 26 38 34 35 27 39 35 +92 140 84 34 104 152 96 35 182 +191 183 182 26 18 42 38 27 19 +43 39 93 141 85 140 105 153 97 +152 191 192 184 183 18 10 46 42 +19 11 47 43 94 142 86 141 106 +154 98 153 192 193 185 184 10 2 +54 46 11 3 55 47 95 143 87 +142 107 155 99 154 193 194 186 185 + +CELL_TYPES 36 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 + diff --git a/tests/unit_tests/mesh_to_vtk/cylindrical-linear.vtk b/tests/unit_tests/mesh_to_vtk/cylindrical-linear.vtk new file mode 100644 index 0000000000..33d1ce497a --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/cylindrical-linear.vtk @@ -0,0 +1,33 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 5 4 4 +POINTS 80 double +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -10 +9.375 11.082531755 -10 8.75 12.165063509 -10 8.125 13.247595264 -10 +7.5 14.330127019 -10 10 10 -10 9.375 8.9174682453 -10 +8.75 7.8349364905 -10 8.125 6.7524047358 -10 7.5 5.6698729811 -10 +10 10 -10 11.25 10 -10 12.5 10 -10 +13.75 10 -10 15 10 -10 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -9.3333333333 9.375 11.082531755 -9.3333333333 +8.75 12.165063509 -9.3333333333 8.125 13.247595264 -9.3333333333 7.5 14.330127019 -9.3333333333 +10 10 -9.3333333333 9.375 8.9174682453 -9.3333333333 8.75 7.8349364905 -9.3333333333 +8.125 6.7524047358 -9.3333333333 7.5 5.6698729811 -9.3333333333 10 10 -9.3333333333 +11.25 10 -9.3333333333 12.5 10 -9.3333333333 13.75 10 -9.3333333333 +15 10 -9.3333333333 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8.6666666667 9.375 11.082531755 -8.6666666667 8.75 12.165063509 -8.6666666667 +8.125 13.247595264 -8.6666666667 7.5 14.330127019 -8.6666666667 10 10 -8.6666666667 +9.375 8.9174682453 -8.6666666667 8.75 7.8349364905 -8.6666666667 8.125 6.7524047358 -8.6666666667 +7.5 5.6698729811 -8.6666666667 10 10 -8.6666666667 11.25 10 -8.6666666667 +12.5 10 -8.6666666667 13.75 10 -8.6666666667 15 10 -8.6666666667 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 10 10 -8 +9.375 11.082531755 -8 8.75 12.165063509 -8 8.125 13.247595264 -8 +7.5 14.330127019 -8 10 10 -8 9.375 8.9174682453 -8 +8.75 7.8349364905 -8 8.125 6.7524047358 -8 7.5 5.6698729811 -8 +10 10 -8 11.25 10 -8 12.5 10 -8 +13.75 10 -8 15 10 -8 diff --git a/tests/unit_tests/mesh_to_vtk/hexes.exo b/tests/unit_tests/mesh_to_vtk/hexes.exo new file mode 100644 index 0000000000..c16138e900 Binary files /dev/null and b/tests/unit_tests/mesh_to_vtk/hexes.exo differ diff --git a/tests/unit_tests/mesh_to_vtk/libmesh_hexes_ref.vtk b/tests/unit_tests/mesh_to_vtk/libmesh_hexes_ref.vtk new file mode 100644 index 0000000000..041d7065dd --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/libmesh_hexes_ref.vtk @@ -0,0 +1,76 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 54 double +-1 -1 2.5 -1 -1 1.5 -1 0 1.5 +-1 0 2.5 0 -1 2.5 0 -1 1.5 +0 0 1.5 0 0 2.5 -1 -1 0.5 +-1 0 0.5 0 -1 0.5 0 0 0.5 +-1 -1 -0.5 -1 0 -0.5 0 -1 -0.5 +0 0 -0.5 -1 -1 -1.5 -1 0 -1.5 +0 -1 -1.5 0 0 -1.5 -1 -1 -2.5 +-1 0 -2.5 0 -1 -2.5 0 0 -2.5 +-1 1 1.5 -1 1 2.5 0 1 1.5 +0 1 2.5 -1 1 0.5 0 1 0.5 +-1 1 -0.5 0 1 -0.5 -1 1 -1.5 +0 1 -1.5 -1 1 -2.5 0 1 -2.5 +1 -1 2.5 1 -1 1.5 1 0 1.5 +1 0 2.5 1 -1 0.5 1 0 0.5 +1 -1 -0.5 1 0 -0.5 1 -1 -1.5 +1 0 -1.5 1 -1 -2.5 1 0 -2.5 +1 1 1.5 1 1 2.5 1 1 0.5 +1 1 -0.5 1 1 -1.5 1 1 -2.5 + +CELLS 21 160 +OFFSETS vtktypeint64 +0 8 16 24 32 40 48 56 64 +72 80 88 96 104 112 120 128 136 +144 152 160 +CONNECTIVITY vtktypeint64 +0 1 2 3 4 5 6 7 1 +8 9 2 5 10 11 6 8 12 +13 9 10 14 15 11 12 16 17 +13 14 18 19 15 16 20 21 17 +18 22 23 19 3 2 24 25 7 +6 26 27 2 9 28 24 6 11 +29 26 9 13 30 28 11 15 31 +29 13 17 32 30 15 19 33 31 +17 21 34 32 19 23 35 33 4 +5 6 7 36 37 38 39 5 10 +11 6 37 40 41 38 10 14 15 +11 40 42 43 41 14 18 19 15 +42 44 45 43 18 22 23 19 44 +46 47 45 7 6 26 27 39 38 +48 49 6 11 29 26 38 41 50 +48 11 15 31 29 41 43 51 50 +15 19 33 31 43 45 52 51 19 +23 35 33 45 47 53 52 +CELL_TYPES 20 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 +12 + +CELL_DATA 20 +FIELD FieldData 1 +ids 1 20 double +0 1 2 3 4 5 6 7 8 +9 10 11 12 13 14 15 16 17 +18 19 diff --git a/tests/unit_tests/mesh_to_vtk/libmesh_tets_ref.vtk b/tests/unit_tests/mesh_to_vtk/libmesh_tets_ref.vtk new file mode 100644 index 0000000000..90f2261212 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/libmesh_tets_ref.vtk @@ -0,0 +1,292 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 58 double +-0.02593964576 -1 -1.1195739536 -0.40239958217 -0.40962166746 -1.9256035383 -1 0.02593964576 -1.1195739536 +-0.2248833639 0.23189144433 -1.2841109811 0.02593964576 1 -1.1195739536 1 -0.02593964576 -1.1195739536 +-0.042928712675 0.066425810853 -0.54270728017 -1 1 -1.5 -1 1 -0.5 +-0.49265312381 0.49332495053 -1.9656359509 -0.02608137991 -1 1.121521147 0.30281888252 -0.30293622513 1.8067963894 +1 -0.02608137991 1.121521147 -0.23236342389 0.26314805583 1.2867556783 0.074337770194 0.074337770194 2.5 +-0.40005770996 -0.36909660956 1.8768871762 -0.4896743493 0.49451064621 1.952829049 -1 1 1.5 +0.02608137991 1 1.121521147 -1 1 0.5 -0.038567136385 0.14628887118 0.55111724874 +-1 0.02608137991 1.121521147 0 -1 -2.5 0.37543954218 -0.36967061971 -1.8691105401 +1 -1 -2.5 0.074337770194 -0.074337770194 -2.5 1 0 -2.5 +0.4135797166 0.38351746262 -1.9274856063 1 -1 -1.5 -1 0 -2.5 +-1 -1 -1.5 -1 -1 -2.5 -1 0 2.5 +-1 1 2.5 0 1 2.5 0.46519182209 0.49356920409 1.95286052 +0 -1 2.5 -1 -1 1.5 -1 -1 2.5 +1 -1 2.5 1 -1 1.5 1 0 2.5 +1 1 2.5 1 1 1.5 0 1 -2.5 +-1 1 -2.5 1 -1 -0.5 1 1 -0.5 +0.038669824604 1 0.0046021677516 -1 -1 0.5 -0.038669824604 -1 0.0046021677516 +1 1 0.5 -1 0.038669824604 0.0046021677516 1 -0.038669824604 0.0046021677516 +1 1 -2.5 1 1 -1.5 1 -1 0.5 +-1 -1 -0.5 +CELLS 155 616 +OFFSETS vtktypeint64 +0 4 8 12 16 20 24 28 32 +36 40 44 48 52 56 60 64 68 +72 76 80 84 88 92 96 100 104 +108 112 116 120 124 128 132 136 140 +144 148 152 156 160 164 168 172 176 +180 184 188 192 196 200 204 208 212 +216 220 224 228 232 236 240 244 248 +252 256 260 264 268 272 276 280 284 +288 292 296 300 304 308 312 316 320 +324 328 332 336 340 344 348 352 356 +360 364 368 372 376 380 384 388 392 +396 400 404 408 412 416 420 424 428 +432 436 440 444 448 452 456 460 464 +468 472 476 480 484 488 492 496 500 +504 508 512 516 520 524 528 532 536 +540 544 548 552 556 560 564 568 572 +576 580 584 588 592 596 600 604 608 +612 616 +CONNECTIVITY vtktypeint64 +0 1 2 3 4 3 5 6 7 +4 8 3 7 3 2 9 8 3 +4 6 10 11 12 13 14 13 15 +16 17 13 18 16 19 18 13 20 +10 13 21 15 22 1 0 23 22 +24 25 23 25 1 3 9 26 23 +5 27 24 28 26 23 0 1 3 +23 29 30 31 1 22 31 30 1 +2 3 1 9 29 2 1 9 22 +0 28 23 32 15 21 16 14 13 +11 15 33 17 34 16 33 34 32 +16 34 16 18 35 10 11 13 15 +18 13 12 35 36 11 10 15 36 +37 38 15 32 38 37 15 32 21 +17 16 36 10 37 15 36 39 40 +11 41 40 39 11 41 12 11 35 +17 21 13 16 10 13 12 20 12 +13 11 35 14 13 16 35 34 17 +18 16 42 34 43 35 41 12 40 +11 14 41 11 35 42 41 14 35 +0 3 5 23 0 5 3 6 44 +4 9 27 29 2 30 1 44 45 +29 9 29 45 7 9 0 46 5 +6 47 48 4 6 0 3 2 6 +4 5 3 27 49 10 50 20 8 +2 3 6 51 18 48 20 49 50 +52 20 19 13 21 20 51 53 12 +20 19 48 18 20 25 3 1 23 +7 4 3 9 25 3 23 27 44 +4 7 9 25 23 26 27 54 55 +44 27 54 26 55 27 18 12 13 +20 14 16 34 35 19 21 52 20 +30 2 0 1 22 25 29 1 5 +23 3 27 25 26 44 27 22 29 +31 1 54 44 26 27 25 44 9 +27 22 28 24 23 26 5 55 27 +25 24 26 23 22 25 1 23 25 +44 29 9 25 29 1 9 48 6 +53 20 8 4 48 6 10 40 56 +12 10 40 12 11 17 19 18 13 +30 57 0 2 25 9 3 27 7 +8 2 3 26 28 5 23 29 7 +2 9 28 0 5 23 37 10 49 +21 49 50 57 52 56 12 53 20 +10 12 56 20 43 18 12 35 32 +37 21 15 14 11 13 35 19 52 +8 48 19 52 48 20 41 39 36 +11 50 6 52 20 46 53 5 6 +4 3 9 27 44 7 45 9 55 +47 4 5 28 0 46 5 8 52 +2 6 57 2 52 6 50 56 53 +20 10 56 50 20 57 50 0 6 +37 10 21 15 33 32 17 16 34 +18 43 35 57 52 50 6 51 48 +47 53 18 16 13 35 50 56 46 +53 14 36 32 15 14 11 36 15 +17 21 19 13 10 21 13 20 51 +12 18 20 50 53 46 6 14 15 +32 16 42 43 41 35 14 41 36 +11 49 52 21 20 36 40 10 11 +47 53 48 6 49 21 10 20 21 +15 13 16 51 48 53 20 55 5 +4 27 44 55 4 27 47 4 5 +6 8 48 52 6 47 5 53 6 +50 53 6 20 57 0 2 6 50 +46 0 6 52 6 48 20 42 14 +34 35 43 18 51 12 41 43 12 +35 22 30 0 1 14 32 34 16 +36 38 32 15 +CELL_TYPES 154 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 + +CELL_DATA 154 +FIELD FieldData 1 +ids 1 154 double +0 1 2 3 4 5 6 7 8 +9 10 11 12 13 14 15 16 17 +18 19 20 21 22 23 24 25 26 +27 28 29 30 31 32 33 34 35 +36 37 38 39 40 41 42 43 44 +45 46 47 48 49 50 51 52 53 +54 55 56 57 58 59 60 61 62 +63 64 65 66 67 68 69 70 71 +72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 +90 91 92 93 94 95 96 97 98 +99 100 101 102 103 104 105 106 107 +108 109 110 111 112 113 114 115 116 +117 118 119 120 121 122 123 124 125 +126 127 128 129 130 131 132 133 134 +135 136 137 138 139 140 141 142 143 +144 145 146 147 148 149 150 151 152 +153 diff --git a/tests/unit_tests/mesh_to_vtk/moab_tets_ref.vtk b/tests/unit_tests/mesh_to_vtk/moab_tets_ref.vtk new file mode 120000 index 0000000000..91e356480f --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/moab_tets_ref.vtk @@ -0,0 +1 @@ +libmesh_tets_ref.vtk \ No newline at end of file diff --git a/tests/unit_tests/mesh_to_vtk/rectilinear.vtk b/tests/unit_tests/mesh_to_vtk/rectilinear.vtk new file mode 100644 index 0000000000..71826392e8 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/rectilinear.vtk @@ -0,0 +1,340 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 5 10 20 +POINTS 1000 double +0 5 1 2.5 5 1 5 5 1 +7.5 5 1 10 5 1 0 5.8326451979 1 +2.5 5.8326451979 1 5 5.8326451979 1 7.5 5.8326451979 1 +10 5.8326451979 1 0 6.8039500009 1 2.5 6.8039500009 1 +5 6.8039500009 1 7.5 6.8039500009 1 10 6.8039500009 1 +0 7.9370052598 1 2.5 7.9370052598 1 5 7.9370052598 1 +7.5 7.9370052598 1 10 7.9370052598 1 0 9.2587471229 1 +2.5 9.2587471229 1 5 9.2587471229 1 7.5 9.2587471229 1 +10 9.2587471229 1 0 10.800597389 1 2.5 10.800597389 1 +5 10.800597389 1 7.5 10.800597389 1 10 10.800597389 1 +0 12.599210499 1 2.5 12.599210499 1 5 12.599210499 1 +7.5 12.599210499 1 10 12.599210499 1 0 14.697344923 1 +2.5 14.697344923 1 5 14.697344923 1 7.5 14.697344923 1 +10 14.697344923 1 0 17.144879657 1 2.5 17.144879657 1 +5 17.144879657 1 7.5 17.144879657 1 10 17.144879657 1 +0 20 1 2.5 20 1 5 20 1 +7.5 20 1 10 20 1 0 5 6.2105263158 +2.5 5 6.2105263158 5 5 6.2105263158 7.5 5 6.2105263158 +10 5 6.2105263158 0 5.8326451979 6.2105263158 2.5 5.8326451979 6.2105263158 +5 5.8326451979 6.2105263158 7.5 5.8326451979 6.2105263158 10 5.8326451979 6.2105263158 +0 6.8039500009 6.2105263158 2.5 6.8039500009 6.2105263158 5 6.8039500009 6.2105263158 +7.5 6.8039500009 6.2105263158 10 6.8039500009 6.2105263158 0 7.9370052598 6.2105263158 +2.5 7.9370052598 6.2105263158 5 7.9370052598 6.2105263158 7.5 7.9370052598 6.2105263158 +10 7.9370052598 6.2105263158 0 9.2587471229 6.2105263158 2.5 9.2587471229 6.2105263158 +5 9.2587471229 6.2105263158 7.5 9.2587471229 6.2105263158 10 9.2587471229 6.2105263158 +0 10.800597389 6.2105263158 2.5 10.800597389 6.2105263158 5 10.800597389 6.2105263158 +7.5 10.800597389 6.2105263158 10 10.800597389 6.2105263158 0 12.599210499 6.2105263158 +2.5 12.599210499 6.2105263158 5 12.599210499 6.2105263158 7.5 12.599210499 6.2105263158 +10 12.599210499 6.2105263158 0 14.697344923 6.2105263158 2.5 14.697344923 6.2105263158 +5 14.697344923 6.2105263158 7.5 14.697344923 6.2105263158 10 14.697344923 6.2105263158 +0 17.144879657 6.2105263158 2.5 17.144879657 6.2105263158 5 17.144879657 6.2105263158 +7.5 17.144879657 6.2105263158 10 17.144879657 6.2105263158 0 20 6.2105263158 +2.5 20 6.2105263158 5 20 6.2105263158 7.5 20 6.2105263158 +10 20 6.2105263158 0 5 11.421052632 2.5 5 11.421052632 +5 5 11.421052632 7.5 5 11.421052632 10 5 11.421052632 +0 5.8326451979 11.421052632 2.5 5.8326451979 11.421052632 5 5.8326451979 11.421052632 +7.5 5.8326451979 11.421052632 10 5.8326451979 11.421052632 0 6.8039500009 11.421052632 +2.5 6.8039500009 11.421052632 5 6.8039500009 11.421052632 7.5 6.8039500009 11.421052632 +10 6.8039500009 11.421052632 0 7.9370052598 11.421052632 2.5 7.9370052598 11.421052632 +5 7.9370052598 11.421052632 7.5 7.9370052598 11.421052632 10 7.9370052598 11.421052632 +0 9.2587471229 11.421052632 2.5 9.2587471229 11.421052632 5 9.2587471229 11.421052632 +7.5 9.2587471229 11.421052632 10 9.2587471229 11.421052632 0 10.800597389 11.421052632 +2.5 10.800597389 11.421052632 5 10.800597389 11.421052632 7.5 10.800597389 11.421052632 +10 10.800597389 11.421052632 0 12.599210499 11.421052632 2.5 12.599210499 11.421052632 +5 12.599210499 11.421052632 7.5 12.599210499 11.421052632 10 12.599210499 11.421052632 +0 14.697344923 11.421052632 2.5 14.697344923 11.421052632 5 14.697344923 11.421052632 +7.5 14.697344923 11.421052632 10 14.697344923 11.421052632 0 17.144879657 11.421052632 +2.5 17.144879657 11.421052632 5 17.144879657 11.421052632 7.5 17.144879657 11.421052632 +10 17.144879657 11.421052632 0 20 11.421052632 2.5 20 11.421052632 +5 20 11.421052632 7.5 20 11.421052632 10 20 11.421052632 +0 5 16.631578947 2.5 5 16.631578947 5 5 16.631578947 +7.5 5 16.631578947 10 5 16.631578947 0 5.8326451979 16.631578947 +2.5 5.8326451979 16.631578947 5 5.8326451979 16.631578947 7.5 5.8326451979 16.631578947 +10 5.8326451979 16.631578947 0 6.8039500009 16.631578947 2.5 6.8039500009 16.631578947 +5 6.8039500009 16.631578947 7.5 6.8039500009 16.631578947 10 6.8039500009 16.631578947 +0 7.9370052598 16.631578947 2.5 7.9370052598 16.631578947 5 7.9370052598 16.631578947 +7.5 7.9370052598 16.631578947 10 7.9370052598 16.631578947 0 9.2587471229 16.631578947 +2.5 9.2587471229 16.631578947 5 9.2587471229 16.631578947 7.5 9.2587471229 16.631578947 +10 9.2587471229 16.631578947 0 10.800597389 16.631578947 2.5 10.800597389 16.631578947 +5 10.800597389 16.631578947 7.5 10.800597389 16.631578947 10 10.800597389 16.631578947 +0 12.599210499 16.631578947 2.5 12.599210499 16.631578947 5 12.599210499 16.631578947 +7.5 12.599210499 16.631578947 10 12.599210499 16.631578947 0 14.697344923 16.631578947 +2.5 14.697344923 16.631578947 5 14.697344923 16.631578947 7.5 14.697344923 16.631578947 +10 14.697344923 16.631578947 0 17.144879657 16.631578947 2.5 17.144879657 16.631578947 +5 17.144879657 16.631578947 7.5 17.144879657 16.631578947 10 17.144879657 16.631578947 +0 20 16.631578947 2.5 20 16.631578947 5 20 16.631578947 +7.5 20 16.631578947 10 20 16.631578947 0 5 21.842105263 +2.5 5 21.842105263 5 5 21.842105263 7.5 5 21.842105263 +10 5 21.842105263 0 5.8326451979 21.842105263 2.5 5.8326451979 21.842105263 +5 5.8326451979 21.842105263 7.5 5.8326451979 21.842105263 10 5.8326451979 21.842105263 +0 6.8039500009 21.842105263 2.5 6.8039500009 21.842105263 5 6.8039500009 21.842105263 +7.5 6.8039500009 21.842105263 10 6.8039500009 21.842105263 0 7.9370052598 21.842105263 +2.5 7.9370052598 21.842105263 5 7.9370052598 21.842105263 7.5 7.9370052598 21.842105263 +10 7.9370052598 21.842105263 0 9.2587471229 21.842105263 2.5 9.2587471229 21.842105263 +5 9.2587471229 21.842105263 7.5 9.2587471229 21.842105263 10 9.2587471229 21.842105263 +0 10.800597389 21.842105263 2.5 10.800597389 21.842105263 5 10.800597389 21.842105263 +7.5 10.800597389 21.842105263 10 10.800597389 21.842105263 0 12.599210499 21.842105263 +2.5 12.599210499 21.842105263 5 12.599210499 21.842105263 7.5 12.599210499 21.842105263 +10 12.599210499 21.842105263 0 14.697344923 21.842105263 2.5 14.697344923 21.842105263 +5 14.697344923 21.842105263 7.5 14.697344923 21.842105263 10 14.697344923 21.842105263 +0 17.144879657 21.842105263 2.5 17.144879657 21.842105263 5 17.144879657 21.842105263 +7.5 17.144879657 21.842105263 10 17.144879657 21.842105263 0 20 21.842105263 +2.5 20 21.842105263 5 20 21.842105263 7.5 20 21.842105263 +10 20 21.842105263 0 5 27.052631579 2.5 5 27.052631579 +5 5 27.052631579 7.5 5 27.052631579 10 5 27.052631579 +0 5.8326451979 27.052631579 2.5 5.8326451979 27.052631579 5 5.8326451979 27.052631579 +7.5 5.8326451979 27.052631579 10 5.8326451979 27.052631579 0 6.8039500009 27.052631579 +2.5 6.8039500009 27.052631579 5 6.8039500009 27.052631579 7.5 6.8039500009 27.052631579 +10 6.8039500009 27.052631579 0 7.9370052598 27.052631579 2.5 7.9370052598 27.052631579 +5 7.9370052598 27.052631579 7.5 7.9370052598 27.052631579 10 7.9370052598 27.052631579 +0 9.2587471229 27.052631579 2.5 9.2587471229 27.052631579 5 9.2587471229 27.052631579 +7.5 9.2587471229 27.052631579 10 9.2587471229 27.052631579 0 10.800597389 27.052631579 +2.5 10.800597389 27.052631579 5 10.800597389 27.052631579 7.5 10.800597389 27.052631579 +10 10.800597389 27.052631579 0 12.599210499 27.052631579 2.5 12.599210499 27.052631579 +5 12.599210499 27.052631579 7.5 12.599210499 27.052631579 10 12.599210499 27.052631579 +0 14.697344923 27.052631579 2.5 14.697344923 27.052631579 5 14.697344923 27.052631579 +7.5 14.697344923 27.052631579 10 14.697344923 27.052631579 0 17.144879657 27.052631579 +2.5 17.144879657 27.052631579 5 17.144879657 27.052631579 7.5 17.144879657 27.052631579 +10 17.144879657 27.052631579 0 20 27.052631579 2.5 20 27.052631579 +5 20 27.052631579 7.5 20 27.052631579 10 20 27.052631579 +0 5 32.263157895 2.5 5 32.263157895 5 5 32.263157895 +7.5 5 32.263157895 10 5 32.263157895 0 5.8326451979 32.263157895 +2.5 5.8326451979 32.263157895 5 5.8326451979 32.263157895 7.5 5.8326451979 32.263157895 +10 5.8326451979 32.263157895 0 6.8039500009 32.263157895 2.5 6.8039500009 32.263157895 +5 6.8039500009 32.263157895 7.5 6.8039500009 32.263157895 10 6.8039500009 32.263157895 +0 7.9370052598 32.263157895 2.5 7.9370052598 32.263157895 5 7.9370052598 32.263157895 +7.5 7.9370052598 32.263157895 10 7.9370052598 32.263157895 0 9.2587471229 32.263157895 +2.5 9.2587471229 32.263157895 5 9.2587471229 32.263157895 7.5 9.2587471229 32.263157895 +10 9.2587471229 32.263157895 0 10.800597389 32.263157895 2.5 10.800597389 32.263157895 +5 10.800597389 32.263157895 7.5 10.800597389 32.263157895 10 10.800597389 32.263157895 +0 12.599210499 32.263157895 2.5 12.599210499 32.263157895 5 12.599210499 32.263157895 +7.5 12.599210499 32.263157895 10 12.599210499 32.263157895 0 14.697344923 32.263157895 +2.5 14.697344923 32.263157895 5 14.697344923 32.263157895 7.5 14.697344923 32.263157895 +10 14.697344923 32.263157895 0 17.144879657 32.263157895 2.5 17.144879657 32.263157895 +5 17.144879657 32.263157895 7.5 17.144879657 32.263157895 10 17.144879657 32.263157895 +0 20 32.263157895 2.5 20 32.263157895 5 20 32.263157895 +7.5 20 32.263157895 10 20 32.263157895 0 5 37.473684211 +2.5 5 37.473684211 5 5 37.473684211 7.5 5 37.473684211 +10 5 37.473684211 0 5.8326451979 37.473684211 2.5 5.8326451979 37.473684211 +5 5.8326451979 37.473684211 7.5 5.8326451979 37.473684211 10 5.8326451979 37.473684211 +0 6.8039500009 37.473684211 2.5 6.8039500009 37.473684211 5 6.8039500009 37.473684211 +7.5 6.8039500009 37.473684211 10 6.8039500009 37.473684211 0 7.9370052598 37.473684211 +2.5 7.9370052598 37.473684211 5 7.9370052598 37.473684211 7.5 7.9370052598 37.473684211 +10 7.9370052598 37.473684211 0 9.2587471229 37.473684211 2.5 9.2587471229 37.473684211 +5 9.2587471229 37.473684211 7.5 9.2587471229 37.473684211 10 9.2587471229 37.473684211 +0 10.800597389 37.473684211 2.5 10.800597389 37.473684211 5 10.800597389 37.473684211 +7.5 10.800597389 37.473684211 10 10.800597389 37.473684211 0 12.599210499 37.473684211 +2.5 12.599210499 37.473684211 5 12.599210499 37.473684211 7.5 12.599210499 37.473684211 +10 12.599210499 37.473684211 0 14.697344923 37.473684211 2.5 14.697344923 37.473684211 +5 14.697344923 37.473684211 7.5 14.697344923 37.473684211 10 14.697344923 37.473684211 +0 17.144879657 37.473684211 2.5 17.144879657 37.473684211 5 17.144879657 37.473684211 +7.5 17.144879657 37.473684211 10 17.144879657 37.473684211 0 20 37.473684211 +2.5 20 37.473684211 5 20 37.473684211 7.5 20 37.473684211 +10 20 37.473684211 0 5 42.684210526 2.5 5 42.684210526 +5 5 42.684210526 7.5 5 42.684210526 10 5 42.684210526 +0 5.8326451979 42.684210526 2.5 5.8326451979 42.684210526 5 5.8326451979 42.684210526 +7.5 5.8326451979 42.684210526 10 5.8326451979 42.684210526 0 6.8039500009 42.684210526 +2.5 6.8039500009 42.684210526 5 6.8039500009 42.684210526 7.5 6.8039500009 42.684210526 +10 6.8039500009 42.684210526 0 7.9370052598 42.684210526 2.5 7.9370052598 42.684210526 +5 7.9370052598 42.684210526 7.5 7.9370052598 42.684210526 10 7.9370052598 42.684210526 +0 9.2587471229 42.684210526 2.5 9.2587471229 42.684210526 5 9.2587471229 42.684210526 +7.5 9.2587471229 42.684210526 10 9.2587471229 42.684210526 0 10.800597389 42.684210526 +2.5 10.800597389 42.684210526 5 10.800597389 42.684210526 7.5 10.800597389 42.684210526 +10 10.800597389 42.684210526 0 12.599210499 42.684210526 2.5 12.599210499 42.684210526 +5 12.599210499 42.684210526 7.5 12.599210499 42.684210526 10 12.599210499 42.684210526 +0 14.697344923 42.684210526 2.5 14.697344923 42.684210526 5 14.697344923 42.684210526 +7.5 14.697344923 42.684210526 10 14.697344923 42.684210526 0 17.144879657 42.684210526 +2.5 17.144879657 42.684210526 5 17.144879657 42.684210526 7.5 17.144879657 42.684210526 +10 17.144879657 42.684210526 0 20 42.684210526 2.5 20 42.684210526 +5 20 42.684210526 7.5 20 42.684210526 10 20 42.684210526 +0 5 47.894736842 2.5 5 47.894736842 5 5 47.894736842 +7.5 5 47.894736842 10 5 47.894736842 0 5.8326451979 47.894736842 +2.5 5.8326451979 47.894736842 5 5.8326451979 47.894736842 7.5 5.8326451979 47.894736842 +10 5.8326451979 47.894736842 0 6.8039500009 47.894736842 2.5 6.8039500009 47.894736842 +5 6.8039500009 47.894736842 7.5 6.8039500009 47.894736842 10 6.8039500009 47.894736842 +0 7.9370052598 47.894736842 2.5 7.9370052598 47.894736842 5 7.9370052598 47.894736842 +7.5 7.9370052598 47.894736842 10 7.9370052598 47.894736842 0 9.2587471229 47.894736842 +2.5 9.2587471229 47.894736842 5 9.2587471229 47.894736842 7.5 9.2587471229 47.894736842 +10 9.2587471229 47.894736842 0 10.800597389 47.894736842 2.5 10.800597389 47.894736842 +5 10.800597389 47.894736842 7.5 10.800597389 47.894736842 10 10.800597389 47.894736842 +0 12.599210499 47.894736842 2.5 12.599210499 47.894736842 5 12.599210499 47.894736842 +7.5 12.599210499 47.894736842 10 12.599210499 47.894736842 0 14.697344923 47.894736842 +2.5 14.697344923 47.894736842 5 14.697344923 47.894736842 7.5 14.697344923 47.894736842 +10 14.697344923 47.894736842 0 17.144879657 47.894736842 2.5 17.144879657 47.894736842 +5 17.144879657 47.894736842 7.5 17.144879657 47.894736842 10 17.144879657 47.894736842 +0 20 47.894736842 2.5 20 47.894736842 5 20 47.894736842 +7.5 20 47.894736842 10 20 47.894736842 0 5 53.105263158 +2.5 5 53.105263158 5 5 53.105263158 7.5 5 53.105263158 +10 5 53.105263158 0 5.8326451979 53.105263158 2.5 5.8326451979 53.105263158 +5 5.8326451979 53.105263158 7.5 5.8326451979 53.105263158 10 5.8326451979 53.105263158 +0 6.8039500009 53.105263158 2.5 6.8039500009 53.105263158 5 6.8039500009 53.105263158 +7.5 6.8039500009 53.105263158 10 6.8039500009 53.105263158 0 7.9370052598 53.105263158 +2.5 7.9370052598 53.105263158 5 7.9370052598 53.105263158 7.5 7.9370052598 53.105263158 +10 7.9370052598 53.105263158 0 9.2587471229 53.105263158 2.5 9.2587471229 53.105263158 +5 9.2587471229 53.105263158 7.5 9.2587471229 53.105263158 10 9.2587471229 53.105263158 +0 10.800597389 53.105263158 2.5 10.800597389 53.105263158 5 10.800597389 53.105263158 +7.5 10.800597389 53.105263158 10 10.800597389 53.105263158 0 12.599210499 53.105263158 +2.5 12.599210499 53.105263158 5 12.599210499 53.105263158 7.5 12.599210499 53.105263158 +10 12.599210499 53.105263158 0 14.697344923 53.105263158 2.5 14.697344923 53.105263158 +5 14.697344923 53.105263158 7.5 14.697344923 53.105263158 10 14.697344923 53.105263158 +0 17.144879657 53.105263158 2.5 17.144879657 53.105263158 5 17.144879657 53.105263158 +7.5 17.144879657 53.105263158 10 17.144879657 53.105263158 0 20 53.105263158 +2.5 20 53.105263158 5 20 53.105263158 7.5 20 53.105263158 +10 20 53.105263158 0 5 58.315789474 2.5 5 58.315789474 +5 5 58.315789474 7.5 5 58.315789474 10 5 58.315789474 +0 5.8326451979 58.315789474 2.5 5.8326451979 58.315789474 5 5.8326451979 58.315789474 +7.5 5.8326451979 58.315789474 10 5.8326451979 58.315789474 0 6.8039500009 58.315789474 +2.5 6.8039500009 58.315789474 5 6.8039500009 58.315789474 7.5 6.8039500009 58.315789474 +10 6.8039500009 58.315789474 0 7.9370052598 58.315789474 2.5 7.9370052598 58.315789474 +5 7.9370052598 58.315789474 7.5 7.9370052598 58.315789474 10 7.9370052598 58.315789474 +0 9.2587471229 58.315789474 2.5 9.2587471229 58.315789474 5 9.2587471229 58.315789474 +7.5 9.2587471229 58.315789474 10 9.2587471229 58.315789474 0 10.800597389 58.315789474 +2.5 10.800597389 58.315789474 5 10.800597389 58.315789474 7.5 10.800597389 58.315789474 +10 10.800597389 58.315789474 0 12.599210499 58.315789474 2.5 12.599210499 58.315789474 +5 12.599210499 58.315789474 7.5 12.599210499 58.315789474 10 12.599210499 58.315789474 +0 14.697344923 58.315789474 2.5 14.697344923 58.315789474 5 14.697344923 58.315789474 +7.5 14.697344923 58.315789474 10 14.697344923 58.315789474 0 17.144879657 58.315789474 +2.5 17.144879657 58.315789474 5 17.144879657 58.315789474 7.5 17.144879657 58.315789474 +10 17.144879657 58.315789474 0 20 58.315789474 2.5 20 58.315789474 +5 20 58.315789474 7.5 20 58.315789474 10 20 58.315789474 +0 5 63.526315789 2.5 5 63.526315789 5 5 63.526315789 +7.5 5 63.526315789 10 5 63.526315789 0 5.8326451979 63.526315789 +2.5 5.8326451979 63.526315789 5 5.8326451979 63.526315789 7.5 5.8326451979 63.526315789 +10 5.8326451979 63.526315789 0 6.8039500009 63.526315789 2.5 6.8039500009 63.526315789 +5 6.8039500009 63.526315789 7.5 6.8039500009 63.526315789 10 6.8039500009 63.526315789 +0 7.9370052598 63.526315789 2.5 7.9370052598 63.526315789 5 7.9370052598 63.526315789 +7.5 7.9370052598 63.526315789 10 7.9370052598 63.526315789 0 9.2587471229 63.526315789 +2.5 9.2587471229 63.526315789 5 9.2587471229 63.526315789 7.5 9.2587471229 63.526315789 +10 9.2587471229 63.526315789 0 10.800597389 63.526315789 2.5 10.800597389 63.526315789 +5 10.800597389 63.526315789 7.5 10.800597389 63.526315789 10 10.800597389 63.526315789 +0 12.599210499 63.526315789 2.5 12.599210499 63.526315789 5 12.599210499 63.526315789 +7.5 12.599210499 63.526315789 10 12.599210499 63.526315789 0 14.697344923 63.526315789 +2.5 14.697344923 63.526315789 5 14.697344923 63.526315789 7.5 14.697344923 63.526315789 +10 14.697344923 63.526315789 0 17.144879657 63.526315789 2.5 17.144879657 63.526315789 +5 17.144879657 63.526315789 7.5 17.144879657 63.526315789 10 17.144879657 63.526315789 +0 20 63.526315789 2.5 20 63.526315789 5 20 63.526315789 +7.5 20 63.526315789 10 20 63.526315789 0 5 68.736842105 +2.5 5 68.736842105 5 5 68.736842105 7.5 5 68.736842105 +10 5 68.736842105 0 5.8326451979 68.736842105 2.5 5.8326451979 68.736842105 +5 5.8326451979 68.736842105 7.5 5.8326451979 68.736842105 10 5.8326451979 68.736842105 +0 6.8039500009 68.736842105 2.5 6.8039500009 68.736842105 5 6.8039500009 68.736842105 +7.5 6.8039500009 68.736842105 10 6.8039500009 68.736842105 0 7.9370052598 68.736842105 +2.5 7.9370052598 68.736842105 5 7.9370052598 68.736842105 7.5 7.9370052598 68.736842105 +10 7.9370052598 68.736842105 0 9.2587471229 68.736842105 2.5 9.2587471229 68.736842105 +5 9.2587471229 68.736842105 7.5 9.2587471229 68.736842105 10 9.2587471229 68.736842105 +0 10.800597389 68.736842105 2.5 10.800597389 68.736842105 5 10.800597389 68.736842105 +7.5 10.800597389 68.736842105 10 10.800597389 68.736842105 0 12.599210499 68.736842105 +2.5 12.599210499 68.736842105 5 12.599210499 68.736842105 7.5 12.599210499 68.736842105 +10 12.599210499 68.736842105 0 14.697344923 68.736842105 2.5 14.697344923 68.736842105 +5 14.697344923 68.736842105 7.5 14.697344923 68.736842105 10 14.697344923 68.736842105 +0 17.144879657 68.736842105 2.5 17.144879657 68.736842105 5 17.144879657 68.736842105 +7.5 17.144879657 68.736842105 10 17.144879657 68.736842105 0 20 68.736842105 +2.5 20 68.736842105 5 20 68.736842105 7.5 20 68.736842105 +10 20 68.736842105 0 5 73.947368421 2.5 5 73.947368421 +5 5 73.947368421 7.5 5 73.947368421 10 5 73.947368421 +0 5.8326451979 73.947368421 2.5 5.8326451979 73.947368421 5 5.8326451979 73.947368421 +7.5 5.8326451979 73.947368421 10 5.8326451979 73.947368421 0 6.8039500009 73.947368421 +2.5 6.8039500009 73.947368421 5 6.8039500009 73.947368421 7.5 6.8039500009 73.947368421 +10 6.8039500009 73.947368421 0 7.9370052598 73.947368421 2.5 7.9370052598 73.947368421 +5 7.9370052598 73.947368421 7.5 7.9370052598 73.947368421 10 7.9370052598 73.947368421 +0 9.2587471229 73.947368421 2.5 9.2587471229 73.947368421 5 9.2587471229 73.947368421 +7.5 9.2587471229 73.947368421 10 9.2587471229 73.947368421 0 10.800597389 73.947368421 +2.5 10.800597389 73.947368421 5 10.800597389 73.947368421 7.5 10.800597389 73.947368421 +10 10.800597389 73.947368421 0 12.599210499 73.947368421 2.5 12.599210499 73.947368421 +5 12.599210499 73.947368421 7.5 12.599210499 73.947368421 10 12.599210499 73.947368421 +0 14.697344923 73.947368421 2.5 14.697344923 73.947368421 5 14.697344923 73.947368421 +7.5 14.697344923 73.947368421 10 14.697344923 73.947368421 0 17.144879657 73.947368421 +2.5 17.144879657 73.947368421 5 17.144879657 73.947368421 7.5 17.144879657 73.947368421 +10 17.144879657 73.947368421 0 20 73.947368421 2.5 20 73.947368421 +5 20 73.947368421 7.5 20 73.947368421 10 20 73.947368421 +0 5 79.157894737 2.5 5 79.157894737 5 5 79.157894737 +7.5 5 79.157894737 10 5 79.157894737 0 5.8326451979 79.157894737 +2.5 5.8326451979 79.157894737 5 5.8326451979 79.157894737 7.5 5.8326451979 79.157894737 +10 5.8326451979 79.157894737 0 6.8039500009 79.157894737 2.5 6.8039500009 79.157894737 +5 6.8039500009 79.157894737 7.5 6.8039500009 79.157894737 10 6.8039500009 79.157894737 +0 7.9370052598 79.157894737 2.5 7.9370052598 79.157894737 5 7.9370052598 79.157894737 +7.5 7.9370052598 79.157894737 10 7.9370052598 79.157894737 0 9.2587471229 79.157894737 +2.5 9.2587471229 79.157894737 5 9.2587471229 79.157894737 7.5 9.2587471229 79.157894737 +10 9.2587471229 79.157894737 0 10.800597389 79.157894737 2.5 10.800597389 79.157894737 +5 10.800597389 79.157894737 7.5 10.800597389 79.157894737 10 10.800597389 79.157894737 +0 12.599210499 79.157894737 2.5 12.599210499 79.157894737 5 12.599210499 79.157894737 +7.5 12.599210499 79.157894737 10 12.599210499 79.157894737 0 14.697344923 79.157894737 +2.5 14.697344923 79.157894737 5 14.697344923 79.157894737 7.5 14.697344923 79.157894737 +10 14.697344923 79.157894737 0 17.144879657 79.157894737 2.5 17.144879657 79.157894737 +5 17.144879657 79.157894737 7.5 17.144879657 79.157894737 10 17.144879657 79.157894737 +0 20 79.157894737 2.5 20 79.157894737 5 20 79.157894737 +7.5 20 79.157894737 10 20 79.157894737 0 5 84.368421053 +2.5 5 84.368421053 5 5 84.368421053 7.5 5 84.368421053 +10 5 84.368421053 0 5.8326451979 84.368421053 2.5 5.8326451979 84.368421053 +5 5.8326451979 84.368421053 7.5 5.8326451979 84.368421053 10 5.8326451979 84.368421053 +0 6.8039500009 84.368421053 2.5 6.8039500009 84.368421053 5 6.8039500009 84.368421053 +7.5 6.8039500009 84.368421053 10 6.8039500009 84.368421053 0 7.9370052598 84.368421053 +2.5 7.9370052598 84.368421053 5 7.9370052598 84.368421053 7.5 7.9370052598 84.368421053 +10 7.9370052598 84.368421053 0 9.2587471229 84.368421053 2.5 9.2587471229 84.368421053 +5 9.2587471229 84.368421053 7.5 9.2587471229 84.368421053 10 9.2587471229 84.368421053 +0 10.800597389 84.368421053 2.5 10.800597389 84.368421053 5 10.800597389 84.368421053 +7.5 10.800597389 84.368421053 10 10.800597389 84.368421053 0 12.599210499 84.368421053 +2.5 12.599210499 84.368421053 5 12.599210499 84.368421053 7.5 12.599210499 84.368421053 +10 12.599210499 84.368421053 0 14.697344923 84.368421053 2.5 14.697344923 84.368421053 +5 14.697344923 84.368421053 7.5 14.697344923 84.368421053 10 14.697344923 84.368421053 +0 17.144879657 84.368421053 2.5 17.144879657 84.368421053 5 17.144879657 84.368421053 +7.5 17.144879657 84.368421053 10 17.144879657 84.368421053 0 20 84.368421053 +2.5 20 84.368421053 5 20 84.368421053 7.5 20 84.368421053 +10 20 84.368421053 0 5 89.578947368 2.5 5 89.578947368 +5 5 89.578947368 7.5 5 89.578947368 10 5 89.578947368 +0 5.8326451979 89.578947368 2.5 5.8326451979 89.578947368 5 5.8326451979 89.578947368 +7.5 5.8326451979 89.578947368 10 5.8326451979 89.578947368 0 6.8039500009 89.578947368 +2.5 6.8039500009 89.578947368 5 6.8039500009 89.578947368 7.5 6.8039500009 89.578947368 +10 6.8039500009 89.578947368 0 7.9370052598 89.578947368 2.5 7.9370052598 89.578947368 +5 7.9370052598 89.578947368 7.5 7.9370052598 89.578947368 10 7.9370052598 89.578947368 +0 9.2587471229 89.578947368 2.5 9.2587471229 89.578947368 5 9.2587471229 89.578947368 +7.5 9.2587471229 89.578947368 10 9.2587471229 89.578947368 0 10.800597389 89.578947368 +2.5 10.800597389 89.578947368 5 10.800597389 89.578947368 7.5 10.800597389 89.578947368 +10 10.800597389 89.578947368 0 12.599210499 89.578947368 2.5 12.599210499 89.578947368 +5 12.599210499 89.578947368 7.5 12.599210499 89.578947368 10 12.599210499 89.578947368 +0 14.697344923 89.578947368 2.5 14.697344923 89.578947368 5 14.697344923 89.578947368 +7.5 14.697344923 89.578947368 10 14.697344923 89.578947368 0 17.144879657 89.578947368 +2.5 17.144879657 89.578947368 5 17.144879657 89.578947368 7.5 17.144879657 89.578947368 +10 17.144879657 89.578947368 0 20 89.578947368 2.5 20 89.578947368 +5 20 89.578947368 7.5 20 89.578947368 10 20 89.578947368 +0 5 94.789473684 2.5 5 94.789473684 5 5 94.789473684 +7.5 5 94.789473684 10 5 94.789473684 0 5.8326451979 94.789473684 +2.5 5.8326451979 94.789473684 5 5.8326451979 94.789473684 7.5 5.8326451979 94.789473684 +10 5.8326451979 94.789473684 0 6.8039500009 94.789473684 2.5 6.8039500009 94.789473684 +5 6.8039500009 94.789473684 7.5 6.8039500009 94.789473684 10 6.8039500009 94.789473684 +0 7.9370052598 94.789473684 2.5 7.9370052598 94.789473684 5 7.9370052598 94.789473684 +7.5 7.9370052598 94.789473684 10 7.9370052598 94.789473684 0 9.2587471229 94.789473684 +2.5 9.2587471229 94.789473684 5 9.2587471229 94.789473684 7.5 9.2587471229 94.789473684 +10 9.2587471229 94.789473684 0 10.800597389 94.789473684 2.5 10.800597389 94.789473684 +5 10.800597389 94.789473684 7.5 10.800597389 94.789473684 10 10.800597389 94.789473684 +0 12.599210499 94.789473684 2.5 12.599210499 94.789473684 5 12.599210499 94.789473684 +7.5 12.599210499 94.789473684 10 12.599210499 94.789473684 0 14.697344923 94.789473684 +2.5 14.697344923 94.789473684 5 14.697344923 94.789473684 7.5 14.697344923 94.789473684 +10 14.697344923 94.789473684 0 17.144879657 94.789473684 2.5 17.144879657 94.789473684 +5 17.144879657 94.789473684 7.5 17.144879657 94.789473684 10 17.144879657 94.789473684 +0 20 94.789473684 2.5 20 94.789473684 5 20 94.789473684 +7.5 20 94.789473684 10 20 94.789473684 0 5 100 +2.5 5 100 5 5 100 7.5 5 100 +10 5 100 0 5.8326451979 100 2.5 5.8326451979 100 +5 5.8326451979 100 7.5 5.8326451979 100 10 5.8326451979 100 +0 6.8039500009 100 2.5 6.8039500009 100 5 6.8039500009 100 +7.5 6.8039500009 100 10 6.8039500009 100 0 7.9370052598 100 +2.5 7.9370052598 100 5 7.9370052598 100 7.5 7.9370052598 100 +10 7.9370052598 100 0 9.2587471229 100 2.5 9.2587471229 100 +5 9.2587471229 100 7.5 9.2587471229 100 10 9.2587471229 100 +0 10.800597389 100 2.5 10.800597389 100 5 10.800597389 100 +7.5 10.800597389 100 10 10.800597389 100 0 12.599210499 100 +2.5 12.599210499 100 5 12.599210499 100 7.5 12.599210499 100 +10 12.599210499 100 0 14.697344923 100 2.5 14.697344923 100 +5 14.697344923 100 7.5 14.697344923 100 10 14.697344923 100 +0 17.144879657 100 2.5 17.144879657 100 5 17.144879657 100 +7.5 17.144879657 100 10 17.144879657 100 0 20 100 +2.5 20 100 5 20 100 7.5 20 100 +10 20 100 diff --git a/tests/unit_tests/mesh_to_vtk/regular.vtk b/tests/unit_tests/mesh_to_vtk/regular.vtk new file mode 100644 index 0000000000..8f8aca5a62 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/regular.vtk @@ -0,0 +1,2394 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 11 21 31 +POINTS 7161 double +0 0 0 2 0 0 4 0 0 +6 0 0 8 0 0 10 0 0 +12 0 0 14 0 0 16 0 0 +18 0 0 20 0 0 0 2.5 0 +2 2.5 0 4 2.5 0 6 2.5 0 +8 2.5 0 10 2.5 0 12 2.5 0 +14 2.5 0 16 2.5 0 18 2.5 0 +20 2.5 0 0 5 0 2 5 0 +4 5 0 6 5 0 8 5 0 +10 5 0 12 5 0 14 5 0 +16 5 0 18 5 0 20 5 0 +0 7.5 0 2 7.5 0 4 7.5 0 +6 7.5 0 8 7.5 0 10 7.5 0 +12 7.5 0 14 7.5 0 16 7.5 0 +18 7.5 0 20 7.5 0 0 10 0 +2 10 0 4 10 0 6 10 0 +8 10 0 10 10 0 12 10 0 +14 10 0 16 10 0 18 10 0 +20 10 0 0 12.5 0 2 12.5 0 +4 12.5 0 6 12.5 0 8 12.5 0 +10 12.5 0 12 12.5 0 14 12.5 0 +16 12.5 0 18 12.5 0 20 12.5 0 +0 15 0 2 15 0 4 15 0 +6 15 0 8 15 0 10 15 0 +12 15 0 14 15 0 16 15 0 +18 15 0 20 15 0 0 17.5 0 +2 17.5 0 4 17.5 0 6 17.5 0 +8 17.5 0 10 17.5 0 12 17.5 0 +14 17.5 0 16 17.5 0 18 17.5 0 +20 17.5 0 0 20 0 2 20 0 +4 20 0 6 20 0 8 20 0 +10 20 0 12 20 0 14 20 0 +16 20 0 18 20 0 20 20 0 +0 22.5 0 2 22.5 0 4 22.5 0 +6 22.5 0 8 22.5 0 10 22.5 0 +12 22.5 0 14 22.5 0 16 22.5 0 +18 22.5 0 20 22.5 0 0 25 0 +2 25 0 4 25 0 6 25 0 +8 25 0 10 25 0 12 25 0 +14 25 0 16 25 0 18 25 0 +20 25 0 0 27.5 0 2 27.5 0 +4 27.5 0 6 27.5 0 8 27.5 0 +10 27.5 0 12 27.5 0 14 27.5 0 +16 27.5 0 18 27.5 0 20 27.5 0 +0 30 0 2 30 0 4 30 0 +6 30 0 8 30 0 10 30 0 +12 30 0 14 30 0 16 30 0 +18 30 0 20 30 0 0 32.5 0 +2 32.5 0 4 32.5 0 6 32.5 0 +8 32.5 0 10 32.5 0 12 32.5 0 +14 32.5 0 16 32.5 0 18 32.5 0 +20 32.5 0 0 35 0 2 35 0 +4 35 0 6 35 0 8 35 0 +10 35 0 12 35 0 14 35 0 +16 35 0 18 35 0 20 35 0 +0 37.5 0 2 37.5 0 4 37.5 0 +6 37.5 0 8 37.5 0 10 37.5 0 +12 37.5 0 14 37.5 0 16 37.5 0 +18 37.5 0 20 37.5 0 0 40 0 +2 40 0 4 40 0 6 40 0 +8 40 0 10 40 0 12 40 0 +14 40 0 16 40 0 18 40 0 +20 40 0 0 42.5 0 2 42.5 0 +4 42.5 0 6 42.5 0 8 42.5 0 +10 42.5 0 12 42.5 0 14 42.5 0 +16 42.5 0 18 42.5 0 20 42.5 0 +0 45 0 2 45 0 4 45 0 +6 45 0 8 45 0 10 45 0 +12 45 0 14 45 0 16 45 0 +18 45 0 20 45 0 0 47.5 0 +2 47.5 0 4 47.5 0 6 47.5 0 +8 47.5 0 10 47.5 0 12 47.5 0 +14 47.5 0 16 47.5 0 18 47.5 0 +20 47.5 0 0 50 0 2 50 0 +4 50 0 6 50 0 8 50 0 +10 50 0 12 50 0 14 50 0 +16 50 0 18 50 0 20 50 0 +0 0 1.6666666667 2 0 1.6666666667 4 0 1.6666666667 +6 0 1.6666666667 8 0 1.6666666667 10 0 1.6666666667 +12 0 1.6666666667 14 0 1.6666666667 16 0 1.6666666667 +18 0 1.6666666667 20 0 1.6666666667 0 2.5 1.6666666667 +2 2.5 1.6666666667 4 2.5 1.6666666667 6 2.5 1.6666666667 +8 2.5 1.6666666667 10 2.5 1.6666666667 12 2.5 1.6666666667 +14 2.5 1.6666666667 16 2.5 1.6666666667 18 2.5 1.6666666667 +20 2.5 1.6666666667 0 5 1.6666666667 2 5 1.6666666667 +4 5 1.6666666667 6 5 1.6666666667 8 5 1.6666666667 +10 5 1.6666666667 12 5 1.6666666667 14 5 1.6666666667 +16 5 1.6666666667 18 5 1.6666666667 20 5 1.6666666667 +0 7.5 1.6666666667 2 7.5 1.6666666667 4 7.5 1.6666666667 +6 7.5 1.6666666667 8 7.5 1.6666666667 10 7.5 1.6666666667 +12 7.5 1.6666666667 14 7.5 1.6666666667 16 7.5 1.6666666667 +18 7.5 1.6666666667 20 7.5 1.6666666667 0 10 1.6666666667 +2 10 1.6666666667 4 10 1.6666666667 6 10 1.6666666667 +8 10 1.6666666667 10 10 1.6666666667 12 10 1.6666666667 +14 10 1.6666666667 16 10 1.6666666667 18 10 1.6666666667 +20 10 1.6666666667 0 12.5 1.6666666667 2 12.5 1.6666666667 +4 12.5 1.6666666667 6 12.5 1.6666666667 8 12.5 1.6666666667 +10 12.5 1.6666666667 12 12.5 1.6666666667 14 12.5 1.6666666667 +16 12.5 1.6666666667 18 12.5 1.6666666667 20 12.5 1.6666666667 +0 15 1.6666666667 2 15 1.6666666667 4 15 1.6666666667 +6 15 1.6666666667 8 15 1.6666666667 10 15 1.6666666667 +12 15 1.6666666667 14 15 1.6666666667 16 15 1.6666666667 +18 15 1.6666666667 20 15 1.6666666667 0 17.5 1.6666666667 +2 17.5 1.6666666667 4 17.5 1.6666666667 6 17.5 1.6666666667 +8 17.5 1.6666666667 10 17.5 1.6666666667 12 17.5 1.6666666667 +14 17.5 1.6666666667 16 17.5 1.6666666667 18 17.5 1.6666666667 +20 17.5 1.6666666667 0 20 1.6666666667 2 20 1.6666666667 +4 20 1.6666666667 6 20 1.6666666667 8 20 1.6666666667 +10 20 1.6666666667 12 20 1.6666666667 14 20 1.6666666667 +16 20 1.6666666667 18 20 1.6666666667 20 20 1.6666666667 +0 22.5 1.6666666667 2 22.5 1.6666666667 4 22.5 1.6666666667 +6 22.5 1.6666666667 8 22.5 1.6666666667 10 22.5 1.6666666667 +12 22.5 1.6666666667 14 22.5 1.6666666667 16 22.5 1.6666666667 +18 22.5 1.6666666667 20 22.5 1.6666666667 0 25 1.6666666667 +2 25 1.6666666667 4 25 1.6666666667 6 25 1.6666666667 +8 25 1.6666666667 10 25 1.6666666667 12 25 1.6666666667 +14 25 1.6666666667 16 25 1.6666666667 18 25 1.6666666667 +20 25 1.6666666667 0 27.5 1.6666666667 2 27.5 1.6666666667 +4 27.5 1.6666666667 6 27.5 1.6666666667 8 27.5 1.6666666667 +10 27.5 1.6666666667 12 27.5 1.6666666667 14 27.5 1.6666666667 +16 27.5 1.6666666667 18 27.5 1.6666666667 20 27.5 1.6666666667 +0 30 1.6666666667 2 30 1.6666666667 4 30 1.6666666667 +6 30 1.6666666667 8 30 1.6666666667 10 30 1.6666666667 +12 30 1.6666666667 14 30 1.6666666667 16 30 1.6666666667 +18 30 1.6666666667 20 30 1.6666666667 0 32.5 1.6666666667 +2 32.5 1.6666666667 4 32.5 1.6666666667 6 32.5 1.6666666667 +8 32.5 1.6666666667 10 32.5 1.6666666667 12 32.5 1.6666666667 +14 32.5 1.6666666667 16 32.5 1.6666666667 18 32.5 1.6666666667 +20 32.5 1.6666666667 0 35 1.6666666667 2 35 1.6666666667 +4 35 1.6666666667 6 35 1.6666666667 8 35 1.6666666667 +10 35 1.6666666667 12 35 1.6666666667 14 35 1.6666666667 +16 35 1.6666666667 18 35 1.6666666667 20 35 1.6666666667 +0 37.5 1.6666666667 2 37.5 1.6666666667 4 37.5 1.6666666667 +6 37.5 1.6666666667 8 37.5 1.6666666667 10 37.5 1.6666666667 +12 37.5 1.6666666667 14 37.5 1.6666666667 16 37.5 1.6666666667 +18 37.5 1.6666666667 20 37.5 1.6666666667 0 40 1.6666666667 +2 40 1.6666666667 4 40 1.6666666667 6 40 1.6666666667 +8 40 1.6666666667 10 40 1.6666666667 12 40 1.6666666667 +14 40 1.6666666667 16 40 1.6666666667 18 40 1.6666666667 +20 40 1.6666666667 0 42.5 1.6666666667 2 42.5 1.6666666667 +4 42.5 1.6666666667 6 42.5 1.6666666667 8 42.5 1.6666666667 +10 42.5 1.6666666667 12 42.5 1.6666666667 14 42.5 1.6666666667 +16 42.5 1.6666666667 18 42.5 1.6666666667 20 42.5 1.6666666667 +0 45 1.6666666667 2 45 1.6666666667 4 45 1.6666666667 +6 45 1.6666666667 8 45 1.6666666667 10 45 1.6666666667 +12 45 1.6666666667 14 45 1.6666666667 16 45 1.6666666667 +18 45 1.6666666667 20 45 1.6666666667 0 47.5 1.6666666667 +2 47.5 1.6666666667 4 47.5 1.6666666667 6 47.5 1.6666666667 +8 47.5 1.6666666667 10 47.5 1.6666666667 12 47.5 1.6666666667 +14 47.5 1.6666666667 16 47.5 1.6666666667 18 47.5 1.6666666667 +20 47.5 1.6666666667 0 50 1.6666666667 2 50 1.6666666667 +4 50 1.6666666667 6 50 1.6666666667 8 50 1.6666666667 +10 50 1.6666666667 12 50 1.6666666667 14 50 1.6666666667 +16 50 1.6666666667 18 50 1.6666666667 20 50 1.6666666667 +0 0 3.3333333333 2 0 3.3333333333 4 0 3.3333333333 +6 0 3.3333333333 8 0 3.3333333333 10 0 3.3333333333 +12 0 3.3333333333 14 0 3.3333333333 16 0 3.3333333333 +18 0 3.3333333333 20 0 3.3333333333 0 2.5 3.3333333333 +2 2.5 3.3333333333 4 2.5 3.3333333333 6 2.5 3.3333333333 +8 2.5 3.3333333333 10 2.5 3.3333333333 12 2.5 3.3333333333 +14 2.5 3.3333333333 16 2.5 3.3333333333 18 2.5 3.3333333333 +20 2.5 3.3333333333 0 5 3.3333333333 2 5 3.3333333333 +4 5 3.3333333333 6 5 3.3333333333 8 5 3.3333333333 +10 5 3.3333333333 12 5 3.3333333333 14 5 3.3333333333 +16 5 3.3333333333 18 5 3.3333333333 20 5 3.3333333333 +0 7.5 3.3333333333 2 7.5 3.3333333333 4 7.5 3.3333333333 +6 7.5 3.3333333333 8 7.5 3.3333333333 10 7.5 3.3333333333 +12 7.5 3.3333333333 14 7.5 3.3333333333 16 7.5 3.3333333333 +18 7.5 3.3333333333 20 7.5 3.3333333333 0 10 3.3333333333 +2 10 3.3333333333 4 10 3.3333333333 6 10 3.3333333333 +8 10 3.3333333333 10 10 3.3333333333 12 10 3.3333333333 +14 10 3.3333333333 16 10 3.3333333333 18 10 3.3333333333 +20 10 3.3333333333 0 12.5 3.3333333333 2 12.5 3.3333333333 +4 12.5 3.3333333333 6 12.5 3.3333333333 8 12.5 3.3333333333 +10 12.5 3.3333333333 12 12.5 3.3333333333 14 12.5 3.3333333333 +16 12.5 3.3333333333 18 12.5 3.3333333333 20 12.5 3.3333333333 +0 15 3.3333333333 2 15 3.3333333333 4 15 3.3333333333 +6 15 3.3333333333 8 15 3.3333333333 10 15 3.3333333333 +12 15 3.3333333333 14 15 3.3333333333 16 15 3.3333333333 +18 15 3.3333333333 20 15 3.3333333333 0 17.5 3.3333333333 +2 17.5 3.3333333333 4 17.5 3.3333333333 6 17.5 3.3333333333 +8 17.5 3.3333333333 10 17.5 3.3333333333 12 17.5 3.3333333333 +14 17.5 3.3333333333 16 17.5 3.3333333333 18 17.5 3.3333333333 +20 17.5 3.3333333333 0 20 3.3333333333 2 20 3.3333333333 +4 20 3.3333333333 6 20 3.3333333333 8 20 3.3333333333 +10 20 3.3333333333 12 20 3.3333333333 14 20 3.3333333333 +16 20 3.3333333333 18 20 3.3333333333 20 20 3.3333333333 +0 22.5 3.3333333333 2 22.5 3.3333333333 4 22.5 3.3333333333 +6 22.5 3.3333333333 8 22.5 3.3333333333 10 22.5 3.3333333333 +12 22.5 3.3333333333 14 22.5 3.3333333333 16 22.5 3.3333333333 +18 22.5 3.3333333333 20 22.5 3.3333333333 0 25 3.3333333333 +2 25 3.3333333333 4 25 3.3333333333 6 25 3.3333333333 +8 25 3.3333333333 10 25 3.3333333333 12 25 3.3333333333 +14 25 3.3333333333 16 25 3.3333333333 18 25 3.3333333333 +20 25 3.3333333333 0 27.5 3.3333333333 2 27.5 3.3333333333 +4 27.5 3.3333333333 6 27.5 3.3333333333 8 27.5 3.3333333333 +10 27.5 3.3333333333 12 27.5 3.3333333333 14 27.5 3.3333333333 +16 27.5 3.3333333333 18 27.5 3.3333333333 20 27.5 3.3333333333 +0 30 3.3333333333 2 30 3.3333333333 4 30 3.3333333333 +6 30 3.3333333333 8 30 3.3333333333 10 30 3.3333333333 +12 30 3.3333333333 14 30 3.3333333333 16 30 3.3333333333 +18 30 3.3333333333 20 30 3.3333333333 0 32.5 3.3333333333 +2 32.5 3.3333333333 4 32.5 3.3333333333 6 32.5 3.3333333333 +8 32.5 3.3333333333 10 32.5 3.3333333333 12 32.5 3.3333333333 +14 32.5 3.3333333333 16 32.5 3.3333333333 18 32.5 3.3333333333 +20 32.5 3.3333333333 0 35 3.3333333333 2 35 3.3333333333 +4 35 3.3333333333 6 35 3.3333333333 8 35 3.3333333333 +10 35 3.3333333333 12 35 3.3333333333 14 35 3.3333333333 +16 35 3.3333333333 18 35 3.3333333333 20 35 3.3333333333 +0 37.5 3.3333333333 2 37.5 3.3333333333 4 37.5 3.3333333333 +6 37.5 3.3333333333 8 37.5 3.3333333333 10 37.5 3.3333333333 +12 37.5 3.3333333333 14 37.5 3.3333333333 16 37.5 3.3333333333 +18 37.5 3.3333333333 20 37.5 3.3333333333 0 40 3.3333333333 +2 40 3.3333333333 4 40 3.3333333333 6 40 3.3333333333 +8 40 3.3333333333 10 40 3.3333333333 12 40 3.3333333333 +14 40 3.3333333333 16 40 3.3333333333 18 40 3.3333333333 +20 40 3.3333333333 0 42.5 3.3333333333 2 42.5 3.3333333333 +4 42.5 3.3333333333 6 42.5 3.3333333333 8 42.5 3.3333333333 +10 42.5 3.3333333333 12 42.5 3.3333333333 14 42.5 3.3333333333 +16 42.5 3.3333333333 18 42.5 3.3333333333 20 42.5 3.3333333333 +0 45 3.3333333333 2 45 3.3333333333 4 45 3.3333333333 +6 45 3.3333333333 8 45 3.3333333333 10 45 3.3333333333 +12 45 3.3333333333 14 45 3.3333333333 16 45 3.3333333333 +18 45 3.3333333333 20 45 3.3333333333 0 47.5 3.3333333333 +2 47.5 3.3333333333 4 47.5 3.3333333333 6 47.5 3.3333333333 +8 47.5 3.3333333333 10 47.5 3.3333333333 12 47.5 3.3333333333 +14 47.5 3.3333333333 16 47.5 3.3333333333 18 47.5 3.3333333333 +20 47.5 3.3333333333 0 50 3.3333333333 2 50 3.3333333333 +4 50 3.3333333333 6 50 3.3333333333 8 50 3.3333333333 +10 50 3.3333333333 12 50 3.3333333333 14 50 3.3333333333 +16 50 3.3333333333 18 50 3.3333333333 20 50 3.3333333333 +0 0 5 2 0 5 4 0 5 +6 0 5 8 0 5 10 0 5 +12 0 5 14 0 5 16 0 5 +18 0 5 20 0 5 0 2.5 5 +2 2.5 5 4 2.5 5 6 2.5 5 +8 2.5 5 10 2.5 5 12 2.5 5 +14 2.5 5 16 2.5 5 18 2.5 5 +20 2.5 5 0 5 5 2 5 5 +4 5 5 6 5 5 8 5 5 +10 5 5 12 5 5 14 5 5 +16 5 5 18 5 5 20 5 5 +0 7.5 5 2 7.5 5 4 7.5 5 +6 7.5 5 8 7.5 5 10 7.5 5 +12 7.5 5 14 7.5 5 16 7.5 5 +18 7.5 5 20 7.5 5 0 10 5 +2 10 5 4 10 5 6 10 5 +8 10 5 10 10 5 12 10 5 +14 10 5 16 10 5 18 10 5 +20 10 5 0 12.5 5 2 12.5 5 +4 12.5 5 6 12.5 5 8 12.5 5 +10 12.5 5 12 12.5 5 14 12.5 5 +16 12.5 5 18 12.5 5 20 12.5 5 +0 15 5 2 15 5 4 15 5 +6 15 5 8 15 5 10 15 5 +12 15 5 14 15 5 16 15 5 +18 15 5 20 15 5 0 17.5 5 +2 17.5 5 4 17.5 5 6 17.5 5 +8 17.5 5 10 17.5 5 12 17.5 5 +14 17.5 5 16 17.5 5 18 17.5 5 +20 17.5 5 0 20 5 2 20 5 +4 20 5 6 20 5 8 20 5 +10 20 5 12 20 5 14 20 5 +16 20 5 18 20 5 20 20 5 +0 22.5 5 2 22.5 5 4 22.5 5 +6 22.5 5 8 22.5 5 10 22.5 5 +12 22.5 5 14 22.5 5 16 22.5 5 +18 22.5 5 20 22.5 5 0 25 5 +2 25 5 4 25 5 6 25 5 +8 25 5 10 25 5 12 25 5 +14 25 5 16 25 5 18 25 5 +20 25 5 0 27.5 5 2 27.5 5 +4 27.5 5 6 27.5 5 8 27.5 5 +10 27.5 5 12 27.5 5 14 27.5 5 +16 27.5 5 18 27.5 5 20 27.5 5 +0 30 5 2 30 5 4 30 5 +6 30 5 8 30 5 10 30 5 +12 30 5 14 30 5 16 30 5 +18 30 5 20 30 5 0 32.5 5 +2 32.5 5 4 32.5 5 6 32.5 5 +8 32.5 5 10 32.5 5 12 32.5 5 +14 32.5 5 16 32.5 5 18 32.5 5 +20 32.5 5 0 35 5 2 35 5 +4 35 5 6 35 5 8 35 5 +10 35 5 12 35 5 14 35 5 +16 35 5 18 35 5 20 35 5 +0 37.5 5 2 37.5 5 4 37.5 5 +6 37.5 5 8 37.5 5 10 37.5 5 +12 37.5 5 14 37.5 5 16 37.5 5 +18 37.5 5 20 37.5 5 0 40 5 +2 40 5 4 40 5 6 40 5 +8 40 5 10 40 5 12 40 5 +14 40 5 16 40 5 18 40 5 +20 40 5 0 42.5 5 2 42.5 5 +4 42.5 5 6 42.5 5 8 42.5 5 +10 42.5 5 12 42.5 5 14 42.5 5 +16 42.5 5 18 42.5 5 20 42.5 5 +0 45 5 2 45 5 4 45 5 +6 45 5 8 45 5 10 45 5 +12 45 5 14 45 5 16 45 5 +18 45 5 20 45 5 0 47.5 5 +2 47.5 5 4 47.5 5 6 47.5 5 +8 47.5 5 10 47.5 5 12 47.5 5 +14 47.5 5 16 47.5 5 18 47.5 5 +20 47.5 5 0 50 5 2 50 5 +4 50 5 6 50 5 8 50 5 +10 50 5 12 50 5 14 50 5 +16 50 5 18 50 5 20 50 5 +0 0 6.6666666667 2 0 6.6666666667 4 0 6.6666666667 +6 0 6.6666666667 8 0 6.6666666667 10 0 6.6666666667 +12 0 6.6666666667 14 0 6.6666666667 16 0 6.6666666667 +18 0 6.6666666667 20 0 6.6666666667 0 2.5 6.6666666667 +2 2.5 6.6666666667 4 2.5 6.6666666667 6 2.5 6.6666666667 +8 2.5 6.6666666667 10 2.5 6.6666666667 12 2.5 6.6666666667 +14 2.5 6.6666666667 16 2.5 6.6666666667 18 2.5 6.6666666667 +20 2.5 6.6666666667 0 5 6.6666666667 2 5 6.6666666667 +4 5 6.6666666667 6 5 6.6666666667 8 5 6.6666666667 +10 5 6.6666666667 12 5 6.6666666667 14 5 6.6666666667 +16 5 6.6666666667 18 5 6.6666666667 20 5 6.6666666667 +0 7.5 6.6666666667 2 7.5 6.6666666667 4 7.5 6.6666666667 +6 7.5 6.6666666667 8 7.5 6.6666666667 10 7.5 6.6666666667 +12 7.5 6.6666666667 14 7.5 6.6666666667 16 7.5 6.6666666667 +18 7.5 6.6666666667 20 7.5 6.6666666667 0 10 6.6666666667 +2 10 6.6666666667 4 10 6.6666666667 6 10 6.6666666667 +8 10 6.6666666667 10 10 6.6666666667 12 10 6.6666666667 +14 10 6.6666666667 16 10 6.6666666667 18 10 6.6666666667 +20 10 6.6666666667 0 12.5 6.6666666667 2 12.5 6.6666666667 +4 12.5 6.6666666667 6 12.5 6.6666666667 8 12.5 6.6666666667 +10 12.5 6.6666666667 12 12.5 6.6666666667 14 12.5 6.6666666667 +16 12.5 6.6666666667 18 12.5 6.6666666667 20 12.5 6.6666666667 +0 15 6.6666666667 2 15 6.6666666667 4 15 6.6666666667 +6 15 6.6666666667 8 15 6.6666666667 10 15 6.6666666667 +12 15 6.6666666667 14 15 6.6666666667 16 15 6.6666666667 +18 15 6.6666666667 20 15 6.6666666667 0 17.5 6.6666666667 +2 17.5 6.6666666667 4 17.5 6.6666666667 6 17.5 6.6666666667 +8 17.5 6.6666666667 10 17.5 6.6666666667 12 17.5 6.6666666667 +14 17.5 6.6666666667 16 17.5 6.6666666667 18 17.5 6.6666666667 +20 17.5 6.6666666667 0 20 6.6666666667 2 20 6.6666666667 +4 20 6.6666666667 6 20 6.6666666667 8 20 6.6666666667 +10 20 6.6666666667 12 20 6.6666666667 14 20 6.6666666667 +16 20 6.6666666667 18 20 6.6666666667 20 20 6.6666666667 +0 22.5 6.6666666667 2 22.5 6.6666666667 4 22.5 6.6666666667 +6 22.5 6.6666666667 8 22.5 6.6666666667 10 22.5 6.6666666667 +12 22.5 6.6666666667 14 22.5 6.6666666667 16 22.5 6.6666666667 +18 22.5 6.6666666667 20 22.5 6.6666666667 0 25 6.6666666667 +2 25 6.6666666667 4 25 6.6666666667 6 25 6.6666666667 +8 25 6.6666666667 10 25 6.6666666667 12 25 6.6666666667 +14 25 6.6666666667 16 25 6.6666666667 18 25 6.6666666667 +20 25 6.6666666667 0 27.5 6.6666666667 2 27.5 6.6666666667 +4 27.5 6.6666666667 6 27.5 6.6666666667 8 27.5 6.6666666667 +10 27.5 6.6666666667 12 27.5 6.6666666667 14 27.5 6.6666666667 +16 27.5 6.6666666667 18 27.5 6.6666666667 20 27.5 6.6666666667 +0 30 6.6666666667 2 30 6.6666666667 4 30 6.6666666667 +6 30 6.6666666667 8 30 6.6666666667 10 30 6.6666666667 +12 30 6.6666666667 14 30 6.6666666667 16 30 6.6666666667 +18 30 6.6666666667 20 30 6.6666666667 0 32.5 6.6666666667 +2 32.5 6.6666666667 4 32.5 6.6666666667 6 32.5 6.6666666667 +8 32.5 6.6666666667 10 32.5 6.6666666667 12 32.5 6.6666666667 +14 32.5 6.6666666667 16 32.5 6.6666666667 18 32.5 6.6666666667 +20 32.5 6.6666666667 0 35 6.6666666667 2 35 6.6666666667 +4 35 6.6666666667 6 35 6.6666666667 8 35 6.6666666667 +10 35 6.6666666667 12 35 6.6666666667 14 35 6.6666666667 +16 35 6.6666666667 18 35 6.6666666667 20 35 6.6666666667 +0 37.5 6.6666666667 2 37.5 6.6666666667 4 37.5 6.6666666667 +6 37.5 6.6666666667 8 37.5 6.6666666667 10 37.5 6.6666666667 +12 37.5 6.6666666667 14 37.5 6.6666666667 16 37.5 6.6666666667 +18 37.5 6.6666666667 20 37.5 6.6666666667 0 40 6.6666666667 +2 40 6.6666666667 4 40 6.6666666667 6 40 6.6666666667 +8 40 6.6666666667 10 40 6.6666666667 12 40 6.6666666667 +14 40 6.6666666667 16 40 6.6666666667 18 40 6.6666666667 +20 40 6.6666666667 0 42.5 6.6666666667 2 42.5 6.6666666667 +4 42.5 6.6666666667 6 42.5 6.6666666667 8 42.5 6.6666666667 +10 42.5 6.6666666667 12 42.5 6.6666666667 14 42.5 6.6666666667 +16 42.5 6.6666666667 18 42.5 6.6666666667 20 42.5 6.6666666667 +0 45 6.6666666667 2 45 6.6666666667 4 45 6.6666666667 +6 45 6.6666666667 8 45 6.6666666667 10 45 6.6666666667 +12 45 6.6666666667 14 45 6.6666666667 16 45 6.6666666667 +18 45 6.6666666667 20 45 6.6666666667 0 47.5 6.6666666667 +2 47.5 6.6666666667 4 47.5 6.6666666667 6 47.5 6.6666666667 +8 47.5 6.6666666667 10 47.5 6.6666666667 12 47.5 6.6666666667 +14 47.5 6.6666666667 16 47.5 6.6666666667 18 47.5 6.6666666667 +20 47.5 6.6666666667 0 50 6.6666666667 2 50 6.6666666667 +4 50 6.6666666667 6 50 6.6666666667 8 50 6.6666666667 +10 50 6.6666666667 12 50 6.6666666667 14 50 6.6666666667 +16 50 6.6666666667 18 50 6.6666666667 20 50 6.6666666667 +0 0 8.3333333333 2 0 8.3333333333 4 0 8.3333333333 +6 0 8.3333333333 8 0 8.3333333333 10 0 8.3333333333 +12 0 8.3333333333 14 0 8.3333333333 16 0 8.3333333333 +18 0 8.3333333333 20 0 8.3333333333 0 2.5 8.3333333333 +2 2.5 8.3333333333 4 2.5 8.3333333333 6 2.5 8.3333333333 +8 2.5 8.3333333333 10 2.5 8.3333333333 12 2.5 8.3333333333 +14 2.5 8.3333333333 16 2.5 8.3333333333 18 2.5 8.3333333333 +20 2.5 8.3333333333 0 5 8.3333333333 2 5 8.3333333333 +4 5 8.3333333333 6 5 8.3333333333 8 5 8.3333333333 +10 5 8.3333333333 12 5 8.3333333333 14 5 8.3333333333 +16 5 8.3333333333 18 5 8.3333333333 20 5 8.3333333333 +0 7.5 8.3333333333 2 7.5 8.3333333333 4 7.5 8.3333333333 +6 7.5 8.3333333333 8 7.5 8.3333333333 10 7.5 8.3333333333 +12 7.5 8.3333333333 14 7.5 8.3333333333 16 7.5 8.3333333333 +18 7.5 8.3333333333 20 7.5 8.3333333333 0 10 8.3333333333 +2 10 8.3333333333 4 10 8.3333333333 6 10 8.3333333333 +8 10 8.3333333333 10 10 8.3333333333 12 10 8.3333333333 +14 10 8.3333333333 16 10 8.3333333333 18 10 8.3333333333 +20 10 8.3333333333 0 12.5 8.3333333333 2 12.5 8.3333333333 +4 12.5 8.3333333333 6 12.5 8.3333333333 8 12.5 8.3333333333 +10 12.5 8.3333333333 12 12.5 8.3333333333 14 12.5 8.3333333333 +16 12.5 8.3333333333 18 12.5 8.3333333333 20 12.5 8.3333333333 +0 15 8.3333333333 2 15 8.3333333333 4 15 8.3333333333 +6 15 8.3333333333 8 15 8.3333333333 10 15 8.3333333333 +12 15 8.3333333333 14 15 8.3333333333 16 15 8.3333333333 +18 15 8.3333333333 20 15 8.3333333333 0 17.5 8.3333333333 +2 17.5 8.3333333333 4 17.5 8.3333333333 6 17.5 8.3333333333 +8 17.5 8.3333333333 10 17.5 8.3333333333 12 17.5 8.3333333333 +14 17.5 8.3333333333 16 17.5 8.3333333333 18 17.5 8.3333333333 +20 17.5 8.3333333333 0 20 8.3333333333 2 20 8.3333333333 +4 20 8.3333333333 6 20 8.3333333333 8 20 8.3333333333 +10 20 8.3333333333 12 20 8.3333333333 14 20 8.3333333333 +16 20 8.3333333333 18 20 8.3333333333 20 20 8.3333333333 +0 22.5 8.3333333333 2 22.5 8.3333333333 4 22.5 8.3333333333 +6 22.5 8.3333333333 8 22.5 8.3333333333 10 22.5 8.3333333333 +12 22.5 8.3333333333 14 22.5 8.3333333333 16 22.5 8.3333333333 +18 22.5 8.3333333333 20 22.5 8.3333333333 0 25 8.3333333333 +2 25 8.3333333333 4 25 8.3333333333 6 25 8.3333333333 +8 25 8.3333333333 10 25 8.3333333333 12 25 8.3333333333 +14 25 8.3333333333 16 25 8.3333333333 18 25 8.3333333333 +20 25 8.3333333333 0 27.5 8.3333333333 2 27.5 8.3333333333 +4 27.5 8.3333333333 6 27.5 8.3333333333 8 27.5 8.3333333333 +10 27.5 8.3333333333 12 27.5 8.3333333333 14 27.5 8.3333333333 +16 27.5 8.3333333333 18 27.5 8.3333333333 20 27.5 8.3333333333 +0 30 8.3333333333 2 30 8.3333333333 4 30 8.3333333333 +6 30 8.3333333333 8 30 8.3333333333 10 30 8.3333333333 +12 30 8.3333333333 14 30 8.3333333333 16 30 8.3333333333 +18 30 8.3333333333 20 30 8.3333333333 0 32.5 8.3333333333 +2 32.5 8.3333333333 4 32.5 8.3333333333 6 32.5 8.3333333333 +8 32.5 8.3333333333 10 32.5 8.3333333333 12 32.5 8.3333333333 +14 32.5 8.3333333333 16 32.5 8.3333333333 18 32.5 8.3333333333 +20 32.5 8.3333333333 0 35 8.3333333333 2 35 8.3333333333 +4 35 8.3333333333 6 35 8.3333333333 8 35 8.3333333333 +10 35 8.3333333333 12 35 8.3333333333 14 35 8.3333333333 +16 35 8.3333333333 18 35 8.3333333333 20 35 8.3333333333 +0 37.5 8.3333333333 2 37.5 8.3333333333 4 37.5 8.3333333333 +6 37.5 8.3333333333 8 37.5 8.3333333333 10 37.5 8.3333333333 +12 37.5 8.3333333333 14 37.5 8.3333333333 16 37.5 8.3333333333 +18 37.5 8.3333333333 20 37.5 8.3333333333 0 40 8.3333333333 +2 40 8.3333333333 4 40 8.3333333333 6 40 8.3333333333 +8 40 8.3333333333 10 40 8.3333333333 12 40 8.3333333333 +14 40 8.3333333333 16 40 8.3333333333 18 40 8.3333333333 +20 40 8.3333333333 0 42.5 8.3333333333 2 42.5 8.3333333333 +4 42.5 8.3333333333 6 42.5 8.3333333333 8 42.5 8.3333333333 +10 42.5 8.3333333333 12 42.5 8.3333333333 14 42.5 8.3333333333 +16 42.5 8.3333333333 18 42.5 8.3333333333 20 42.5 8.3333333333 +0 45 8.3333333333 2 45 8.3333333333 4 45 8.3333333333 +6 45 8.3333333333 8 45 8.3333333333 10 45 8.3333333333 +12 45 8.3333333333 14 45 8.3333333333 16 45 8.3333333333 +18 45 8.3333333333 20 45 8.3333333333 0 47.5 8.3333333333 +2 47.5 8.3333333333 4 47.5 8.3333333333 6 47.5 8.3333333333 +8 47.5 8.3333333333 10 47.5 8.3333333333 12 47.5 8.3333333333 +14 47.5 8.3333333333 16 47.5 8.3333333333 18 47.5 8.3333333333 +20 47.5 8.3333333333 0 50 8.3333333333 2 50 8.3333333333 +4 50 8.3333333333 6 50 8.3333333333 8 50 8.3333333333 +10 50 8.3333333333 12 50 8.3333333333 14 50 8.3333333333 +16 50 8.3333333333 18 50 8.3333333333 20 50 8.3333333333 +0 0 10 2 0 10 4 0 10 +6 0 10 8 0 10 10 0 10 +12 0 10 14 0 10 16 0 10 +18 0 10 20 0 10 0 2.5 10 +2 2.5 10 4 2.5 10 6 2.5 10 +8 2.5 10 10 2.5 10 12 2.5 10 +14 2.5 10 16 2.5 10 18 2.5 10 +20 2.5 10 0 5 10 2 5 10 +4 5 10 6 5 10 8 5 10 +10 5 10 12 5 10 14 5 10 +16 5 10 18 5 10 20 5 10 +0 7.5 10 2 7.5 10 4 7.5 10 +6 7.5 10 8 7.5 10 10 7.5 10 +12 7.5 10 14 7.5 10 16 7.5 10 +18 7.5 10 20 7.5 10 0 10 10 +2 10 10 4 10 10 6 10 10 +8 10 10 10 10 10 12 10 10 +14 10 10 16 10 10 18 10 10 +20 10 10 0 12.5 10 2 12.5 10 +4 12.5 10 6 12.5 10 8 12.5 10 +10 12.5 10 12 12.5 10 14 12.5 10 +16 12.5 10 18 12.5 10 20 12.5 10 +0 15 10 2 15 10 4 15 10 +6 15 10 8 15 10 10 15 10 +12 15 10 14 15 10 16 15 10 +18 15 10 20 15 10 0 17.5 10 +2 17.5 10 4 17.5 10 6 17.5 10 +8 17.5 10 10 17.5 10 12 17.5 10 +14 17.5 10 16 17.5 10 18 17.5 10 +20 17.5 10 0 20 10 2 20 10 +4 20 10 6 20 10 8 20 10 +10 20 10 12 20 10 14 20 10 +16 20 10 18 20 10 20 20 10 +0 22.5 10 2 22.5 10 4 22.5 10 +6 22.5 10 8 22.5 10 10 22.5 10 +12 22.5 10 14 22.5 10 16 22.5 10 +18 22.5 10 20 22.5 10 0 25 10 +2 25 10 4 25 10 6 25 10 +8 25 10 10 25 10 12 25 10 +14 25 10 16 25 10 18 25 10 +20 25 10 0 27.5 10 2 27.5 10 +4 27.5 10 6 27.5 10 8 27.5 10 +10 27.5 10 12 27.5 10 14 27.5 10 +16 27.5 10 18 27.5 10 20 27.5 10 +0 30 10 2 30 10 4 30 10 +6 30 10 8 30 10 10 30 10 +12 30 10 14 30 10 16 30 10 +18 30 10 20 30 10 0 32.5 10 +2 32.5 10 4 32.5 10 6 32.5 10 +8 32.5 10 10 32.5 10 12 32.5 10 +14 32.5 10 16 32.5 10 18 32.5 10 +20 32.5 10 0 35 10 2 35 10 +4 35 10 6 35 10 8 35 10 +10 35 10 12 35 10 14 35 10 +16 35 10 18 35 10 20 35 10 +0 37.5 10 2 37.5 10 4 37.5 10 +6 37.5 10 8 37.5 10 10 37.5 10 +12 37.5 10 14 37.5 10 16 37.5 10 +18 37.5 10 20 37.5 10 0 40 10 +2 40 10 4 40 10 6 40 10 +8 40 10 10 40 10 12 40 10 +14 40 10 16 40 10 18 40 10 +20 40 10 0 42.5 10 2 42.5 10 +4 42.5 10 6 42.5 10 8 42.5 10 +10 42.5 10 12 42.5 10 14 42.5 10 +16 42.5 10 18 42.5 10 20 42.5 10 +0 45 10 2 45 10 4 45 10 +6 45 10 8 45 10 10 45 10 +12 45 10 14 45 10 16 45 10 +18 45 10 20 45 10 0 47.5 10 +2 47.5 10 4 47.5 10 6 47.5 10 +8 47.5 10 10 47.5 10 12 47.5 10 +14 47.5 10 16 47.5 10 18 47.5 10 +20 47.5 10 0 50 10 2 50 10 +4 50 10 6 50 10 8 50 10 +10 50 10 12 50 10 14 50 10 +16 50 10 18 50 10 20 50 10 +0 0 11.666666667 2 0 11.666666667 4 0 11.666666667 +6 0 11.666666667 8 0 11.666666667 10 0 11.666666667 +12 0 11.666666667 14 0 11.666666667 16 0 11.666666667 +18 0 11.666666667 20 0 11.666666667 0 2.5 11.666666667 +2 2.5 11.666666667 4 2.5 11.666666667 6 2.5 11.666666667 +8 2.5 11.666666667 10 2.5 11.666666667 12 2.5 11.666666667 +14 2.5 11.666666667 16 2.5 11.666666667 18 2.5 11.666666667 +20 2.5 11.666666667 0 5 11.666666667 2 5 11.666666667 +4 5 11.666666667 6 5 11.666666667 8 5 11.666666667 +10 5 11.666666667 12 5 11.666666667 14 5 11.666666667 +16 5 11.666666667 18 5 11.666666667 20 5 11.666666667 +0 7.5 11.666666667 2 7.5 11.666666667 4 7.5 11.666666667 +6 7.5 11.666666667 8 7.5 11.666666667 10 7.5 11.666666667 +12 7.5 11.666666667 14 7.5 11.666666667 16 7.5 11.666666667 +18 7.5 11.666666667 20 7.5 11.666666667 0 10 11.666666667 +2 10 11.666666667 4 10 11.666666667 6 10 11.666666667 +8 10 11.666666667 10 10 11.666666667 12 10 11.666666667 +14 10 11.666666667 16 10 11.666666667 18 10 11.666666667 +20 10 11.666666667 0 12.5 11.666666667 2 12.5 11.666666667 +4 12.5 11.666666667 6 12.5 11.666666667 8 12.5 11.666666667 +10 12.5 11.666666667 12 12.5 11.666666667 14 12.5 11.666666667 +16 12.5 11.666666667 18 12.5 11.666666667 20 12.5 11.666666667 +0 15 11.666666667 2 15 11.666666667 4 15 11.666666667 +6 15 11.666666667 8 15 11.666666667 10 15 11.666666667 +12 15 11.666666667 14 15 11.666666667 16 15 11.666666667 +18 15 11.666666667 20 15 11.666666667 0 17.5 11.666666667 +2 17.5 11.666666667 4 17.5 11.666666667 6 17.5 11.666666667 +8 17.5 11.666666667 10 17.5 11.666666667 12 17.5 11.666666667 +14 17.5 11.666666667 16 17.5 11.666666667 18 17.5 11.666666667 +20 17.5 11.666666667 0 20 11.666666667 2 20 11.666666667 +4 20 11.666666667 6 20 11.666666667 8 20 11.666666667 +10 20 11.666666667 12 20 11.666666667 14 20 11.666666667 +16 20 11.666666667 18 20 11.666666667 20 20 11.666666667 +0 22.5 11.666666667 2 22.5 11.666666667 4 22.5 11.666666667 +6 22.5 11.666666667 8 22.5 11.666666667 10 22.5 11.666666667 +12 22.5 11.666666667 14 22.5 11.666666667 16 22.5 11.666666667 +18 22.5 11.666666667 20 22.5 11.666666667 0 25 11.666666667 +2 25 11.666666667 4 25 11.666666667 6 25 11.666666667 +8 25 11.666666667 10 25 11.666666667 12 25 11.666666667 +14 25 11.666666667 16 25 11.666666667 18 25 11.666666667 +20 25 11.666666667 0 27.5 11.666666667 2 27.5 11.666666667 +4 27.5 11.666666667 6 27.5 11.666666667 8 27.5 11.666666667 +10 27.5 11.666666667 12 27.5 11.666666667 14 27.5 11.666666667 +16 27.5 11.666666667 18 27.5 11.666666667 20 27.5 11.666666667 +0 30 11.666666667 2 30 11.666666667 4 30 11.666666667 +6 30 11.666666667 8 30 11.666666667 10 30 11.666666667 +12 30 11.666666667 14 30 11.666666667 16 30 11.666666667 +18 30 11.666666667 20 30 11.666666667 0 32.5 11.666666667 +2 32.5 11.666666667 4 32.5 11.666666667 6 32.5 11.666666667 +8 32.5 11.666666667 10 32.5 11.666666667 12 32.5 11.666666667 +14 32.5 11.666666667 16 32.5 11.666666667 18 32.5 11.666666667 +20 32.5 11.666666667 0 35 11.666666667 2 35 11.666666667 +4 35 11.666666667 6 35 11.666666667 8 35 11.666666667 +10 35 11.666666667 12 35 11.666666667 14 35 11.666666667 +16 35 11.666666667 18 35 11.666666667 20 35 11.666666667 +0 37.5 11.666666667 2 37.5 11.666666667 4 37.5 11.666666667 +6 37.5 11.666666667 8 37.5 11.666666667 10 37.5 11.666666667 +12 37.5 11.666666667 14 37.5 11.666666667 16 37.5 11.666666667 +18 37.5 11.666666667 20 37.5 11.666666667 0 40 11.666666667 +2 40 11.666666667 4 40 11.666666667 6 40 11.666666667 +8 40 11.666666667 10 40 11.666666667 12 40 11.666666667 +14 40 11.666666667 16 40 11.666666667 18 40 11.666666667 +20 40 11.666666667 0 42.5 11.666666667 2 42.5 11.666666667 +4 42.5 11.666666667 6 42.5 11.666666667 8 42.5 11.666666667 +10 42.5 11.666666667 12 42.5 11.666666667 14 42.5 11.666666667 +16 42.5 11.666666667 18 42.5 11.666666667 20 42.5 11.666666667 +0 45 11.666666667 2 45 11.666666667 4 45 11.666666667 +6 45 11.666666667 8 45 11.666666667 10 45 11.666666667 +12 45 11.666666667 14 45 11.666666667 16 45 11.666666667 +18 45 11.666666667 20 45 11.666666667 0 47.5 11.666666667 +2 47.5 11.666666667 4 47.5 11.666666667 6 47.5 11.666666667 +8 47.5 11.666666667 10 47.5 11.666666667 12 47.5 11.666666667 +14 47.5 11.666666667 16 47.5 11.666666667 18 47.5 11.666666667 +20 47.5 11.666666667 0 50 11.666666667 2 50 11.666666667 +4 50 11.666666667 6 50 11.666666667 8 50 11.666666667 +10 50 11.666666667 12 50 11.666666667 14 50 11.666666667 +16 50 11.666666667 18 50 11.666666667 20 50 11.666666667 +0 0 13.333333333 2 0 13.333333333 4 0 13.333333333 +6 0 13.333333333 8 0 13.333333333 10 0 13.333333333 +12 0 13.333333333 14 0 13.333333333 16 0 13.333333333 +18 0 13.333333333 20 0 13.333333333 0 2.5 13.333333333 +2 2.5 13.333333333 4 2.5 13.333333333 6 2.5 13.333333333 +8 2.5 13.333333333 10 2.5 13.333333333 12 2.5 13.333333333 +14 2.5 13.333333333 16 2.5 13.333333333 18 2.5 13.333333333 +20 2.5 13.333333333 0 5 13.333333333 2 5 13.333333333 +4 5 13.333333333 6 5 13.333333333 8 5 13.333333333 +10 5 13.333333333 12 5 13.333333333 14 5 13.333333333 +16 5 13.333333333 18 5 13.333333333 20 5 13.333333333 +0 7.5 13.333333333 2 7.5 13.333333333 4 7.5 13.333333333 +6 7.5 13.333333333 8 7.5 13.333333333 10 7.5 13.333333333 +12 7.5 13.333333333 14 7.5 13.333333333 16 7.5 13.333333333 +18 7.5 13.333333333 20 7.5 13.333333333 0 10 13.333333333 +2 10 13.333333333 4 10 13.333333333 6 10 13.333333333 +8 10 13.333333333 10 10 13.333333333 12 10 13.333333333 +14 10 13.333333333 16 10 13.333333333 18 10 13.333333333 +20 10 13.333333333 0 12.5 13.333333333 2 12.5 13.333333333 +4 12.5 13.333333333 6 12.5 13.333333333 8 12.5 13.333333333 +10 12.5 13.333333333 12 12.5 13.333333333 14 12.5 13.333333333 +16 12.5 13.333333333 18 12.5 13.333333333 20 12.5 13.333333333 +0 15 13.333333333 2 15 13.333333333 4 15 13.333333333 +6 15 13.333333333 8 15 13.333333333 10 15 13.333333333 +12 15 13.333333333 14 15 13.333333333 16 15 13.333333333 +18 15 13.333333333 20 15 13.333333333 0 17.5 13.333333333 +2 17.5 13.333333333 4 17.5 13.333333333 6 17.5 13.333333333 +8 17.5 13.333333333 10 17.5 13.333333333 12 17.5 13.333333333 +14 17.5 13.333333333 16 17.5 13.333333333 18 17.5 13.333333333 +20 17.5 13.333333333 0 20 13.333333333 2 20 13.333333333 +4 20 13.333333333 6 20 13.333333333 8 20 13.333333333 +10 20 13.333333333 12 20 13.333333333 14 20 13.333333333 +16 20 13.333333333 18 20 13.333333333 20 20 13.333333333 +0 22.5 13.333333333 2 22.5 13.333333333 4 22.5 13.333333333 +6 22.5 13.333333333 8 22.5 13.333333333 10 22.5 13.333333333 +12 22.5 13.333333333 14 22.5 13.333333333 16 22.5 13.333333333 +18 22.5 13.333333333 20 22.5 13.333333333 0 25 13.333333333 +2 25 13.333333333 4 25 13.333333333 6 25 13.333333333 +8 25 13.333333333 10 25 13.333333333 12 25 13.333333333 +14 25 13.333333333 16 25 13.333333333 18 25 13.333333333 +20 25 13.333333333 0 27.5 13.333333333 2 27.5 13.333333333 +4 27.5 13.333333333 6 27.5 13.333333333 8 27.5 13.333333333 +10 27.5 13.333333333 12 27.5 13.333333333 14 27.5 13.333333333 +16 27.5 13.333333333 18 27.5 13.333333333 20 27.5 13.333333333 +0 30 13.333333333 2 30 13.333333333 4 30 13.333333333 +6 30 13.333333333 8 30 13.333333333 10 30 13.333333333 +12 30 13.333333333 14 30 13.333333333 16 30 13.333333333 +18 30 13.333333333 20 30 13.333333333 0 32.5 13.333333333 +2 32.5 13.333333333 4 32.5 13.333333333 6 32.5 13.333333333 +8 32.5 13.333333333 10 32.5 13.333333333 12 32.5 13.333333333 +14 32.5 13.333333333 16 32.5 13.333333333 18 32.5 13.333333333 +20 32.5 13.333333333 0 35 13.333333333 2 35 13.333333333 +4 35 13.333333333 6 35 13.333333333 8 35 13.333333333 +10 35 13.333333333 12 35 13.333333333 14 35 13.333333333 +16 35 13.333333333 18 35 13.333333333 20 35 13.333333333 +0 37.5 13.333333333 2 37.5 13.333333333 4 37.5 13.333333333 +6 37.5 13.333333333 8 37.5 13.333333333 10 37.5 13.333333333 +12 37.5 13.333333333 14 37.5 13.333333333 16 37.5 13.333333333 +18 37.5 13.333333333 20 37.5 13.333333333 0 40 13.333333333 +2 40 13.333333333 4 40 13.333333333 6 40 13.333333333 +8 40 13.333333333 10 40 13.333333333 12 40 13.333333333 +14 40 13.333333333 16 40 13.333333333 18 40 13.333333333 +20 40 13.333333333 0 42.5 13.333333333 2 42.5 13.333333333 +4 42.5 13.333333333 6 42.5 13.333333333 8 42.5 13.333333333 +10 42.5 13.333333333 12 42.5 13.333333333 14 42.5 13.333333333 +16 42.5 13.333333333 18 42.5 13.333333333 20 42.5 13.333333333 +0 45 13.333333333 2 45 13.333333333 4 45 13.333333333 +6 45 13.333333333 8 45 13.333333333 10 45 13.333333333 +12 45 13.333333333 14 45 13.333333333 16 45 13.333333333 +18 45 13.333333333 20 45 13.333333333 0 47.5 13.333333333 +2 47.5 13.333333333 4 47.5 13.333333333 6 47.5 13.333333333 +8 47.5 13.333333333 10 47.5 13.333333333 12 47.5 13.333333333 +14 47.5 13.333333333 16 47.5 13.333333333 18 47.5 13.333333333 +20 47.5 13.333333333 0 50 13.333333333 2 50 13.333333333 +4 50 13.333333333 6 50 13.333333333 8 50 13.333333333 +10 50 13.333333333 12 50 13.333333333 14 50 13.333333333 +16 50 13.333333333 18 50 13.333333333 20 50 13.333333333 +0 0 15 2 0 15 4 0 15 +6 0 15 8 0 15 10 0 15 +12 0 15 14 0 15 16 0 15 +18 0 15 20 0 15 0 2.5 15 +2 2.5 15 4 2.5 15 6 2.5 15 +8 2.5 15 10 2.5 15 12 2.5 15 +14 2.5 15 16 2.5 15 18 2.5 15 +20 2.5 15 0 5 15 2 5 15 +4 5 15 6 5 15 8 5 15 +10 5 15 12 5 15 14 5 15 +16 5 15 18 5 15 20 5 15 +0 7.5 15 2 7.5 15 4 7.5 15 +6 7.5 15 8 7.5 15 10 7.5 15 +12 7.5 15 14 7.5 15 16 7.5 15 +18 7.5 15 20 7.5 15 0 10 15 +2 10 15 4 10 15 6 10 15 +8 10 15 10 10 15 12 10 15 +14 10 15 16 10 15 18 10 15 +20 10 15 0 12.5 15 2 12.5 15 +4 12.5 15 6 12.5 15 8 12.5 15 +10 12.5 15 12 12.5 15 14 12.5 15 +16 12.5 15 18 12.5 15 20 12.5 15 +0 15 15 2 15 15 4 15 15 +6 15 15 8 15 15 10 15 15 +12 15 15 14 15 15 16 15 15 +18 15 15 20 15 15 0 17.5 15 +2 17.5 15 4 17.5 15 6 17.5 15 +8 17.5 15 10 17.5 15 12 17.5 15 +14 17.5 15 16 17.5 15 18 17.5 15 +20 17.5 15 0 20 15 2 20 15 +4 20 15 6 20 15 8 20 15 +10 20 15 12 20 15 14 20 15 +16 20 15 18 20 15 20 20 15 +0 22.5 15 2 22.5 15 4 22.5 15 +6 22.5 15 8 22.5 15 10 22.5 15 +12 22.5 15 14 22.5 15 16 22.5 15 +18 22.5 15 20 22.5 15 0 25 15 +2 25 15 4 25 15 6 25 15 +8 25 15 10 25 15 12 25 15 +14 25 15 16 25 15 18 25 15 +20 25 15 0 27.5 15 2 27.5 15 +4 27.5 15 6 27.5 15 8 27.5 15 +10 27.5 15 12 27.5 15 14 27.5 15 +16 27.5 15 18 27.5 15 20 27.5 15 +0 30 15 2 30 15 4 30 15 +6 30 15 8 30 15 10 30 15 +12 30 15 14 30 15 16 30 15 +18 30 15 20 30 15 0 32.5 15 +2 32.5 15 4 32.5 15 6 32.5 15 +8 32.5 15 10 32.5 15 12 32.5 15 +14 32.5 15 16 32.5 15 18 32.5 15 +20 32.5 15 0 35 15 2 35 15 +4 35 15 6 35 15 8 35 15 +10 35 15 12 35 15 14 35 15 +16 35 15 18 35 15 20 35 15 +0 37.5 15 2 37.5 15 4 37.5 15 +6 37.5 15 8 37.5 15 10 37.5 15 +12 37.5 15 14 37.5 15 16 37.5 15 +18 37.5 15 20 37.5 15 0 40 15 +2 40 15 4 40 15 6 40 15 +8 40 15 10 40 15 12 40 15 +14 40 15 16 40 15 18 40 15 +20 40 15 0 42.5 15 2 42.5 15 +4 42.5 15 6 42.5 15 8 42.5 15 +10 42.5 15 12 42.5 15 14 42.5 15 +16 42.5 15 18 42.5 15 20 42.5 15 +0 45 15 2 45 15 4 45 15 +6 45 15 8 45 15 10 45 15 +12 45 15 14 45 15 16 45 15 +18 45 15 20 45 15 0 47.5 15 +2 47.5 15 4 47.5 15 6 47.5 15 +8 47.5 15 10 47.5 15 12 47.5 15 +14 47.5 15 16 47.5 15 18 47.5 15 +20 47.5 15 0 50 15 2 50 15 +4 50 15 6 50 15 8 50 15 +10 50 15 12 50 15 14 50 15 +16 50 15 18 50 15 20 50 15 +0 0 16.666666667 2 0 16.666666667 4 0 16.666666667 +6 0 16.666666667 8 0 16.666666667 10 0 16.666666667 +12 0 16.666666667 14 0 16.666666667 16 0 16.666666667 +18 0 16.666666667 20 0 16.666666667 0 2.5 16.666666667 +2 2.5 16.666666667 4 2.5 16.666666667 6 2.5 16.666666667 +8 2.5 16.666666667 10 2.5 16.666666667 12 2.5 16.666666667 +14 2.5 16.666666667 16 2.5 16.666666667 18 2.5 16.666666667 +20 2.5 16.666666667 0 5 16.666666667 2 5 16.666666667 +4 5 16.666666667 6 5 16.666666667 8 5 16.666666667 +10 5 16.666666667 12 5 16.666666667 14 5 16.666666667 +16 5 16.666666667 18 5 16.666666667 20 5 16.666666667 +0 7.5 16.666666667 2 7.5 16.666666667 4 7.5 16.666666667 +6 7.5 16.666666667 8 7.5 16.666666667 10 7.5 16.666666667 +12 7.5 16.666666667 14 7.5 16.666666667 16 7.5 16.666666667 +18 7.5 16.666666667 20 7.5 16.666666667 0 10 16.666666667 +2 10 16.666666667 4 10 16.666666667 6 10 16.666666667 +8 10 16.666666667 10 10 16.666666667 12 10 16.666666667 +14 10 16.666666667 16 10 16.666666667 18 10 16.666666667 +20 10 16.666666667 0 12.5 16.666666667 2 12.5 16.666666667 +4 12.5 16.666666667 6 12.5 16.666666667 8 12.5 16.666666667 +10 12.5 16.666666667 12 12.5 16.666666667 14 12.5 16.666666667 +16 12.5 16.666666667 18 12.5 16.666666667 20 12.5 16.666666667 +0 15 16.666666667 2 15 16.666666667 4 15 16.666666667 +6 15 16.666666667 8 15 16.666666667 10 15 16.666666667 +12 15 16.666666667 14 15 16.666666667 16 15 16.666666667 +18 15 16.666666667 20 15 16.666666667 0 17.5 16.666666667 +2 17.5 16.666666667 4 17.5 16.666666667 6 17.5 16.666666667 +8 17.5 16.666666667 10 17.5 16.666666667 12 17.5 16.666666667 +14 17.5 16.666666667 16 17.5 16.666666667 18 17.5 16.666666667 +20 17.5 16.666666667 0 20 16.666666667 2 20 16.666666667 +4 20 16.666666667 6 20 16.666666667 8 20 16.666666667 +10 20 16.666666667 12 20 16.666666667 14 20 16.666666667 +16 20 16.666666667 18 20 16.666666667 20 20 16.666666667 +0 22.5 16.666666667 2 22.5 16.666666667 4 22.5 16.666666667 +6 22.5 16.666666667 8 22.5 16.666666667 10 22.5 16.666666667 +12 22.5 16.666666667 14 22.5 16.666666667 16 22.5 16.666666667 +18 22.5 16.666666667 20 22.5 16.666666667 0 25 16.666666667 +2 25 16.666666667 4 25 16.666666667 6 25 16.666666667 +8 25 16.666666667 10 25 16.666666667 12 25 16.666666667 +14 25 16.666666667 16 25 16.666666667 18 25 16.666666667 +20 25 16.666666667 0 27.5 16.666666667 2 27.5 16.666666667 +4 27.5 16.666666667 6 27.5 16.666666667 8 27.5 16.666666667 +10 27.5 16.666666667 12 27.5 16.666666667 14 27.5 16.666666667 +16 27.5 16.666666667 18 27.5 16.666666667 20 27.5 16.666666667 +0 30 16.666666667 2 30 16.666666667 4 30 16.666666667 +6 30 16.666666667 8 30 16.666666667 10 30 16.666666667 +12 30 16.666666667 14 30 16.666666667 16 30 16.666666667 +18 30 16.666666667 20 30 16.666666667 0 32.5 16.666666667 +2 32.5 16.666666667 4 32.5 16.666666667 6 32.5 16.666666667 +8 32.5 16.666666667 10 32.5 16.666666667 12 32.5 16.666666667 +14 32.5 16.666666667 16 32.5 16.666666667 18 32.5 16.666666667 +20 32.5 16.666666667 0 35 16.666666667 2 35 16.666666667 +4 35 16.666666667 6 35 16.666666667 8 35 16.666666667 +10 35 16.666666667 12 35 16.666666667 14 35 16.666666667 +16 35 16.666666667 18 35 16.666666667 20 35 16.666666667 +0 37.5 16.666666667 2 37.5 16.666666667 4 37.5 16.666666667 +6 37.5 16.666666667 8 37.5 16.666666667 10 37.5 16.666666667 +12 37.5 16.666666667 14 37.5 16.666666667 16 37.5 16.666666667 +18 37.5 16.666666667 20 37.5 16.666666667 0 40 16.666666667 +2 40 16.666666667 4 40 16.666666667 6 40 16.666666667 +8 40 16.666666667 10 40 16.666666667 12 40 16.666666667 +14 40 16.666666667 16 40 16.666666667 18 40 16.666666667 +20 40 16.666666667 0 42.5 16.666666667 2 42.5 16.666666667 +4 42.5 16.666666667 6 42.5 16.666666667 8 42.5 16.666666667 +10 42.5 16.666666667 12 42.5 16.666666667 14 42.5 16.666666667 +16 42.5 16.666666667 18 42.5 16.666666667 20 42.5 16.666666667 +0 45 16.666666667 2 45 16.666666667 4 45 16.666666667 +6 45 16.666666667 8 45 16.666666667 10 45 16.666666667 +12 45 16.666666667 14 45 16.666666667 16 45 16.666666667 +18 45 16.666666667 20 45 16.666666667 0 47.5 16.666666667 +2 47.5 16.666666667 4 47.5 16.666666667 6 47.5 16.666666667 +8 47.5 16.666666667 10 47.5 16.666666667 12 47.5 16.666666667 +14 47.5 16.666666667 16 47.5 16.666666667 18 47.5 16.666666667 +20 47.5 16.666666667 0 50 16.666666667 2 50 16.666666667 +4 50 16.666666667 6 50 16.666666667 8 50 16.666666667 +10 50 16.666666667 12 50 16.666666667 14 50 16.666666667 +16 50 16.666666667 18 50 16.666666667 20 50 16.666666667 +0 0 18.333333333 2 0 18.333333333 4 0 18.333333333 +6 0 18.333333333 8 0 18.333333333 10 0 18.333333333 +12 0 18.333333333 14 0 18.333333333 16 0 18.333333333 +18 0 18.333333333 20 0 18.333333333 0 2.5 18.333333333 +2 2.5 18.333333333 4 2.5 18.333333333 6 2.5 18.333333333 +8 2.5 18.333333333 10 2.5 18.333333333 12 2.5 18.333333333 +14 2.5 18.333333333 16 2.5 18.333333333 18 2.5 18.333333333 +20 2.5 18.333333333 0 5 18.333333333 2 5 18.333333333 +4 5 18.333333333 6 5 18.333333333 8 5 18.333333333 +10 5 18.333333333 12 5 18.333333333 14 5 18.333333333 +16 5 18.333333333 18 5 18.333333333 20 5 18.333333333 +0 7.5 18.333333333 2 7.5 18.333333333 4 7.5 18.333333333 +6 7.5 18.333333333 8 7.5 18.333333333 10 7.5 18.333333333 +12 7.5 18.333333333 14 7.5 18.333333333 16 7.5 18.333333333 +18 7.5 18.333333333 20 7.5 18.333333333 0 10 18.333333333 +2 10 18.333333333 4 10 18.333333333 6 10 18.333333333 +8 10 18.333333333 10 10 18.333333333 12 10 18.333333333 +14 10 18.333333333 16 10 18.333333333 18 10 18.333333333 +20 10 18.333333333 0 12.5 18.333333333 2 12.5 18.333333333 +4 12.5 18.333333333 6 12.5 18.333333333 8 12.5 18.333333333 +10 12.5 18.333333333 12 12.5 18.333333333 14 12.5 18.333333333 +16 12.5 18.333333333 18 12.5 18.333333333 20 12.5 18.333333333 +0 15 18.333333333 2 15 18.333333333 4 15 18.333333333 +6 15 18.333333333 8 15 18.333333333 10 15 18.333333333 +12 15 18.333333333 14 15 18.333333333 16 15 18.333333333 +18 15 18.333333333 20 15 18.333333333 0 17.5 18.333333333 +2 17.5 18.333333333 4 17.5 18.333333333 6 17.5 18.333333333 +8 17.5 18.333333333 10 17.5 18.333333333 12 17.5 18.333333333 +14 17.5 18.333333333 16 17.5 18.333333333 18 17.5 18.333333333 +20 17.5 18.333333333 0 20 18.333333333 2 20 18.333333333 +4 20 18.333333333 6 20 18.333333333 8 20 18.333333333 +10 20 18.333333333 12 20 18.333333333 14 20 18.333333333 +16 20 18.333333333 18 20 18.333333333 20 20 18.333333333 +0 22.5 18.333333333 2 22.5 18.333333333 4 22.5 18.333333333 +6 22.5 18.333333333 8 22.5 18.333333333 10 22.5 18.333333333 +12 22.5 18.333333333 14 22.5 18.333333333 16 22.5 18.333333333 +18 22.5 18.333333333 20 22.5 18.333333333 0 25 18.333333333 +2 25 18.333333333 4 25 18.333333333 6 25 18.333333333 +8 25 18.333333333 10 25 18.333333333 12 25 18.333333333 +14 25 18.333333333 16 25 18.333333333 18 25 18.333333333 +20 25 18.333333333 0 27.5 18.333333333 2 27.5 18.333333333 +4 27.5 18.333333333 6 27.5 18.333333333 8 27.5 18.333333333 +10 27.5 18.333333333 12 27.5 18.333333333 14 27.5 18.333333333 +16 27.5 18.333333333 18 27.5 18.333333333 20 27.5 18.333333333 +0 30 18.333333333 2 30 18.333333333 4 30 18.333333333 +6 30 18.333333333 8 30 18.333333333 10 30 18.333333333 +12 30 18.333333333 14 30 18.333333333 16 30 18.333333333 +18 30 18.333333333 20 30 18.333333333 0 32.5 18.333333333 +2 32.5 18.333333333 4 32.5 18.333333333 6 32.5 18.333333333 +8 32.5 18.333333333 10 32.5 18.333333333 12 32.5 18.333333333 +14 32.5 18.333333333 16 32.5 18.333333333 18 32.5 18.333333333 +20 32.5 18.333333333 0 35 18.333333333 2 35 18.333333333 +4 35 18.333333333 6 35 18.333333333 8 35 18.333333333 +10 35 18.333333333 12 35 18.333333333 14 35 18.333333333 +16 35 18.333333333 18 35 18.333333333 20 35 18.333333333 +0 37.5 18.333333333 2 37.5 18.333333333 4 37.5 18.333333333 +6 37.5 18.333333333 8 37.5 18.333333333 10 37.5 18.333333333 +12 37.5 18.333333333 14 37.5 18.333333333 16 37.5 18.333333333 +18 37.5 18.333333333 20 37.5 18.333333333 0 40 18.333333333 +2 40 18.333333333 4 40 18.333333333 6 40 18.333333333 +8 40 18.333333333 10 40 18.333333333 12 40 18.333333333 +14 40 18.333333333 16 40 18.333333333 18 40 18.333333333 +20 40 18.333333333 0 42.5 18.333333333 2 42.5 18.333333333 +4 42.5 18.333333333 6 42.5 18.333333333 8 42.5 18.333333333 +10 42.5 18.333333333 12 42.5 18.333333333 14 42.5 18.333333333 +16 42.5 18.333333333 18 42.5 18.333333333 20 42.5 18.333333333 +0 45 18.333333333 2 45 18.333333333 4 45 18.333333333 +6 45 18.333333333 8 45 18.333333333 10 45 18.333333333 +12 45 18.333333333 14 45 18.333333333 16 45 18.333333333 +18 45 18.333333333 20 45 18.333333333 0 47.5 18.333333333 +2 47.5 18.333333333 4 47.5 18.333333333 6 47.5 18.333333333 +8 47.5 18.333333333 10 47.5 18.333333333 12 47.5 18.333333333 +14 47.5 18.333333333 16 47.5 18.333333333 18 47.5 18.333333333 +20 47.5 18.333333333 0 50 18.333333333 2 50 18.333333333 +4 50 18.333333333 6 50 18.333333333 8 50 18.333333333 +10 50 18.333333333 12 50 18.333333333 14 50 18.333333333 +16 50 18.333333333 18 50 18.333333333 20 50 18.333333333 +0 0 20 2 0 20 4 0 20 +6 0 20 8 0 20 10 0 20 +12 0 20 14 0 20 16 0 20 +18 0 20 20 0 20 0 2.5 20 +2 2.5 20 4 2.5 20 6 2.5 20 +8 2.5 20 10 2.5 20 12 2.5 20 +14 2.5 20 16 2.5 20 18 2.5 20 +20 2.5 20 0 5 20 2 5 20 +4 5 20 6 5 20 8 5 20 +10 5 20 12 5 20 14 5 20 +16 5 20 18 5 20 20 5 20 +0 7.5 20 2 7.5 20 4 7.5 20 +6 7.5 20 8 7.5 20 10 7.5 20 +12 7.5 20 14 7.5 20 16 7.5 20 +18 7.5 20 20 7.5 20 0 10 20 +2 10 20 4 10 20 6 10 20 +8 10 20 10 10 20 12 10 20 +14 10 20 16 10 20 18 10 20 +20 10 20 0 12.5 20 2 12.5 20 +4 12.5 20 6 12.5 20 8 12.5 20 +10 12.5 20 12 12.5 20 14 12.5 20 +16 12.5 20 18 12.5 20 20 12.5 20 +0 15 20 2 15 20 4 15 20 +6 15 20 8 15 20 10 15 20 +12 15 20 14 15 20 16 15 20 +18 15 20 20 15 20 0 17.5 20 +2 17.5 20 4 17.5 20 6 17.5 20 +8 17.5 20 10 17.5 20 12 17.5 20 +14 17.5 20 16 17.5 20 18 17.5 20 +20 17.5 20 0 20 20 2 20 20 +4 20 20 6 20 20 8 20 20 +10 20 20 12 20 20 14 20 20 +16 20 20 18 20 20 20 20 20 +0 22.5 20 2 22.5 20 4 22.5 20 +6 22.5 20 8 22.5 20 10 22.5 20 +12 22.5 20 14 22.5 20 16 22.5 20 +18 22.5 20 20 22.5 20 0 25 20 +2 25 20 4 25 20 6 25 20 +8 25 20 10 25 20 12 25 20 +14 25 20 16 25 20 18 25 20 +20 25 20 0 27.5 20 2 27.5 20 +4 27.5 20 6 27.5 20 8 27.5 20 +10 27.5 20 12 27.5 20 14 27.5 20 +16 27.5 20 18 27.5 20 20 27.5 20 +0 30 20 2 30 20 4 30 20 +6 30 20 8 30 20 10 30 20 +12 30 20 14 30 20 16 30 20 +18 30 20 20 30 20 0 32.5 20 +2 32.5 20 4 32.5 20 6 32.5 20 +8 32.5 20 10 32.5 20 12 32.5 20 +14 32.5 20 16 32.5 20 18 32.5 20 +20 32.5 20 0 35 20 2 35 20 +4 35 20 6 35 20 8 35 20 +10 35 20 12 35 20 14 35 20 +16 35 20 18 35 20 20 35 20 +0 37.5 20 2 37.5 20 4 37.5 20 +6 37.5 20 8 37.5 20 10 37.5 20 +12 37.5 20 14 37.5 20 16 37.5 20 +18 37.5 20 20 37.5 20 0 40 20 +2 40 20 4 40 20 6 40 20 +8 40 20 10 40 20 12 40 20 +14 40 20 16 40 20 18 40 20 +20 40 20 0 42.5 20 2 42.5 20 +4 42.5 20 6 42.5 20 8 42.5 20 +10 42.5 20 12 42.5 20 14 42.5 20 +16 42.5 20 18 42.5 20 20 42.5 20 +0 45 20 2 45 20 4 45 20 +6 45 20 8 45 20 10 45 20 +12 45 20 14 45 20 16 45 20 +18 45 20 20 45 20 0 47.5 20 +2 47.5 20 4 47.5 20 6 47.5 20 +8 47.5 20 10 47.5 20 12 47.5 20 +14 47.5 20 16 47.5 20 18 47.5 20 +20 47.5 20 0 50 20 2 50 20 +4 50 20 6 50 20 8 50 20 +10 50 20 12 50 20 14 50 20 +16 50 20 18 50 20 20 50 20 +0 0 21.666666667 2 0 21.666666667 4 0 21.666666667 +6 0 21.666666667 8 0 21.666666667 10 0 21.666666667 +12 0 21.666666667 14 0 21.666666667 16 0 21.666666667 +18 0 21.666666667 20 0 21.666666667 0 2.5 21.666666667 +2 2.5 21.666666667 4 2.5 21.666666667 6 2.5 21.666666667 +8 2.5 21.666666667 10 2.5 21.666666667 12 2.5 21.666666667 +14 2.5 21.666666667 16 2.5 21.666666667 18 2.5 21.666666667 +20 2.5 21.666666667 0 5 21.666666667 2 5 21.666666667 +4 5 21.666666667 6 5 21.666666667 8 5 21.666666667 +10 5 21.666666667 12 5 21.666666667 14 5 21.666666667 +16 5 21.666666667 18 5 21.666666667 20 5 21.666666667 +0 7.5 21.666666667 2 7.5 21.666666667 4 7.5 21.666666667 +6 7.5 21.666666667 8 7.5 21.666666667 10 7.5 21.666666667 +12 7.5 21.666666667 14 7.5 21.666666667 16 7.5 21.666666667 +18 7.5 21.666666667 20 7.5 21.666666667 0 10 21.666666667 +2 10 21.666666667 4 10 21.666666667 6 10 21.666666667 +8 10 21.666666667 10 10 21.666666667 12 10 21.666666667 +14 10 21.666666667 16 10 21.666666667 18 10 21.666666667 +20 10 21.666666667 0 12.5 21.666666667 2 12.5 21.666666667 +4 12.5 21.666666667 6 12.5 21.666666667 8 12.5 21.666666667 +10 12.5 21.666666667 12 12.5 21.666666667 14 12.5 21.666666667 +16 12.5 21.666666667 18 12.5 21.666666667 20 12.5 21.666666667 +0 15 21.666666667 2 15 21.666666667 4 15 21.666666667 +6 15 21.666666667 8 15 21.666666667 10 15 21.666666667 +12 15 21.666666667 14 15 21.666666667 16 15 21.666666667 +18 15 21.666666667 20 15 21.666666667 0 17.5 21.666666667 +2 17.5 21.666666667 4 17.5 21.666666667 6 17.5 21.666666667 +8 17.5 21.666666667 10 17.5 21.666666667 12 17.5 21.666666667 +14 17.5 21.666666667 16 17.5 21.666666667 18 17.5 21.666666667 +20 17.5 21.666666667 0 20 21.666666667 2 20 21.666666667 +4 20 21.666666667 6 20 21.666666667 8 20 21.666666667 +10 20 21.666666667 12 20 21.666666667 14 20 21.666666667 +16 20 21.666666667 18 20 21.666666667 20 20 21.666666667 +0 22.5 21.666666667 2 22.5 21.666666667 4 22.5 21.666666667 +6 22.5 21.666666667 8 22.5 21.666666667 10 22.5 21.666666667 +12 22.5 21.666666667 14 22.5 21.666666667 16 22.5 21.666666667 +18 22.5 21.666666667 20 22.5 21.666666667 0 25 21.666666667 +2 25 21.666666667 4 25 21.666666667 6 25 21.666666667 +8 25 21.666666667 10 25 21.666666667 12 25 21.666666667 +14 25 21.666666667 16 25 21.666666667 18 25 21.666666667 +20 25 21.666666667 0 27.5 21.666666667 2 27.5 21.666666667 +4 27.5 21.666666667 6 27.5 21.666666667 8 27.5 21.666666667 +10 27.5 21.666666667 12 27.5 21.666666667 14 27.5 21.666666667 +16 27.5 21.666666667 18 27.5 21.666666667 20 27.5 21.666666667 +0 30 21.666666667 2 30 21.666666667 4 30 21.666666667 +6 30 21.666666667 8 30 21.666666667 10 30 21.666666667 +12 30 21.666666667 14 30 21.666666667 16 30 21.666666667 +18 30 21.666666667 20 30 21.666666667 0 32.5 21.666666667 +2 32.5 21.666666667 4 32.5 21.666666667 6 32.5 21.666666667 +8 32.5 21.666666667 10 32.5 21.666666667 12 32.5 21.666666667 +14 32.5 21.666666667 16 32.5 21.666666667 18 32.5 21.666666667 +20 32.5 21.666666667 0 35 21.666666667 2 35 21.666666667 +4 35 21.666666667 6 35 21.666666667 8 35 21.666666667 +10 35 21.666666667 12 35 21.666666667 14 35 21.666666667 +16 35 21.666666667 18 35 21.666666667 20 35 21.666666667 +0 37.5 21.666666667 2 37.5 21.666666667 4 37.5 21.666666667 +6 37.5 21.666666667 8 37.5 21.666666667 10 37.5 21.666666667 +12 37.5 21.666666667 14 37.5 21.666666667 16 37.5 21.666666667 +18 37.5 21.666666667 20 37.5 21.666666667 0 40 21.666666667 +2 40 21.666666667 4 40 21.666666667 6 40 21.666666667 +8 40 21.666666667 10 40 21.666666667 12 40 21.666666667 +14 40 21.666666667 16 40 21.666666667 18 40 21.666666667 +20 40 21.666666667 0 42.5 21.666666667 2 42.5 21.666666667 +4 42.5 21.666666667 6 42.5 21.666666667 8 42.5 21.666666667 +10 42.5 21.666666667 12 42.5 21.666666667 14 42.5 21.666666667 +16 42.5 21.666666667 18 42.5 21.666666667 20 42.5 21.666666667 +0 45 21.666666667 2 45 21.666666667 4 45 21.666666667 +6 45 21.666666667 8 45 21.666666667 10 45 21.666666667 +12 45 21.666666667 14 45 21.666666667 16 45 21.666666667 +18 45 21.666666667 20 45 21.666666667 0 47.5 21.666666667 +2 47.5 21.666666667 4 47.5 21.666666667 6 47.5 21.666666667 +8 47.5 21.666666667 10 47.5 21.666666667 12 47.5 21.666666667 +14 47.5 21.666666667 16 47.5 21.666666667 18 47.5 21.666666667 +20 47.5 21.666666667 0 50 21.666666667 2 50 21.666666667 +4 50 21.666666667 6 50 21.666666667 8 50 21.666666667 +10 50 21.666666667 12 50 21.666666667 14 50 21.666666667 +16 50 21.666666667 18 50 21.666666667 20 50 21.666666667 +0 0 23.333333333 2 0 23.333333333 4 0 23.333333333 +6 0 23.333333333 8 0 23.333333333 10 0 23.333333333 +12 0 23.333333333 14 0 23.333333333 16 0 23.333333333 +18 0 23.333333333 20 0 23.333333333 0 2.5 23.333333333 +2 2.5 23.333333333 4 2.5 23.333333333 6 2.5 23.333333333 +8 2.5 23.333333333 10 2.5 23.333333333 12 2.5 23.333333333 +14 2.5 23.333333333 16 2.5 23.333333333 18 2.5 23.333333333 +20 2.5 23.333333333 0 5 23.333333333 2 5 23.333333333 +4 5 23.333333333 6 5 23.333333333 8 5 23.333333333 +10 5 23.333333333 12 5 23.333333333 14 5 23.333333333 +16 5 23.333333333 18 5 23.333333333 20 5 23.333333333 +0 7.5 23.333333333 2 7.5 23.333333333 4 7.5 23.333333333 +6 7.5 23.333333333 8 7.5 23.333333333 10 7.5 23.333333333 +12 7.5 23.333333333 14 7.5 23.333333333 16 7.5 23.333333333 +18 7.5 23.333333333 20 7.5 23.333333333 0 10 23.333333333 +2 10 23.333333333 4 10 23.333333333 6 10 23.333333333 +8 10 23.333333333 10 10 23.333333333 12 10 23.333333333 +14 10 23.333333333 16 10 23.333333333 18 10 23.333333333 +20 10 23.333333333 0 12.5 23.333333333 2 12.5 23.333333333 +4 12.5 23.333333333 6 12.5 23.333333333 8 12.5 23.333333333 +10 12.5 23.333333333 12 12.5 23.333333333 14 12.5 23.333333333 +16 12.5 23.333333333 18 12.5 23.333333333 20 12.5 23.333333333 +0 15 23.333333333 2 15 23.333333333 4 15 23.333333333 +6 15 23.333333333 8 15 23.333333333 10 15 23.333333333 +12 15 23.333333333 14 15 23.333333333 16 15 23.333333333 +18 15 23.333333333 20 15 23.333333333 0 17.5 23.333333333 +2 17.5 23.333333333 4 17.5 23.333333333 6 17.5 23.333333333 +8 17.5 23.333333333 10 17.5 23.333333333 12 17.5 23.333333333 +14 17.5 23.333333333 16 17.5 23.333333333 18 17.5 23.333333333 +20 17.5 23.333333333 0 20 23.333333333 2 20 23.333333333 +4 20 23.333333333 6 20 23.333333333 8 20 23.333333333 +10 20 23.333333333 12 20 23.333333333 14 20 23.333333333 +16 20 23.333333333 18 20 23.333333333 20 20 23.333333333 +0 22.5 23.333333333 2 22.5 23.333333333 4 22.5 23.333333333 +6 22.5 23.333333333 8 22.5 23.333333333 10 22.5 23.333333333 +12 22.5 23.333333333 14 22.5 23.333333333 16 22.5 23.333333333 +18 22.5 23.333333333 20 22.5 23.333333333 0 25 23.333333333 +2 25 23.333333333 4 25 23.333333333 6 25 23.333333333 +8 25 23.333333333 10 25 23.333333333 12 25 23.333333333 +14 25 23.333333333 16 25 23.333333333 18 25 23.333333333 +20 25 23.333333333 0 27.5 23.333333333 2 27.5 23.333333333 +4 27.5 23.333333333 6 27.5 23.333333333 8 27.5 23.333333333 +10 27.5 23.333333333 12 27.5 23.333333333 14 27.5 23.333333333 +16 27.5 23.333333333 18 27.5 23.333333333 20 27.5 23.333333333 +0 30 23.333333333 2 30 23.333333333 4 30 23.333333333 +6 30 23.333333333 8 30 23.333333333 10 30 23.333333333 +12 30 23.333333333 14 30 23.333333333 16 30 23.333333333 +18 30 23.333333333 20 30 23.333333333 0 32.5 23.333333333 +2 32.5 23.333333333 4 32.5 23.333333333 6 32.5 23.333333333 +8 32.5 23.333333333 10 32.5 23.333333333 12 32.5 23.333333333 +14 32.5 23.333333333 16 32.5 23.333333333 18 32.5 23.333333333 +20 32.5 23.333333333 0 35 23.333333333 2 35 23.333333333 +4 35 23.333333333 6 35 23.333333333 8 35 23.333333333 +10 35 23.333333333 12 35 23.333333333 14 35 23.333333333 +16 35 23.333333333 18 35 23.333333333 20 35 23.333333333 +0 37.5 23.333333333 2 37.5 23.333333333 4 37.5 23.333333333 +6 37.5 23.333333333 8 37.5 23.333333333 10 37.5 23.333333333 +12 37.5 23.333333333 14 37.5 23.333333333 16 37.5 23.333333333 +18 37.5 23.333333333 20 37.5 23.333333333 0 40 23.333333333 +2 40 23.333333333 4 40 23.333333333 6 40 23.333333333 +8 40 23.333333333 10 40 23.333333333 12 40 23.333333333 +14 40 23.333333333 16 40 23.333333333 18 40 23.333333333 +20 40 23.333333333 0 42.5 23.333333333 2 42.5 23.333333333 +4 42.5 23.333333333 6 42.5 23.333333333 8 42.5 23.333333333 +10 42.5 23.333333333 12 42.5 23.333333333 14 42.5 23.333333333 +16 42.5 23.333333333 18 42.5 23.333333333 20 42.5 23.333333333 +0 45 23.333333333 2 45 23.333333333 4 45 23.333333333 +6 45 23.333333333 8 45 23.333333333 10 45 23.333333333 +12 45 23.333333333 14 45 23.333333333 16 45 23.333333333 +18 45 23.333333333 20 45 23.333333333 0 47.5 23.333333333 +2 47.5 23.333333333 4 47.5 23.333333333 6 47.5 23.333333333 +8 47.5 23.333333333 10 47.5 23.333333333 12 47.5 23.333333333 +14 47.5 23.333333333 16 47.5 23.333333333 18 47.5 23.333333333 +20 47.5 23.333333333 0 50 23.333333333 2 50 23.333333333 +4 50 23.333333333 6 50 23.333333333 8 50 23.333333333 +10 50 23.333333333 12 50 23.333333333 14 50 23.333333333 +16 50 23.333333333 18 50 23.333333333 20 50 23.333333333 +0 0 25 2 0 25 4 0 25 +6 0 25 8 0 25 10 0 25 +12 0 25 14 0 25 16 0 25 +18 0 25 20 0 25 0 2.5 25 +2 2.5 25 4 2.5 25 6 2.5 25 +8 2.5 25 10 2.5 25 12 2.5 25 +14 2.5 25 16 2.5 25 18 2.5 25 +20 2.5 25 0 5 25 2 5 25 +4 5 25 6 5 25 8 5 25 +10 5 25 12 5 25 14 5 25 +16 5 25 18 5 25 20 5 25 +0 7.5 25 2 7.5 25 4 7.5 25 +6 7.5 25 8 7.5 25 10 7.5 25 +12 7.5 25 14 7.5 25 16 7.5 25 +18 7.5 25 20 7.5 25 0 10 25 +2 10 25 4 10 25 6 10 25 +8 10 25 10 10 25 12 10 25 +14 10 25 16 10 25 18 10 25 +20 10 25 0 12.5 25 2 12.5 25 +4 12.5 25 6 12.5 25 8 12.5 25 +10 12.5 25 12 12.5 25 14 12.5 25 +16 12.5 25 18 12.5 25 20 12.5 25 +0 15 25 2 15 25 4 15 25 +6 15 25 8 15 25 10 15 25 +12 15 25 14 15 25 16 15 25 +18 15 25 20 15 25 0 17.5 25 +2 17.5 25 4 17.5 25 6 17.5 25 +8 17.5 25 10 17.5 25 12 17.5 25 +14 17.5 25 16 17.5 25 18 17.5 25 +20 17.5 25 0 20 25 2 20 25 +4 20 25 6 20 25 8 20 25 +10 20 25 12 20 25 14 20 25 +16 20 25 18 20 25 20 20 25 +0 22.5 25 2 22.5 25 4 22.5 25 +6 22.5 25 8 22.5 25 10 22.5 25 +12 22.5 25 14 22.5 25 16 22.5 25 +18 22.5 25 20 22.5 25 0 25 25 +2 25 25 4 25 25 6 25 25 +8 25 25 10 25 25 12 25 25 +14 25 25 16 25 25 18 25 25 +20 25 25 0 27.5 25 2 27.5 25 +4 27.5 25 6 27.5 25 8 27.5 25 +10 27.5 25 12 27.5 25 14 27.5 25 +16 27.5 25 18 27.5 25 20 27.5 25 +0 30 25 2 30 25 4 30 25 +6 30 25 8 30 25 10 30 25 +12 30 25 14 30 25 16 30 25 +18 30 25 20 30 25 0 32.5 25 +2 32.5 25 4 32.5 25 6 32.5 25 +8 32.5 25 10 32.5 25 12 32.5 25 +14 32.5 25 16 32.5 25 18 32.5 25 +20 32.5 25 0 35 25 2 35 25 +4 35 25 6 35 25 8 35 25 +10 35 25 12 35 25 14 35 25 +16 35 25 18 35 25 20 35 25 +0 37.5 25 2 37.5 25 4 37.5 25 +6 37.5 25 8 37.5 25 10 37.5 25 +12 37.5 25 14 37.5 25 16 37.5 25 +18 37.5 25 20 37.5 25 0 40 25 +2 40 25 4 40 25 6 40 25 +8 40 25 10 40 25 12 40 25 +14 40 25 16 40 25 18 40 25 +20 40 25 0 42.5 25 2 42.5 25 +4 42.5 25 6 42.5 25 8 42.5 25 +10 42.5 25 12 42.5 25 14 42.5 25 +16 42.5 25 18 42.5 25 20 42.5 25 +0 45 25 2 45 25 4 45 25 +6 45 25 8 45 25 10 45 25 +12 45 25 14 45 25 16 45 25 +18 45 25 20 45 25 0 47.5 25 +2 47.5 25 4 47.5 25 6 47.5 25 +8 47.5 25 10 47.5 25 12 47.5 25 +14 47.5 25 16 47.5 25 18 47.5 25 +20 47.5 25 0 50 25 2 50 25 +4 50 25 6 50 25 8 50 25 +10 50 25 12 50 25 14 50 25 +16 50 25 18 50 25 20 50 25 +0 0 26.666666667 2 0 26.666666667 4 0 26.666666667 +6 0 26.666666667 8 0 26.666666667 10 0 26.666666667 +12 0 26.666666667 14 0 26.666666667 16 0 26.666666667 +18 0 26.666666667 20 0 26.666666667 0 2.5 26.666666667 +2 2.5 26.666666667 4 2.5 26.666666667 6 2.5 26.666666667 +8 2.5 26.666666667 10 2.5 26.666666667 12 2.5 26.666666667 +14 2.5 26.666666667 16 2.5 26.666666667 18 2.5 26.666666667 +20 2.5 26.666666667 0 5 26.666666667 2 5 26.666666667 +4 5 26.666666667 6 5 26.666666667 8 5 26.666666667 +10 5 26.666666667 12 5 26.666666667 14 5 26.666666667 +16 5 26.666666667 18 5 26.666666667 20 5 26.666666667 +0 7.5 26.666666667 2 7.5 26.666666667 4 7.5 26.666666667 +6 7.5 26.666666667 8 7.5 26.666666667 10 7.5 26.666666667 +12 7.5 26.666666667 14 7.5 26.666666667 16 7.5 26.666666667 +18 7.5 26.666666667 20 7.5 26.666666667 0 10 26.666666667 +2 10 26.666666667 4 10 26.666666667 6 10 26.666666667 +8 10 26.666666667 10 10 26.666666667 12 10 26.666666667 +14 10 26.666666667 16 10 26.666666667 18 10 26.666666667 +20 10 26.666666667 0 12.5 26.666666667 2 12.5 26.666666667 +4 12.5 26.666666667 6 12.5 26.666666667 8 12.5 26.666666667 +10 12.5 26.666666667 12 12.5 26.666666667 14 12.5 26.666666667 +16 12.5 26.666666667 18 12.5 26.666666667 20 12.5 26.666666667 +0 15 26.666666667 2 15 26.666666667 4 15 26.666666667 +6 15 26.666666667 8 15 26.666666667 10 15 26.666666667 +12 15 26.666666667 14 15 26.666666667 16 15 26.666666667 +18 15 26.666666667 20 15 26.666666667 0 17.5 26.666666667 +2 17.5 26.666666667 4 17.5 26.666666667 6 17.5 26.666666667 +8 17.5 26.666666667 10 17.5 26.666666667 12 17.5 26.666666667 +14 17.5 26.666666667 16 17.5 26.666666667 18 17.5 26.666666667 +20 17.5 26.666666667 0 20 26.666666667 2 20 26.666666667 +4 20 26.666666667 6 20 26.666666667 8 20 26.666666667 +10 20 26.666666667 12 20 26.666666667 14 20 26.666666667 +16 20 26.666666667 18 20 26.666666667 20 20 26.666666667 +0 22.5 26.666666667 2 22.5 26.666666667 4 22.5 26.666666667 +6 22.5 26.666666667 8 22.5 26.666666667 10 22.5 26.666666667 +12 22.5 26.666666667 14 22.5 26.666666667 16 22.5 26.666666667 +18 22.5 26.666666667 20 22.5 26.666666667 0 25 26.666666667 +2 25 26.666666667 4 25 26.666666667 6 25 26.666666667 +8 25 26.666666667 10 25 26.666666667 12 25 26.666666667 +14 25 26.666666667 16 25 26.666666667 18 25 26.666666667 +20 25 26.666666667 0 27.5 26.666666667 2 27.5 26.666666667 +4 27.5 26.666666667 6 27.5 26.666666667 8 27.5 26.666666667 +10 27.5 26.666666667 12 27.5 26.666666667 14 27.5 26.666666667 +16 27.5 26.666666667 18 27.5 26.666666667 20 27.5 26.666666667 +0 30 26.666666667 2 30 26.666666667 4 30 26.666666667 +6 30 26.666666667 8 30 26.666666667 10 30 26.666666667 +12 30 26.666666667 14 30 26.666666667 16 30 26.666666667 +18 30 26.666666667 20 30 26.666666667 0 32.5 26.666666667 +2 32.5 26.666666667 4 32.5 26.666666667 6 32.5 26.666666667 +8 32.5 26.666666667 10 32.5 26.666666667 12 32.5 26.666666667 +14 32.5 26.666666667 16 32.5 26.666666667 18 32.5 26.666666667 +20 32.5 26.666666667 0 35 26.666666667 2 35 26.666666667 +4 35 26.666666667 6 35 26.666666667 8 35 26.666666667 +10 35 26.666666667 12 35 26.666666667 14 35 26.666666667 +16 35 26.666666667 18 35 26.666666667 20 35 26.666666667 +0 37.5 26.666666667 2 37.5 26.666666667 4 37.5 26.666666667 +6 37.5 26.666666667 8 37.5 26.666666667 10 37.5 26.666666667 +12 37.5 26.666666667 14 37.5 26.666666667 16 37.5 26.666666667 +18 37.5 26.666666667 20 37.5 26.666666667 0 40 26.666666667 +2 40 26.666666667 4 40 26.666666667 6 40 26.666666667 +8 40 26.666666667 10 40 26.666666667 12 40 26.666666667 +14 40 26.666666667 16 40 26.666666667 18 40 26.666666667 +20 40 26.666666667 0 42.5 26.666666667 2 42.5 26.666666667 +4 42.5 26.666666667 6 42.5 26.666666667 8 42.5 26.666666667 +10 42.5 26.666666667 12 42.5 26.666666667 14 42.5 26.666666667 +16 42.5 26.666666667 18 42.5 26.666666667 20 42.5 26.666666667 +0 45 26.666666667 2 45 26.666666667 4 45 26.666666667 +6 45 26.666666667 8 45 26.666666667 10 45 26.666666667 +12 45 26.666666667 14 45 26.666666667 16 45 26.666666667 +18 45 26.666666667 20 45 26.666666667 0 47.5 26.666666667 +2 47.5 26.666666667 4 47.5 26.666666667 6 47.5 26.666666667 +8 47.5 26.666666667 10 47.5 26.666666667 12 47.5 26.666666667 +14 47.5 26.666666667 16 47.5 26.666666667 18 47.5 26.666666667 +20 47.5 26.666666667 0 50 26.666666667 2 50 26.666666667 +4 50 26.666666667 6 50 26.666666667 8 50 26.666666667 +10 50 26.666666667 12 50 26.666666667 14 50 26.666666667 +16 50 26.666666667 18 50 26.666666667 20 50 26.666666667 +0 0 28.333333333 2 0 28.333333333 4 0 28.333333333 +6 0 28.333333333 8 0 28.333333333 10 0 28.333333333 +12 0 28.333333333 14 0 28.333333333 16 0 28.333333333 +18 0 28.333333333 20 0 28.333333333 0 2.5 28.333333333 +2 2.5 28.333333333 4 2.5 28.333333333 6 2.5 28.333333333 +8 2.5 28.333333333 10 2.5 28.333333333 12 2.5 28.333333333 +14 2.5 28.333333333 16 2.5 28.333333333 18 2.5 28.333333333 +20 2.5 28.333333333 0 5 28.333333333 2 5 28.333333333 +4 5 28.333333333 6 5 28.333333333 8 5 28.333333333 +10 5 28.333333333 12 5 28.333333333 14 5 28.333333333 +16 5 28.333333333 18 5 28.333333333 20 5 28.333333333 +0 7.5 28.333333333 2 7.5 28.333333333 4 7.5 28.333333333 +6 7.5 28.333333333 8 7.5 28.333333333 10 7.5 28.333333333 +12 7.5 28.333333333 14 7.5 28.333333333 16 7.5 28.333333333 +18 7.5 28.333333333 20 7.5 28.333333333 0 10 28.333333333 +2 10 28.333333333 4 10 28.333333333 6 10 28.333333333 +8 10 28.333333333 10 10 28.333333333 12 10 28.333333333 +14 10 28.333333333 16 10 28.333333333 18 10 28.333333333 +20 10 28.333333333 0 12.5 28.333333333 2 12.5 28.333333333 +4 12.5 28.333333333 6 12.5 28.333333333 8 12.5 28.333333333 +10 12.5 28.333333333 12 12.5 28.333333333 14 12.5 28.333333333 +16 12.5 28.333333333 18 12.5 28.333333333 20 12.5 28.333333333 +0 15 28.333333333 2 15 28.333333333 4 15 28.333333333 +6 15 28.333333333 8 15 28.333333333 10 15 28.333333333 +12 15 28.333333333 14 15 28.333333333 16 15 28.333333333 +18 15 28.333333333 20 15 28.333333333 0 17.5 28.333333333 +2 17.5 28.333333333 4 17.5 28.333333333 6 17.5 28.333333333 +8 17.5 28.333333333 10 17.5 28.333333333 12 17.5 28.333333333 +14 17.5 28.333333333 16 17.5 28.333333333 18 17.5 28.333333333 +20 17.5 28.333333333 0 20 28.333333333 2 20 28.333333333 +4 20 28.333333333 6 20 28.333333333 8 20 28.333333333 +10 20 28.333333333 12 20 28.333333333 14 20 28.333333333 +16 20 28.333333333 18 20 28.333333333 20 20 28.333333333 +0 22.5 28.333333333 2 22.5 28.333333333 4 22.5 28.333333333 +6 22.5 28.333333333 8 22.5 28.333333333 10 22.5 28.333333333 +12 22.5 28.333333333 14 22.5 28.333333333 16 22.5 28.333333333 +18 22.5 28.333333333 20 22.5 28.333333333 0 25 28.333333333 +2 25 28.333333333 4 25 28.333333333 6 25 28.333333333 +8 25 28.333333333 10 25 28.333333333 12 25 28.333333333 +14 25 28.333333333 16 25 28.333333333 18 25 28.333333333 +20 25 28.333333333 0 27.5 28.333333333 2 27.5 28.333333333 +4 27.5 28.333333333 6 27.5 28.333333333 8 27.5 28.333333333 +10 27.5 28.333333333 12 27.5 28.333333333 14 27.5 28.333333333 +16 27.5 28.333333333 18 27.5 28.333333333 20 27.5 28.333333333 +0 30 28.333333333 2 30 28.333333333 4 30 28.333333333 +6 30 28.333333333 8 30 28.333333333 10 30 28.333333333 +12 30 28.333333333 14 30 28.333333333 16 30 28.333333333 +18 30 28.333333333 20 30 28.333333333 0 32.5 28.333333333 +2 32.5 28.333333333 4 32.5 28.333333333 6 32.5 28.333333333 +8 32.5 28.333333333 10 32.5 28.333333333 12 32.5 28.333333333 +14 32.5 28.333333333 16 32.5 28.333333333 18 32.5 28.333333333 +20 32.5 28.333333333 0 35 28.333333333 2 35 28.333333333 +4 35 28.333333333 6 35 28.333333333 8 35 28.333333333 +10 35 28.333333333 12 35 28.333333333 14 35 28.333333333 +16 35 28.333333333 18 35 28.333333333 20 35 28.333333333 +0 37.5 28.333333333 2 37.5 28.333333333 4 37.5 28.333333333 +6 37.5 28.333333333 8 37.5 28.333333333 10 37.5 28.333333333 +12 37.5 28.333333333 14 37.5 28.333333333 16 37.5 28.333333333 +18 37.5 28.333333333 20 37.5 28.333333333 0 40 28.333333333 +2 40 28.333333333 4 40 28.333333333 6 40 28.333333333 +8 40 28.333333333 10 40 28.333333333 12 40 28.333333333 +14 40 28.333333333 16 40 28.333333333 18 40 28.333333333 +20 40 28.333333333 0 42.5 28.333333333 2 42.5 28.333333333 +4 42.5 28.333333333 6 42.5 28.333333333 8 42.5 28.333333333 +10 42.5 28.333333333 12 42.5 28.333333333 14 42.5 28.333333333 +16 42.5 28.333333333 18 42.5 28.333333333 20 42.5 28.333333333 +0 45 28.333333333 2 45 28.333333333 4 45 28.333333333 +6 45 28.333333333 8 45 28.333333333 10 45 28.333333333 +12 45 28.333333333 14 45 28.333333333 16 45 28.333333333 +18 45 28.333333333 20 45 28.333333333 0 47.5 28.333333333 +2 47.5 28.333333333 4 47.5 28.333333333 6 47.5 28.333333333 +8 47.5 28.333333333 10 47.5 28.333333333 12 47.5 28.333333333 +14 47.5 28.333333333 16 47.5 28.333333333 18 47.5 28.333333333 +20 47.5 28.333333333 0 50 28.333333333 2 50 28.333333333 +4 50 28.333333333 6 50 28.333333333 8 50 28.333333333 +10 50 28.333333333 12 50 28.333333333 14 50 28.333333333 +16 50 28.333333333 18 50 28.333333333 20 50 28.333333333 +0 0 30 2 0 30 4 0 30 +6 0 30 8 0 30 10 0 30 +12 0 30 14 0 30 16 0 30 +18 0 30 20 0 30 0 2.5 30 +2 2.5 30 4 2.5 30 6 2.5 30 +8 2.5 30 10 2.5 30 12 2.5 30 +14 2.5 30 16 2.5 30 18 2.5 30 +20 2.5 30 0 5 30 2 5 30 +4 5 30 6 5 30 8 5 30 +10 5 30 12 5 30 14 5 30 +16 5 30 18 5 30 20 5 30 +0 7.5 30 2 7.5 30 4 7.5 30 +6 7.5 30 8 7.5 30 10 7.5 30 +12 7.5 30 14 7.5 30 16 7.5 30 +18 7.5 30 20 7.5 30 0 10 30 +2 10 30 4 10 30 6 10 30 +8 10 30 10 10 30 12 10 30 +14 10 30 16 10 30 18 10 30 +20 10 30 0 12.5 30 2 12.5 30 +4 12.5 30 6 12.5 30 8 12.5 30 +10 12.5 30 12 12.5 30 14 12.5 30 +16 12.5 30 18 12.5 30 20 12.5 30 +0 15 30 2 15 30 4 15 30 +6 15 30 8 15 30 10 15 30 +12 15 30 14 15 30 16 15 30 +18 15 30 20 15 30 0 17.5 30 +2 17.5 30 4 17.5 30 6 17.5 30 +8 17.5 30 10 17.5 30 12 17.5 30 +14 17.5 30 16 17.5 30 18 17.5 30 +20 17.5 30 0 20 30 2 20 30 +4 20 30 6 20 30 8 20 30 +10 20 30 12 20 30 14 20 30 +16 20 30 18 20 30 20 20 30 +0 22.5 30 2 22.5 30 4 22.5 30 +6 22.5 30 8 22.5 30 10 22.5 30 +12 22.5 30 14 22.5 30 16 22.5 30 +18 22.5 30 20 22.5 30 0 25 30 +2 25 30 4 25 30 6 25 30 +8 25 30 10 25 30 12 25 30 +14 25 30 16 25 30 18 25 30 +20 25 30 0 27.5 30 2 27.5 30 +4 27.5 30 6 27.5 30 8 27.5 30 +10 27.5 30 12 27.5 30 14 27.5 30 +16 27.5 30 18 27.5 30 20 27.5 30 +0 30 30 2 30 30 4 30 30 +6 30 30 8 30 30 10 30 30 +12 30 30 14 30 30 16 30 30 +18 30 30 20 30 30 0 32.5 30 +2 32.5 30 4 32.5 30 6 32.5 30 +8 32.5 30 10 32.5 30 12 32.5 30 +14 32.5 30 16 32.5 30 18 32.5 30 +20 32.5 30 0 35 30 2 35 30 +4 35 30 6 35 30 8 35 30 +10 35 30 12 35 30 14 35 30 +16 35 30 18 35 30 20 35 30 +0 37.5 30 2 37.5 30 4 37.5 30 +6 37.5 30 8 37.5 30 10 37.5 30 +12 37.5 30 14 37.5 30 16 37.5 30 +18 37.5 30 20 37.5 30 0 40 30 +2 40 30 4 40 30 6 40 30 +8 40 30 10 40 30 12 40 30 +14 40 30 16 40 30 18 40 30 +20 40 30 0 42.5 30 2 42.5 30 +4 42.5 30 6 42.5 30 8 42.5 30 +10 42.5 30 12 42.5 30 14 42.5 30 +16 42.5 30 18 42.5 30 20 42.5 30 +0 45 30 2 45 30 4 45 30 +6 45 30 8 45 30 10 45 30 +12 45 30 14 45 30 16 45 30 +18 45 30 20 45 30 0 47.5 30 +2 47.5 30 4 47.5 30 6 47.5 30 +8 47.5 30 10 47.5 30 12 47.5 30 +14 47.5 30 16 47.5 30 18 47.5 30 +20 47.5 30 0 50 30 2 50 30 +4 50 30 6 50 30 8 50 30 +10 50 30 12 50 30 14 50 30 +16 50 30 18 50 30 20 50 30 +0 0 31.666666667 2 0 31.666666667 4 0 31.666666667 +6 0 31.666666667 8 0 31.666666667 10 0 31.666666667 +12 0 31.666666667 14 0 31.666666667 16 0 31.666666667 +18 0 31.666666667 20 0 31.666666667 0 2.5 31.666666667 +2 2.5 31.666666667 4 2.5 31.666666667 6 2.5 31.666666667 +8 2.5 31.666666667 10 2.5 31.666666667 12 2.5 31.666666667 +14 2.5 31.666666667 16 2.5 31.666666667 18 2.5 31.666666667 +20 2.5 31.666666667 0 5 31.666666667 2 5 31.666666667 +4 5 31.666666667 6 5 31.666666667 8 5 31.666666667 +10 5 31.666666667 12 5 31.666666667 14 5 31.666666667 +16 5 31.666666667 18 5 31.666666667 20 5 31.666666667 +0 7.5 31.666666667 2 7.5 31.666666667 4 7.5 31.666666667 +6 7.5 31.666666667 8 7.5 31.666666667 10 7.5 31.666666667 +12 7.5 31.666666667 14 7.5 31.666666667 16 7.5 31.666666667 +18 7.5 31.666666667 20 7.5 31.666666667 0 10 31.666666667 +2 10 31.666666667 4 10 31.666666667 6 10 31.666666667 +8 10 31.666666667 10 10 31.666666667 12 10 31.666666667 +14 10 31.666666667 16 10 31.666666667 18 10 31.666666667 +20 10 31.666666667 0 12.5 31.666666667 2 12.5 31.666666667 +4 12.5 31.666666667 6 12.5 31.666666667 8 12.5 31.666666667 +10 12.5 31.666666667 12 12.5 31.666666667 14 12.5 31.666666667 +16 12.5 31.666666667 18 12.5 31.666666667 20 12.5 31.666666667 +0 15 31.666666667 2 15 31.666666667 4 15 31.666666667 +6 15 31.666666667 8 15 31.666666667 10 15 31.666666667 +12 15 31.666666667 14 15 31.666666667 16 15 31.666666667 +18 15 31.666666667 20 15 31.666666667 0 17.5 31.666666667 +2 17.5 31.666666667 4 17.5 31.666666667 6 17.5 31.666666667 +8 17.5 31.666666667 10 17.5 31.666666667 12 17.5 31.666666667 +14 17.5 31.666666667 16 17.5 31.666666667 18 17.5 31.666666667 +20 17.5 31.666666667 0 20 31.666666667 2 20 31.666666667 +4 20 31.666666667 6 20 31.666666667 8 20 31.666666667 +10 20 31.666666667 12 20 31.666666667 14 20 31.666666667 +16 20 31.666666667 18 20 31.666666667 20 20 31.666666667 +0 22.5 31.666666667 2 22.5 31.666666667 4 22.5 31.666666667 +6 22.5 31.666666667 8 22.5 31.666666667 10 22.5 31.666666667 +12 22.5 31.666666667 14 22.5 31.666666667 16 22.5 31.666666667 +18 22.5 31.666666667 20 22.5 31.666666667 0 25 31.666666667 +2 25 31.666666667 4 25 31.666666667 6 25 31.666666667 +8 25 31.666666667 10 25 31.666666667 12 25 31.666666667 +14 25 31.666666667 16 25 31.666666667 18 25 31.666666667 +20 25 31.666666667 0 27.5 31.666666667 2 27.5 31.666666667 +4 27.5 31.666666667 6 27.5 31.666666667 8 27.5 31.666666667 +10 27.5 31.666666667 12 27.5 31.666666667 14 27.5 31.666666667 +16 27.5 31.666666667 18 27.5 31.666666667 20 27.5 31.666666667 +0 30 31.666666667 2 30 31.666666667 4 30 31.666666667 +6 30 31.666666667 8 30 31.666666667 10 30 31.666666667 +12 30 31.666666667 14 30 31.666666667 16 30 31.666666667 +18 30 31.666666667 20 30 31.666666667 0 32.5 31.666666667 +2 32.5 31.666666667 4 32.5 31.666666667 6 32.5 31.666666667 +8 32.5 31.666666667 10 32.5 31.666666667 12 32.5 31.666666667 +14 32.5 31.666666667 16 32.5 31.666666667 18 32.5 31.666666667 +20 32.5 31.666666667 0 35 31.666666667 2 35 31.666666667 +4 35 31.666666667 6 35 31.666666667 8 35 31.666666667 +10 35 31.666666667 12 35 31.666666667 14 35 31.666666667 +16 35 31.666666667 18 35 31.666666667 20 35 31.666666667 +0 37.5 31.666666667 2 37.5 31.666666667 4 37.5 31.666666667 +6 37.5 31.666666667 8 37.5 31.666666667 10 37.5 31.666666667 +12 37.5 31.666666667 14 37.5 31.666666667 16 37.5 31.666666667 +18 37.5 31.666666667 20 37.5 31.666666667 0 40 31.666666667 +2 40 31.666666667 4 40 31.666666667 6 40 31.666666667 +8 40 31.666666667 10 40 31.666666667 12 40 31.666666667 +14 40 31.666666667 16 40 31.666666667 18 40 31.666666667 +20 40 31.666666667 0 42.5 31.666666667 2 42.5 31.666666667 +4 42.5 31.666666667 6 42.5 31.666666667 8 42.5 31.666666667 +10 42.5 31.666666667 12 42.5 31.666666667 14 42.5 31.666666667 +16 42.5 31.666666667 18 42.5 31.666666667 20 42.5 31.666666667 +0 45 31.666666667 2 45 31.666666667 4 45 31.666666667 +6 45 31.666666667 8 45 31.666666667 10 45 31.666666667 +12 45 31.666666667 14 45 31.666666667 16 45 31.666666667 +18 45 31.666666667 20 45 31.666666667 0 47.5 31.666666667 +2 47.5 31.666666667 4 47.5 31.666666667 6 47.5 31.666666667 +8 47.5 31.666666667 10 47.5 31.666666667 12 47.5 31.666666667 +14 47.5 31.666666667 16 47.5 31.666666667 18 47.5 31.666666667 +20 47.5 31.666666667 0 50 31.666666667 2 50 31.666666667 +4 50 31.666666667 6 50 31.666666667 8 50 31.666666667 +10 50 31.666666667 12 50 31.666666667 14 50 31.666666667 +16 50 31.666666667 18 50 31.666666667 20 50 31.666666667 +0 0 33.333333333 2 0 33.333333333 4 0 33.333333333 +6 0 33.333333333 8 0 33.333333333 10 0 33.333333333 +12 0 33.333333333 14 0 33.333333333 16 0 33.333333333 +18 0 33.333333333 20 0 33.333333333 0 2.5 33.333333333 +2 2.5 33.333333333 4 2.5 33.333333333 6 2.5 33.333333333 +8 2.5 33.333333333 10 2.5 33.333333333 12 2.5 33.333333333 +14 2.5 33.333333333 16 2.5 33.333333333 18 2.5 33.333333333 +20 2.5 33.333333333 0 5 33.333333333 2 5 33.333333333 +4 5 33.333333333 6 5 33.333333333 8 5 33.333333333 +10 5 33.333333333 12 5 33.333333333 14 5 33.333333333 +16 5 33.333333333 18 5 33.333333333 20 5 33.333333333 +0 7.5 33.333333333 2 7.5 33.333333333 4 7.5 33.333333333 +6 7.5 33.333333333 8 7.5 33.333333333 10 7.5 33.333333333 +12 7.5 33.333333333 14 7.5 33.333333333 16 7.5 33.333333333 +18 7.5 33.333333333 20 7.5 33.333333333 0 10 33.333333333 +2 10 33.333333333 4 10 33.333333333 6 10 33.333333333 +8 10 33.333333333 10 10 33.333333333 12 10 33.333333333 +14 10 33.333333333 16 10 33.333333333 18 10 33.333333333 +20 10 33.333333333 0 12.5 33.333333333 2 12.5 33.333333333 +4 12.5 33.333333333 6 12.5 33.333333333 8 12.5 33.333333333 +10 12.5 33.333333333 12 12.5 33.333333333 14 12.5 33.333333333 +16 12.5 33.333333333 18 12.5 33.333333333 20 12.5 33.333333333 +0 15 33.333333333 2 15 33.333333333 4 15 33.333333333 +6 15 33.333333333 8 15 33.333333333 10 15 33.333333333 +12 15 33.333333333 14 15 33.333333333 16 15 33.333333333 +18 15 33.333333333 20 15 33.333333333 0 17.5 33.333333333 +2 17.5 33.333333333 4 17.5 33.333333333 6 17.5 33.333333333 +8 17.5 33.333333333 10 17.5 33.333333333 12 17.5 33.333333333 +14 17.5 33.333333333 16 17.5 33.333333333 18 17.5 33.333333333 +20 17.5 33.333333333 0 20 33.333333333 2 20 33.333333333 +4 20 33.333333333 6 20 33.333333333 8 20 33.333333333 +10 20 33.333333333 12 20 33.333333333 14 20 33.333333333 +16 20 33.333333333 18 20 33.333333333 20 20 33.333333333 +0 22.5 33.333333333 2 22.5 33.333333333 4 22.5 33.333333333 +6 22.5 33.333333333 8 22.5 33.333333333 10 22.5 33.333333333 +12 22.5 33.333333333 14 22.5 33.333333333 16 22.5 33.333333333 +18 22.5 33.333333333 20 22.5 33.333333333 0 25 33.333333333 +2 25 33.333333333 4 25 33.333333333 6 25 33.333333333 +8 25 33.333333333 10 25 33.333333333 12 25 33.333333333 +14 25 33.333333333 16 25 33.333333333 18 25 33.333333333 +20 25 33.333333333 0 27.5 33.333333333 2 27.5 33.333333333 +4 27.5 33.333333333 6 27.5 33.333333333 8 27.5 33.333333333 +10 27.5 33.333333333 12 27.5 33.333333333 14 27.5 33.333333333 +16 27.5 33.333333333 18 27.5 33.333333333 20 27.5 33.333333333 +0 30 33.333333333 2 30 33.333333333 4 30 33.333333333 +6 30 33.333333333 8 30 33.333333333 10 30 33.333333333 +12 30 33.333333333 14 30 33.333333333 16 30 33.333333333 +18 30 33.333333333 20 30 33.333333333 0 32.5 33.333333333 +2 32.5 33.333333333 4 32.5 33.333333333 6 32.5 33.333333333 +8 32.5 33.333333333 10 32.5 33.333333333 12 32.5 33.333333333 +14 32.5 33.333333333 16 32.5 33.333333333 18 32.5 33.333333333 +20 32.5 33.333333333 0 35 33.333333333 2 35 33.333333333 +4 35 33.333333333 6 35 33.333333333 8 35 33.333333333 +10 35 33.333333333 12 35 33.333333333 14 35 33.333333333 +16 35 33.333333333 18 35 33.333333333 20 35 33.333333333 +0 37.5 33.333333333 2 37.5 33.333333333 4 37.5 33.333333333 +6 37.5 33.333333333 8 37.5 33.333333333 10 37.5 33.333333333 +12 37.5 33.333333333 14 37.5 33.333333333 16 37.5 33.333333333 +18 37.5 33.333333333 20 37.5 33.333333333 0 40 33.333333333 +2 40 33.333333333 4 40 33.333333333 6 40 33.333333333 +8 40 33.333333333 10 40 33.333333333 12 40 33.333333333 +14 40 33.333333333 16 40 33.333333333 18 40 33.333333333 +20 40 33.333333333 0 42.5 33.333333333 2 42.5 33.333333333 +4 42.5 33.333333333 6 42.5 33.333333333 8 42.5 33.333333333 +10 42.5 33.333333333 12 42.5 33.333333333 14 42.5 33.333333333 +16 42.5 33.333333333 18 42.5 33.333333333 20 42.5 33.333333333 +0 45 33.333333333 2 45 33.333333333 4 45 33.333333333 +6 45 33.333333333 8 45 33.333333333 10 45 33.333333333 +12 45 33.333333333 14 45 33.333333333 16 45 33.333333333 +18 45 33.333333333 20 45 33.333333333 0 47.5 33.333333333 +2 47.5 33.333333333 4 47.5 33.333333333 6 47.5 33.333333333 +8 47.5 33.333333333 10 47.5 33.333333333 12 47.5 33.333333333 +14 47.5 33.333333333 16 47.5 33.333333333 18 47.5 33.333333333 +20 47.5 33.333333333 0 50 33.333333333 2 50 33.333333333 +4 50 33.333333333 6 50 33.333333333 8 50 33.333333333 +10 50 33.333333333 12 50 33.333333333 14 50 33.333333333 +16 50 33.333333333 18 50 33.333333333 20 50 33.333333333 +0 0 35 2 0 35 4 0 35 +6 0 35 8 0 35 10 0 35 +12 0 35 14 0 35 16 0 35 +18 0 35 20 0 35 0 2.5 35 +2 2.5 35 4 2.5 35 6 2.5 35 +8 2.5 35 10 2.5 35 12 2.5 35 +14 2.5 35 16 2.5 35 18 2.5 35 +20 2.5 35 0 5 35 2 5 35 +4 5 35 6 5 35 8 5 35 +10 5 35 12 5 35 14 5 35 +16 5 35 18 5 35 20 5 35 +0 7.5 35 2 7.5 35 4 7.5 35 +6 7.5 35 8 7.5 35 10 7.5 35 +12 7.5 35 14 7.5 35 16 7.5 35 +18 7.5 35 20 7.5 35 0 10 35 +2 10 35 4 10 35 6 10 35 +8 10 35 10 10 35 12 10 35 +14 10 35 16 10 35 18 10 35 +20 10 35 0 12.5 35 2 12.5 35 +4 12.5 35 6 12.5 35 8 12.5 35 +10 12.5 35 12 12.5 35 14 12.5 35 +16 12.5 35 18 12.5 35 20 12.5 35 +0 15 35 2 15 35 4 15 35 +6 15 35 8 15 35 10 15 35 +12 15 35 14 15 35 16 15 35 +18 15 35 20 15 35 0 17.5 35 +2 17.5 35 4 17.5 35 6 17.5 35 +8 17.5 35 10 17.5 35 12 17.5 35 +14 17.5 35 16 17.5 35 18 17.5 35 +20 17.5 35 0 20 35 2 20 35 +4 20 35 6 20 35 8 20 35 +10 20 35 12 20 35 14 20 35 +16 20 35 18 20 35 20 20 35 +0 22.5 35 2 22.5 35 4 22.5 35 +6 22.5 35 8 22.5 35 10 22.5 35 +12 22.5 35 14 22.5 35 16 22.5 35 +18 22.5 35 20 22.5 35 0 25 35 +2 25 35 4 25 35 6 25 35 +8 25 35 10 25 35 12 25 35 +14 25 35 16 25 35 18 25 35 +20 25 35 0 27.5 35 2 27.5 35 +4 27.5 35 6 27.5 35 8 27.5 35 +10 27.5 35 12 27.5 35 14 27.5 35 +16 27.5 35 18 27.5 35 20 27.5 35 +0 30 35 2 30 35 4 30 35 +6 30 35 8 30 35 10 30 35 +12 30 35 14 30 35 16 30 35 +18 30 35 20 30 35 0 32.5 35 +2 32.5 35 4 32.5 35 6 32.5 35 +8 32.5 35 10 32.5 35 12 32.5 35 +14 32.5 35 16 32.5 35 18 32.5 35 +20 32.5 35 0 35 35 2 35 35 +4 35 35 6 35 35 8 35 35 +10 35 35 12 35 35 14 35 35 +16 35 35 18 35 35 20 35 35 +0 37.5 35 2 37.5 35 4 37.5 35 +6 37.5 35 8 37.5 35 10 37.5 35 +12 37.5 35 14 37.5 35 16 37.5 35 +18 37.5 35 20 37.5 35 0 40 35 +2 40 35 4 40 35 6 40 35 +8 40 35 10 40 35 12 40 35 +14 40 35 16 40 35 18 40 35 +20 40 35 0 42.5 35 2 42.5 35 +4 42.5 35 6 42.5 35 8 42.5 35 +10 42.5 35 12 42.5 35 14 42.5 35 +16 42.5 35 18 42.5 35 20 42.5 35 +0 45 35 2 45 35 4 45 35 +6 45 35 8 45 35 10 45 35 +12 45 35 14 45 35 16 45 35 +18 45 35 20 45 35 0 47.5 35 +2 47.5 35 4 47.5 35 6 47.5 35 +8 47.5 35 10 47.5 35 12 47.5 35 +14 47.5 35 16 47.5 35 18 47.5 35 +20 47.5 35 0 50 35 2 50 35 +4 50 35 6 50 35 8 50 35 +10 50 35 12 50 35 14 50 35 +16 50 35 18 50 35 20 50 35 +0 0 36.666666667 2 0 36.666666667 4 0 36.666666667 +6 0 36.666666667 8 0 36.666666667 10 0 36.666666667 +12 0 36.666666667 14 0 36.666666667 16 0 36.666666667 +18 0 36.666666667 20 0 36.666666667 0 2.5 36.666666667 +2 2.5 36.666666667 4 2.5 36.666666667 6 2.5 36.666666667 +8 2.5 36.666666667 10 2.5 36.666666667 12 2.5 36.666666667 +14 2.5 36.666666667 16 2.5 36.666666667 18 2.5 36.666666667 +20 2.5 36.666666667 0 5 36.666666667 2 5 36.666666667 +4 5 36.666666667 6 5 36.666666667 8 5 36.666666667 +10 5 36.666666667 12 5 36.666666667 14 5 36.666666667 +16 5 36.666666667 18 5 36.666666667 20 5 36.666666667 +0 7.5 36.666666667 2 7.5 36.666666667 4 7.5 36.666666667 +6 7.5 36.666666667 8 7.5 36.666666667 10 7.5 36.666666667 +12 7.5 36.666666667 14 7.5 36.666666667 16 7.5 36.666666667 +18 7.5 36.666666667 20 7.5 36.666666667 0 10 36.666666667 +2 10 36.666666667 4 10 36.666666667 6 10 36.666666667 +8 10 36.666666667 10 10 36.666666667 12 10 36.666666667 +14 10 36.666666667 16 10 36.666666667 18 10 36.666666667 +20 10 36.666666667 0 12.5 36.666666667 2 12.5 36.666666667 +4 12.5 36.666666667 6 12.5 36.666666667 8 12.5 36.666666667 +10 12.5 36.666666667 12 12.5 36.666666667 14 12.5 36.666666667 +16 12.5 36.666666667 18 12.5 36.666666667 20 12.5 36.666666667 +0 15 36.666666667 2 15 36.666666667 4 15 36.666666667 +6 15 36.666666667 8 15 36.666666667 10 15 36.666666667 +12 15 36.666666667 14 15 36.666666667 16 15 36.666666667 +18 15 36.666666667 20 15 36.666666667 0 17.5 36.666666667 +2 17.5 36.666666667 4 17.5 36.666666667 6 17.5 36.666666667 +8 17.5 36.666666667 10 17.5 36.666666667 12 17.5 36.666666667 +14 17.5 36.666666667 16 17.5 36.666666667 18 17.5 36.666666667 +20 17.5 36.666666667 0 20 36.666666667 2 20 36.666666667 +4 20 36.666666667 6 20 36.666666667 8 20 36.666666667 +10 20 36.666666667 12 20 36.666666667 14 20 36.666666667 +16 20 36.666666667 18 20 36.666666667 20 20 36.666666667 +0 22.5 36.666666667 2 22.5 36.666666667 4 22.5 36.666666667 +6 22.5 36.666666667 8 22.5 36.666666667 10 22.5 36.666666667 +12 22.5 36.666666667 14 22.5 36.666666667 16 22.5 36.666666667 +18 22.5 36.666666667 20 22.5 36.666666667 0 25 36.666666667 +2 25 36.666666667 4 25 36.666666667 6 25 36.666666667 +8 25 36.666666667 10 25 36.666666667 12 25 36.666666667 +14 25 36.666666667 16 25 36.666666667 18 25 36.666666667 +20 25 36.666666667 0 27.5 36.666666667 2 27.5 36.666666667 +4 27.5 36.666666667 6 27.5 36.666666667 8 27.5 36.666666667 +10 27.5 36.666666667 12 27.5 36.666666667 14 27.5 36.666666667 +16 27.5 36.666666667 18 27.5 36.666666667 20 27.5 36.666666667 +0 30 36.666666667 2 30 36.666666667 4 30 36.666666667 +6 30 36.666666667 8 30 36.666666667 10 30 36.666666667 +12 30 36.666666667 14 30 36.666666667 16 30 36.666666667 +18 30 36.666666667 20 30 36.666666667 0 32.5 36.666666667 +2 32.5 36.666666667 4 32.5 36.666666667 6 32.5 36.666666667 +8 32.5 36.666666667 10 32.5 36.666666667 12 32.5 36.666666667 +14 32.5 36.666666667 16 32.5 36.666666667 18 32.5 36.666666667 +20 32.5 36.666666667 0 35 36.666666667 2 35 36.666666667 +4 35 36.666666667 6 35 36.666666667 8 35 36.666666667 +10 35 36.666666667 12 35 36.666666667 14 35 36.666666667 +16 35 36.666666667 18 35 36.666666667 20 35 36.666666667 +0 37.5 36.666666667 2 37.5 36.666666667 4 37.5 36.666666667 +6 37.5 36.666666667 8 37.5 36.666666667 10 37.5 36.666666667 +12 37.5 36.666666667 14 37.5 36.666666667 16 37.5 36.666666667 +18 37.5 36.666666667 20 37.5 36.666666667 0 40 36.666666667 +2 40 36.666666667 4 40 36.666666667 6 40 36.666666667 +8 40 36.666666667 10 40 36.666666667 12 40 36.666666667 +14 40 36.666666667 16 40 36.666666667 18 40 36.666666667 +20 40 36.666666667 0 42.5 36.666666667 2 42.5 36.666666667 +4 42.5 36.666666667 6 42.5 36.666666667 8 42.5 36.666666667 +10 42.5 36.666666667 12 42.5 36.666666667 14 42.5 36.666666667 +16 42.5 36.666666667 18 42.5 36.666666667 20 42.5 36.666666667 +0 45 36.666666667 2 45 36.666666667 4 45 36.666666667 +6 45 36.666666667 8 45 36.666666667 10 45 36.666666667 +12 45 36.666666667 14 45 36.666666667 16 45 36.666666667 +18 45 36.666666667 20 45 36.666666667 0 47.5 36.666666667 +2 47.5 36.666666667 4 47.5 36.666666667 6 47.5 36.666666667 +8 47.5 36.666666667 10 47.5 36.666666667 12 47.5 36.666666667 +14 47.5 36.666666667 16 47.5 36.666666667 18 47.5 36.666666667 +20 47.5 36.666666667 0 50 36.666666667 2 50 36.666666667 +4 50 36.666666667 6 50 36.666666667 8 50 36.666666667 +10 50 36.666666667 12 50 36.666666667 14 50 36.666666667 +16 50 36.666666667 18 50 36.666666667 20 50 36.666666667 +0 0 38.333333333 2 0 38.333333333 4 0 38.333333333 +6 0 38.333333333 8 0 38.333333333 10 0 38.333333333 +12 0 38.333333333 14 0 38.333333333 16 0 38.333333333 +18 0 38.333333333 20 0 38.333333333 0 2.5 38.333333333 +2 2.5 38.333333333 4 2.5 38.333333333 6 2.5 38.333333333 +8 2.5 38.333333333 10 2.5 38.333333333 12 2.5 38.333333333 +14 2.5 38.333333333 16 2.5 38.333333333 18 2.5 38.333333333 +20 2.5 38.333333333 0 5 38.333333333 2 5 38.333333333 +4 5 38.333333333 6 5 38.333333333 8 5 38.333333333 +10 5 38.333333333 12 5 38.333333333 14 5 38.333333333 +16 5 38.333333333 18 5 38.333333333 20 5 38.333333333 +0 7.5 38.333333333 2 7.5 38.333333333 4 7.5 38.333333333 +6 7.5 38.333333333 8 7.5 38.333333333 10 7.5 38.333333333 +12 7.5 38.333333333 14 7.5 38.333333333 16 7.5 38.333333333 +18 7.5 38.333333333 20 7.5 38.333333333 0 10 38.333333333 +2 10 38.333333333 4 10 38.333333333 6 10 38.333333333 +8 10 38.333333333 10 10 38.333333333 12 10 38.333333333 +14 10 38.333333333 16 10 38.333333333 18 10 38.333333333 +20 10 38.333333333 0 12.5 38.333333333 2 12.5 38.333333333 +4 12.5 38.333333333 6 12.5 38.333333333 8 12.5 38.333333333 +10 12.5 38.333333333 12 12.5 38.333333333 14 12.5 38.333333333 +16 12.5 38.333333333 18 12.5 38.333333333 20 12.5 38.333333333 +0 15 38.333333333 2 15 38.333333333 4 15 38.333333333 +6 15 38.333333333 8 15 38.333333333 10 15 38.333333333 +12 15 38.333333333 14 15 38.333333333 16 15 38.333333333 +18 15 38.333333333 20 15 38.333333333 0 17.5 38.333333333 +2 17.5 38.333333333 4 17.5 38.333333333 6 17.5 38.333333333 +8 17.5 38.333333333 10 17.5 38.333333333 12 17.5 38.333333333 +14 17.5 38.333333333 16 17.5 38.333333333 18 17.5 38.333333333 +20 17.5 38.333333333 0 20 38.333333333 2 20 38.333333333 +4 20 38.333333333 6 20 38.333333333 8 20 38.333333333 +10 20 38.333333333 12 20 38.333333333 14 20 38.333333333 +16 20 38.333333333 18 20 38.333333333 20 20 38.333333333 +0 22.5 38.333333333 2 22.5 38.333333333 4 22.5 38.333333333 +6 22.5 38.333333333 8 22.5 38.333333333 10 22.5 38.333333333 +12 22.5 38.333333333 14 22.5 38.333333333 16 22.5 38.333333333 +18 22.5 38.333333333 20 22.5 38.333333333 0 25 38.333333333 +2 25 38.333333333 4 25 38.333333333 6 25 38.333333333 +8 25 38.333333333 10 25 38.333333333 12 25 38.333333333 +14 25 38.333333333 16 25 38.333333333 18 25 38.333333333 +20 25 38.333333333 0 27.5 38.333333333 2 27.5 38.333333333 +4 27.5 38.333333333 6 27.5 38.333333333 8 27.5 38.333333333 +10 27.5 38.333333333 12 27.5 38.333333333 14 27.5 38.333333333 +16 27.5 38.333333333 18 27.5 38.333333333 20 27.5 38.333333333 +0 30 38.333333333 2 30 38.333333333 4 30 38.333333333 +6 30 38.333333333 8 30 38.333333333 10 30 38.333333333 +12 30 38.333333333 14 30 38.333333333 16 30 38.333333333 +18 30 38.333333333 20 30 38.333333333 0 32.5 38.333333333 +2 32.5 38.333333333 4 32.5 38.333333333 6 32.5 38.333333333 +8 32.5 38.333333333 10 32.5 38.333333333 12 32.5 38.333333333 +14 32.5 38.333333333 16 32.5 38.333333333 18 32.5 38.333333333 +20 32.5 38.333333333 0 35 38.333333333 2 35 38.333333333 +4 35 38.333333333 6 35 38.333333333 8 35 38.333333333 +10 35 38.333333333 12 35 38.333333333 14 35 38.333333333 +16 35 38.333333333 18 35 38.333333333 20 35 38.333333333 +0 37.5 38.333333333 2 37.5 38.333333333 4 37.5 38.333333333 +6 37.5 38.333333333 8 37.5 38.333333333 10 37.5 38.333333333 +12 37.5 38.333333333 14 37.5 38.333333333 16 37.5 38.333333333 +18 37.5 38.333333333 20 37.5 38.333333333 0 40 38.333333333 +2 40 38.333333333 4 40 38.333333333 6 40 38.333333333 +8 40 38.333333333 10 40 38.333333333 12 40 38.333333333 +14 40 38.333333333 16 40 38.333333333 18 40 38.333333333 +20 40 38.333333333 0 42.5 38.333333333 2 42.5 38.333333333 +4 42.5 38.333333333 6 42.5 38.333333333 8 42.5 38.333333333 +10 42.5 38.333333333 12 42.5 38.333333333 14 42.5 38.333333333 +16 42.5 38.333333333 18 42.5 38.333333333 20 42.5 38.333333333 +0 45 38.333333333 2 45 38.333333333 4 45 38.333333333 +6 45 38.333333333 8 45 38.333333333 10 45 38.333333333 +12 45 38.333333333 14 45 38.333333333 16 45 38.333333333 +18 45 38.333333333 20 45 38.333333333 0 47.5 38.333333333 +2 47.5 38.333333333 4 47.5 38.333333333 6 47.5 38.333333333 +8 47.5 38.333333333 10 47.5 38.333333333 12 47.5 38.333333333 +14 47.5 38.333333333 16 47.5 38.333333333 18 47.5 38.333333333 +20 47.5 38.333333333 0 50 38.333333333 2 50 38.333333333 +4 50 38.333333333 6 50 38.333333333 8 50 38.333333333 +10 50 38.333333333 12 50 38.333333333 14 50 38.333333333 +16 50 38.333333333 18 50 38.333333333 20 50 38.333333333 +0 0 40 2 0 40 4 0 40 +6 0 40 8 0 40 10 0 40 +12 0 40 14 0 40 16 0 40 +18 0 40 20 0 40 0 2.5 40 +2 2.5 40 4 2.5 40 6 2.5 40 +8 2.5 40 10 2.5 40 12 2.5 40 +14 2.5 40 16 2.5 40 18 2.5 40 +20 2.5 40 0 5 40 2 5 40 +4 5 40 6 5 40 8 5 40 +10 5 40 12 5 40 14 5 40 +16 5 40 18 5 40 20 5 40 +0 7.5 40 2 7.5 40 4 7.5 40 +6 7.5 40 8 7.5 40 10 7.5 40 +12 7.5 40 14 7.5 40 16 7.5 40 +18 7.5 40 20 7.5 40 0 10 40 +2 10 40 4 10 40 6 10 40 +8 10 40 10 10 40 12 10 40 +14 10 40 16 10 40 18 10 40 +20 10 40 0 12.5 40 2 12.5 40 +4 12.5 40 6 12.5 40 8 12.5 40 +10 12.5 40 12 12.5 40 14 12.5 40 +16 12.5 40 18 12.5 40 20 12.5 40 +0 15 40 2 15 40 4 15 40 +6 15 40 8 15 40 10 15 40 +12 15 40 14 15 40 16 15 40 +18 15 40 20 15 40 0 17.5 40 +2 17.5 40 4 17.5 40 6 17.5 40 +8 17.5 40 10 17.5 40 12 17.5 40 +14 17.5 40 16 17.5 40 18 17.5 40 +20 17.5 40 0 20 40 2 20 40 +4 20 40 6 20 40 8 20 40 +10 20 40 12 20 40 14 20 40 +16 20 40 18 20 40 20 20 40 +0 22.5 40 2 22.5 40 4 22.5 40 +6 22.5 40 8 22.5 40 10 22.5 40 +12 22.5 40 14 22.5 40 16 22.5 40 +18 22.5 40 20 22.5 40 0 25 40 +2 25 40 4 25 40 6 25 40 +8 25 40 10 25 40 12 25 40 +14 25 40 16 25 40 18 25 40 +20 25 40 0 27.5 40 2 27.5 40 +4 27.5 40 6 27.5 40 8 27.5 40 +10 27.5 40 12 27.5 40 14 27.5 40 +16 27.5 40 18 27.5 40 20 27.5 40 +0 30 40 2 30 40 4 30 40 +6 30 40 8 30 40 10 30 40 +12 30 40 14 30 40 16 30 40 +18 30 40 20 30 40 0 32.5 40 +2 32.5 40 4 32.5 40 6 32.5 40 +8 32.5 40 10 32.5 40 12 32.5 40 +14 32.5 40 16 32.5 40 18 32.5 40 +20 32.5 40 0 35 40 2 35 40 +4 35 40 6 35 40 8 35 40 +10 35 40 12 35 40 14 35 40 +16 35 40 18 35 40 20 35 40 +0 37.5 40 2 37.5 40 4 37.5 40 +6 37.5 40 8 37.5 40 10 37.5 40 +12 37.5 40 14 37.5 40 16 37.5 40 +18 37.5 40 20 37.5 40 0 40 40 +2 40 40 4 40 40 6 40 40 +8 40 40 10 40 40 12 40 40 +14 40 40 16 40 40 18 40 40 +20 40 40 0 42.5 40 2 42.5 40 +4 42.5 40 6 42.5 40 8 42.5 40 +10 42.5 40 12 42.5 40 14 42.5 40 +16 42.5 40 18 42.5 40 20 42.5 40 +0 45 40 2 45 40 4 45 40 +6 45 40 8 45 40 10 45 40 +12 45 40 14 45 40 16 45 40 +18 45 40 20 45 40 0 47.5 40 +2 47.5 40 4 47.5 40 6 47.5 40 +8 47.5 40 10 47.5 40 12 47.5 40 +14 47.5 40 16 47.5 40 18 47.5 40 +20 47.5 40 0 50 40 2 50 40 +4 50 40 6 50 40 8 50 40 +10 50 40 12 50 40 14 50 40 +16 50 40 18 50 40 20 50 40 +0 0 41.666666667 2 0 41.666666667 4 0 41.666666667 +6 0 41.666666667 8 0 41.666666667 10 0 41.666666667 +12 0 41.666666667 14 0 41.666666667 16 0 41.666666667 +18 0 41.666666667 20 0 41.666666667 0 2.5 41.666666667 +2 2.5 41.666666667 4 2.5 41.666666667 6 2.5 41.666666667 +8 2.5 41.666666667 10 2.5 41.666666667 12 2.5 41.666666667 +14 2.5 41.666666667 16 2.5 41.666666667 18 2.5 41.666666667 +20 2.5 41.666666667 0 5 41.666666667 2 5 41.666666667 +4 5 41.666666667 6 5 41.666666667 8 5 41.666666667 +10 5 41.666666667 12 5 41.666666667 14 5 41.666666667 +16 5 41.666666667 18 5 41.666666667 20 5 41.666666667 +0 7.5 41.666666667 2 7.5 41.666666667 4 7.5 41.666666667 +6 7.5 41.666666667 8 7.5 41.666666667 10 7.5 41.666666667 +12 7.5 41.666666667 14 7.5 41.666666667 16 7.5 41.666666667 +18 7.5 41.666666667 20 7.5 41.666666667 0 10 41.666666667 +2 10 41.666666667 4 10 41.666666667 6 10 41.666666667 +8 10 41.666666667 10 10 41.666666667 12 10 41.666666667 +14 10 41.666666667 16 10 41.666666667 18 10 41.666666667 +20 10 41.666666667 0 12.5 41.666666667 2 12.5 41.666666667 +4 12.5 41.666666667 6 12.5 41.666666667 8 12.5 41.666666667 +10 12.5 41.666666667 12 12.5 41.666666667 14 12.5 41.666666667 +16 12.5 41.666666667 18 12.5 41.666666667 20 12.5 41.666666667 +0 15 41.666666667 2 15 41.666666667 4 15 41.666666667 +6 15 41.666666667 8 15 41.666666667 10 15 41.666666667 +12 15 41.666666667 14 15 41.666666667 16 15 41.666666667 +18 15 41.666666667 20 15 41.666666667 0 17.5 41.666666667 +2 17.5 41.666666667 4 17.5 41.666666667 6 17.5 41.666666667 +8 17.5 41.666666667 10 17.5 41.666666667 12 17.5 41.666666667 +14 17.5 41.666666667 16 17.5 41.666666667 18 17.5 41.666666667 +20 17.5 41.666666667 0 20 41.666666667 2 20 41.666666667 +4 20 41.666666667 6 20 41.666666667 8 20 41.666666667 +10 20 41.666666667 12 20 41.666666667 14 20 41.666666667 +16 20 41.666666667 18 20 41.666666667 20 20 41.666666667 +0 22.5 41.666666667 2 22.5 41.666666667 4 22.5 41.666666667 +6 22.5 41.666666667 8 22.5 41.666666667 10 22.5 41.666666667 +12 22.5 41.666666667 14 22.5 41.666666667 16 22.5 41.666666667 +18 22.5 41.666666667 20 22.5 41.666666667 0 25 41.666666667 +2 25 41.666666667 4 25 41.666666667 6 25 41.666666667 +8 25 41.666666667 10 25 41.666666667 12 25 41.666666667 +14 25 41.666666667 16 25 41.666666667 18 25 41.666666667 +20 25 41.666666667 0 27.5 41.666666667 2 27.5 41.666666667 +4 27.5 41.666666667 6 27.5 41.666666667 8 27.5 41.666666667 +10 27.5 41.666666667 12 27.5 41.666666667 14 27.5 41.666666667 +16 27.5 41.666666667 18 27.5 41.666666667 20 27.5 41.666666667 +0 30 41.666666667 2 30 41.666666667 4 30 41.666666667 +6 30 41.666666667 8 30 41.666666667 10 30 41.666666667 +12 30 41.666666667 14 30 41.666666667 16 30 41.666666667 +18 30 41.666666667 20 30 41.666666667 0 32.5 41.666666667 +2 32.5 41.666666667 4 32.5 41.666666667 6 32.5 41.666666667 +8 32.5 41.666666667 10 32.5 41.666666667 12 32.5 41.666666667 +14 32.5 41.666666667 16 32.5 41.666666667 18 32.5 41.666666667 +20 32.5 41.666666667 0 35 41.666666667 2 35 41.666666667 +4 35 41.666666667 6 35 41.666666667 8 35 41.666666667 +10 35 41.666666667 12 35 41.666666667 14 35 41.666666667 +16 35 41.666666667 18 35 41.666666667 20 35 41.666666667 +0 37.5 41.666666667 2 37.5 41.666666667 4 37.5 41.666666667 +6 37.5 41.666666667 8 37.5 41.666666667 10 37.5 41.666666667 +12 37.5 41.666666667 14 37.5 41.666666667 16 37.5 41.666666667 +18 37.5 41.666666667 20 37.5 41.666666667 0 40 41.666666667 +2 40 41.666666667 4 40 41.666666667 6 40 41.666666667 +8 40 41.666666667 10 40 41.666666667 12 40 41.666666667 +14 40 41.666666667 16 40 41.666666667 18 40 41.666666667 +20 40 41.666666667 0 42.5 41.666666667 2 42.5 41.666666667 +4 42.5 41.666666667 6 42.5 41.666666667 8 42.5 41.666666667 +10 42.5 41.666666667 12 42.5 41.666666667 14 42.5 41.666666667 +16 42.5 41.666666667 18 42.5 41.666666667 20 42.5 41.666666667 +0 45 41.666666667 2 45 41.666666667 4 45 41.666666667 +6 45 41.666666667 8 45 41.666666667 10 45 41.666666667 +12 45 41.666666667 14 45 41.666666667 16 45 41.666666667 +18 45 41.666666667 20 45 41.666666667 0 47.5 41.666666667 +2 47.5 41.666666667 4 47.5 41.666666667 6 47.5 41.666666667 +8 47.5 41.666666667 10 47.5 41.666666667 12 47.5 41.666666667 +14 47.5 41.666666667 16 47.5 41.666666667 18 47.5 41.666666667 +20 47.5 41.666666667 0 50 41.666666667 2 50 41.666666667 +4 50 41.666666667 6 50 41.666666667 8 50 41.666666667 +10 50 41.666666667 12 50 41.666666667 14 50 41.666666667 +16 50 41.666666667 18 50 41.666666667 20 50 41.666666667 +0 0 43.333333333 2 0 43.333333333 4 0 43.333333333 +6 0 43.333333333 8 0 43.333333333 10 0 43.333333333 +12 0 43.333333333 14 0 43.333333333 16 0 43.333333333 +18 0 43.333333333 20 0 43.333333333 0 2.5 43.333333333 +2 2.5 43.333333333 4 2.5 43.333333333 6 2.5 43.333333333 +8 2.5 43.333333333 10 2.5 43.333333333 12 2.5 43.333333333 +14 2.5 43.333333333 16 2.5 43.333333333 18 2.5 43.333333333 +20 2.5 43.333333333 0 5 43.333333333 2 5 43.333333333 +4 5 43.333333333 6 5 43.333333333 8 5 43.333333333 +10 5 43.333333333 12 5 43.333333333 14 5 43.333333333 +16 5 43.333333333 18 5 43.333333333 20 5 43.333333333 +0 7.5 43.333333333 2 7.5 43.333333333 4 7.5 43.333333333 +6 7.5 43.333333333 8 7.5 43.333333333 10 7.5 43.333333333 +12 7.5 43.333333333 14 7.5 43.333333333 16 7.5 43.333333333 +18 7.5 43.333333333 20 7.5 43.333333333 0 10 43.333333333 +2 10 43.333333333 4 10 43.333333333 6 10 43.333333333 +8 10 43.333333333 10 10 43.333333333 12 10 43.333333333 +14 10 43.333333333 16 10 43.333333333 18 10 43.333333333 +20 10 43.333333333 0 12.5 43.333333333 2 12.5 43.333333333 +4 12.5 43.333333333 6 12.5 43.333333333 8 12.5 43.333333333 +10 12.5 43.333333333 12 12.5 43.333333333 14 12.5 43.333333333 +16 12.5 43.333333333 18 12.5 43.333333333 20 12.5 43.333333333 +0 15 43.333333333 2 15 43.333333333 4 15 43.333333333 +6 15 43.333333333 8 15 43.333333333 10 15 43.333333333 +12 15 43.333333333 14 15 43.333333333 16 15 43.333333333 +18 15 43.333333333 20 15 43.333333333 0 17.5 43.333333333 +2 17.5 43.333333333 4 17.5 43.333333333 6 17.5 43.333333333 +8 17.5 43.333333333 10 17.5 43.333333333 12 17.5 43.333333333 +14 17.5 43.333333333 16 17.5 43.333333333 18 17.5 43.333333333 +20 17.5 43.333333333 0 20 43.333333333 2 20 43.333333333 +4 20 43.333333333 6 20 43.333333333 8 20 43.333333333 +10 20 43.333333333 12 20 43.333333333 14 20 43.333333333 +16 20 43.333333333 18 20 43.333333333 20 20 43.333333333 +0 22.5 43.333333333 2 22.5 43.333333333 4 22.5 43.333333333 +6 22.5 43.333333333 8 22.5 43.333333333 10 22.5 43.333333333 +12 22.5 43.333333333 14 22.5 43.333333333 16 22.5 43.333333333 +18 22.5 43.333333333 20 22.5 43.333333333 0 25 43.333333333 +2 25 43.333333333 4 25 43.333333333 6 25 43.333333333 +8 25 43.333333333 10 25 43.333333333 12 25 43.333333333 +14 25 43.333333333 16 25 43.333333333 18 25 43.333333333 +20 25 43.333333333 0 27.5 43.333333333 2 27.5 43.333333333 +4 27.5 43.333333333 6 27.5 43.333333333 8 27.5 43.333333333 +10 27.5 43.333333333 12 27.5 43.333333333 14 27.5 43.333333333 +16 27.5 43.333333333 18 27.5 43.333333333 20 27.5 43.333333333 +0 30 43.333333333 2 30 43.333333333 4 30 43.333333333 +6 30 43.333333333 8 30 43.333333333 10 30 43.333333333 +12 30 43.333333333 14 30 43.333333333 16 30 43.333333333 +18 30 43.333333333 20 30 43.333333333 0 32.5 43.333333333 +2 32.5 43.333333333 4 32.5 43.333333333 6 32.5 43.333333333 +8 32.5 43.333333333 10 32.5 43.333333333 12 32.5 43.333333333 +14 32.5 43.333333333 16 32.5 43.333333333 18 32.5 43.333333333 +20 32.5 43.333333333 0 35 43.333333333 2 35 43.333333333 +4 35 43.333333333 6 35 43.333333333 8 35 43.333333333 +10 35 43.333333333 12 35 43.333333333 14 35 43.333333333 +16 35 43.333333333 18 35 43.333333333 20 35 43.333333333 +0 37.5 43.333333333 2 37.5 43.333333333 4 37.5 43.333333333 +6 37.5 43.333333333 8 37.5 43.333333333 10 37.5 43.333333333 +12 37.5 43.333333333 14 37.5 43.333333333 16 37.5 43.333333333 +18 37.5 43.333333333 20 37.5 43.333333333 0 40 43.333333333 +2 40 43.333333333 4 40 43.333333333 6 40 43.333333333 +8 40 43.333333333 10 40 43.333333333 12 40 43.333333333 +14 40 43.333333333 16 40 43.333333333 18 40 43.333333333 +20 40 43.333333333 0 42.5 43.333333333 2 42.5 43.333333333 +4 42.5 43.333333333 6 42.5 43.333333333 8 42.5 43.333333333 +10 42.5 43.333333333 12 42.5 43.333333333 14 42.5 43.333333333 +16 42.5 43.333333333 18 42.5 43.333333333 20 42.5 43.333333333 +0 45 43.333333333 2 45 43.333333333 4 45 43.333333333 +6 45 43.333333333 8 45 43.333333333 10 45 43.333333333 +12 45 43.333333333 14 45 43.333333333 16 45 43.333333333 +18 45 43.333333333 20 45 43.333333333 0 47.5 43.333333333 +2 47.5 43.333333333 4 47.5 43.333333333 6 47.5 43.333333333 +8 47.5 43.333333333 10 47.5 43.333333333 12 47.5 43.333333333 +14 47.5 43.333333333 16 47.5 43.333333333 18 47.5 43.333333333 +20 47.5 43.333333333 0 50 43.333333333 2 50 43.333333333 +4 50 43.333333333 6 50 43.333333333 8 50 43.333333333 +10 50 43.333333333 12 50 43.333333333 14 50 43.333333333 +16 50 43.333333333 18 50 43.333333333 20 50 43.333333333 +0 0 45 2 0 45 4 0 45 +6 0 45 8 0 45 10 0 45 +12 0 45 14 0 45 16 0 45 +18 0 45 20 0 45 0 2.5 45 +2 2.5 45 4 2.5 45 6 2.5 45 +8 2.5 45 10 2.5 45 12 2.5 45 +14 2.5 45 16 2.5 45 18 2.5 45 +20 2.5 45 0 5 45 2 5 45 +4 5 45 6 5 45 8 5 45 +10 5 45 12 5 45 14 5 45 +16 5 45 18 5 45 20 5 45 +0 7.5 45 2 7.5 45 4 7.5 45 +6 7.5 45 8 7.5 45 10 7.5 45 +12 7.5 45 14 7.5 45 16 7.5 45 +18 7.5 45 20 7.5 45 0 10 45 +2 10 45 4 10 45 6 10 45 +8 10 45 10 10 45 12 10 45 +14 10 45 16 10 45 18 10 45 +20 10 45 0 12.5 45 2 12.5 45 +4 12.5 45 6 12.5 45 8 12.5 45 +10 12.5 45 12 12.5 45 14 12.5 45 +16 12.5 45 18 12.5 45 20 12.5 45 +0 15 45 2 15 45 4 15 45 +6 15 45 8 15 45 10 15 45 +12 15 45 14 15 45 16 15 45 +18 15 45 20 15 45 0 17.5 45 +2 17.5 45 4 17.5 45 6 17.5 45 +8 17.5 45 10 17.5 45 12 17.5 45 +14 17.5 45 16 17.5 45 18 17.5 45 +20 17.5 45 0 20 45 2 20 45 +4 20 45 6 20 45 8 20 45 +10 20 45 12 20 45 14 20 45 +16 20 45 18 20 45 20 20 45 +0 22.5 45 2 22.5 45 4 22.5 45 +6 22.5 45 8 22.5 45 10 22.5 45 +12 22.5 45 14 22.5 45 16 22.5 45 +18 22.5 45 20 22.5 45 0 25 45 +2 25 45 4 25 45 6 25 45 +8 25 45 10 25 45 12 25 45 +14 25 45 16 25 45 18 25 45 +20 25 45 0 27.5 45 2 27.5 45 +4 27.5 45 6 27.5 45 8 27.5 45 +10 27.5 45 12 27.5 45 14 27.5 45 +16 27.5 45 18 27.5 45 20 27.5 45 +0 30 45 2 30 45 4 30 45 +6 30 45 8 30 45 10 30 45 +12 30 45 14 30 45 16 30 45 +18 30 45 20 30 45 0 32.5 45 +2 32.5 45 4 32.5 45 6 32.5 45 +8 32.5 45 10 32.5 45 12 32.5 45 +14 32.5 45 16 32.5 45 18 32.5 45 +20 32.5 45 0 35 45 2 35 45 +4 35 45 6 35 45 8 35 45 +10 35 45 12 35 45 14 35 45 +16 35 45 18 35 45 20 35 45 +0 37.5 45 2 37.5 45 4 37.5 45 +6 37.5 45 8 37.5 45 10 37.5 45 +12 37.5 45 14 37.5 45 16 37.5 45 +18 37.5 45 20 37.5 45 0 40 45 +2 40 45 4 40 45 6 40 45 +8 40 45 10 40 45 12 40 45 +14 40 45 16 40 45 18 40 45 +20 40 45 0 42.5 45 2 42.5 45 +4 42.5 45 6 42.5 45 8 42.5 45 +10 42.5 45 12 42.5 45 14 42.5 45 +16 42.5 45 18 42.5 45 20 42.5 45 +0 45 45 2 45 45 4 45 45 +6 45 45 8 45 45 10 45 45 +12 45 45 14 45 45 16 45 45 +18 45 45 20 45 45 0 47.5 45 +2 47.5 45 4 47.5 45 6 47.5 45 +8 47.5 45 10 47.5 45 12 47.5 45 +14 47.5 45 16 47.5 45 18 47.5 45 +20 47.5 45 0 50 45 2 50 45 +4 50 45 6 50 45 8 50 45 +10 50 45 12 50 45 14 50 45 +16 50 45 18 50 45 20 50 45 +0 0 46.666666667 2 0 46.666666667 4 0 46.666666667 +6 0 46.666666667 8 0 46.666666667 10 0 46.666666667 +12 0 46.666666667 14 0 46.666666667 16 0 46.666666667 +18 0 46.666666667 20 0 46.666666667 0 2.5 46.666666667 +2 2.5 46.666666667 4 2.5 46.666666667 6 2.5 46.666666667 +8 2.5 46.666666667 10 2.5 46.666666667 12 2.5 46.666666667 +14 2.5 46.666666667 16 2.5 46.666666667 18 2.5 46.666666667 +20 2.5 46.666666667 0 5 46.666666667 2 5 46.666666667 +4 5 46.666666667 6 5 46.666666667 8 5 46.666666667 +10 5 46.666666667 12 5 46.666666667 14 5 46.666666667 +16 5 46.666666667 18 5 46.666666667 20 5 46.666666667 +0 7.5 46.666666667 2 7.5 46.666666667 4 7.5 46.666666667 +6 7.5 46.666666667 8 7.5 46.666666667 10 7.5 46.666666667 +12 7.5 46.666666667 14 7.5 46.666666667 16 7.5 46.666666667 +18 7.5 46.666666667 20 7.5 46.666666667 0 10 46.666666667 +2 10 46.666666667 4 10 46.666666667 6 10 46.666666667 +8 10 46.666666667 10 10 46.666666667 12 10 46.666666667 +14 10 46.666666667 16 10 46.666666667 18 10 46.666666667 +20 10 46.666666667 0 12.5 46.666666667 2 12.5 46.666666667 +4 12.5 46.666666667 6 12.5 46.666666667 8 12.5 46.666666667 +10 12.5 46.666666667 12 12.5 46.666666667 14 12.5 46.666666667 +16 12.5 46.666666667 18 12.5 46.666666667 20 12.5 46.666666667 +0 15 46.666666667 2 15 46.666666667 4 15 46.666666667 +6 15 46.666666667 8 15 46.666666667 10 15 46.666666667 +12 15 46.666666667 14 15 46.666666667 16 15 46.666666667 +18 15 46.666666667 20 15 46.666666667 0 17.5 46.666666667 +2 17.5 46.666666667 4 17.5 46.666666667 6 17.5 46.666666667 +8 17.5 46.666666667 10 17.5 46.666666667 12 17.5 46.666666667 +14 17.5 46.666666667 16 17.5 46.666666667 18 17.5 46.666666667 +20 17.5 46.666666667 0 20 46.666666667 2 20 46.666666667 +4 20 46.666666667 6 20 46.666666667 8 20 46.666666667 +10 20 46.666666667 12 20 46.666666667 14 20 46.666666667 +16 20 46.666666667 18 20 46.666666667 20 20 46.666666667 +0 22.5 46.666666667 2 22.5 46.666666667 4 22.5 46.666666667 +6 22.5 46.666666667 8 22.5 46.666666667 10 22.5 46.666666667 +12 22.5 46.666666667 14 22.5 46.666666667 16 22.5 46.666666667 +18 22.5 46.666666667 20 22.5 46.666666667 0 25 46.666666667 +2 25 46.666666667 4 25 46.666666667 6 25 46.666666667 +8 25 46.666666667 10 25 46.666666667 12 25 46.666666667 +14 25 46.666666667 16 25 46.666666667 18 25 46.666666667 +20 25 46.666666667 0 27.5 46.666666667 2 27.5 46.666666667 +4 27.5 46.666666667 6 27.5 46.666666667 8 27.5 46.666666667 +10 27.5 46.666666667 12 27.5 46.666666667 14 27.5 46.666666667 +16 27.5 46.666666667 18 27.5 46.666666667 20 27.5 46.666666667 +0 30 46.666666667 2 30 46.666666667 4 30 46.666666667 +6 30 46.666666667 8 30 46.666666667 10 30 46.666666667 +12 30 46.666666667 14 30 46.666666667 16 30 46.666666667 +18 30 46.666666667 20 30 46.666666667 0 32.5 46.666666667 +2 32.5 46.666666667 4 32.5 46.666666667 6 32.5 46.666666667 +8 32.5 46.666666667 10 32.5 46.666666667 12 32.5 46.666666667 +14 32.5 46.666666667 16 32.5 46.666666667 18 32.5 46.666666667 +20 32.5 46.666666667 0 35 46.666666667 2 35 46.666666667 +4 35 46.666666667 6 35 46.666666667 8 35 46.666666667 +10 35 46.666666667 12 35 46.666666667 14 35 46.666666667 +16 35 46.666666667 18 35 46.666666667 20 35 46.666666667 +0 37.5 46.666666667 2 37.5 46.666666667 4 37.5 46.666666667 +6 37.5 46.666666667 8 37.5 46.666666667 10 37.5 46.666666667 +12 37.5 46.666666667 14 37.5 46.666666667 16 37.5 46.666666667 +18 37.5 46.666666667 20 37.5 46.666666667 0 40 46.666666667 +2 40 46.666666667 4 40 46.666666667 6 40 46.666666667 +8 40 46.666666667 10 40 46.666666667 12 40 46.666666667 +14 40 46.666666667 16 40 46.666666667 18 40 46.666666667 +20 40 46.666666667 0 42.5 46.666666667 2 42.5 46.666666667 +4 42.5 46.666666667 6 42.5 46.666666667 8 42.5 46.666666667 +10 42.5 46.666666667 12 42.5 46.666666667 14 42.5 46.666666667 +16 42.5 46.666666667 18 42.5 46.666666667 20 42.5 46.666666667 +0 45 46.666666667 2 45 46.666666667 4 45 46.666666667 +6 45 46.666666667 8 45 46.666666667 10 45 46.666666667 +12 45 46.666666667 14 45 46.666666667 16 45 46.666666667 +18 45 46.666666667 20 45 46.666666667 0 47.5 46.666666667 +2 47.5 46.666666667 4 47.5 46.666666667 6 47.5 46.666666667 +8 47.5 46.666666667 10 47.5 46.666666667 12 47.5 46.666666667 +14 47.5 46.666666667 16 47.5 46.666666667 18 47.5 46.666666667 +20 47.5 46.666666667 0 50 46.666666667 2 50 46.666666667 +4 50 46.666666667 6 50 46.666666667 8 50 46.666666667 +10 50 46.666666667 12 50 46.666666667 14 50 46.666666667 +16 50 46.666666667 18 50 46.666666667 20 50 46.666666667 +0 0 48.333333333 2 0 48.333333333 4 0 48.333333333 +6 0 48.333333333 8 0 48.333333333 10 0 48.333333333 +12 0 48.333333333 14 0 48.333333333 16 0 48.333333333 +18 0 48.333333333 20 0 48.333333333 0 2.5 48.333333333 +2 2.5 48.333333333 4 2.5 48.333333333 6 2.5 48.333333333 +8 2.5 48.333333333 10 2.5 48.333333333 12 2.5 48.333333333 +14 2.5 48.333333333 16 2.5 48.333333333 18 2.5 48.333333333 +20 2.5 48.333333333 0 5 48.333333333 2 5 48.333333333 +4 5 48.333333333 6 5 48.333333333 8 5 48.333333333 +10 5 48.333333333 12 5 48.333333333 14 5 48.333333333 +16 5 48.333333333 18 5 48.333333333 20 5 48.333333333 +0 7.5 48.333333333 2 7.5 48.333333333 4 7.5 48.333333333 +6 7.5 48.333333333 8 7.5 48.333333333 10 7.5 48.333333333 +12 7.5 48.333333333 14 7.5 48.333333333 16 7.5 48.333333333 +18 7.5 48.333333333 20 7.5 48.333333333 0 10 48.333333333 +2 10 48.333333333 4 10 48.333333333 6 10 48.333333333 +8 10 48.333333333 10 10 48.333333333 12 10 48.333333333 +14 10 48.333333333 16 10 48.333333333 18 10 48.333333333 +20 10 48.333333333 0 12.5 48.333333333 2 12.5 48.333333333 +4 12.5 48.333333333 6 12.5 48.333333333 8 12.5 48.333333333 +10 12.5 48.333333333 12 12.5 48.333333333 14 12.5 48.333333333 +16 12.5 48.333333333 18 12.5 48.333333333 20 12.5 48.333333333 +0 15 48.333333333 2 15 48.333333333 4 15 48.333333333 +6 15 48.333333333 8 15 48.333333333 10 15 48.333333333 +12 15 48.333333333 14 15 48.333333333 16 15 48.333333333 +18 15 48.333333333 20 15 48.333333333 0 17.5 48.333333333 +2 17.5 48.333333333 4 17.5 48.333333333 6 17.5 48.333333333 +8 17.5 48.333333333 10 17.5 48.333333333 12 17.5 48.333333333 +14 17.5 48.333333333 16 17.5 48.333333333 18 17.5 48.333333333 +20 17.5 48.333333333 0 20 48.333333333 2 20 48.333333333 +4 20 48.333333333 6 20 48.333333333 8 20 48.333333333 +10 20 48.333333333 12 20 48.333333333 14 20 48.333333333 +16 20 48.333333333 18 20 48.333333333 20 20 48.333333333 +0 22.5 48.333333333 2 22.5 48.333333333 4 22.5 48.333333333 +6 22.5 48.333333333 8 22.5 48.333333333 10 22.5 48.333333333 +12 22.5 48.333333333 14 22.5 48.333333333 16 22.5 48.333333333 +18 22.5 48.333333333 20 22.5 48.333333333 0 25 48.333333333 +2 25 48.333333333 4 25 48.333333333 6 25 48.333333333 +8 25 48.333333333 10 25 48.333333333 12 25 48.333333333 +14 25 48.333333333 16 25 48.333333333 18 25 48.333333333 +20 25 48.333333333 0 27.5 48.333333333 2 27.5 48.333333333 +4 27.5 48.333333333 6 27.5 48.333333333 8 27.5 48.333333333 +10 27.5 48.333333333 12 27.5 48.333333333 14 27.5 48.333333333 +16 27.5 48.333333333 18 27.5 48.333333333 20 27.5 48.333333333 +0 30 48.333333333 2 30 48.333333333 4 30 48.333333333 +6 30 48.333333333 8 30 48.333333333 10 30 48.333333333 +12 30 48.333333333 14 30 48.333333333 16 30 48.333333333 +18 30 48.333333333 20 30 48.333333333 0 32.5 48.333333333 +2 32.5 48.333333333 4 32.5 48.333333333 6 32.5 48.333333333 +8 32.5 48.333333333 10 32.5 48.333333333 12 32.5 48.333333333 +14 32.5 48.333333333 16 32.5 48.333333333 18 32.5 48.333333333 +20 32.5 48.333333333 0 35 48.333333333 2 35 48.333333333 +4 35 48.333333333 6 35 48.333333333 8 35 48.333333333 +10 35 48.333333333 12 35 48.333333333 14 35 48.333333333 +16 35 48.333333333 18 35 48.333333333 20 35 48.333333333 +0 37.5 48.333333333 2 37.5 48.333333333 4 37.5 48.333333333 +6 37.5 48.333333333 8 37.5 48.333333333 10 37.5 48.333333333 +12 37.5 48.333333333 14 37.5 48.333333333 16 37.5 48.333333333 +18 37.5 48.333333333 20 37.5 48.333333333 0 40 48.333333333 +2 40 48.333333333 4 40 48.333333333 6 40 48.333333333 +8 40 48.333333333 10 40 48.333333333 12 40 48.333333333 +14 40 48.333333333 16 40 48.333333333 18 40 48.333333333 +20 40 48.333333333 0 42.5 48.333333333 2 42.5 48.333333333 +4 42.5 48.333333333 6 42.5 48.333333333 8 42.5 48.333333333 +10 42.5 48.333333333 12 42.5 48.333333333 14 42.5 48.333333333 +16 42.5 48.333333333 18 42.5 48.333333333 20 42.5 48.333333333 +0 45 48.333333333 2 45 48.333333333 4 45 48.333333333 +6 45 48.333333333 8 45 48.333333333 10 45 48.333333333 +12 45 48.333333333 14 45 48.333333333 16 45 48.333333333 +18 45 48.333333333 20 45 48.333333333 0 47.5 48.333333333 +2 47.5 48.333333333 4 47.5 48.333333333 6 47.5 48.333333333 +8 47.5 48.333333333 10 47.5 48.333333333 12 47.5 48.333333333 +14 47.5 48.333333333 16 47.5 48.333333333 18 47.5 48.333333333 +20 47.5 48.333333333 0 50 48.333333333 2 50 48.333333333 +4 50 48.333333333 6 50 48.333333333 8 50 48.333333333 +10 50 48.333333333 12 50 48.333333333 14 50 48.333333333 +16 50 48.333333333 18 50 48.333333333 20 50 48.333333333 +0 0 50 2 0 50 4 0 50 +6 0 50 8 0 50 10 0 50 +12 0 50 14 0 50 16 0 50 +18 0 50 20 0 50 0 2.5 50 +2 2.5 50 4 2.5 50 6 2.5 50 +8 2.5 50 10 2.5 50 12 2.5 50 +14 2.5 50 16 2.5 50 18 2.5 50 +20 2.5 50 0 5 50 2 5 50 +4 5 50 6 5 50 8 5 50 +10 5 50 12 5 50 14 5 50 +16 5 50 18 5 50 20 5 50 +0 7.5 50 2 7.5 50 4 7.5 50 +6 7.5 50 8 7.5 50 10 7.5 50 +12 7.5 50 14 7.5 50 16 7.5 50 +18 7.5 50 20 7.5 50 0 10 50 +2 10 50 4 10 50 6 10 50 +8 10 50 10 10 50 12 10 50 +14 10 50 16 10 50 18 10 50 +20 10 50 0 12.5 50 2 12.5 50 +4 12.5 50 6 12.5 50 8 12.5 50 +10 12.5 50 12 12.5 50 14 12.5 50 +16 12.5 50 18 12.5 50 20 12.5 50 +0 15 50 2 15 50 4 15 50 +6 15 50 8 15 50 10 15 50 +12 15 50 14 15 50 16 15 50 +18 15 50 20 15 50 0 17.5 50 +2 17.5 50 4 17.5 50 6 17.5 50 +8 17.5 50 10 17.5 50 12 17.5 50 +14 17.5 50 16 17.5 50 18 17.5 50 +20 17.5 50 0 20 50 2 20 50 +4 20 50 6 20 50 8 20 50 +10 20 50 12 20 50 14 20 50 +16 20 50 18 20 50 20 20 50 +0 22.5 50 2 22.5 50 4 22.5 50 +6 22.5 50 8 22.5 50 10 22.5 50 +12 22.5 50 14 22.5 50 16 22.5 50 +18 22.5 50 20 22.5 50 0 25 50 +2 25 50 4 25 50 6 25 50 +8 25 50 10 25 50 12 25 50 +14 25 50 16 25 50 18 25 50 +20 25 50 0 27.5 50 2 27.5 50 +4 27.5 50 6 27.5 50 8 27.5 50 +10 27.5 50 12 27.5 50 14 27.5 50 +16 27.5 50 18 27.5 50 20 27.5 50 +0 30 50 2 30 50 4 30 50 +6 30 50 8 30 50 10 30 50 +12 30 50 14 30 50 16 30 50 +18 30 50 20 30 50 0 32.5 50 +2 32.5 50 4 32.5 50 6 32.5 50 +8 32.5 50 10 32.5 50 12 32.5 50 +14 32.5 50 16 32.5 50 18 32.5 50 +20 32.5 50 0 35 50 2 35 50 +4 35 50 6 35 50 8 35 50 +10 35 50 12 35 50 14 35 50 +16 35 50 18 35 50 20 35 50 +0 37.5 50 2 37.5 50 4 37.5 50 +6 37.5 50 8 37.5 50 10 37.5 50 +12 37.5 50 14 37.5 50 16 37.5 50 +18 37.5 50 20 37.5 50 0 40 50 +2 40 50 4 40 50 6 40 50 +8 40 50 10 40 50 12 40 50 +14 40 50 16 40 50 18 40 50 +20 40 50 0 42.5 50 2 42.5 50 +4 42.5 50 6 42.5 50 8 42.5 50 +10 42.5 50 12 42.5 50 14 42.5 50 +16 42.5 50 18 42.5 50 20 42.5 50 +0 45 50 2 45 50 4 45 50 +6 45 50 8 45 50 10 45 50 +12 45 50 14 45 50 16 45 50 +18 45 50 20 45 50 0 47.5 50 +2 47.5 50 4 47.5 50 6 47.5 50 +8 47.5 50 10 47.5 50 12 47.5 50 +14 47.5 50 16 47.5 50 18 47.5 50 +20 47.5 50 0 50 50 2 50 50 +4 50 50 6 50 50 8 50 50 +10 50 50 12 50 50 14 50 50 +16 50 50 18 50 50 20 50 50 + diff --git a/tests/unit_tests/mesh_to_vtk/spherical-curvilinear.vtk b/tests/unit_tests/mesh_to_vtk/spherical-curvilinear.vtk new file mode 100644 index 0000000000..2308432eec --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/spherical-curvilinear.vtk @@ -0,0 +1,210 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 175 double +5.4951556605 7.8305813044 -10 5.4951556605 12.169418696 -10 6.0986903619 8.1212282982 -7.5 +6.0986903619 11.878771702 -7.5 7.7475778302 8.9152906522 -10 7.7475778302 8.9152906522 -5.6698729811 +7.7475778302 11.084709348 -10 7.7475778302 11.084709348 -5.6698729811 8.0493451809 9.0606141491 -8.75 +8.0493451809 10.939385851 -8.75 8.8737889151 9.4576453261 -7.8349364905 8.8737889151 10.542354674 -7.8349364905 +8.8873953302 5.1253604391 -10 8.8873953302 14.874639561 -10 9.0364560916 5.778438306 -7.5 +9.0364560916 14.221561694 -7.5 9.4436976651 7.5626802195 -10 9.4436976651 7.5626802195 -5.6698729811 +9.4436976651 12.43731978 -10 9.4436976651 12.43731978 -5.6698729811 9.5182280458 7.889219153 -8.75 +9.5182280458 12.110780847 -8.75 9.7218488326 8.7813401098 -7.8349364905 9.7218488326 11.21865989 -7.8349364905 +10 10 -10 10 10 -7.5 10 10 -5 +10.779362252 9.0227106469 -7.8349364905 10.779362252 10.977289353 -7.8349364905 11.25 10 -7.8349364905 +11.349895019 8.3072851868 -8.75 11.349895019 11.692714813 -8.75 11.558724505 8.0454212938 -10 +11.558724505 8.0454212938 -5.6698729811 11.558724505 11.954578706 -10 11.558724505 11.954578706 -5.6698729811 +12.165063509 10 -8.75 12.5 10 -10 12.5 10 -5.6698729811 +12.699790037 6.6145703735 -7.5 12.699790037 13.385429626 -7.5 13.117449009 6.0908425877 -10 +13.117449009 13.909157412 -10 14.330127019 10 -7.5 14.330127019 10 -7.5 +15 10 -10 15 10 -10 10 10 -8.75 +10 10 -6.25 10.625 10 -8.9174682453 11.875 10 -6.7524047358 +11.082531755 10 -9.375 13.247595264 10 -8.125 11.25 10 -10 +13.75 10 -10 10.389681126 10.488644677 -8.9174682453 11.169043378 11.46593403 -6.7524047358 +10.674947509 10.846357407 -9.375 12.024842528 12.53907222 -8.125 10.779362252 10.977289353 -10 +12.338086757 12.931868059 -10 9.8609244163 10.609329945 -8.9174682453 9.5827732488 11.827989835 -6.7524047358 +9.7591140229 11.055390424 -9.375 9.2773420687 13.166171271 -8.125 9.7218488326 11.21865989 -10 +9.1655464977 13.655979671 -10 9.4368944576 10.271177337 -8.9174682453 8.3106833727 10.813532011 -6.7524047358 +9.0246725905 10.469692925 -9.375 7.0740177714 11.409078776 -8.125 8.8737889151 10.542354674 -10 +6.6213667454 11.627064022 -10 9.4368944576 9.7288226631 -8.9174682453 8.3106833727 9.1864679892 -6.7524047358 +9.0246725905 9.5303070745 -9.375 7.0740177714 8.5909212236 -8.125 8.8737889151 9.4576453261 -10 +6.6213667454 8.3729359783 -10 9.8609244163 9.3906700549 -8.9174682453 9.5827732488 8.1720101647 -6.7524047358 +9.7591140229 8.9446095765 -9.375 9.2773420687 6.8338287295 -8.125 9.7218488326 8.7813401098 -10 +9.1655464977 6.3440203293 -10 10.389681126 9.5113553235 -8.9174682453 11.169043378 8.5340659704 -6.7524047358 +10.674947509 9.1536425934 -9.375 12.024842528 7.4609277801 -8.125 10.779362252 9.0227106469 -10 +12.338086757 7.0681319407 -10 10.647047613 10 -7.5851854343 11.294095226 10 -5.1703708686 +11.767766953 10 -8.232233047 13.535533906 10 -6.4644660941 12.414814566 10 -9.3529523872 +14.829629131 10 -8.7059047745 10.403427588 10.505882194 -7.5851854343 10.806855176 11.011764389 -5.1703708686 +11.102184667 11.382095857 -8.232233047 12.204369334 12.764191715 -6.4644660941 11.505612255 11.887978052 -9.3529523872 +13.01122451 13.775956104 -8.7059047745 9.8560183609 10.630824778 -7.5851854343 9.7120367218 11.261649556 -5.1703708686 +9.6066348466 11.723445345 -8.232233047 9.2132696932 13.446890689 -6.4644660941 9.4626532075 12.354270123 -9.3529523872 +8.925306415 14.708540246 -8.7059047745 9.4170302449 10.280743438 -7.5851854343 8.8340604897 10.561486875 -5.1703708686 +8.4072970097 10.767005335 -8.232233047 6.8145940193 11.534010671 -6.4644660941 7.8243272545 11.047748773 -9.3529523872 +5.6486545091 12.095497546 -8.7059047745 9.4170302449 9.7192565624 -7.5851854343 8.8340604897 9.4385131248 -5.1703708686 +8.4072970097 9.2329946646 -8.232233047 6.8145940193 8.4659893291 -6.4644660941 7.8243272545 8.9522512269 -9.3529523872 +5.6486545091 7.9045024539 -8.7059047745 9.8560183609 9.3691752218 -7.5851854343 9.7120367218 8.7383504436 -5.1703708686 +9.6066348466 8.2765546553 -8.232233047 9.2132696932 6.5531093106 -6.4644660941 9.4626532075 7.6457298771 -9.3529523872 +8.925306415 5.2914597543 -8.7059047745 10.403427588 9.4941178057 -7.5851854343 10.806855176 8.9882356114 -5.1703708686 +11.102184667 8.6179041425 -8.232233047 12.204369334 7.235808285 -6.4644660941 11.505612255 8.1120219482 -9.3529523872 +13.01122451 6.2240438964 -8.7059047745 11.126211085 10.542354674 -7.8349364905 12.25242217 11.084709348 -5.6698729811 +11.950654819 10.939385851 -8.75 13.901309638 11.878771702 -7.5 12.25242217 11.084709348 -10 +14.50484434 12.169418696 -10 10.278151167 11.21865989 -7.8349364905 10.556302335 12.43731978 -5.6698729811 +10.481771954 12.110780847 -8.75 10.963543908 14.221561694 -7.5 10.556302335 12.43731978 -10 +11.11260467 14.874639561 -10 9.2206377477 10.977289353 -7.8349364905 8.4412754954 11.954578706 -5.6698729811 +8.6501049815 11.692714813 -8.75 7.3002099629 13.385429626 -7.5 8.4412754954 11.954578706 -10 +6.8825509907 13.909157412 -10 8.75 10 -7.8349364905 7.5 10 -5.6698729811 +7.8349364905 10 -8.75 5.6698729811 10 -7.5 7.5 10 -10 +5 10 -10 9.2206377477 9.0227106469 -7.8349364905 8.4412754954 8.0454212938 -5.6698729811 +8.6501049815 8.3072851868 -8.75 7.3002099629 6.6145703735 -7.5 8.4412754954 8.0454212938 -10 +6.8825509907 6.0908425877 -10 10.278151167 8.7813401098 -7.8349364905 10.556302335 7.5626802195 -5.6698729811 +10.481771954 7.889219153 -8.75 10.963543908 5.778438306 -7.5 10.556302335 7.5626802195 -10 +11.11260467 5.1253604391 -10 11.126211085 9.4576453261 -7.8349364905 12.25242217 8.9152906522 -5.6698729811 +11.950654819 9.0606141491 -8.75 13.901309638 8.1212282982 -7.5 12.25242217 8.9152906522 -10 +14.50484434 7.8305813044 -10 +CELLS 43 840 +OFFSETS vtktypeint64 +0 20 40 60 80 100 120 140 160 +180 200 220 240 260 280 300 320 340 +360 380 400 420 440 460 480 500 520 +540 560 580 600 620 640 660 680 700 +720 740 760 780 800 820 840 +CONNECTIVITY vtktypeint64 +24 25 29 24 24 25 28 24 47 +91 49 24 47 97 55 24 24 25 +133 24 25 26 38 29 25 26 35 +28 48 92 50 91 48 98 56 97 +25 26 134 133 24 29 36 24 24 +28 31 24 49 93 51 24 55 99 +57 24 24 133 135 24 29 38 43 +36 28 35 40 31 50 94 52 93 +56 100 58 99 133 134 136 135 24 +36 37 24 24 31 34 24 51 95 +53 24 57 101 59 24 24 135 137 +24 36 43 45 37 31 40 42 34 +52 96 54 95 58 102 60 101 135 +136 138 137 24 25 28 24 24 25 +23 24 47 97 55 24 47 103 61 +24 24 25 139 24 25 26 35 28 +25 26 19 23 48 98 56 97 48 +104 62 103 25 26 140 139 24 28 +31 24 24 23 21 24 55 99 57 +24 61 105 63 24 24 139 141 24 +28 35 40 31 23 19 15 21 56 +100 58 99 62 106 64 105 139 140 +142 141 24 31 34 24 24 21 18 +24 57 101 59 24 63 107 65 24 +24 141 143 24 31 40 42 34 21 +15 13 18 58 102 60 101 64 108 +66 107 141 142 144 143 24 25 23 +24 24 25 11 24 47 103 61 24 +47 109 67 24 24 25 145 24 25 +26 19 23 25 26 7 11 48 104 +62 103 48 110 68 109 25 26 146 +145 24 23 21 24 24 11 9 24 +61 105 63 24 67 111 69 24 24 +145 147 24 23 19 15 21 11 7 +3 9 62 106 64 105 68 112 70 +111 145 146 148 147 24 21 18 24 +24 9 6 24 63 107 65 24 69 +113 71 24 24 147 149 24 21 15 +13 18 9 3 1 6 64 108 66 +107 70 114 72 113 147 148 150 149 +24 25 11 24 24 25 10 24 47 +109 67 24 47 115 73 24 24 25 +151 24 25 26 7 11 25 26 5 +10 48 110 68 109 48 116 74 115 +25 26 152 151 24 11 9 24 24 +10 8 24 67 111 69 24 73 117 +75 24 24 151 153 24 11 7 3 +9 10 5 2 8 68 112 70 111 +74 118 76 117 151 152 154 153 24 +9 6 24 24 8 4 24 69 113 +71 24 75 119 77 24 24 153 155 +24 9 3 1 6 8 2 0 4 +70 114 72 113 76 120 78 119 153 +154 156 155 24 25 10 24 24 25 +22 24 47 115 73 24 47 121 79 +24 24 25 157 24 25 26 5 10 +25 26 17 22 48 116 74 115 48 +122 80 121 25 26 158 157 24 10 +8 24 24 22 20 24 73 117 75 +24 79 123 81 24 24 157 159 24 +10 5 2 8 22 17 14 20 74 +118 76 117 80 124 82 123 157 158 +160 159 24 8 4 24 24 20 16 +24 75 119 77 24 81 125 83 24 +24 159 161 24 8 2 0 4 20 +14 12 16 76 120 78 119 82 126 +84 125 159 160 162 161 24 25 22 +24 24 25 27 24 47 121 79 24 +47 127 85 24 24 25 163 24 25 +26 17 22 25 26 33 27 48 122 +80 121 48 128 86 127 25 26 164 +163 24 22 20 24 24 27 30 24 +79 123 81 24 85 129 87 24 24 +163 165 24 22 17 14 20 27 33 +39 30 80 124 82 123 86 130 88 +129 163 164 166 165 24 20 16 24 +24 30 32 24 81 125 83 24 87 +131 89 24 24 165 167 24 20 14 +12 16 30 39 41 32 82 126 84 +125 88 132 90 131 165 166 168 167 +24 25 27 24 24 25 29 24 47 +127 85 24 47 91 49 24 24 25 +169 24 25 26 33 27 25 26 38 +29 48 128 86 127 48 92 50 91 +25 26 170 169 24 27 30 24 24 +29 36 24 85 129 87 24 49 93 +51 24 24 169 171 24 27 33 39 +30 29 38 43 36 86 130 88 129 +50 94 52 93 169 170 172 171 24 +30 32 24 24 36 37 24 87 131 +89 24 51 95 53 24 24 171 173 +24 30 39 41 32 36 43 45 37 +88 132 90 131 52 96 54 95 171 +172 174 173 +CELL_TYPES 42 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 +25 + diff --git a/tests/unit_tests/mesh_to_vtk/spherical-linear.vtk b/tests/unit_tests/mesh_to_vtk/spherical-linear.vtk new file mode 100644 index 0000000000..3dff18a083 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/spherical-linear.vtk @@ -0,0 +1,39 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET STRUCTURED_GRID +DIMENSIONS 3 4 8 +POINTS 96 double +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 11.25 10 -7.8349364905 12.5 10 -5.6698729811 +10 10 -10 12.165063509 10 -8.75 14.330127019 10 -7.5 +10 10 -10 12.5 10 -10 15 10 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 10.779362252 10.977289353 -7.8349364905 11.558724505 11.954578706 -5.6698729811 +10 10 -10 11.349895019 11.692714813 -8.75 12.699790037 13.385429626 -7.5 +10 10 -10 11.558724505 11.954578706 -10 13.117449009 13.909157412 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 9.7218488326 11.21865989 -7.8349364905 9.4436976651 12.43731978 -5.6698729811 +10 10 -10 9.5182280458 12.110780847 -8.75 9.0364560916 14.221561694 -7.5 +10 10 -10 9.4436976651 12.43731978 -10 8.8873953302 14.874639561 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 8.8737889151 10.542354674 -7.8349364905 7.7475778302 11.084709348 -5.6698729811 +10 10 -10 8.0493451809 10.939385851 -8.75 6.0986903619 11.878771702 -7.5 +10 10 -10 7.7475778302 11.084709348 -10 5.4951556605 12.169418696 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 8.8737889151 9.4576453261 -7.8349364905 7.7475778302 8.9152906522 -5.6698729811 +10 10 -10 8.0493451809 9.0606141491 -8.75 6.0986903619 8.1212282982 -7.5 +10 10 -10 7.7475778302 8.9152906522 -10 5.4951556605 7.8305813044 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 9.7218488326 8.7813401098 -7.8349364905 9.4436976651 7.5626802195 -5.6698729811 +10 10 -10 9.5182280458 7.889219153 -8.75 9.0364560916 5.778438306 -7.5 +10 10 -10 9.4436976651 7.5626802195 -10 8.8873953302 5.1253604391 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 10.779362252 9.0227106469 -7.8349364905 11.558724505 8.0454212938 -5.6698729811 +10 10 -10 11.349895019 8.3072851868 -8.75 12.699790037 6.6145703735 -7.5 +10 10 -10 11.558724505 8.0454212938 -10 13.117449009 6.0908425877 -10 +10 10 -10 10 10 -7.5 10 10 -5 +10 10 -10 11.25 10 -7.8349364905 12.5 10 -5.6698729811 +10 10 -10 12.165063509 10 -8.75 14.330127019 10 -7.5 +10 10 -10 12.5 10 -10 15 10 -10 + diff --git a/tests/unit_tests/mesh_to_vtk/test.py b/tests/unit_tests/mesh_to_vtk/test.py new file mode 100644 index 0000000000..f00aa46261 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/test.py @@ -0,0 +1,103 @@ +import difflib +import filecmp +import numpy as np +from pathlib import Path + +import openmc +import pytest + +from tests.regression_tests import config + +pytest.importorskip('vtk') + +def full_path(f): + return Path(__file__).parent.absolute() / f + +def diff_file(file1, file2): + with open(file1) as fh: + f1_text = fh.readlines() + with open(file2) as fh: + f2_text = fh.readlines() + diff_lines = difflib.unified_diff(f1_text, f2_text) + return ''.join(diff_lines) + +# test meshes +reg_mesh = openmc.RegularMesh() +reg_mesh.lower_left = (0, 0, 0) +reg_mesh.upper_right = (20, 50, 50) +reg_mesh.dimension = (10, 20, 30) + +rect_mesh = openmc.RectilinearMesh() +rect_mesh.x_grid = np.linspace(0, 10, 5) +rect_mesh.y_grid = np.geomspace(5., 20., 10) +rect_mesh.z_grid = np.linspace(1, 100, 20) + +cyl_mesh = openmc.CylindricalMesh( + origin=(10, 10, -10), + r_grid=np.linspace(0, 5, 5), + phi_grid=np.linspace(0, 2 * np.pi, 4), + z_grid=np.linspace(0, 2, 4), +) + +sphere_mesh = openmc.SphericalMesh( + origin=(10, 10, -10), + r_grid=np.linspace(0, 5, 3), + theta_grid=np.linspace(0, 0.5 * np.pi, 4), + phi_grid=np.linspace(0, 2*np.pi, 8), +) + + +def mesh_data(mesh_dims): + data = 100 * np.arange(np.prod(mesh_dims), dtype=float) + # data is returned reshaped with order 'F' to ensure that + # the resulting data is interpreted correctly by the + # write_data_to_vtk method + return data.reshape(*mesh_dims, order='F') + +test_data = ((reg_mesh, False, 'regular'), + (rect_mesh, False, 'rectilinear'), + (cyl_mesh, False, 'cylindrical-linear'), + (cyl_mesh, True, 'cylindrical-curvilinear'), + (sphere_mesh, False, 'spherical-linear'), + (sphere_mesh, True, 'spherical-curvilinear')) + +@pytest.mark.parametrize('mesh_params', + test_data, + ids=lambda params: params[2]) +def test_mesh_write_vtk(mesh_params, run_in_tmpdir): + mesh, curvilinear, filename = mesh_params + + test_data = full_path(filename + ".vtk") + kwargs = {} + if curvilinear: + kwargs['curvilinear'] = curvilinear + + # set output filename based on test configuration + filename = test_data if config['update'] else filename + "-actual.vtk" + + # write the mesh file and compare to the expected version + mesh.write_data_to_vtk(filename, **kwargs) + + try: + assert filecmp.cmp(test_data, filename) + except AssertionError as e: + diff = diff_file(test_data, filename) + raise AssertionError(diff) from e + +# check data writing +def test_mesh_write_vtk_data(run_in_tmpdir): + data = {'ascending_data': mesh_data(cyl_mesh.dimension)} + filename_expected = full_path('cyl-data.vtk') + filename_actual = full_path('cyl-data-actual.vtk') + # update the test file if requested + filename = filename_expected if config['update'] else filename_actual + cyl_mesh.write_data_to_vtk(filename, datasets=data, volume_normalization=False) + + try: + assert filecmp.cmp(filename, filename_expected) + except AssertionError as e: + diff = diff_file(filename_expected, filename) + raise AssertionError(diff) from e + + + diff --git a/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py b/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py new file mode 100644 index 0000000000..8166ba68c8 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/test_vtk_dims.py @@ -0,0 +1,320 @@ +from itertools import product + +import numpy as np +from pathlib import Path +import pytest + +vtk = pytest.importorskip("vtk") +from vtk.util import numpy_support as nps + +import openmc + +@pytest.fixture +def model(): + openmc.reset_auto_ids() + + surf1 = openmc.Sphere(r=10, boundary_type='vacuum') + surf2 = openmc.XPlane(x0=-0.001, boundary_type='vacuum') + + cell = openmc.Cell(region=-surf1 & -surf2) + + geometry = openmc.Geometry([cell]) + + settings = openmc.Settings() + settings.batches = 2 + settings.particles = 100 + settings.run_mode = 'fixed source' + + source = openmc.IndependentSource() + source.angle = openmc.stats.Isotropic() + source.energy = openmc.stats.Discrete([1.0e6], [1.0]) + source.space = openmc.stats.Point((-0.01, -0.01, -0.01)) + + settings.source = source + + model = openmc.Model(geometry=geometry, settings=settings) + + return model + + +regular_mesh = openmc.RegularMesh() +regular_mesh.lower_left = (-10, -10, -10) +regular_mesh.upper_right = (10, 10, 10) +regular_mesh.dimension = [30, 20, 10] + +rectilinear_mesh = openmc.RectilinearMesh() +rectilinear_mesh.x_grid = np.linspace(-10, 10, 6) +rectilinear_mesh.y_grid = np.logspace(0, 1, 7) +rectilinear_mesh.y_grid = \ + np.concatenate((-rectilinear_mesh.y_grid[::-1], rectilinear_mesh.y_grid)) +rectilinear_mesh.z_grid = np.linspace(-10, 10, 11) + +cylinder_mesh = openmc.CylindricalMesh( + r_grid=np.linspace(0, 10, 23), + z_grid=np.linspace(0, 1, 15) +) +cylinder_mesh.phi_grid = np.linspace(0, np.pi, 21) + +spherical_mesh = openmc.SphericalMesh( + r_grid=np.linspace(1, 10, 30), + phi_grid=np.linspace(0, 0.8*np.pi, 25), + theta_grid=np.linspace(0, np.pi / 2, 15), +) + +MESHES = [cylinder_mesh, regular_mesh, rectilinear_mesh, spherical_mesh] + +x_plane = openmc.XPlane(x0=-0.001, boundary_type='vacuum') +y_plane = openmc.YPlane(y0=-0.001, boundary_type='vacuum') +z_plane = openmc.ZPlane(z0=-0.001, boundary_type='vacuum') + +SURFS = [x_plane, y_plane, z_plane] + + +def ids(mesh): + if isinstance(mesh, openmc.CylindricalMesh): + return 'cylindrical_mesh' + elif isinstance(mesh, openmc.RegularMesh): + return 'regular_mesh' + elif isinstance(mesh, openmc.RectilinearMesh): + return 'rectilinear_mesh' + elif isinstance(mesh, openmc.SphericalMesh): + return 'spherical_mesh' + + +@pytest.mark.parametrize("mesh", MESHES, ids=ids) +def test_write_data_to_vtk(mesh, tmpdir): + # BUILD + filename = Path(tmpdir) / "out.vtk" + + # use mesh element volumes as data to check volume-normalization ordering + # kji (i changing fastest) orering is expected for input data + # by using the volumes transposed as the data here, we can ensure the + # normalization is happening correctly + data = mesh.volumes + + # RUN + mesh.write_data_to_vtk(filename=filename, datasets={"label1": data, "label2": data}) + + # TEST + assert filename.is_file() + + # read file + reader = vtk.vtkStructuredGridReader() + reader.SetFileName(str(filename)) + reader.Update() + + # check name of datasets + vtk_grid = reader.GetOutput() + array1 = vtk_grid.GetCellData().GetArray(0) + array2 = vtk_grid.GetCellData().GetArray(1) + + assert array1.GetName() == "label1" + assert array2.GetName() == "label2" + + # check size of datasets + data1 = nps.vtk_to_numpy(array1) + data2 = nps.vtk_to_numpy(array2) + assert data1.size == data.size + assert data2.size == data.size + + assert all(data1 == data2) + assert all(data1 == 1.0) + + +@pytest.mark.parametrize("mesh", MESHES, ids=ids) +def test_write_data_to_vtk_size_mismatch(mesh): + """Checks that an error is raised when the size of the dataset + doesn't match the mesh number of cells + + Parameters + ---------- + mesh : openmc.StructuredMesh + The mesh to test + """ + right_size = mesh.num_mesh_cells + data = np.random.random(right_size + 1) + + # Error message has \ in to escape characters that are otherwise recognized + # by regex. These are needed to make the test string match the error message + # string when using the match argument as that uses regular expression + expected_error_msg = ( + fr"The size of the dataset 'label' \({len(data)}\) should be equal to " + fr"the number of mesh cells \({mesh.num_mesh_cells}\)" + ) + with pytest.raises(ValueError, match=expected_error_msg): + mesh.write_data_to_vtk(filename="out.vtk", datasets={"label": data}) + +def test_write_data_to_vtk_round_trip(run_in_tmpdir): + cmesh = openmc.CylindricalMesh( + r_grid=(0.0, 1.0, 2.0), + z_grid=(0.0, 2.0, 4.0, 5.0), + phi_grid=(0.0, 3.0, 6.0), + ) + + smesh = openmc.SphericalMesh( + r_grid=(0.0, 1.0, 2.0), + theta_grid=(0.0, 0.5, 1.0, 2.0), + phi_grid=(0.0, 3.0, 6.0), + ) + rmesh = openmc.RegularMesh() + rmesh.lower_left = (0.0, 0.0, 0.0) + rmesh.upper_right = (1.0, 3.0, 5.0) + rmesh.dimension = (2, 1, 6) + + for mesh in [smesh, cmesh, rmesh]: + + filename = "mesh.vtk" + data = np.array([1.0] * 12) # there are 12 voxels in each mesh + mesh.write_data_to_vtk( + filename=filename, + datasets={"normalized": data}, + volume_normalization=True + ) + + reader = vtk.vtkStructuredGridReader() + reader.SetFileName(filename) + reader.ReadAllFieldsOn() + reader.Update() + + cell_data = reader.GetOutput().GetCellData() + uniform_array = cell_data.GetArray("normalized") + num_tuples = uniform_array.GetNumberOfTuples() + vtk_values = [uniform_array.GetValue(i) for i in range(num_tuples)] + + # checks that the vtk cell values are equal to the data / mesh volumes + assert np.allclose(vtk_values, data / mesh.volumes.T.flatten()) + + mesh.write_data_to_vtk( + filename=filename, + datasets={"not_normalized": data}, + volume_normalization=False, + ) + + reader = vtk.vtkStructuredGridReader() + reader.SetFileName(filename) + reader.ReadAllFieldsOn() + reader.Update() + + cell_data = reader.GetOutput().GetCellData() + uniform_array = cell_data.GetArray("not_normalized") + num_tuples = uniform_array.GetNumberOfTuples() + vtk_values = [uniform_array.GetValue(i) for i in range(num_tuples)] + + # checks that the vtk cell values are equal to the data + assert np.array_equal(vtk_values, data) + +def mesh_surf_id(param): + if isinstance(param, openmc.MeshBase): + return ids(param) + elif isinstance(param, openmc.XPlane): + return 'XPlane' + elif isinstance(param, openmc.YPlane): + return 'YPlane' + elif isinstance(param, openmc.ZPlane): + return 'ZPlane' + + +@pytest.mark.parametrize("mesh,surface", product(MESHES, SURFS), ids=mesh_surf_id) +def test_vtk_write_ordering(run_in_tmpdir, model, mesh, surface): + + tally = openmc.Tally() + tally.scores = ['flux'] + # use the mesh on the specified tally + mesh_filter = openmc.MeshFilter(mesh) + tally.filters = [mesh_filter] + + model.tallies = openmc.Tallies([tally]) + + # run the problem + sp_filename = model.run() + + with openmc.StatePoint(sp_filename) as sp: + mean = sp.tallies[tally.id].mean + + # write the data to a VTK file + vtk_filename = 'test.vtk' + mesh.write_data_to_vtk(vtk_filename, datasets={'mean': mean}) + + # read file + reader = vtk.vtkStructuredGridReader() + reader.SetFileName(str(vtk_filename)) + reader.Update() + + # check name of datasets + vtk_grid = reader.GetOutput() + array = vtk_grid.GetCellData().GetArray(0) + vtk_data = nps.vtk_to_numpy(array) + + # convenience function for determining if a mesh + # element has vertices in the geometry. This + # particular geometry allows us to assume that tally results + # in the element should be zero if none of its vertices lie in the geometry + def in_geom(cell): + point_ids = cell.GetPointIds() + + for i in range(point_ids.GetNumberOfIds()): + p = vtk_grid.GetPoint(point_ids.GetId(i)) + if model.geometry.find(p): + return True + + return False + + # reshape mean according to mesh dimensions + mean = mean.reshape(mesh.dimension[::-1]).T + centroid = [0.0, 0.0, 0.0] + + # check that tally and vtk array results are zero where expected + for ijk in mesh.indices: + ijk = tuple(n - 1 for n in ijk) + # get the cell from the stuctured mesh object + cell = vtk_grid.GetCell(*ijk) + if not in_geom(cell): + cell.GetCentroid(centroid) + err_msg = f'IJK: {ijk} should be zero but is not. Centroid: {centroid}' + assert mean[ijk] == 0.0, err_msg + + # need to get flat index with axes reversed due to ordering passed into the VTK file + flat_idx = np.ravel_multi_index(tuple(ijk[::-1]), mesh.dimension[::-1]) + assert vtk_data[flat_idx] == 0.0, err_msg + + +def test_sphere_mesh_coordinates(run_in_tmpdir): + mesh = openmc.SphericalMesh( + r_grid=np.linspace(0.1, 10, 30), + phi_grid=np.linspace(0, 1.5*np.pi, 25), + theta_grid=np.linspace(0, np.pi / 2, 15), + ) + # write the data to a VTK file (no data) + vtk_filename = 'test.vtk' + mesh.write_data_to_vtk(vtk_filename, {}) + + # read file + reader = vtk.vtkStructuredGridReader() + reader.SetFileName(str(vtk_filename)) + reader.Update() + + vtk_grid = reader.GetOutput() + + # create a region that matches the spherical mesh description + x = openmc.XPlane() + z = openmc.ZPlane() + y = openmc.YPlane() + s = openmc.Sphere(r=10.0) + + region = +z & +y & -s | -x & -y & +z & -s + + # the VTK interface will update this list when GetCentroid is called + centroid = np.zeros(3) + + # ensure all centroids of the sphere mesh are inside the cell region + for i in range(vtk_grid.GetNumberOfCells()): + # get the cell from the stuctured mesh object + cell = vtk_grid.GetCell(i) + cell.GetCentroid(centroid) + + # if the coordinate conversion is happening correctly, + # every one of the cell centroids should be in the CSG region + assert centroid in region, \ + f'Cell centroid {centroid} not in equivalent ' \ + f'CSG region for spherical mesh {mesh}' + diff --git a/tests/unit_tests/mesh_to_vtk/tets.exo b/tests/unit_tests/mesh_to_vtk/tets.exo new file mode 100644 index 0000000000..add4f7cfac Binary files /dev/null and b/tests/unit_tests/mesh_to_vtk/tets.exo differ diff --git a/tests/unit_tests/mesh_to_vtk/umesh.vtk b/tests/unit_tests/mesh_to_vtk/umesh.vtk new file mode 100644 index 0000000000..90f2261212 --- /dev/null +++ b/tests/unit_tests/mesh_to_vtk/umesh.vtk @@ -0,0 +1,292 @@ +# vtk DataFile Version 5.1 +vtk output +ASCII +DATASET UNSTRUCTURED_GRID +POINTS 58 double +-0.02593964576 -1 -1.1195739536 -0.40239958217 -0.40962166746 -1.9256035383 -1 0.02593964576 -1.1195739536 +-0.2248833639 0.23189144433 -1.2841109811 0.02593964576 1 -1.1195739536 1 -0.02593964576 -1.1195739536 +-0.042928712675 0.066425810853 -0.54270728017 -1 1 -1.5 -1 1 -0.5 +-0.49265312381 0.49332495053 -1.9656359509 -0.02608137991 -1 1.121521147 0.30281888252 -0.30293622513 1.8067963894 +1 -0.02608137991 1.121521147 -0.23236342389 0.26314805583 1.2867556783 0.074337770194 0.074337770194 2.5 +-0.40005770996 -0.36909660956 1.8768871762 -0.4896743493 0.49451064621 1.952829049 -1 1 1.5 +0.02608137991 1 1.121521147 -1 1 0.5 -0.038567136385 0.14628887118 0.55111724874 +-1 0.02608137991 1.121521147 0 -1 -2.5 0.37543954218 -0.36967061971 -1.8691105401 +1 -1 -2.5 0.074337770194 -0.074337770194 -2.5 1 0 -2.5 +0.4135797166 0.38351746262 -1.9274856063 1 -1 -1.5 -1 0 -2.5 +-1 -1 -1.5 -1 -1 -2.5 -1 0 2.5 +-1 1 2.5 0 1 2.5 0.46519182209 0.49356920409 1.95286052 +0 -1 2.5 -1 -1 1.5 -1 -1 2.5 +1 -1 2.5 1 -1 1.5 1 0 2.5 +1 1 2.5 1 1 1.5 0 1 -2.5 +-1 1 -2.5 1 -1 -0.5 1 1 -0.5 +0.038669824604 1 0.0046021677516 -1 -1 0.5 -0.038669824604 -1 0.0046021677516 +1 1 0.5 -1 0.038669824604 0.0046021677516 1 -0.038669824604 0.0046021677516 +1 1 -2.5 1 1 -1.5 1 -1 0.5 +-1 -1 -0.5 +CELLS 155 616 +OFFSETS vtktypeint64 +0 4 8 12 16 20 24 28 32 +36 40 44 48 52 56 60 64 68 +72 76 80 84 88 92 96 100 104 +108 112 116 120 124 128 132 136 140 +144 148 152 156 160 164 168 172 176 +180 184 188 192 196 200 204 208 212 +216 220 224 228 232 236 240 244 248 +252 256 260 264 268 272 276 280 284 +288 292 296 300 304 308 312 316 320 +324 328 332 336 340 344 348 352 356 +360 364 368 372 376 380 384 388 392 +396 400 404 408 412 416 420 424 428 +432 436 440 444 448 452 456 460 464 +468 472 476 480 484 488 492 496 500 +504 508 512 516 520 524 528 532 536 +540 544 548 552 556 560 564 568 572 +576 580 584 588 592 596 600 604 608 +612 616 +CONNECTIVITY vtktypeint64 +0 1 2 3 4 3 5 6 7 +4 8 3 7 3 2 9 8 3 +4 6 10 11 12 13 14 13 15 +16 17 13 18 16 19 18 13 20 +10 13 21 15 22 1 0 23 22 +24 25 23 25 1 3 9 26 23 +5 27 24 28 26 23 0 1 3 +23 29 30 31 1 22 31 30 1 +2 3 1 9 29 2 1 9 22 +0 28 23 32 15 21 16 14 13 +11 15 33 17 34 16 33 34 32 +16 34 16 18 35 10 11 13 15 +18 13 12 35 36 11 10 15 36 +37 38 15 32 38 37 15 32 21 +17 16 36 10 37 15 36 39 40 +11 41 40 39 11 41 12 11 35 +17 21 13 16 10 13 12 20 12 +13 11 35 14 13 16 35 34 17 +18 16 42 34 43 35 41 12 40 +11 14 41 11 35 42 41 14 35 +0 3 5 23 0 5 3 6 44 +4 9 27 29 2 30 1 44 45 +29 9 29 45 7 9 0 46 5 +6 47 48 4 6 0 3 2 6 +4 5 3 27 49 10 50 20 8 +2 3 6 51 18 48 20 49 50 +52 20 19 13 21 20 51 53 12 +20 19 48 18 20 25 3 1 23 +7 4 3 9 25 3 23 27 44 +4 7 9 25 23 26 27 54 55 +44 27 54 26 55 27 18 12 13 +20 14 16 34 35 19 21 52 20 +30 2 0 1 22 25 29 1 5 +23 3 27 25 26 44 27 22 29 +31 1 54 44 26 27 25 44 9 +27 22 28 24 23 26 5 55 27 +25 24 26 23 22 25 1 23 25 +44 29 9 25 29 1 9 48 6 +53 20 8 4 48 6 10 40 56 +12 10 40 12 11 17 19 18 13 +30 57 0 2 25 9 3 27 7 +8 2 3 26 28 5 23 29 7 +2 9 28 0 5 23 37 10 49 +21 49 50 57 52 56 12 53 20 +10 12 56 20 43 18 12 35 32 +37 21 15 14 11 13 35 19 52 +8 48 19 52 48 20 41 39 36 +11 50 6 52 20 46 53 5 6 +4 3 9 27 44 7 45 9 55 +47 4 5 28 0 46 5 8 52 +2 6 57 2 52 6 50 56 53 +20 10 56 50 20 57 50 0 6 +37 10 21 15 33 32 17 16 34 +18 43 35 57 52 50 6 51 48 +47 53 18 16 13 35 50 56 46 +53 14 36 32 15 14 11 36 15 +17 21 19 13 10 21 13 20 51 +12 18 20 50 53 46 6 14 15 +32 16 42 43 41 35 14 41 36 +11 49 52 21 20 36 40 10 11 +47 53 48 6 49 21 10 20 21 +15 13 16 51 48 53 20 55 5 +4 27 44 55 4 27 47 4 5 +6 8 48 52 6 47 5 53 6 +50 53 6 20 57 0 2 6 50 +46 0 6 52 6 48 20 42 14 +34 35 43 18 51 12 41 43 12 +35 22 30 0 1 14 32 34 16 +36 38 32 15 +CELL_TYPES 154 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 + +CELL_DATA 154 +FIELD FieldData 1 +ids 1 154 double +0 1 2 3 4 5 6 7 8 +9 10 11 12 13 14 15 16 17 +18 19 20 21 22 23 24 25 26 +27 28 29 30 31 32 33 34 35 +36 37 38 39 40 41 42 43 44 +45 46 47 48 49 50 51 52 53 +54 55 56 57 58 59 60 61 62 +63 64 65 66 67 68 69 70 71 +72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 +90 91 92 93 94 95 96 97 98 +99 100 101 102 103 104 105 106 107 +108 109 110 111 112 113 114 115 116 +117 118 119 120 121 122 123 124 125 +126 127 128 129 130 131 132 133 134 +135 136 137 138 139 140 141 142 143 +144 145 146 147 148 149 150 151 152 +153 diff --git a/tests/unit_tests/test_bounding_box.py b/tests/unit_tests/test_bounding_box.py new file mode 100644 index 0000000000..57c880092e --- /dev/null +++ b/tests/unit_tests/test_bounding_box.py @@ -0,0 +1,190 @@ +import numpy as np +import openmc +import pytest + + +test_bb_1 = openmc.BoundingBox((-10.0, -20.0, -30.0), (1.0, 2.0, 3.0)) +test_bb_2 = openmc.BoundingBox((1.0, 2.0, 3.0), (11.0, 22.0, 33.0)) +test_bb_3 = openmc.BoundingBox((-10.0, -20.0, -30.0), (-1.0, -2.0, -3.0)) + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, 7986), # 11 * 22 * 33 + (test_bb_2, 6000), # 10 * 20 * 30 + (test_bb_3, 4374), # 9 * 18 * 27 + ], +) +def test_bounding_box_volume(bb, expected): + assert bb.volume == expected + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([-10.0, -20.0, -30.0])), + (test_bb_2, np.array([1.0, 2.0, 3.0])), + (test_bb_3, np.array([-10.0, -20.0, -30.0])), + ], +) +def test_bounding_lower_left(bb, expected): + assert np.array_equiv(expected, bb.lower_left) + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([1.0, 2.0, 3.0])), + (test_bb_2, np.array([11.0, 22.0, 33.0])), + (test_bb_3, np.array([-1.0, -2.0, -3.0])), + ], +) +def test_bounding_upper_right(bb, expected): + assert np.array_equiv(expected, bb.upper_right) + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([-4.5, -9.0, -13.5])), + (test_bb_2, np.array([6.0, 12.0, 18.0])), + (test_bb_3, np.array([-5.5, -11.0, -16.5])), + ], +) +def test_bounding_box_center(bb, expected): + assert np.array_equiv(expected, bb.center) + + +def test_bounding_box_input_checking(): + # checks that only passing lower_left is not accepted + with pytest.raises(TypeError): + openmc.BoundingBox((-10, -20, -3)) + # checks that a tuple with three entry is not accepted + with pytest.raises(TypeError): + openmc.BoundingBox((-1, -2, -3), (-1, -2, -3), (-1, -2, -3)) + # checks that a numpy array with two entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox(np.array([-10, -30]), np.array([1, 2, 3])) + # checks that a numpy array with two entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox(np.array([-10, -20, -30]), np.array([1, 3])) + # checks that a numpy array with four entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox(np.array([-10, -20, -3, -4]), np.array([1, 2, 3])) + # checks that a numpy array with four entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox(np.array([-10, -20, -4]), np.array([1, 2, 3, 4])) + + +def test_bounding_box_extents(): + assert test_bb_1.extent["xy"] == (-10.0, 1.0, -20.0, 2.0) + assert test_bb_1.extent["xz"] == (-10.0, 1.0, -30.0, 3.0) + assert test_bb_1.extent["yz"] == (-20.0, 2.0, -30.0, 3.0) + + +def test_bounding_box_methods(): + test_bb = openmc.BoundingBox.infinite() + + # check assignment operator + test_bb[0] = [-10, -11, -12] + test_bb[1] = [13, 14, 15] + + assert all(test_bb[0] == [-10, -11, -12]) + assert all(test_bb[1] == [13, 14, 15]) + + # check length and iteration + assert len(test_bb) == 2 + ll, ur = test_bb + assert all(ll == [-10, -11, -12]) + assert all(ur == [13, 14, 15]) + + # test expand/reduce methods + other_bb = openmc.BoundingBox([-5, -5, -50], [5, 50, 5]) + + reduced_bb = test_bb & other_bb + + # inplace was False by default. BoundingBox.reduce should return a new object + assert test_bb is not reduced_bb + + # the original bounding box should be unchanged + assert all(test_bb[0] == [-10, -11, -12]) + assert all(test_bb[1] == [13, 14, 15]) + + assert all(reduced_bb[0] == [-5, -5, -12]) + assert all(reduced_bb[1] == [5, 14, 5]) + + test_bb &= other_bb + + assert all(test_bb[0] == [-5, -5, -12]) + assert all(test_bb[1] == [5, 14, 5]) + + other_bb = openmc.BoundingBox([-50, -50, -1], [50, 1, 50]) + + expanded_bb = test_bb | other_bb + + # inplace was False by default. BoundingBox.expand should return a new object + assert test_bb is not expanded_bb + + # the original bounding box should be unchanged + assert all(test_bb[0] == [-5, -5, -12]) + assert all(test_bb[1] == [5, 14, 5]) + + assert all(expanded_bb[0] == [-50, -50, -12]) + assert all(expanded_bb[1] == [50, 14, 50]) + + test_bb |= other_bb + + assert all(test_bb[0] == [-50, -50, -12]) + assert all(test_bb[1] == [50, 14, 50]) + + extended_bbox = test_bb.expand(0.1) + + assert extended_bbox is not test_bb + + # the original bounding box should not be changed with inplace as False + assert all(test_bb[0] == [-50, -50, -12]) + assert all(test_bb[1] == [50, 14, 50]) + + assert all(extended_bbox[0] == [-50.1, -50.1, -12.1]) + assert all(extended_bbox[1] == [50.1, 14.1, 50.1]) + + extended_bbox = test_bb.expand(0.1, True) + + # inplace was set to True. BoundingBox.reduce should return the same object + assert extended_bbox is test_bb + + assert all(test_bb[0] == [-50.1, -50.1, -12.1]) + assert all(test_bb[1] == [50.1, 14.1, 50.1]) + + +@pytest.mark.parametrize( + "bb, other, expected", + [ + (test_bb_1, (0, 0, 0), True), + (test_bb_2, (3, 3, 3), False), + # completely disjoint + (test_bb_1, test_bb_2, False), + # contained but touching border + (test_bb_1, test_bb_3, False), + # Fully contained + (test_bb_1, openmc.BoundingBox((-9, -19, -29), (0, 0, 0)), True), + # intersecting boxes + (test_bb_1, openmc.BoundingBox((-9, -19, -29), (1, 2, 5)), False), + ], +) +def test_bounding_box_contains(bb, other, expected): + assert (other in bb) == expected + + +@pytest.mark.parametrize( + "invalid, ex", + [ + ((1, 0), ValueError), + ((1, 2, 3, 4), ValueError), + ("foo", TypeError), + ], +) +def test_bounding_box_contains_checking(invalid, ex): + with pytest.raises(ex): + invalid in test_bb_1 diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 9234e7e4b5..60b2058186 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -1,11 +1,9 @@ -import xml.etree. ElementTree as ET - +import lxml.etree as ET import numpy as np from uncertainties import ufloat import openmc import pytest - from tests.unit_tests import assert_unbounded from openmc.data import atomic_mass, AVOGADRO @@ -57,25 +55,51 @@ def test_clone(): m = openmc.Material() cyl = openmc.ZCylinder() c = openmc.Cell(fill=m, region=-cyl) - c.temperature = 650. + # Check cloning with all optional params as the defaults c2 = c.clone() assert c2.id != c.id assert c2.fill != c.fill assert c2.region != c.region - assert c2.temperature == c.temperature c3 = c.clone(clone_materials=False) assert c3.id != c.id assert c3.fill == c.fill assert c3.region != c.region - assert c3.temperature == c.temperature c4 = c.clone(clone_regions=False) assert c4.id != c.id assert c4.fill != c.fill assert c4.region == c.region - assert c4.temperature == c.temperature + + # Add optional properties to the original cell to ensure they're cloned successfully + c.temperature = 650. + c.translation = (1., 2., 3.) + c.rotation = (4., 5., 6.) + c.volume = 100 + + c5 = c.clone(clone_materials=False, clone_regions=False) + assert c5.id != c.id + assert c5.fill == c.fill + assert c5.region == c.region + assert c5.temperature == c.temperature + assert c5.volume == c.volume + assert all(c5.translation == c.translation) + assert all(c5.rotation == c.rotation) + + # Mutate the original to ensure the changes are not seen in the clones + c.fill = openmc.Material() + c.region = +openmc.ZCylinder() + c.translation = (-1., -2., -3.) + c.rotation = (-4., -5., -6.) + c.temperature = 1 + c.volume = 1 + assert c5.fill != c.fill + assert c5.region != c.region + assert c5.temperature != c.temperature + assert c5.volume != c.volume + assert all(c5.translation != c.translation) + assert all(c5.rotation != c.rotation) def test_temperature(cell_with_lattice): @@ -92,6 +116,9 @@ def test_temperature(cell_with_lattice): assert c2.temperature == 400.0 with pytest.raises(ValueError): c.temperature = -100. + c.temperature = None + assert c1.temperature == None + assert c2.temperature == None # distributed temperature cells, _, _, _ = cell_with_lattice @@ -99,6 +126,29 @@ def test_temperature(cell_with_lattice): c.temperature = (300., 600., 900.) +def test_densities(cell_with_lattice): + # Make sure density propagates through universes + m = openmc.Material() + s = openmc.XPlane() + c1 = openmc.Cell(fill=m, region=+s) + c2 = openmc.Cell(fill=m, region=-s) + u1 = openmc.Universe(cells=[c1, c2]) + c = openmc.Cell(fill=u1) + + c.density = 1. + assert c1.density == 1. + assert c2.density == 1. + with pytest.raises(ValueError): + c.density = -1. + c.density = None + assert c1.density == None + assert c2.density == None + + # distributed density + cells, _, _, _ = cell_with_lattice + c = cells[0] + c.density = (1., 2., 3.) + def test_rotation(): u = openmc.Universe() c = openmc.Cell(fill=u) @@ -294,6 +344,43 @@ def test_to_xml_element(cell_with_lattice): c = cells[0] c.temperature = 900.0 + c.volume = 1.0 elem = c.create_xml_subelement(root) assert elem.get('region') == str(c.region) assert elem.get('temperature') == str(c.temperature) + assert elem.get('volume') == str(c.volume) + + +@pytest.mark.parametrize("rotation", [ + (90, 45, 0), + [[1.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]] +]) +def test_rotation_from_xml(rotation): + # Make sure rotation attribute (matrix) round trips through XML correctly + s = openmc.ZCylinder(r=10.0) + cell = openmc.Cell(region=-s) + cell.rotation = rotation + root = ET.Element('geometry') + elem = cell.create_xml_subelement(root) + new_cell = openmc.Cell.from_xml_element( + elem, {s.id: s}, {'void': None}, openmc.Universe + ) + np.testing.assert_allclose(new_cell.rotation, cell.rotation) + + +def test_plot(run_in_tmpdir): + zcyl = openmc.ZCylinder() + c = openmc.Cell(region=-zcyl) + + # create a universe before the plot + u_before = openmc.Universe() + + # create a plot of the cell + c.plot() + + # create a universe after the plot + u_after = openmc.Universe() + + # ensure that calling the plot method doesn't + # affect the universe ID space + assert u_before.id + 1 == u_after.id diff --git a/tests/unit_tests/test_complex_cell_bb.py b/tests/unit_tests/test_complex_cell_bb.py index ad6491cca9..8a94d9ddcf 100644 --- a/tests/unit_tests/test_complex_cell_bb.py +++ b/tests/unit_tests/test_complex_cell_bb.py @@ -66,7 +66,7 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( + model.settings.source = openmc.IndependentSource(space=openmc.stats.Box( [-10., -10., -1.], [10., 10., 1.])) model.settings.verbosity = 1 diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py new file mode 100644 index 0000000000..45e6a7e5a1 --- /dev/null +++ b/tests/unit_tests/test_config.py @@ -0,0 +1,104 @@ +from collections.abc import Mapping +import os +from pathlib import Path + +import openmc +from openmc.config import _default_config +from openmc.data import decay +import pytest + + +@pytest.fixture(autouse=True, scope='function') +def reset_config_and_env(): + """A fixture to ensure each test has a clean config, env, and CWD.""" + original_env = dict(os.environ) + original_cwd = os.getcwd() + original_resolve_paths = openmc.config["resolve_paths"] + + # Reset environment variables that affect config + for key in ['OPENMC_CROSS_SECTIONS', 'OPENMC_MG_CROSS_SECTIONS', 'OPENMC_CHAIN_FILE']: + if key in os.environ: + del os.environ[key] + + # Re-initialize the global config object + openmc.config = _default_config() + + try: + yield + finally: + # Restore environment, CWD and resolve_paths + os.environ.clear() + os.environ.update(original_env) + os.chdir(original_cwd) + + # Restore config one last time for safety between modules + openmc.config = _default_config(resolve_paths=original_resolve_paths) + + +def test_config_basics(): + assert isinstance(openmc.config, Mapping) + with pytest.warns(UserWarning): + openmc.config['cross_sections'] = '/path/to/cross_sections.xml' + del openmc.config['cross_sections'] + assert 'cross_sections' not in openmc.config + assert 'OPENMC_CROSS_SECTIONS' not in os.environ + with pytest.raises(KeyError, match="Unrecognized config key: nuke"): + openmc.config['nuke'] = '/like/to/eat/bacon' + with pytest.raises(TypeError): + openmc.config['resolve_paths'] = 'not a bool' + + +def test_config_path_resolution(tmp_path): + """Test path resolution logic.""" + os.chdir(tmp_path) + relative_path = Path("some/file.xml") + absolute_path = relative_path.resolve() + + # Test with resolve_paths = True (default) + with pytest.warns(UserWarning): + openmc.config['cross_sections'] = relative_path + assert openmc.config['cross_sections'] == absolute_path + assert openmc.config['cross_sections'].is_absolute() + + # Test with resolve_paths = False + with openmc.config.patch('resolve_paths', False): + with pytest.warns(UserWarning): + openmc.config['chain_file'] = relative_path + assert openmc.config['chain_file'] == relative_path + assert not openmc.config['chain_file'].is_absolute() + + assert openmc.config['resolve_paths'] is True + + +def test_config_patch(tmp_path): + file_a = tmp_path / "a.xml"; file_a.touch() + file_b = tmp_path / "b.xml"; file_b.touch() + openmc.config['cross_sections'] = file_a + with openmc.config.patch('cross_sections', file_b): + assert openmc.config['cross_sections'] == file_b.resolve() + assert openmc.config['cross_sections'] == file_a.resolve() + +def test_config_set_envvar(tmp_path): + """Test that setting config also sets environment variables correctly.""" + os.chdir(tmp_path) + relative_path = Path("relative.xml") + with pytest.warns(UserWarning): + openmc.config['cross_sections'] = relative_path + expected_path = str(relative_path.resolve()) + assert os.environ['OPENMC_CROSS_SECTIONS'] == expected_path + + +def test_config_warning_nonexistent_path(tmp_path): + """Test that a warning is issued for a path that does not exist.""" + bad_path = tmp_path / "a/path/that/does/not/exist.xml" + with pytest.warns(UserWarning, match=f"Path '{bad_path}' does not exist."): + openmc.config['chain_file'] = bad_path + + +def test_config_chain_side_effect(tmp_path): + """Test that modifying chain_file clears decay data caches.""" + chain_file = tmp_path / "chain.xml"; chain_file.touch() + decay._DECAY_ENERGY['U235'] = (1.0, 2.0) + decay._DECAY_PHOTON_ENERGY['PU239'] = {} + openmc.config['chain_file'] = chain_file + assert not decay._DECAY_ENERGY and not decay._DECAY_PHOTON_ENERGY diff --git a/tests/unit_tests/test_cylindrical_mesh.py b/tests/unit_tests/test_cylindrical_mesh.py new file mode 100644 index 0000000000..645269825a --- /dev/null +++ b/tests/unit_tests/test_cylindrical_mesh.py @@ -0,0 +1,202 @@ +from itertools import product, permutations + +import openmc +import numpy as np + +import pytest + +geom_size = 5 + +@pytest.fixture() +def model(): + openmc.reset_auto_ids() + + water = openmc.Material(name='water') + water.add_element('H', 2.0) + water.add_element('O', 1.0) + water.set_density('g/cc', 1.0) + + rpp = openmc.model.RectangularParallelepiped(*([-geom_size, geom_size] * 3), + boundary_type='vacuum') + + cell = openmc.Cell(region=-rpp, fill=water) + + geom = openmc.Geometry([cell]) + + source = openmc.IndependentSource() + source.space = openmc.stats.Point() + source.energy = openmc.stats.Discrete([10000], [1.0]) + + settings = openmc.Settings() + settings.particles = 2000 + settings.batches = 10 + settings.run_mode = 'fixed source' + + # build + mesh = openmc.CylindricalMesh( + phi_grid=np.linspace(0, 2*np.pi, 21), + z_grid=np.linspace(-geom_size, geom_size, 11), + r_grid=np.linspace(0, geom_size, geom_size) + ) + tally = openmc.Tally() + + mesh_filter = openmc.MeshFilter(mesh) + tally.filters.append(mesh_filter) + + tally.scores.append("flux") + + tallies = openmc.Tallies([tally]) + + return openmc.Model(geometry=geom, settings=settings, tallies=tallies) + + +def test_origin_read_write_to_xml(run_in_tmpdir, model): + """Tests that the origin attribute can be written and read back to XML + """ + mesh = model.tallies[0].filters[0].mesh + mesh.origin = [0.1, 0.2, 0.3] + model.tallies.export_to_xml() + + # read back + new_tallies = openmc.Tallies.from_xml() + new_tally = new_tallies[0] + new_mesh = new_tally.filters[0].mesh + np.testing.assert_equal(new_mesh.origin, mesh.origin) + +estimators = ('tracklength', 'collision') +offset = geom_size + 0.001 +origins = set(permutations((-offset , 0, 0))) +origins |= set(permutations((offset, 0, 0))) + +test_cases = product(estimators, origins) + +def label(p): + if isinstance(p, tuple): + return f'origin:{p}' + if isinstance(p, str): + return f'estimator:{p}' + + +@pytest.mark.parametrize('estimator,origin', test_cases, ids=label) +def test_offset_mesh(model, estimator, origin): + """Tests that the mesh has been moved based on tally results + """ + mesh = model.tallies[0].filters[0].mesh + model.tallies[0].estimator = estimator + # move the center of the cylinder mesh upwards + mesh.origin = origin + + sp_filename = model.run() + + with openmc.StatePoint(sp_filename) as sp: + tally = sp.tallies[1] + + # we've translated half of the cylinder mesh above the model, + # so ensure that half of the bins are populated + assert np.count_nonzero(tally.mean) == tally.mean.size / 2 + + # check that the half of the mesh that is outside of the geometry + # contains the zero values + mean = tally.get_reshaped_data('mean', expand_dims=True) + centroids = mesh.centroids + for ijk in mesh.indices: + i, j, k = np.array(ijk) - 1 + if model.geometry.find(centroids[i, j, k]): + mean[i, j, k] == 0.0 + else: + mean[i, j, k] != 0.0 + + +@pytest.fixture() +def void_coincident_geom_model(): + """A model with many geometric boundaries coincident with mesh boundaries + across many scales + """ + openmc.reset_auto_ids() + model = openmc.model.Model() + + model.materials = openmc.Materials() + radii = [0.1,1, 5, 50, 100, 150, 250] + cylinders = [openmc.ZCylinder(r=ri) for ri in radii] + cylinders[-1].boundary_type = 'vacuum' + + regions = openmc.model.subdivide(cylinders)[:-1] + cells = [openmc.Cell(region=r, fill=None) for r in regions] + geom = openmc.Geometry(cells) + + model.geometry = geom + + settings = openmc.Settings(run_mode='fixed source') + settings.batches = 2 + settings.particles = 1000 + model.settings = settings + + mesh = openmc.CylindricalMesh( + r_grid=np.linspace(0, 250, 501), + z_grid=[-250, 250], + phi_grid=np.linspace(0, 2*np.pi, 2), + ) + mesh_filter = openmc.MeshFilter(mesh) + + tally = openmc.Tally() + tally.scores = ['flux'] + tally.filters = [mesh_filter] + + model.tallies = openmc.Tallies([tally]) + + return model + + +# convenience function for checking tally results +# in the following tests +def _check_void_cylindrical_tally(statepoint_filename): + with openmc.StatePoint(statepoint_filename) as sp: + flux_tally = sp.tallies[1] + mesh = flux_tally.find_filter(openmc.MeshFilter).mesh + neutron_flux = flux_tally.get_reshaped_data().squeeze() + # we expect the tally results to be the same as the mesh grid width + # for these cases + d_r = mesh.r_grid[1] - mesh.r_grid[0] + assert neutron_flux == pytest.approx(d_r) + + +def test_void_geom_pnt_src(run_in_tmpdir, void_coincident_geom_model): + src = openmc.IndependentSource() + src.space = openmc.stats.Point() + src.angle = openmc.stats.PolarAzimuthal(mu=openmc.stats.Discrete([0.0], [1.0])) + src.energy = openmc.stats.Discrete([14.06e6], [1]) + void_coincident_geom_model.settings.source = src + + sp_filename = void_coincident_geom_model.run() + _check_void_cylindrical_tally(sp_filename) + + +def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): + # update source to a number of points on the outside of the cylinder + # with directions pointing toward the origin + bbox = void_coincident_geom_model.geometry.bounding_box + + # can't source particle directly on the geometry boundary + outer_r = bbox[1][0] - 1e-08 + + n_sources = 100 + radial_vals = np.linspace(0.0, 2.0*np.pi, n_sources) + + sources = [] + + energy = openmc.stats.Discrete([14.06e6], [1]) + for val in radial_vals: + src = openmc.IndependentSource() + src.energy = energy + + pnt = np.array([np.cos(val), np.sin(val), 0.0]) + u = -pnt + src.space = openmc.stats.Point(outer_r*pnt) + src.angle = openmc.stats.Monodirectional(u) + src.strength = 0.5/n_sources + sources.append(src) + + void_coincident_geom_model.settings.source = sources + sp_filename = void_coincident_geom_model.run() + + _check_void_cylindrical_tally(sp_filename) \ No newline at end of file diff --git a/tests/unit_tests/test_d1s.py b/tests/unit_tests/test_d1s.py new file mode 100644 index 0000000000..9410f2da2e --- /dev/null +++ b/tests/unit_tests/test_d1s.py @@ -0,0 +1,138 @@ +from pathlib import Path +from math import exp + +import numpy as np +import pytest +import openmc +import openmc.deplete +from openmc.deplete import d1s + + +CHAIN_PATH = Path(__file__).parents[1] / "chain_ni.xml" + + +@pytest.fixture +def model(): + """Simple model with natural Ni""" + mat = openmc.Material() + mat.add_element('Ni', 1.0) + geom = openmc.Geometry([openmc.Cell(fill=mat)]) + return openmc.Model(geometry=geom) + + +def test_get_radionuclides(model): + # Check that radionuclides are correct and are unstable + chain = openmc.deplete.Chain.from_xml(CHAIN_PATH) + nuclides = d1s.get_radionuclides(model, chain) + assert sorted(nuclides) == [ + 'Co58', 'Co60', 'Co61', 'Co62', 'Co64', + 'Fe55', 'Fe59', 'Fe61', 'Ni57', 'Ni59', 'Ni63', 'Ni65' + ] + for nuc in nuclides: + assert openmc.data.half_life(nuc) is not None + + +@pytest.mark.parametrize("nuclide", ['Co60', 'Ni63', 'H3', 'Na24', 'K40']) +def test_time_correction_factors(nuclide): + # Irradiation schedule turning unit neutron source on and off + timesteps = [1.0, 1.0, 1.0] + source_rates = [1.0, 0.0, 1.0] + + # Compute expected solution + decay_rate = openmc.data.decay_constant(nuclide) + g = exp(-decay_rate) + expected = [0.0, (1 - g), (1 - g)*g, (1 - g)*(1 + g*g)] + + # Test against expected solution + tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates) + assert tcf[nuclide] == pytest.approx(expected) + + # Make sure all values at first timestep and onward are positive (K40 case + # has very small decay constant that stresses this) + assert np.all(tcf[nuclide][1:] > 0.0) + + # Timesteps as a tuple + timesteps = [(1.0, 's'), (1.0, 's'), (1.0, 's')] + tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates) + assert tcf[nuclide] == pytest.approx(expected) + + # Test changing units + timesteps = [1.0/60.0, 1.0/60.0, 1.0/60.0] + tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates, + timestep_units='min') + assert tcf[nuclide] == pytest.approx(expected) + + +def test_prepare_tallies(model): + tally = openmc.Tally() + tally.filters = [openmc.ParticleFilter('photon')] + tally.scores = ['flux'] + model.tallies = [tally] + + # Check that prepare_tallies adds a ParentNuclideFilter + nuclides = ['Co58', 'Co60', 'Fe55'] + d1s.prepare_tallies(model, nuclides, chain_file=CHAIN_PATH) + assert tally.contains_filter(openmc.ParentNuclideFilter) + assert list(tally.filters[-1].bins) == nuclides + + # Get rid of parent nuclide filter + tally.filters.pop() + + # With no nuclides specified, filter should use get_radionuclides + radionuclides = d1s.get_radionuclides(model, CHAIN_PATH) + d1s.prepare_tallies(model, chain_file=CHAIN_PATH) + assert tally.contains_filter(openmc.ParentNuclideFilter) + assert sorted(tally.filters[-1].bins) == sorted(radionuclides) + + assert len(tally.filters) == 2 + # calling prepare_tallies twice should not add another ParentNuclideFilter + d1s.prepare_tallies(model, chain_file=CHAIN_PATH) + assert len(tally.filters) == 2 + + +def test_apply_time_correction(run_in_tmpdir): + # Make simple sphere model with elemental Ni + mat = openmc.Material() + mat.add_element('Ni', 1.0) + sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.run_mode = 'fixed source' + model.settings.batches = 3 + model.settings.particles = 10 + model.settings.photon_transport = True + model.settings.use_decay_photons = True + particle_filter = openmc.ParticleFilter('photon') + tally = openmc.Tally() + tally.filters = [particle_filter] + tally.scores = ['flux'] + model.tallies = [tally] + + # Prepare tallies for D1S and compute time correction factors + nuclides = d1s.prepare_tallies(model, chain_file=CHAIN_PATH) + factors = d1s.time_correction_factors(nuclides, [1.0e10], [1.0]) + + # Run OpenMC and get tally result + with openmc.config.patch('chain_file', CHAIN_PATH): + output_path = model.run() + with openmc.StatePoint(output_path) as sp: + tally = sp.tallies[tally.id] + flux = tally.mean.flatten() + + # Apply TCF and make sure results are consistent + result = d1s.apply_time_correction(tally, factors, sum_nuclides=False) + tcf = np.array([factors[nuc][-1] for nuc in nuclides]) + assert result.mean.flatten() == pytest.approx(tcf * flux) + + # Make sure summed results match a manual sum + result_summed = d1s.apply_time_correction(tally, factors) + assert result_summed.mean.flatten()[0] == pytest.approx(result.mean.sum()) + + # Make sure various tally methods work + result.get_values() + result_summed.get_values() + result.get_reshaped_data() + result_summed.get_reshaped_data() + result.get_pandas_dataframe() + result_summed.get_pandas_dataframe() diff --git a/tests/unit_tests/test_data_decay.py b/tests/unit_tests/test_data_decay.py index 06b8e6bedd..de8d90a434 100644 --- a/tests/unit_tests/test_data_decay.py +++ b/tests/unit_tests/test_data_decay.py @@ -1,13 +1,14 @@ #!/usr/bin/env python -from collections.abc import Mapping import os from math import log +from pathlib import Path import numpy as np import pytest from uncertainties import ufloat import openmc.data +from openmc.exceptions import DataError def ufloat_close(a, b): @@ -16,17 +17,22 @@ def ufloat_close(a, b): @pytest.fixture(scope='module') -def nb90(): +def nb90(endf_data): """Nb90 decay data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'decay', 'dec-041_Nb_090.endf') return openmc.data.Decay.from_endf(filename) @pytest.fixture(scope='module') -def u235_yields(): +def ba137m(endf_data): + """Ba137_m1 decay data.""" + filename = os.path.join(endf_data, 'decay', 'dec-056_Ba_137m1.endf') + return openmc.data.Decay.from_endf(filename) + + +@pytest.fixture(scope='module') +def u235_yields(endf_data): """U235 fission product yield data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'nfy', 'nfy-092_U_235.endf') return openmc.data.FissionProductYields.from_endf(filename) @@ -48,6 +54,7 @@ def test_nb90_halflife(nb90): ufloat_close(nb90.decay_constant, log(2.)/nb90.half_life) ufloat_close(nb90.decay_energy, ufloat(2265527.5, 25159.400474401213)) + def test_nb90_nuclide(nb90): assert nb90.nuclide['atomic_number'] == 41 assert nb90.nuclide['mass_number'] == 90 @@ -91,3 +98,51 @@ def test_fpy(u235_yields): assert len(u235_yields.independent) == 3 thermal = u235_yields.independent[0] ufloat_close(thermal['I135'], ufloat(0.0292737, 0.000819663)) + + +def test_sources(ba137m, nb90): + # Running .sources twice should give same objects + sources = ba137m.sources + sources2 = ba137m.sources + for key in sources: + assert sources[key] is sources2[key] + + # Each source should be a univariate distribution + for dist in sources.values(): + assert isinstance(dist, openmc.stats.Univariate) + + # Check for presence of 662 keV gamma ray in decay of Ba137m + gamma_source = ba137m.sources['photon'] + assert isinstance(gamma_source, openmc.stats.Discrete) + b = np.isclose(gamma_source.x, 661657.) + assert np.count_nonzero(b) == 1 + + # Check value of decay/s/atom + idx = np.flatnonzero(b)[0] + assert gamma_source.p[idx] == pytest.approx(0.004069614) + + # Nb90 decays by β+ and should emit positrons, electrons, and photons + sources = nb90.sources + assert len(set(sources.keys()) ^ {'positron', 'electron', 'photon'}) == 0 + + +def test_decay_photon_energy(): + # If chain file is not set, we should get a data error + if 'chain_file' in openmc.config: + del openmc.config['chain_file'] + with pytest.raises(DataError): + openmc.data.decay_photon_energy('I135') + + # Set chain file to simple chain + openmc.config['chain_file'] = Path(__file__).parents[1] / "chain_simple.xml" + + # Check strength of I135 source and presence of specific spectral line + src = openmc.data.decay_photon_energy('I135') + assert isinstance(src, openmc.stats.Discrete) + assert src.integral() == pytest.approx(3.920996223799345e-05) + assert 1260409. in src.x + + # Check Xe135 source, which should be tabular + src = openmc.data.decay_photon_energy('Xe135') + assert isinstance(src, openmc.stats.Tabular) + assert src.integral() == pytest.approx(2.076506258964966e-05) diff --git a/tests/unit_tests/test_data_dose.py b/tests/unit_tests/test_data_dose.py index 348143e0b0..4f18800143 100644 --- a/tests/unit_tests/test_data_dose.py +++ b/tests/unit_tests/test_data_dose.py @@ -22,8 +22,42 @@ def test_dose_coefficients(): assert energy[-1] == approx(10e9) assert dose[-1] == approx(699.0) + energy, dose = dose_coefficients('photon', data_source='icrp74') + assert energy[0] == approx(0.01e6) + assert dose[0] == approx(7.43*0.00653) + assert energy[-1] == approx(10.0e6) + assert dose[-1] == approx(24.0*0.990) + + energy, dose = dose_coefficients('neutron', 'LLAT', data_source='icrp74') + assert energy[0] == approx(1e-3) + assert dose[0] == approx(1.68) + assert energy[-1] == approx(20.0e6) + assert dose[-1] == approx(338.0) + # Invalid particle/geometry should raise an exception with raises(ValueError): dose_coefficients('slime', 'LAT') with raises(ValueError): dose_coefficients('neutron', 'ZZ') + with raises(ValueError): + dose_coefficients('neutron', data_source='icrp7000') + with raises(ValueError) as excinfo: + dose_coefficients("photons", data_source="icrp116") + expected_particles = [ + "electron", + "helium", + "mu+", + "mu-", + "neutron", + "photon", + "photon kerma", + "pi+", + "pi-", + "positron", + "proton", + ] + expected_msg = ( + "'photons' has no dose data in data source icrp116. " + f"Available particles for icrp116 are: {expected_particles}" + ) + assert str(excinfo.value) == expected_msg diff --git a/tests/unit_tests/test_data_kalbach_mann.py b/tests/unit_tests/test_data_kalbach_mann.py new file mode 100644 index 0000000000..5d06669f7b --- /dev/null +++ b/tests/unit_tests/test_data_kalbach_mann.py @@ -0,0 +1,177 @@ +"""Test of the Kalbach-Mann slope calculation when data are +retrieved from ENDF files.""" + +import os +from pathlib import Path +import pytest + +import numpy as np + +import openmc +from openmc.data import IncidentNeutron +from openmc.data.kalbach_mann import _separation_energy, _AtomicRepresentation +from openmc.data import kalbach_slope +from openmc.data import KalbachMann + +from . import needs_njoy + + +@pytest.fixture(scope='module') +def neutron(): + """Neutron AtomicRepresentation.""" + return _AtomicRepresentation(z=0, a=1) + + +@pytest.fixture(scope='module') +def triton(): + """Triton AtomicRepresentation.""" + return _AtomicRepresentation(z=1, a=3) + + +@pytest.fixture(scope='module') +def b10(): + """B10 AtomicRepresentation.""" + return _AtomicRepresentation(z=5, a=10) + + +@pytest.fixture(scope='module') +def c12(): + """C12 AtomicRepresentation.""" + return _AtomicRepresentation(z=6, a=12) + + +@pytest.fixture(scope='module') +def c13(): + """C13 AtomicRepresentation.""" + return _AtomicRepresentation(z=6, a=13) + + +@pytest.fixture(scope='module') +def na23(): + """Na23 AtomicRepresentation.""" + return _AtomicRepresentation(z=11, a=23) + + +def test_atomic_representation(neutron, triton, b10, c12, c13, na23): + """Test the _AtomicRepresentation class.""" + # Test instantiation from_za + assert b10 == _AtomicRepresentation.from_za(5010) + + # Test addition + assert c13 + b10 == na23 + + # Test substraction + assert c13 - c12 == neutron + assert c13 - b10 == triton + + # Test properties when no information for Kalbach-Mann are given + assert c13.a == 13 + assert c13.z == 6 + assert c13.n == 7 + assert c13.za == 6013 + + # Test properties when information for Kalbach-Mann are given + assert triton.a == 3 + assert triton.z == 1 + assert triton.n == 2 + assert triton.za == 1003 + + # Test instantiation errors + with pytest.raises(ValueError): + _AtomicRepresentation(z=5, a=1) + with pytest.raises(ValueError): + _AtomicRepresentation(z=-1, a=1) + with pytest.raises(ValueError): + _AtomicRepresentation(z=5, a=0) + with pytest.raises(ValueError): + _AtomicRepresentation(z=5, a=-2) + with pytest.raises(ValueError): + neutron - triton + + +def test_separation_energy(triton, b10, c13): + """Comparison to hand-calculations on a simple example.""" + assert _separation_energy( + compound=c13, + nucleus=b10, + particle=triton + ) == pytest.approx(18.6880713) + + +def test_kalbach_slope(): + """Comparison to hand-calculations for n + c12 -> c13 -> triton + b10.""" + energy_projectile = 10.2 # [eV] + energy_emitted = 5.4 # [eV] + + # Check that NotImplementedError is raised if the projectile is not + # a neutron + with pytest.raises(NotImplementedError): + kalbach_slope( + energy_projectile=energy_projectile, + energy_emitted=energy_emitted, + za_projectile=1000, + za_emitted=1, + za_target=6012 + ) + + assert kalbach_slope( + energy_projectile=energy_projectile, + energy_emitted=energy_emitted, + za_projectile=1, + za_emitted=1003, + za_target=6012 + ) == pytest.approx(0.8409921475) + + +@pytest.mark.parametrize( + "hdf5_filename, endf_filename", [ + ('O16.h5', 'n-008_O_016.endf'), + ('Ca46.h5', 'n-020_Ca_046.endf'), + ('Hg204.h5', 'n-080_Hg_204.endf') + ] +) +def test_comparison_slope_hdf5(hdf5_filename, endf_filename, endf_data): + """Test the calculation of the Kalbach-Mann slope done by OpenMC + by comparing it to HDF5 data. The test is based on the first product + of MT=5 (neutron). The isotopes tested have been selected because the + corresponding products in ENDF/B-VII.1 are described using MF=6, LAW=1, + LANG=2 (i.e., Kalbach-Mann systematics) and the slope is not given + explicitly. + + If an error occurs during the "validity check", this means that + the nuclear data evaluation has evolved and the distribution might + no longer be described using Kalbach-Mann systematics. Another + isotope needs to be identified and tested. + + Warning: This test is valid as long as ENDF files are not directly + used to generate the HDF5 files used in the tests. + + """ + # HDF5 data + hdf5_directory = Path(openmc.config.get('cross_sections')).parent + hdf5_data = IncidentNeutron.from_hdf5(hdf5_directory / hdf5_filename) + hdf5_product = hdf5_data[5].products[0] + hdf5_distribution = hdf5_product.distribution[0] + + # ENDF data + endf_directory = Path(endf_data) + endf_path = endf_directory / 'neutrons' / endf_filename + endf_data = IncidentNeutron.from_endf(endf_path) + endf_product = endf_data[5].products[0] + endf_distribution = endf_product.distribution[0] + + # Validity check + assert isinstance(endf_distribution, KalbachMann) + assert isinstance(hdf5_distribution, KalbachMann) + assert endf_product.particle == hdf5_product.particle + assert len(endf_distribution.slope) == len(hdf5_distribution.slope) + + # Results check + for i, hdf5_slope in enumerate(hdf5_distribution.slope): + assert endf_distribution._calculated_slope[i] + + np.testing.assert_array_almost_equal( + endf_distribution.slope[i].y, + hdf5_slope.y, + decimal=5 + ) diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 6a81fb1e08..14db689130 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from collections.abc import Mapping +from math import log import os from pathlib import Path @@ -11,7 +11,7 @@ import openmc.data def test_data_library(tmpdir): lib = openmc.data.DataLibrary.from_xml() - for f in lib.libraries: + for f in lib: assert sorted(f.keys()) == ['materials', 'path', 'type'] f = lib.get_by_material('U235') @@ -22,16 +22,19 @@ def test_data_library(tmpdir): assert f['type'] == 'thermal' assert 'c_H_in_H2O' in f['materials'] + lib.remove_by_material('Pu239') + assert lib.get_by_material('Pu239') is None + filename = str(tmpdir.join('test.xml')) lib.export_to_xml(filename) assert os.path.exists(filename) new_lib = openmc.data.DataLibrary() - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) + directory = os.path.dirname(openmc.config.get('cross_sections')) new_lib.register_file(os.path.join(directory, 'H1.h5')) - assert new_lib.libraries[-1]['type'] == 'neutron' + assert new_lib[-1]['type'] == 'neutron' new_lib.register_file(os.path.join(directory, 'c_Zr_in_ZrH.h5')) - assert new_lib.libraries[-1]['type'] == 'thermal' + assert new_lib[-1]['type'] == 'thermal' def test_depletion_chain_data_library(run_in_tmpdir): @@ -76,15 +79,17 @@ def test_thin(): def test_atomic_mass(): - assert openmc.data.atomic_mass('H1') == 1.00782503224 - assert openmc.data.atomic_mass('U235') == 235.04392819 + assert openmc.data.atomic_mass('H1') == 1.007825031898 + assert openmc.data.atomic_mass('U235') == 235.043928117 + assert openmc.data.atomic_mass('Li6') == 6.01512288742 + assert openmc.data.atomic_mass('Pb220') == 220.025905 with pytest.raises(KeyError): openmc.data.atomic_mass('U100') def test_atomic_weight(): - assert openmc.data.atomic_weight('C') == 12.011115164864455 - assert openmc.data.atomic_weight('carbon') == 12.011115164864455 + assert openmc.data.atomic_weight('C') == 12.011115164865895 + assert openmc.data.atomic_weight('carbon') == 12.011115164865895 with pytest.raises(ValueError): openmc.data.atomic_weight('Qt') @@ -99,12 +104,12 @@ def test_water_density(): assert dens(500.0, 3.0) == pytest.approx(1e-3/0.120241800e-2, 1e-6) -def test_gnd_name(): - assert openmc.data.gnd_name(1, 1) == 'H1' - assert openmc.data.gnd_name(40, 90) == ('Zr90') - assert openmc.data.gnd_name(95, 242, 0) == ('Am242') - assert openmc.data.gnd_name(95, 242, 1) == ('Am242_m1') - assert openmc.data.gnd_name(95, 242, 10) == ('Am242_m10') +def test_gnds_name(): + assert openmc.data.gnds_name(1, 1) == 'H1' + assert openmc.data.gnds_name(40, 90) == ('Zr90') + assert openmc.data.gnds_name(95, 242, 0) == ('Am242') + assert openmc.data.gnds_name(95, 242, 1) == ('Am242_m1') + assert openmc.data.gnds_name(95, 242, 10) == ('Am242_m10') def test_isotopes(): @@ -126,3 +131,15 @@ def test_zam(): assert openmc.data.zam('Am242_m10') == (95, 242, 10) with pytest.raises(ValueError): openmc.data.zam('garbage') + with pytest.raises(ValueError): + openmc.data.zam('Am242-m1') + +def test_half_life(): + assert openmc.data.half_life('H2') is None + assert openmc.data.half_life('U235') == pytest.approx(2.22102e16) + assert openmc.data.half_life('Am242') == pytest.approx(57672.0) + assert openmc.data.half_life('Am242_m1') == pytest.approx(4449622000.0) + assert openmc.data.decay_constant('H2') == 0.0 + assert openmc.data.decay_constant('U235') == pytest.approx(log(2.0)/2.22102e16) + assert openmc.data.decay_constant('Am242') == pytest.approx(log(2.0)/57672.0) + assert openmc.data.decay_constant('Am242_m1') == pytest.approx(log(2.0)/4449622000.0) diff --git a/tests/unit_tests/test_data_multipole.py b/tests/unit_tests/test_data_multipole.py index 4c2ba96d62..105099bb94 100644 --- a/tests/unit_tests/test_data_multipole.py +++ b/tests/unit_tests/test_data_multipole.py @@ -8,14 +8,14 @@ import openmc.data @pytest.fixture(scope='module') def u235(): - directory = pathlib.Path(os.environ['OPENMC_CROSS_SECTIONS']).parent + directory = pathlib.Path(openmc.config.get('cross_sections')).parent u235 = directory / 'wmp' / '092235.h5' return openmc.data.WindowedMultipole.from_hdf5(u235) @pytest.fixture(scope='module') def b10(): - directory = pathlib.Path(os.environ['OPENMC_CROSS_SECTIONS']).parent + directory = pathlib.Path(openmc.config.get('cross_sections')).parent b10 = directory / 'wmp' / '005010.h5' return openmc.data.WindowedMultipole.from_hdf5(b10) @@ -48,17 +48,15 @@ def test_export_to_hdf5(tmpdir, u235): assert os.path.exists(filename) -def test_from_endf(): +def test_from_endf(endf_data): pytest.importorskip('vectfit') - endf_data = os.environ['OPENMC_ENDF_DATA'] endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') - return openmc.data.WindowedMultipole.from_endf( + assert openmc.data.WindowedMultipole.from_endf( endf_file, log=True, wmp_options={"n_win": 400, "n_cf": 3}) -def test_from_endf_search(): +def test_from_endf_search(endf_data): pytest.importorskip('vectfit') - endf_data = os.environ['OPENMC_ENDF_DATA'] endf_file = os.path.join(endf_data, 'neutrons', 'n-095_Am_244.endf') - return openmc.data.WindowedMultipole.from_endf( + assert openmc.data.WindowedMultipole.from_endf( endf_file, log=True, wmp_options={"search": True, 'rtol':1e-2}) diff --git a/tests/unit_tests/test_data_neutron.py b/tests/unit_tests/test_data_neutron.py index 0b33f05fc9..d43d93ae51 100644 --- a/tests/unit_tests/test_data_neutron.py +++ b/tests/unit_tests/test_data_neutron.py @@ -14,128 +14,113 @@ _TEMPERATURES = [300., 600., 900.] @pytest.fixture(scope='module') def pu239(): """Pu239 HDF5 data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) + directory = os.path.dirname(openmc.config.get('cross_sections')) filename = os.path.join(directory, 'Pu239.h5') return openmc.data.IncidentNeutron.from_hdf5(filename) @pytest.fixture(scope='module') -def xe135(): +def xe135(endf_data): """Xe135 ENDF data (contains SLBW resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-054_Xe_135.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def sm150(): +def sm150(endf_data): """Sm150 ENDF data (contains MLBW resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-062_Sm_150.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def gd154(): +def gd154(endf_data): """Gd154 ENDF data (contains Reich Moore resonance range and reosnance covariance with LCOMP=1).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-064_Gd_154.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') -def cl35(): +def cl35(endf_data): """Cl35 ENDF data (contains RML resonance range)""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-017_Cl_035.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def am241(): +def am241(endf_data): """Am241 ENDF data (contains Madland-Nix fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-095_Am_241.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def u233(): +def u233(endf_data): """U233 ENDF data (contains Watt fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-092_U_233.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def u236(): +def u236(endf_data): """U236 ENDF data (contains Watt fission energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-092_U_236.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def na22(): +def na22(endf_data): """Na22 ENDF data (contains evaporation spectrum).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_022.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def na23(): +def na23(endf_data): """Na23 ENDF data (contains MLBW resonance covariance with LCOMP=0).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_023.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') -def be9(): +def be9(endf_data): """Be9 ENDF data (contains laboratory angle-energy distribution).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-004_Be_009.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') -def h2(): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def h2(endf_data): endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_002.endf') return openmc.data.IncidentNeutron.from_njoy( endf_file, temperatures=_TEMPERATURES) @pytest.fixture(scope='module') -def am244(): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def am244(endf_data): endf_file = os.path.join(endf_data, 'neutrons', 'n-095_Am_244.endf') return openmc.data.IncidentNeutron.from_njoy(endf_file) @pytest.fixture(scope='module') -def ti50(): +def ti50(endf_data): """Ti50 ENDF data (contains Multi-level Breit-Wigner resonance range and resonance covariance with LCOMP=1).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-022_Ti_050.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') -def cf252(): +def cf252(endf_data): """Cf252 ENDF data (contains RM resonance covariance with LCOMP=0).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-098_Cf_252.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') -def th232(): +def th232(endf_data): """Th232 ENDF data (contains RM resonance covariance with LCOMP=2).""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'neutrons', 'n-090_Th_232.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @@ -282,10 +267,6 @@ def test_slbw(xe135): s = resolved.parameters.iloc[0] assert s['energy'] == pytest.approx(0.084) - xs = resolved.reconstruct([10., 30., 100.]) - assert sorted(xs.keys()) == [2, 18, 102] - assert np.all(xs[18] == 0.0) - def test_mlbw(sm150): resolved = sm150.resonances.resolved @@ -294,10 +275,6 @@ def test_mlbw(sm150): assert resolved.energy_max == pytest.approx(1570.) assert resolved.target_spin == 0.0 - xs = resolved.reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] - assert np.all(xs[18] == 0.0) - def test_reichmoore(gd154): res = gd154.resonances @@ -319,7 +296,6 @@ def test_reichmoore(gd154): elastic = gd154.reactions[2].xs['0K'] assert isinstance(elastic, openmc.data.ResonancesWithBackground) - assert elastic(0.0253) == pytest.approx(5.7228949796394524) def test_rml(cl35): @@ -347,8 +323,6 @@ def test_mlbw_cov_lcomp0(cf252): assert not subset.parameters.empty assert (subset.file2res.parameters['energy'] < 100).all() samples = cov.sample(1) - xs = samples[0].reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] def test_mlbw_cov_lcomp1(ti50): @@ -365,9 +339,7 @@ def test_mlbw_cov_lcomp1(ti50): subset = cov.subset('L', [1, 1]) assert not subset.parameters.empty assert (subset.file2res.parameters['L'] == 1).all() - samples = cov.sample(1) - xs = samples[0].reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] + cov.sample(1) def test_mlbw_cov_lcomp2(na23): @@ -384,9 +356,7 @@ def test_mlbw_cov_lcomp2(na23): subset = cov.subset('L', [1, 1]) assert not subset.parameters.empty assert (subset.file2res.parameters['L'] == 1).all() - samples = cov.sample(1) - xs = samples[0].reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] + cov.sample(1) def test_rmcov_lcomp1(gd154): @@ -403,9 +373,7 @@ def test_rmcov_lcomp1(gd154): subset = cov.subset('energy', [0, 100]) assert not subset.parameters.empty assert (subset.file2res.parameters['energy'] < 100).all() - samples = cov.sample(1) - xs = samples[0].reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] + cov.sample(1) def test_rmcov_lcomp2(th232): @@ -422,9 +390,7 @@ def test_rmcov_lcomp2(th232): subset = cov.subset('energy', [0, 100]) assert not subset.parameters.empty assert (subset.file2res.parameters['energy'] < 100).all() - samples = cov.sample(1) - xs = samples[0].reconstruct([10., 100., 1000.]) - assert sorted(xs.keys()) == [2, 18, 102] + cov.sample(1) def test_madland_nix(am241): @@ -472,8 +438,7 @@ def test_laboratory(be9): @needs_njoy -def test_correlated(tmpdir): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def test_correlated(tmpdir, endf_data): endf_file = os.path.join(endf_data, 'neutrons', 'n-014_Si_030.endf') si30 = openmc.data.IncidentNeutron.from_njoy(endf_file, heatr=False) @@ -499,8 +464,7 @@ def test_nbody(tmpdir, h2): @needs_njoy -def test_ace_convert(run_in_tmpdir): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def test_ace_convert(run_in_tmpdir, endf_data): filename = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') ace_ascii = 'ace_ascii' ace_binary = 'ace_binary' @@ -531,3 +495,11 @@ def test_ace_table_types(): assert TT.from_suffix('20t') == TT.THERMAL_SCATTERING with pytest.raises(ValueError): TT.from_suffix('z') + + +@needs_njoy +def test_high_temperature(endf_data): + endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') + + # Ensure that from_njoy works when given a high temperature + openmc.data.IncidentNeutron.from_njoy(endf_file, temperatures=[123_456.0]) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index f7274e9c19..98b180f525 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from collections.abc import Mapping, Callable import os from pathlib import Path @@ -11,9 +9,8 @@ import openmc.data @pytest.fixture(scope='module') -def elements_endf(): +def elements_endf(endf_data): """Dictionary of element ENDF data indexed by atomic symbol.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] elements = {'H': 1, 'O': 8, 'Al': 13, 'Cu': 29, 'Ag': 47, 'U': 92, 'Pu': 94} data = {} for symbol, Z in elements.items(): @@ -146,8 +143,9 @@ def test_export_to_hdf5(tmpdir, element): # Export to hdf5 again element2.export_to_hdf5(filename, 'w') -def test_photodat_only(run_in_tmpdir): - endf_dir = Path(os.environ['OPENMC_ENDF_DATA']) + +def test_photodat_only(run_in_tmpdir, endf_data): + endf_dir = Path(endf_data) photoatomic_file = endf_dir / 'photoat' / 'photoat-001_H_000.endf' data = openmc.data.IncidentPhoton.from_endf(photoatomic_file) - data.export_to_hdf5('tmp.h5', 'w') \ No newline at end of file + data.export_to_hdf5('tmp.h5', 'w') diff --git a/tests/unit_tests/test_data_thermal.py b/tests/unit_tests/test_data_thermal.py index dc4d24628b..c444d0c586 100644 --- a/tests/unit_tests/test_data_thermal.py +++ b/tests/unit_tests/test_data_thermal.py @@ -13,7 +13,7 @@ from . import needs_njoy @pytest.fixture(scope='module') def h2o(): """H in H2O thermal scattering data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) + directory = os.path.dirname(openmc.config.get('cross_sections')) filename = os.path.join(directory, 'c_H_in_H2O.h5') return openmc.data.ThermalScattering.from_hdf5(filename) @@ -21,15 +21,14 @@ def h2o(): @pytest.fixture(scope='module') def graphite(): """Graphite thermal scattering data.""" - directory = os.path.dirname(os.environ['OPENMC_CROSS_SECTIONS']) + directory = os.path.dirname(openmc.config.get('cross_sections')) filename = os.path.join(directory, 'c_Graphite.h5') return openmc.data.ThermalScattering.from_hdf5(filename) @pytest.fixture(scope='module') -def h2o_njoy(): +def h2o_njoy(endf_data): """H in H2O generated using NJOY.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') path_h2o = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') return openmc.data.ThermalScattering.from_njoy( @@ -37,17 +36,15 @@ def h2o_njoy(): @pytest.fixture(scope='module') -def hzrh(): +def hzrh(endf_data): """H in ZrH thermal scattering data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') - return openmc.data.ThermalScattering.from_endf(filename) + return openmc.data.ThermalScattering.from_endf(filename, divide_incoherent_elastic=True) @pytest.fixture(scope='module') -def hzrh_njoy(): +def hzrh_njoy(endf_data): """H in ZrH generated using NJOY.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') path_hzrh = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') with_endf_data = openmc.data.ThermalScattering.from_njoy( @@ -60,11 +57,10 @@ def hzrh_njoy(): @pytest.fixture(scope='module') -def sio2(): +def sio2(endf_data): """SiO2 thermal scattering data.""" - endf_data = os.environ['OPENMC_ENDF_DATA'] filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-SiO2.endf') - return openmc.data.ThermalScattering.from_endf(filename) + return openmc.data.ThermalScattering.from_endf(filename, divide_incoherent_elastic=True) def test_h2o_attributes(h2o): @@ -102,8 +98,7 @@ def test_graphite_xs(graphite): assert elastic([1e-3, 1.0]) == pytest.approx([0.0, 0.62586153]) @needs_njoy -def test_graphite_njoy(): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def test_graphite_njoy(endf_data): path_c0 = os.path.join(endf_data, 'neutrons', 'n-006_C_000.endf') path_gr = os.path.join(endf_data, 'thermal_scatt', 'tsl-graphite.endf') graphite = openmc.data.ThermalScattering.from_njoy( @@ -141,10 +136,9 @@ def test_continuous_dist(h2o_njoy): assert isinstance(dist, openmc.data.IncoherentInelasticAE) -def test_h2o_endf(): - endf_data = os.environ['OPENMC_ENDF_DATA'] +def test_h2o_endf(endf_data): filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') - h2o = openmc.data.ThermalScattering.from_endf(filename) + h2o = openmc.data.ThermalScattering.from_endf(filename, divide_incoherent_elastic=True) assert not h2o.elastic assert h2o.atomic_weight_ratio == pytest.approx(0.99917) assert h2o.energy_max == pytest.approx(3.99993) @@ -262,3 +256,108 @@ def test_get_thermal_name(): # Names that don't remotely match anything assert f('boogie_monster') == 'c_boogie_monster' + + +@pytest.fixture +def fake_mixed_elastic(): + fake_tsl = openmc.data.ThermalScattering("c_D_in_7LiD", 1.9968, 4.9, [0.0253]) + fake_tsl.nuclides = ['H2'] + + # Create elastic reaction + bragg_edges = [0.00370672, 0.00494229, 0.00988458, 0.01359131, 0.01482688, + 0.01976918, 0.02347589, 0.02471147, 0.02965376, 0.03336048, + 0.03953834, 0.04324506, 0.04448063, 0.04942292, 0.05312964, + 0.05436522, 0.05930751, 0.06301423, 0.0642498 , 0.06919209, + 0.07289881, 0.07907667, 0.08278339, 0.08401896, 0.08896126, + 0.09266798, 0.09390355, 0.09884584, 0.1025526 , 0.1037882 , + 0.1087305 , 0.1124372 , 0.1186151 , 0.1223218 , 0.1235574 , + 0.1284997 , 0.1322064 , 0.133442 , 0.142091 , 0.1433266 , + 0.1482688 , 0.1519756 , 0.1581534 , 0.1618601 , 0.1630957 , + 0.168038 , 0.1717447 , 0.1729803 , 0.1779226 , 0.1816293 , + 0.1828649 , 0.1878072 , 0.1915139 , 0.1976918 , 0.2026341 , + 0.2075763 , 0.2125186 , 0.2174609 , 0.2224032 , 0.2273455 , + 0.2421724 , 0.2471147 , 0.252057 , 0.2569993 , 0.2619415 , + 0.2668838 , 0.2767684 , 0.2817107 , 0.2915953 , 0.3064222 , + 0.3261913 , 0.366965] + factors = [0.00375735, 0.01386287, 0.02595574, 0.02992438, 0.03549502, + 0.03855745, 0.04058831, 0.04986305, 0.05703106, 0.05855471, + 0.06078031, 0.06212291, 0.06656602, 0.06930339, 0.0697072 , + 0.07201456, 0.07263853, 0.07313129, 0.07465531, 0.07714482, + 0.07759976, 0.077809 , 0.07790282, 0.07927957, 0.08013058, + 0.08026637, 0.08073475, 0.08112202, 0.08123039, 0.08187171, + 0.08213756, 0.08218236, 0.08236572, 0.08240729, 0.08259795, + 0.08297893, 0.08300455, 0.08314566, 0.08315611, 0.08337715, + 0.08350026, 0.08350663, 0.08352815, 0.08353776, 0.0836098 , + 0.08367017, 0.08367361, 0.0837242 , 0.08375069, 0.08375227, + 0.08377006, 0.08381488, 0.08381644, 0.08382698, 0.08386266, + 0.08387756, 0.08388445, 0.08388974, 0.08390341, 0.08391088, + 0.08391695, 0.08392361, 0.08392684, 0.08392818, 0.08393161, + 0.08393546, 0.08393685, 0.08393801, 0.08393976, 0.08394167, + 0.08394288, 0.08394398] + coherent_xs = openmc.data.CoherentElastic(bragg_edges, factors) + incoherent_xs = openmc.data.Tabulated1D([0.00370672, 0.00370672], [0.00370672, 0.00370672]) + elastic_xs = {'294K': openmc.data.Sum((coherent_xs, incoherent_xs))} + coherent_dist = openmc.data.CoherentElasticAE(coherent_xs) + incoherent_dist = openmc.data.IncoherentElasticAEDiscrete([ + [-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6] + ]) + elastic_dist = {'294K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist)} + fake_tsl.elastic = openmc.data.ThermalScatteringReaction(elastic_xs, elastic_dist) + + # Create inelastic reaction + inelastic_xs = {'294K': openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35])} + breakpoints = [3] + interpolation = [2] + energy = [1.0e-5, 4.3e-2, 4.9] + energy_out = [ + openmc.data.Tabular([0.0002, 0.067, 0.146, 0.366], [0.25, 0.25, 0.25, 0.25]), + openmc.data.Tabular([0.0001, 0.009, 0.137, 0.277], [0.25, 0.25, 0.25, 0.25]), + openmc.data.Tabular([0.0579, 4.555, 4.803, 4.874], [0.25, 0.25, 0.25, 0.25]), + ] + for eout in energy_out: + eout.normalize() + eout.c = eout.cdf() + discrete = openmc.stats.Discrete([-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1/8]*8) + discrete.c = discrete.cdf()[1:] + mu = [[discrete]*4]*3 + inelastic_dist = {'294K': openmc.data.IncoherentInelasticAE( + breakpoints, interpolation, energy, energy_out, mu)} + inelastic = openmc.data.ThermalScatteringReaction(inelastic_xs, inelastic_dist) + fake_tsl.inelastic = inelastic + + return fake_tsl + + +def test_mixed_elastic(fake_mixed_elastic, run_in_tmpdir): + # Write data to HDF5 and then read back + original = fake_mixed_elastic + original.export_to_hdf5('c_D_in_7LiD.h5') + copy = openmc.data.ThermalScattering.from_hdf5('c_D_in_7LiD.h5') + + # Make sure data did not change as a result of HDF5 writing/reading + assert original == copy + + # Create modified cross_sections.xml file that includes the above data + xs = openmc.data.DataLibrary.from_xml() + xs.register_file('c_D_in_7LiD.h5') + xs.export_to_xml('cross_sections_mixed.xml') + + # Create a minimal model that includes the new data and run it + mat = openmc.Material() + mat.add_nuclide('H2', 1.0) + mat.add_nuclide('Li7', 1.0) + mat.set_density('g/cm3', 1.0) + mat.add_s_alpha_beta('c_D_in_7LiD') + sph = openmc.Sphere(r=10.0, boundary_type="vacuum") + cell = openmc.Cell(fill=mat, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.materials = openmc.Materials([mat]) + model.materials.cross_sections = "cross_sections_mixed.xml" + model.settings.particles = 1000 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource( + energy=openmc.stats.Discrete([3.0], [1.0]) # 3 eV source + ) + model.run() diff --git a/tests/unit_tests/test_deplete_activation.py b/tests/unit_tests/test_deplete_activation.py index 37b29e1eab..eace1976ef 100644 --- a/tests/unit_tests/test_deplete_activation.py +++ b/tests/unit_tests/test_deplete_activation.py @@ -11,7 +11,7 @@ import pytest @pytest.fixture def model(): """Sphere of single nuclide""" - model = openmc.model.Model() + model = openmc.Model() w = openmc.Material(name='tungsten') w.add_nuclide('W186', 1.0) @@ -27,13 +27,13 @@ def model(): model.settings.batches = 10 model.settings.particles = 1000 - model.settings.source = openmc.Source( + model.settings.source = openmc.IndependentSource( space=openmc.stats.Point(), energy=openmc.stats.Discrete([1.0e6], [1.0]) ) model.settings.run_mode = 'fixed source' - rx_tally = openmc.Tally() + rx_tally = openmc.Tally(name='activation tally') rx_tally.scores = ['(n,gamma)'] model.tallies.append(rx_tally) @@ -45,15 +45,16 @@ ENERGIES = np.logspace(log10(1e-5), log10(2e7), 100) @pytest.mark.parametrize("reaction_rate_mode,reaction_rate_opts,tolerance", [ ("direct", {}, 1e-5), - ("flux", {'energies': ENERGIES}, 0.01), + ("flux", {'energies': ENERGIES}, 0.1), ("flux", {'energies': ENERGIES, 'reactions': ['(n,gamma)']}, 1e-5), - ("flux", {'energies': ENERGIES, 'reactions': ['(n,gamma)'], 'nuclides': ['W186']}, 1e-5), + ("flux", {'energies': ENERGIES, 'reactions': ['(n,gamma)'], 'nuclides': ['W186', 'H3']}, 1e-2), ]) +@pytest.mark.flaky(reruns=1) def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts, tolerance): # Determine (n.gamma) reaction rate using initial run sp = model.run() with openmc.StatePoint(sp) as sp: - tally = sp.tallies[1] + tally = sp.get_tally(name='activation tally') capture_rate = tally.mean.flat[0] # Create one-nuclide depletion chain @@ -61,11 +62,10 @@ def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts w186 = openmc.deplete.Nuclide('W186') w186.add_reaction('(n,gamma)', None, 0.0, 1.0) chain.add_nuclide(w186) - chain.export_to_xml('test_chain.xml') # Create transport operator - op = openmc.deplete.Operator( - model, 'test_chain.xml', + op = openmc.deplete.CoupledOperator( + model, chain, normalization_mode="source-rate", reaction_rate_mode=reaction_rate_mode, reaction_rate_opts=reaction_rate_opts, @@ -92,7 +92,7 @@ def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts w = model.geometry.get_materials_by_name('tungsten')[0] atom_densities = w.get_nuclide_atom_densities() - atom_per_cc = 1e24 * atom_densities['W186'][1] # Density in atom/cm^3 + atom_per_cc = 1e24 * atom_densities['W186'] # Density in atom/cm^3 n0 = atom_per_cc * w.volume # Absolute number of atoms # Pick a random irradiation time and then determine necessary source rate to @@ -107,8 +107,8 @@ def test_activation(run_in_tmpdir, model, reaction_rate_mode, reaction_rate_opts integrator.integrate() # Get resulting number of atoms - results = openmc.deplete.ResultsList.from_hdf5('depletion_results.h5') - _, atoms = results.get_atoms(str(w.id), "W186") + results = openmc.deplete.Results('depletion_results.h5') + _, atoms = results.get_atoms(w, "W186") assert atoms[0] == pytest.approx(n0) assert atoms[1] / atoms[0] == pytest.approx(0.5, rel=tolerance) @@ -144,7 +144,7 @@ def test_decay(run_in_tmpdir): model = openmc.Model(geometry=geometry, settings=settings) # Create transport operator - op = openmc.deplete.Operator( + op = openmc.deplete.CoupledOperator( model, 'test_chain.xml', normalization_mode="source-rate" ) @@ -155,9 +155,36 @@ def test_decay(run_in_tmpdir): integrator.integrate() # Get resulting number of atoms - results = openmc.deplete.ResultsList.from_hdf5('depletion_results.h5') - _, atoms = results.get_atoms(str(mat.id), "Sr89") + results = openmc.deplete.Results('depletion_results.h5') + _, atoms = results.get_atoms(mat, "Sr89") # Ensure density goes down by a factor of 2 after each half-life assert atoms[1] / atoms[0] == pytest.approx(0.5) assert atoms[2] / atoms[1] == pytest.approx(0.25) + + +def test_flux_rr_missing_nuclide(run_in_tmpdir, model): + # Create two-nuclide depletion chain -- since W184 is not in the model, this + # test ensures that FluxCollapseHelper loads missing nuclides appropriately + chain = openmc.deplete.Chain() + w184 = openmc.deplete.Nuclide('W184') + w184.add_reaction('(n,gamma)', None, 0.0, 1.0) + chain.add_nuclide(w184) + w186 = openmc.deplete.Nuclide('W186') + w186.add_reaction('(n,gamma)', None, 0.0, 1.0) + chain.add_nuclide(w186) + chain.export_to_xml('test_chain.xml') + + # Create transport operator + op = openmc.deplete.CoupledOperator( + model, 'test_chain.xml', + normalization_mode="source-rate", + reaction_rate_mode="flux", + reaction_rate_opts={'energies': [0.0, 20.0e6]}, + ) + + # Deplete with two decay steps + integrator = openmc.deplete.PredictorIntegrator( + op, [100.0], source_rates=[10.0] + ) + integrator.integrate() diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index 4bfdaa63e5..e90b610224 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -5,10 +5,12 @@ from itertools import product from math import log import os from pathlib import Path +import warnings import numpy as np from openmc.mpi import comm from openmc.deplete import Chain, reaction_rates, nuclide, cram, pool +from openmc.stats import Discrete import pytest from tests import cdtemp @@ -52,11 +54,11 @@ def simple_chain(): @pytest.fixture(scope='module') -def endf_chain(): - endf_data = Path(os.environ['OPENMC_ENDF_DATA']) - decay_data = (endf_data / 'decay').glob('*.endf') - fpy_data = (endf_data / 'nfy').glob('*.endf') - neutron_data = (endf_data / 'neutrons').glob('*.endf') +def endf_chain(endf_data): + endf_dir = Path(endf_data) + decay_data = (endf_dir / 'decay').glob('*.endf') + fpy_data = (endf_dir / 'nfy').glob('*.endf') + neutron_data = (endf_dir / 'neutrons').glob('*.endf') return Chain.from_endf(decay_data, fpy_data, neutron_data) @@ -84,6 +86,14 @@ def test_from_endf(endf_chain): assert nuc == chain[nuc.name] +def test_unstable_nuclides(simple_chain: Chain): + assert [nuc.name for nuc in simple_chain.unstable_nuclides] == ["A", "B"] + + +def test_stable_nuclides(simple_chain: Chain): + assert [nuc.name for nuc in simple_chain.stable_nuclides] == ["H1", "C"] + + def test_from_xml(simple_chain): """Read chain_test.xml and ensure all values are correct.""" # Unfortunately, this routine touches a lot of the code, but most of @@ -300,8 +310,7 @@ def test_capture_branch_infer_ground(): # Create nuclide to be added into the chain xe136m = nuclide.Nuclide("Xe136_m1") - chain.nuclides.append(xe136m) - chain.nuclide_dict[xe136m.name] = len(chain.nuclides) - 1 + chain.add_nuclide(xe136m) chain.set_branch_ratios(infer_br, "(n,gamma)") @@ -317,8 +326,7 @@ def test_capture_branch_no_rxn(): u5m = nuclide.Nuclide("U235_m1") - chain.nuclides.append(u5m) - chain.nuclide_dict[u5m.name] = len(chain.nuclides) - 1 + chain.add_nuclide(u5m) with pytest.raises(AttributeError, match="U234"): chain.set_branch_ratios(u4br) @@ -437,9 +445,9 @@ def test_validate(simple_chain): simple_chain["C"].yield_data = {0.0253: {"A": 1.4, "B": 0.6}} assert simple_chain.validate(strict=True, tolerance=0.0) - with pytest.warns(None) as record: + with warnings.catch_warnings(): + warnings.simplefilter("error") assert simple_chain.validate(strict=False, quiet=False, tolerance=0.0) - assert len(record) == 0 # Mess up "earlier" nuclide's reactions decay_mode = simple_chain["A"].decay_modes.pop() @@ -476,6 +484,15 @@ def gnd_simple_chain(): return Chain.from_xml(chainfile) +def test_chain_sources(gnd_simple_chain): + i135 = gnd_simple_chain['I135'] + assert isinstance(i135.sources, dict) + assert list(i135.sources.keys()) == ['photon'] + photon_src = i135.sources['photon'] + assert isinstance(photon_src, Discrete) + assert photon_src.integral() == pytest.approx(3.920996223799345e-05) + + def test_reduce(gnd_simple_chain, endf_chain): ref_U5 = gnd_simple_chain["U235"] ref_iodine = gnd_simple_chain["I135"] @@ -490,6 +507,7 @@ def test_reduce(gnd_simple_chain, endf_chain): assert u5_round0.n_decay_modes == ref_U5.n_decay_modes assert u5_round0.half_life == ref_U5.half_life assert u5_round0.decay_energy == ref_U5.decay_energy + assert u5_round0.sources == ref_U5.sources for newmode, refmode in zip(u5_round0.decay_modes, ref_U5.decay_modes): assert newmode.target is None assert newmode.type == refmode.type @@ -512,6 +530,7 @@ def test_reduce(gnd_simple_chain, endf_chain): assert bareI5.n_decay_modes == ref_iodine.n_decay_modes assert bareI5.half_life == ref_iodine.half_life assert bareI5.decay_energy == ref_iodine.decay_energy + assert bareI5.sources == ref_iodine.sources for newmode, refmode in zip(bareI5.decay_modes, ref_iodine.decay_modes): assert newmode.target is None assert newmode.type == refmode.type diff --git a/tests/unit_tests/test_deplete_continue.py b/tests/unit_tests/test_deplete_continue.py new file mode 100644 index 0000000000..637c9d5e44 --- /dev/null +++ b/tests/unit_tests/test_deplete_continue.py @@ -0,0 +1,114 @@ +"""Unit tests for openmc.deplete continue run capability. + +These tests run in two steps: first a normal run and then a continue run using the previous results +""" + +import pytest +import numpy as np +import openmc.deplete + +from tests import dummy_operator + + +def test_continue(run_in_tmpdir): + """Test to ensure that a properly defined continue run works""" + # set up the problem + bundle = dummy_operator.SCHEMES['predictor'] + operator = dummy_operator.DummyOperator() + + # initial depletion + bundle.solver(operator, [1.0, 2.0], [1.0, 2.0]).integrate() + + # set up continue run + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + operator = dummy_operator.DummyOperator(prev_res) + + # if continue run happens, test passes + bundle.solver(operator, [1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0], + continue_timesteps=True).integrate() + + final_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + + assert np.array_equal( + np.diff(final_res.get_times(time_units="s")), + [1.0, 2.0, 3.0, 4.0] + ) + + +def test_continue_continue(run_in_tmpdir): + """Test to ensure that a continue run can be continued""" + # set up the problem + bundle = dummy_operator.SCHEMES['predictor'] + operator = dummy_operator.DummyOperator() + + # initial depletion + bundle.solver(operator, [1.0, 2.0], [1.0, 2.0]).integrate() + + # set up continue run + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + operator = dummy_operator.DummyOperator(prev_res) + + # first continue run + bundle.solver(operator, [1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0], + continue_timesteps=True).integrate() + + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + # second continue run + bundle.solver(operator, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0], + continue_timesteps=True).integrate() + + final_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + + assert np.array_equal( + np.diff(final_res.get_times(time_units="s")), + [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] + ) + + +def test_mismatched_initial_times(run_in_tmpdir): + """Test to ensure that a continue run with different initial steps is properly caught""" + # set up the problem + bundle = dummy_operator.SCHEMES['predictor'] + operator = dummy_operator.DummyOperator() + + # perform initial steps + bundle.solver(operator, [0.75, 0.75], [1.0, 1.0]).integrate() + + # restart + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + operator = dummy_operator.DummyOperator(prev_res) + + with pytest.raises( + ValueError, + match="You are attempting to continue a run in which the previous timesteps " + "do not have the same initial timesteps as those provided to the " + "Integrator. Please make sure you are using the correct timesteps.", + ): + bundle.solver( + operator, [0.75, 0.5, 0.75], [1.0, 1.0, 1.0], continue_timesteps=True + ).integrate() + + +def test_mismatched_initial_source_rates(run_in_tmpdir): + """Test to ensure that a continue run with different initial steps is properly caught""" + # set up the problem + bundle = dummy_operator.SCHEMES['predictor'] + operator = dummy_operator.DummyOperator() + + # perform initial steps + bundle.solver(operator, [0.75, 0.75], [1.0, 1.0]).integrate() + + # restart + prev_res = openmc.deplete.Results(operator.output_dir / "depletion_results.h5") + operator = dummy_operator.DummyOperator(prev_res) + + with pytest.raises( + ValueError, + match="You are attempting to continue a run in which the previous results " + "do not have the same initial source rates, powers, or power densities " + "as those provided to the Integrator. Please make sure you are using " + "the correct powers, power densities, or source rates and previous results file.", + ): + bundle.solver( + operator, [0.75, 0.75, 0.75], [1.0, 2.0, 1.0], continue_timesteps=True + ).integrate() diff --git a/tests/unit_tests/test_deplete_coupled_operator.py b/tests/unit_tests/test_deplete_coupled_operator.py new file mode 100644 index 0000000000..119a15923f --- /dev/null +++ b/tests/unit_tests/test_deplete_coupled_operator.py @@ -0,0 +1,122 @@ +"""Basic unit tests for openmc.deplete.CoupledOperator instantiation + +""" + +from pathlib import Path + +import pytest +from openmc.deplete import CoupledOperator, Chain +import openmc +import numpy as np + +CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + + +@pytest.fixture(scope="module") +def model(): + fuel = openmc.Material(name="uo2") + fuel.add_element("U", 1, percent_type="ao", enrichment=4.25) + fuel.add_element("O", 2) + fuel.set_density("g/cc", 10.4) + + clad = openmc.Material(name="clad") + clad.add_element("Zr", 1) + clad.set_density("g/cc", 6) + + water = openmc.Material(name="water") + water.add_element("O", 1) + water.add_element("H", 2) + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + + radii = [0.42, 0.45] + fuel.volume = np.pi * radii[0] ** 2 + clad.volume = np.pi * (radii[1]**2 - radii[0]**2) + water.volume = 1.24**2 - (np.pi * radii[1]**2) + + materials = openmc.Materials([fuel, clad, water]) + + pin_surfaces = [openmc.ZCylinder(r=r) for r in radii] + pin_univ = openmc.model.pin(pin_surfaces, materials) + bound_box = openmc.model.RectangularPrism( + 1.24, 1.24, boundary_type="reflective") + root_cell = openmc.Cell(fill=pin_univ, region=-bound_box) + geometry = openmc.Geometry([root_cell]) + + settings = openmc.Settings() + settings.particles = 1000 + settings.inactive = 10 + settings.batches = 50 + + return openmc.Model(geometry, materials, settings) + + +@pytest.fixture() +def model_with_volumes(): + mat1 = openmc.Material() + mat1.add_element("Ag", 1, percent_type="ao") + mat1.set_density("g/cm3", 10.49) + mat1.depletable = True + mat1.volume = 102 + + mat2 = openmc.Material() + mat2.add_element("Ag", 1, percent_type="ao") + mat2.set_density("g/cm3", 10.49) + + sph1 = openmc.Sphere(r=1.0) + sph2 = openmc.Sphere(r=2.0, x0=3) + sph3 = openmc.Sphere(r=5.0, boundary_type="vacuum") + + cell1 = openmc.Cell(region=-sph1, fill=mat1) + cell1.volume = 4.19 + cell2 = openmc.Cell(region=-sph2, fill=mat1) + cell2.volume = 33.51 + cell3 = openmc.Cell(region=-sph3 & +sph1 & +sph2, fill=mat2) + cell3.volume = 485.9 + + geometry = openmc.Geometry([cell1, cell2, cell3]) + + return openmc.Model(geometry) + + +def test_operator_init(model): + """The test uses a temporary dummy chain. This file will be removed + at the end of the test, and only contains a depletion_chain node.""" + + CoupledOperator(model, CHAIN_PATH) + + +def test_diff_volume_method_match_cell(model_with_volumes): + """Tests the volumes assigned to the materials match the cell volumes""" + + operator = openmc.deplete.CoupledOperator( + model=model_with_volumes, + diff_burnable_mats=True, + diff_volume_method='match cell', + chain_file=CHAIN_PATH + ) + + all_cells = list(operator.model.geometry.get_all_cells().values()) + assert all_cells[0].fill.volume == 4.19 + assert all_cells[1].fill.volume == 33.51 + # mat2 is not depletable + assert all_cells[2].fill.volume is None + + +def test_diff_volume_method_divide_equally(model_with_volumes): + """Tests the volumes assigned to the materials are divided equally""" + + chain = Chain.from_xml(CHAIN_PATH) + + operator = openmc.deplete.CoupledOperator( + model=model_with_volumes, + diff_burnable_mats=True, + diff_volume_method='divide equally', + chain_file=chain + ) + + all_cells = list(operator.model.geometry.get_all_cells().values()) + assert all_cells[0].fill.volume == 51 + assert all_cells[1].fill.volume == 51 + # mat2 is not depletable + assert all_cells[2].fill.volume is None diff --git a/tests/unit_tests/test_deplete_decay.py b/tests/unit_tests/test_deplete_decay.py new file mode 100644 index 0000000000..6e7b0b101e --- /dev/null +++ b/tests/unit_tests/test_deplete_decay.py @@ -0,0 +1,84 @@ +from pathlib import Path + +import openmc.deplete +import numpy as np +import pytest + + +def test_deplete_decay_products(run_in_tmpdir): + # Create chain file with H1, He4, and Li5 + with open('test_chain.xml', 'w') as chain_file: + chain_file.write(""" + + + + + + + + + """) + + # Create MicroXS object with no cross sections + micro_xs = openmc.deplete.MicroXS(np.empty((0, 0, 0)), [], []) + + # Create depletion operator with no reactions + op = openmc.deplete.IndependentOperator.from_nuclides( + volume=1.0, + nuclides={'Li5': 1.0}, + flux=0.0, + micro_xs=micro_xs, + chain_file='test_chain.xml', + normalization_mode='source-rate' + ) + + # Create time-integrator and integrate + integrator = openmc.deplete.PredictorIntegrator( + op, timesteps=[1.0], source_rates=[0.0], timestep_units='d' + ) + integrator.integrate(final_step=False) + + # Get concentration of H1 and He4 + results = openmc.deplete.Results('depletion_results.h5') + mat_id = op.materials[0].id + _, h1 = results.get_atoms(f"{mat_id}", "H1") + _, he4 = results.get_atoms(f"{mat_id}", "He4") + + # Since we started with 1e24 atoms of Li5, we should have 1e24 atoms of both + # H1 and He4 + assert h1[1] == pytest.approx(1e24) + assert he4[1] == pytest.approx(1e24) + + +def test_deplete_decay_step_fissionable(run_in_tmpdir): + """Ensures that power is not computed in zero power cases with + fissionable material present. This tests decay calculations without + power, although this specific example does not exhibit any decay. + + Proves github issue #2963 is fixed + """ + + # Set up a pure decay operator + micro_xs = openmc.deplete.MicroXS(np.empty((0, 0, 0)), [], []) + mat = openmc.Material() + mat.name = 'I do not decay.' + mat.add_nuclide('U238', 1.0, 'ao') + mat.volume = 10.0 + mat.set_density('g/cc', 1.0) + original_atoms = mat.get_nuclide_atoms()['U238'] + + mats = openmc.Materials([mat]) + op = openmc.deplete.IndependentOperator( + mats, [1.0], [micro_xs], Path(__file__).parents[1] / "chain_simple.xml") + + # Create time integrator and integrate + integrator = openmc.deplete.PredictorIntegrator( + op, [1.0], power=[0.0], timestep_units='s' + ) + integrator.integrate() + + # Get concentration of U238. It should be unchanged since this chain has no U238 decay. + results = openmc.deplete.Results('depletion_results.h5') + _, u238 = results.get_atoms(f"{mat.id}", "U238") + + assert u238[1] == pytest.approx(original_atoms) diff --git a/tests/unit_tests/test_deplete_external_source_rates.py b/tests/unit_tests/test_deplete_external_source_rates.py new file mode 100644 index 0000000000..a8cf9dde53 --- /dev/null +++ b/tests/unit_tests/test_deplete_external_source_rates.py @@ -0,0 +1,170 @@ +""" Tests for ExternalSourceRates class """ + +from pathlib import Path + +import pytest +import numpy as np +import re + +import openmc +from openmc.data import AVOGADRO, atomic_mass +from openmc.deplete import CoupledOperator +from openmc.deplete.transfer_rates import ExternalSourceRates +from openmc.deplete.abc import (_SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, + _SECONDS_PER_DAY, _SECONDS_PER_JULIAN_YEAR) + +CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + +@pytest.fixture +def model(): + openmc.reset_auto_ids() + f = openmc.Material(name="f") + f.add_element("U", 1, enrichment=4.25) + f.add_element("O", 2) + f.set_density("g/cm3", 10.4) + + w = openmc.Material(name="w") + w.add_element("O", 1) + w.add_element("H", 2) + w.set_density("g/cm3", 1.0) + w.depletable = True + + # material just to test multiple destination material + h = openmc.Material(name="h") + h.add_element("He", 1) + h.set_density("g/cm3", 1.78e-4) + + radii = [0.42, 0.45] + f.volume = np.pi * radii[0] ** 2 + w.volume = np.pi * (radii[1]**2 - radii[0]**2) + h.volume = 1 + materials = openmc.Materials([f, w, h]) + + surf_f = openmc.Sphere(r=radii[0]) + surf_w = openmc.Sphere(r=radii[1], boundary_type='vacuum') + surf_h = openmc.Sphere(x0=10, r=1, boundary_type='vacuum') + cell_f = openmc.Cell(fill=f, region=-surf_f) + cell_w = openmc.Cell(fill=w, region=+surf_f & -surf_w) + cell_h = openmc.Cell(fill=h, region=-surf_h) + geometry = openmc.Geometry([cell_f, cell_w, cell_h]) + + settings = openmc.Settings() + settings.particles = 1000 + settings.inactive = 10 + settings.batches = 50 + + return openmc.Model(geometry, materials, settings) + + +@pytest.mark.parametrize( +"case_name, external_source_vectors, external_source_rate, timesteps", [ + ('elements', [{'U': 0.9, 'Xe': 0.1}], 1, None), + ('nuclides', [{'I135': 0.1, 'Gd156': 0.3, 'Gd157': 0.6}], 1, None), + ('nuclides_elements', [{'I135': 0.01, 'Gd156': 0.1, 'Gd157': 0.01, 'U': 0.8, + 'Xe': 0.08}], 1, None), + ('elements_nuclides', [{'U': 0.78, 'Xe': 0.1, 'I135': 0.01, 'Gd156': 0.1, + 'Gd157': 0.01}], 1, None), + ('multiple_vectors', [{'U': 1.}, {'Xe': 1}], 1, None), + ('timesteps', [{'U': 0.9, 'Xe': 0.1}], 1, [1]), + ('rates_invalid_1', [{'Gb': 1.}], 1, None), + ('rates_invalid_2', [{'Pu': 1.}], 1, None) + ]) +def test_get_set(model, case_name, external_source_vectors, external_source_rate, + timesteps): + """Tests the get/set methods""" + + op = CoupledOperator(model, CHAIN_PATH) + number_of_timesteps = 2 + transfer = ExternalSourceRates(op, model.materials, number_of_timesteps) + + if timesteps is None: + timesteps = np.arange(number_of_timesteps) + + # Test by Openmc material, material name and material id + material= [m for m in model.materials if m.depletable][0] + + for material_input in [material, material.name, material.id]: + for external_source_vector in external_source_vectors: + if case_name == 'rates_invalid_1': + with pytest.raises(ValueError, match='Gb is not a valid ' + 'nuclide or element.'): + transfer.set_external_source_rate(material_input, + external_source_vector, + external_source_rate) + elif case_name == 'rates_invalid_2': + with pytest.raises(ValueError, match='Cannot add element Pu'): + transfer.set_external_source_rate(material_input, + external_source_vector, + external_source_rate) + else: + transfer.set_external_source_rate(material_input, + external_source_vector, + external_source_rate, + timesteps=timesteps) + for component, percent in external_source_vector.items(): + split_component = re.split(r'\d+', component) + if len(split_component) == 1: + for nuc, frac in openmc.data.isotopes(component): + val = external_source_rate * percent * frac * \ + AVOGADRO / atomic_mass(nuc) + assert transfer.get_external_rate( + material_input, nuc, timesteps)[0] == pytest.approx(val) + else: + val = external_source_rate * percent * AVOGADRO / atomic_mass(component) + assert transfer.get_external_rate( + material_input, component, timesteps)[0] == pytest.approx(val) + + assert np.all(transfer.external_timesteps == timesteps) + + +@pytest.mark.parametrize("units, unit_conv", [ + ('g/s', 1), + ('g/sec', 1), + ('g/min', _SECONDS_PER_MINUTE), + ('g/minute', _SECONDS_PER_MINUTE), + ('g/h', _SECONDS_PER_HOUR), + ('g/hr', _SECONDS_PER_HOUR), + ('g/hour', _SECONDS_PER_HOUR), + ('g/d', _SECONDS_PER_DAY), + ('g/day', _SECONDS_PER_DAY), + ('g/a', _SECONDS_PER_JULIAN_YEAR), + ('g/year', _SECONDS_PER_JULIAN_YEAR), + ]) +def test_units(units, unit_conv, model): + """ Units testing""" + # create external rate Xe + components = ['Xe135', 'U235'] + external_source_rate = 1.0 + number_of_timesteps = 2 + op = CoupledOperator(model, CHAIN_PATH) + transfer = ExternalSourceRates(op, model.materials, number_of_timesteps) + timesteps = np.arange(number_of_timesteps) + + for component in components: + rate = external_source_rate * unit_conv * atomic_mass(component) / AVOGADRO + transfer.set_external_source_rate('f', {component: 1}, rate, rate_units=units) + assert transfer.get_external_rate( + 'f', component, timesteps)[0] == pytest.approx(external_source_rate) + + +def test_external_source(run_in_tmpdir, model): + """Tests external source depletion class without neither reaction rates nor + decay but only external source rates""" + # create transfer rate for U + vector = {'U235': 1} + external_source = 10 # grams + op = CoupledOperator(model, CHAIN_PATH) + integrator = openmc.deplete.PredictorIntegrator( + op, [1, 1], 0.0, timestep_units = 'd') + integrator.add_external_source_rate('f', vector, external_source/(24*3600)) + integrator.integrate() + + # Get number of U238 atoms from results + results = openmc.deplete.Results('depletion_results.h5') + _, atoms = results.get_atoms(model.materials[0], "U235") + + # Ensure number of atoms equal external source + assert atoms[1] - atoms[0] == pytest.approx( + external_source * AVOGADRO / atomic_mass('U235')) + assert atoms[2] - atoms[1] == pytest.approx( + external_source * AVOGADRO / atomic_mass('U235')) diff --git a/tests/unit_tests/test_deplete_fission_yields.py b/tests/unit_tests/test_deplete_fission_yields.py index b603710f34..1937e61e33 100644 --- a/tests/unit_tests/test_deplete_fission_yields.py +++ b/tests/unit_tests/test_deplete_fission_yields.py @@ -27,8 +27,8 @@ def materials(tmpdir_factory): mfuel.add_nuclide(nuclide, 1.0) openmc.Materials([mfuel]).export_to_xml() # Geometry - box = openmc.rectangular_prism(1.0, 1.0, boundary_type="reflective") - cell = openmc.Cell(fill=mfuel, region=box) + box = openmc.model.RectangularPrism(1.0, 1.0, boundary_type="reflective") + cell = openmc.Cell(fill=mfuel, region=-box) root = openmc.Universe(cells=[cell]) openmc.Geometry(root).export_to_xml() # settings diff --git a/tests/unit_tests/test_deplete_independent_operator.py b/tests/unit_tests/test_deplete_independent_operator.py new file mode 100644 index 0000000000..aca83399a0 --- /dev/null +++ b/tests/unit_tests/test_deplete_independent_operator.py @@ -0,0 +1,55 @@ +"""Basic unit tests for openmc.deplete.IndependentOperator instantiation + +""" + +from pathlib import Path + +import pytest + +from openmc import Material +from openmc.deplete import IndependentOperator, MicroXS, Chain + +CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" +ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv" + + +def test_operator_init(): + """The test uses a temporary dummy chain. This file will be removed + at the end of the test, and only contains a depletion_chain node.""" + volume = 1 + nuclides = {'U234': 8.922411359424315e+18, + 'U235': 9.98240191860822e+20, + 'U238': 2.2192386373095893e+22, + 'U236': 4.5724195495061115e+18, + 'O16': 4.639065406771322e+22, + 'O17': 1.7588724018066158e+19} + flux = 1.0 + micro_xs = MicroXS.from_csv(ONE_GROUP_XS) + chain = Chain.from_xml(CHAIN_PATH) + IndependentOperator.from_nuclides( + volume, nuclides, flux, micro_xs, chain, nuc_units='atom/cm3') + + fuel = Material(name="uo2") + fuel.add_element("U", 1, percent_type="ao", enrichment=4.25) + fuel.add_element("O", 2) + fuel.set_density("g/cc", 10.4) + fuel.depletable = True + fuel.volume = 1 + materials = [fuel] + fluxes = [1.0] + micros = [micro_xs] + IndependentOperator(materials, fluxes, micros, CHAIN_PATH) + + +def test_error_handling(): + micro_xs = MicroXS.from_csv(ONE_GROUP_XS) + fuel = Material(name="oxygen") + fuel.add_element("O", 2) + fuel.set_density("g/cc", 1) + fuel.depletable = True + fuel.volume = 1 + materials = [fuel] + fluxes = [1.0, 2.0] + micros = [micro_xs] + with pytest.raises(ValueError, match=r"The length of fluxes \(2\)"): + IndependentOperator(materials, fluxes, micros, CHAIN_PATH) diff --git a/tests/unit_tests/test_deplete_integrator.py b/tests/unit_tests/test_deplete_integrator.py index a08d8738c5..b1d2cb950e 100644 --- a/tests/unit_tests/test_deplete_integrator.py +++ b/tests/unit_tests/test_deplete_integrator.py @@ -16,7 +16,7 @@ import pytest from openmc.mpi import comm from openmc.deplete import ( - ReactionRates, Results, ResultsList, OperatorResult, PredictorIntegrator, + ReactionRates, StepResult, Results, OperatorResult, PredictorIntegrator, CECMIntegrator, CF4Integrator, CELIIntegrator, EPCRK4Integrator, LEQIIntegrator, SICELIIntegrator, SILEQIIntegrator, cram) @@ -40,7 +40,7 @@ def test_results_save(run_in_tmpdir): stages = 3 - np.random.seed(comm.rank) + rng = np.random.RandomState(comm.rank) # Mock geometry op = MagicMock() @@ -68,26 +68,26 @@ def test_results_save(run_in_tmpdir): x2 = [] for i in range(stages): - x1.append([np.random.rand(2), np.random.rand(2)]) - x2.append([np.random.rand(2), np.random.rand(2)]) + x1.append([rng.random(2), rng.random(2)]) + x2.append([rng.random(2), rng.random(2)]) # Construct r r1 = ReactionRates(burn_list, ["na", "nb"], ["ra", "rb"]) - r1[:] = np.random.rand(2, 2, 2) + r1[:] = rng.random((2, 2, 2)) rate1 = [] rate2 = [] for i in range(stages): rate1.append(copy.deepcopy(r1)) - r1[:] = np.random.rand(2, 2, 2) + r1[:] = rng.random((2, 2, 2)) rate2.append(copy.deepcopy(r1)) - r1[:] = np.random.rand(2, 2, 2) + r1[:] = rng.random((2, 2, 2)) # Create global terms # Col 0: eig, Col 1: uncertainty - eigvl1 = np.random.rand(stages, 2) - eigvl2 = np.random.rand(stages, 2) + eigvl1 = rng.random((stages, 2)) + eigvl2 = rng.random((stages, 2)) eigvl1 = comm.bcast(eigvl1, root=0) eigvl2 = comm.bcast(eigvl2, root=0) @@ -99,11 +99,17 @@ def test_results_save(run_in_tmpdir): for k, rates in zip(eigvl1, rate1)] op_result2 = [OperatorResult(ufloat(*k), rates) for k, rates in zip(eigvl2, rate2)] - Results.save(op, x1, op_result1, t1, 0, 0) - Results.save(op, x2, op_result2, t2, 0, 1) + + # saves within a subdirectory + StepResult.save(op, x1, op_result1, t1, 0, 0, path='out/put/depletion.h5') + res = Results('out/put/depletion.h5') + + # saves with default filename + StepResult.save(op, x1, op_result1, t1, 0, 0) + StepResult.save(op, x2, op_result2, t2, 0, 1) # Load the files - res = ResultsList.from_hdf5("depletion_results.h5") + res = Results("depletion_results.h5") for i in range(stages): for mat_i, mat in enumerate(burn_list): @@ -176,8 +182,7 @@ def test_integrator(run_in_tmpdir, scheme): # get expected results - res = ResultsList.from_hdf5( - operator.output_dir / "depletion_results.h5") + res = Results(operator.output_dir / "depletion_results.h5") t1, y1 = res.get_atoms("1", "1") t2, y2 = res.get_atoms("1", "2") diff --git a/tests/unit_tests/test_deplete_microxs.py b/tests/unit_tests/test_deplete_microxs.py new file mode 100644 index 0000000000..073b3f162d --- /dev/null +++ b/tests/unit_tests/test_deplete_microxs.py @@ -0,0 +1,113 @@ +"""Basic unit tests for openmc.deplete.IndependentOperator instantiation + +Modifies and resets environment variable OPENMC_CROSS_SECTIONS +to a custom file with new depletion_chain node +""" + +from os import remove +from pathlib import Path + +import pytest +from openmc.deplete import MicroXS +import numpy as np + +ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv" + + +def test_from_array(): + nuclides = [ + 'U234', + 'U235', + 'U238', + 'U236', + 'O16', + 'O17', + 'I135', + 'Xe135', + 'Xe136', + 'Cs135', + 'Gd157', + 'Gd156'] + reactions = ['fission', '(n,gamma)'] + # These values are placeholders and are not at all + # physically meaningful. + data = np.array([[0.1, 0.], + [0.1, 0.], + [0.9, 0.], + [0.4, 0.], + [0., 0.], + [0., 0.], + [0., 0.1], + [0., 0.9], + [0., 0.], + [0., 0.], + [0., 0.1], + [0., 0.1]]) + data.shape = (12, 2, 1) + + MicroXS(data, nuclides, reactions) + with pytest.raises(ValueError, match='Data array must be 3D'): + MicroXS(data[:, 0], nuclides, reactions) + + +def test_csv(): + ref_xs = MicroXS.from_csv(ONE_GROUP_XS) + ref_xs.to_csv('temp_xs.csv') + temp_xs = MicroXS.from_csv('temp_xs.csv') + assert np.all(ref_xs.data == temp_xs.data) + remove('temp_xs.csv') + + +def test_from_multigroup_flux(): + energies = [0., 6.25e-1, 5.53e3, 8.21e5, 2.e7] + flux = [1.1e-7, 1.2e-6, 1.3e-5, 1.4e-4] + chain_file = Path(__file__).parents[1] / 'chain_simple.xml' + kwargs = {'multigroup_flux': flux, 'chain_file': chain_file} + + # test with energy group structure from string + microxs = MicroXS.from_multigroup_flux(energies='CASMO-4', **kwargs) + assert isinstance(microxs, MicroXS) + + # test with energy group structure as floats + microxs = MicroXS.from_multigroup_flux(energies=energies, **kwargs) + assert isinstance(microxs, MicroXS) + + # test with nuclides provided + microxs = MicroXS.from_multigroup_flux( + energies=energies, nuclides=['Gd157', 'H1'], **kwargs + ) + assert isinstance(microxs, MicroXS) + assert microxs.nuclides == ['Gd157', 'H1'] + + # test with reactions provided + microxs = MicroXS.from_multigroup_flux( + energies=energies, reactions=['fission', '(n,2n)'], **kwargs + ) + assert isinstance(microxs, MicroXS) + assert microxs.reactions == ['fission', '(n,2n)'] + + +def test_multigroup_flux_same(): + chain_file = Path(__file__).parents[1] / 'chain_simple.xml' + + # Generate micro XS based on 4-group flux + energies = [0., 6.25e-1, 5.53e3, 8.21e5, 2.e7] + flux_per_ev = [0.3, 0.3, 1.0, 1.0] + flux = flux_per_ev * np.diff(energies) + flux_sum = flux.sum() + microxs_4g = MicroXS.from_multigroup_flux( + energies=energies, multigroup_flux=flux, chain_file=chain_file) + + # from_multigroup_flux should not modify the flux + assert flux.sum() == flux_sum + + # Generate micro XS based on 2-group flux, where the boundaries line up with + # the 4 group flux and have the same flux per eV across the full energy + # range + energies = [0., 5.53e3, 2.0e7] + flux_per_ev = [0.3, 1.0] + flux = flux_per_ev * np.diff(energies) + microxs_2g = MicroXS.from_multigroup_flux( + energies=energies, multigroup_flux=flux, chain_file=chain_file) + + assert microxs_4g.data == pytest.approx(microxs_2g.data) diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index 705beb32a2..f2bb7d1b65 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -1,7 +1,9 @@ """Tests for the openmc.deplete.Nuclide class.""" -import xml.etree.ElementTree as ET +import copy +import warnings +import lxml.etree as ET import numpy as np import pytest from openmc.deplete import nuclide @@ -276,9 +278,9 @@ def test_validate(): } # nuclide is good and should have no warnings raise - with pytest.warns(None) as record: + with warnings.catch_warnings(): + warnings.simplefilter("error") assert nuc.validate(strict=True, quiet=False, tolerance=0.0) - assert len(record) == 0 # invalidate decay modes decay = nuc.decay_modes.pop() @@ -335,3 +337,14 @@ def test_validate(): assert "decay mode" in record[0].message.args[0] assert "0 reaction" in record[1].message.args[0] assert "1.0" in record[2].message.args[0] + + +def test_deepcopy(): + """Test deepcopying a FissionYield object""" + nuc = nuclide.FissionYield(products=("I129", "Sm149", "Xe135"), yields=np.array((0.001, 0.0003, 0.002))) + copied_nuc = copy.deepcopy(nuc) + # Check the deepcopy equals the original + assert copied_nuc == nuc + # Mutate the original and verify the copy remains intact + nuc *= 2 + assert copied_nuc != nuc diff --git a/tests/unit_tests/test_deplete_operator.py b/tests/unit_tests/test_deplete_operator.py index 5fe8715ac1..6ea89fc4a5 100644 --- a/tests/unit_tests/test_deplete_operator.py +++ b/tests/unit_tests/test_deplete_operator.py @@ -1,37 +1,15 @@ """Basic unit tests for openmc.deplete.Operator instantiation -Modifies and resets environment variable OPENMC_CROSS_SECTIONS -to a custom file with new depletion_chain node """ from pathlib import Path -import pytest from openmc.deplete.abc import TransportOperator -from openmc.deplete.chain import Chain, _find_chain_file +from openmc.deplete.chain import Chain -BARE_XS_FILE = "bare_cross_sections.xml" CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" -@pytest.fixture() -def bare_xs(run_in_tmpdir): - """Create a very basic cross_sections file, return simple Chain. - - """ - - bare_xs_contents = """ - - - -""".format(CHAIN_PATH) - - with open(BARE_XS_FILE, "w") as out: - out.write(bare_xs_contents) - - yield BARE_XS_FILE - - class BareDepleteOperator(TransportOperator): """Very basic class for testing the initialization.""" @@ -52,10 +30,10 @@ class BareDepleteOperator(TransportOperator): pass -def test_operator_init(bare_xs): +def test_operator_init(): """The test uses a temporary dummy chain. This file will be removed at the end of the test, and only contains a depletion_chain node.""" - bare_op = BareDepleteOperator(_find_chain_file(bare_xs)) + bare_op = BareDepleteOperator(CHAIN_PATH) act_chain = bare_op.chain ref_chain = Chain.from_xml(CHAIN_PATH) assert len(act_chain) == len(ref_chain) @@ -73,8 +51,7 @@ def test_operator_init(bare_xs): def test_operator_fiss_q(): """Make sure fission q values can be set""" new_q = {"U235": 2.0E8, "U238": 2.0E8, "U234": 5.0E7} - chain_file = Path(__file__).parents[1] / "chain_simple.xml" - operator = BareDepleteOperator(chain_file=chain_file, fission_q=new_q) + operator = BareDepleteOperator(chain_file=CHAIN_PATH, fission_q=new_q) mod_chain = operator.chain for name, q in new_q.items(): chain_nuc = mod_chain[name] diff --git a/tests/unit_tests/test_deplete_restart.py b/tests/unit_tests/test_deplete_restart.py index 82ef05ac7b..e8bfc062a0 100644 --- a/tests/unit_tests/test_deplete_restart.py +++ b/tests/unit_tests/test_deplete_restart.py @@ -24,8 +24,7 @@ def test_restart_predictor_cecm(run_in_tmpdir): openmc.deplete.PredictorIntegrator(op, dt, power).integrate() # Load the files - prev_res = openmc.deplete.ResultsList.from_hdf5( - op.output_dir / "depletion_results.h5") + prev_res = openmc.deplete.Results(op.output_dir / "depletion_results.h5") # Re-create depletion operator and load previous results op = dummy_operator.DummyOperator(prev_res) @@ -51,8 +50,7 @@ def test_restart_cecm_predictor(run_in_tmpdir): cecm.integrate() # Load the files - prev_res = openmc.deplete.ResultsList.from_hdf5( - op.output_dir / "depletion_results.h5") + prev_res = openmc.deplete.Results(op.output_dir / "depletion_results.h5") # Re-create depletion operator and load previous results op = dummy_operator.DummyOperator(prev_res) @@ -75,7 +73,7 @@ def test_restart(run_in_tmpdir, scheme): bundle.solver(operator, [0.75], 1.0).integrate() # restart - prev_res = openmc.deplete.ResultsList.from_hdf5( + prev_res = openmc.deplete.Results( operator.output_dir / "depletion_results.h5") operator = dummy_operator.DummyOperator(prev_res) @@ -84,7 +82,7 @@ def test_restart(run_in_tmpdir, scheme): # compare results - results = openmc.deplete.ResultsList.from_hdf5( + results = openmc.deplete.Results( operator.output_dir / "depletion_results.h5") _t, y1 = results.get_atoms("1", "1") diff --git a/tests/unit_tests/test_deplete_resultslist.py b/tests/unit_tests/test_deplete_resultslist.py index 5d8fb11e45..9a4699a4fd 100644 --- a/tests/unit_tests/test_deplete_resultslist.py +++ b/tests/unit_tests/test_deplete_resultslist.py @@ -1,4 +1,4 @@ -"""Tests the ResultsList class""" +"""Tests the Results class""" from pathlib import Path from math import inf @@ -11,9 +11,30 @@ import openmc.deplete @pytest.fixture def res(): """Load the reference results""" - filename = (Path(__file__).parents[1] / 'regression_tests' / 'deplete' + filename = (Path(__file__).parents[1] / 'regression_tests' / 'deplete_with_transport' / 'test_reference.h5') - return openmc.deplete.ResultsList.from_hdf5(filename) + return openmc.deplete.Results(filename) + +def test_get_activity(res): + """Tests evaluating activity""" + t, a = res.get_activity("1") + + t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) + a_ref = np.array( + [1.25167956e+06, 3.69842310e+11, 3.70099291e+11, 3.53629755e+11]) + + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(a, a_ref) + + # Check by_nuclide + a_xe135_ref = np.array( + [2.10657422e+05, 1.12825236e+11, 1.09055177e+11, 1.07491257e+11]) + t_nuc, a_nuc = res.get_activity("1", by_nuclide=True) + + a_xe135 = np.array([a_nuc_i["Xe135"] for a_nuc_i in a_nuc]) + + np.testing.assert_allclose(t_nuc, t_ref) + np.testing.assert_allclose(a_xe135, a_xe135_ref) def test_get_atoms(res): @@ -22,7 +43,7 @@ def test_get_atoms(res): t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) n_ref = np.array( - [6.67473282e+08, 3.72442707e+14, 3.61129692e+14, 4.01920099e+14]) + [6.67473282e+08, 3.57489567e+14, 3.45544042e+14, 3.40588723e+14]) np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(n, n_ref) @@ -43,39 +64,97 @@ def test_get_atoms(res): assert t_hour == pytest.approx(t_ref / (60 * 60)) +def test_get_decay_heat(res): + """Tests evaluating decay heat.""" + # Set chain file for testing + openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + + t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) + dh_ref = np.array( + [1.27933813e-09, 5.95370258e-03, 6.01335600e-03, 5.69831173e-03]) + + t, dh = res.get_decay_heat("1") + + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(dh, dh_ref) + + # Check by nuclide + dh_xe135_ref = np.array( + [1.27933813e-09, 6.85196014e-04, 6.62300168e-04, 6.52802366e-04]) + t_nuc, dh_nuc = res.get_decay_heat("1", by_nuclide=True) + + dh_nuc_xe135 = np.array([dh_nuc_i["Xe135"] for dh_nuc_i in dh_nuc]) + + np.testing.assert_allclose(t_nuc, t_ref) + np.testing.assert_allclose(dh_nuc_xe135, dh_xe135_ref) + + +def test_get_mass(res): + """Tests evaluating single nuclide concentration.""" + t, n = res.get_mass("1", "Xe135") + + t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) + n_ref = np.array( + [6.67473282e+08, 3.57489567e+14, 3.45544042e+14, 3.40588723e+14]) + + # Get g + n_ref *= openmc.data.atomic_mass('Xe135') / openmc.data.AVOGADRO + + np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(n, n_ref) + + # Check alternate units + volume = res[0].volume["1"] + t_days, n_cm3 = res.get_mass("1", "Xe135", mass_units="g/cm3", time_units="d") + + assert t_days == pytest.approx(t_ref / (60 * 60 * 24)) + assert n_cm3 == pytest.approx(n_ref / volume) + + t_min, n_bcm = res.get_mass("1", "Xe135", mass_units="kg", time_units="min") + assert n_bcm == pytest.approx(n_ref / 1e3) + assert t_min == pytest.approx(t_ref / 60) + + t_hour, _n = res.get_mass("1", "Xe135", time_units="h") + assert t_hour == pytest.approx(t_ref / (60 * 60)) + + def test_get_reaction_rate(res): """Tests evaluating reaction rate.""" t, r = res.get_reaction_rate("1", "Xe135", "(n,gamma)") t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - n_ref = [6.67473282e+08, 3.72442707e+14, 3.61129692e+14, 4.01920099e+14] - xs_ref = [5.10301159e-05, 3.19379638e-05, 4.50543806e-05, 4.71004301e-05] + n_ref = [6.67473282e+08, 3.57489567e+14, 3.45544042e+14, 3.40588723e+14] + xs_ref = [3.10220818e-05, 3.36754072e-05, 3.12740350e-05, 3.86717693e-05] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(r, np.array(n_ref) * xs_ref) -def test_get_eigenvalue(res): - """Tests evaluating eigenvalue.""" - t, k = res.get_eigenvalue() +def test_get_keff(res): + """Tests evaluating keff.""" + t, k = res.get_keff() + t_min, k = res.get_keff(time_units='min') t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - k_ref = [1.21409662, 1.16518654, 1.25357797, 1.22611968] - u_ref = [0.0278795195, 0.0233141097, 0.0167899218, 0.0246734716] + k_ref = [1.1773089172, 1.2231748584, 1.1611455694, 1.1714783649] + u_ref = [0.0384666252, 0.0311915665, 0.0226370102, 0.0315964732] np.testing.assert_allclose(t, t_ref) + np.testing.assert_allclose(t_min * 60, t_ref) np.testing.assert_allclose(k[:, 0], k_ref) np.testing.assert_allclose(k[:, 1], u_ref) -@pytest.mark.parametrize("unit", ("s", "d", "min", "h")) +@pytest.mark.parametrize("unit", ("s", "d", "min", "h", "a")) def test_get_steps(unit): - # Make a ResultsList full of near-empty Result instances + # Make a Results full of near-empty Result instances # Just fill out a time schedule - results = openmc.deplete.ResultsList() + results = openmc.deplete.Results(filename=None) # Time in units of unit times = np.linspace(0, 100, num=5) - if unit == "d": + if unit == "a": + conversion_to_seconds = 60 * 60 * 24 * 365.25 + elif unit == "d": conversion_to_seconds = 60 * 60 * 24 elif unit == "h": conversion_to_seconds = 60 * 60 @@ -85,7 +164,7 @@ def test_get_steps(unit): conversion_to_seconds = 1 for ix in range(times.size): - res = openmc.deplete.Results() + res = openmc.deplete.StepResult() res.time = times[ix:ix + 1] * conversion_to_seconds results.append(res) @@ -129,3 +208,16 @@ def test_get_steps(unit): actual = results.get_step_where( times[-1] * 100, time_units=unit, atol=inf, rtol=inf) assert actual == times.size - 1 + + +def test_stepresult_get_material(res): + # Get material at first timestep + step_result = res[0] + mat1 = step_result.get_material("1") + assert mat1.id == 1 + assert mat1.volume == step_result.volume["1"] + + # Spot check number densities + densities = mat1.get_nuclide_atom_densities() + assert densities['Xe135'] == pytest.approx(1e-14) + assert densities['U234'] == pytest.approx(1.00506e-05) diff --git a/tests/unit_tests/test_deplete_transfer_rates.py b/tests/unit_tests/test_deplete_transfer_rates.py new file mode 100644 index 0000000000..a3228e9fb7 --- /dev/null +++ b/tests/unit_tests/test_deplete_transfer_rates.py @@ -0,0 +1,200 @@ +""" Tests for TransferRates class """ + +from pathlib import Path +from math import exp + +import pytest +import numpy as np + +import openmc +from openmc.deplete import CoupledOperator +from openmc.deplete.transfer_rates import TransferRates +from openmc.deplete.abc import (_SECONDS_PER_MINUTE, _SECONDS_PER_HOUR, + _SECONDS_PER_DAY, _SECONDS_PER_JULIAN_YEAR) + +CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + +@pytest.fixture +def model(): + openmc.reset_auto_ids() + f = openmc.Material(name="f") + f.add_element("U", 1, percent_type="ao", enrichment=4.25) + f.add_element("O", 2) + f.set_density("g/cc", 10.4) + + w = openmc.Material(name="w") + w.add_element("O", 1) + w.add_element("H", 2) + w.set_density("g/cc", 1.0) + w.depletable = True + + # material just to test multiple destination material + h = openmc.Material(name="h") + h.add_element("He", 1) + h.set_density("g/cc", 1.78e-4) + h.depletable = True + + radii = [0.42, 0.45] + f.volume = np.pi * radii[0] ** 2 + w.volume = np.pi * (radii[1]**2 - radii[0]**2) + h.volume = 1 + materials = openmc.Materials([f, w, h]) + + surf_f = openmc.Sphere(r=radii[0]) + surf_w = openmc.Sphere(r=radii[1], boundary_type='vacuum') + surf_h = openmc.Sphere(x0=10, r=1, boundary_type='vacuum') + cell_f = openmc.Cell(fill=f, region=-surf_f) + cell_w = openmc.Cell(fill=w, region=+surf_f & -surf_w) + cell_h = openmc.Cell(fill=h, region=-surf_h) + geometry = openmc.Geometry([cell_f, cell_w, cell_h]) + + settings = openmc.Settings() + settings.particles = 1000 + settings.inactive = 10 + settings.batches = 50 + + return openmc.Model(geometry, materials, settings) + +@pytest.mark.parametrize("case_name, transfer_rates, timesteps", [ + ('elements', {'U': 0.01, 'Xe': 0.1}, None), + ('nuclides', {'I135': 0.01, 'Gd156': 0.1, 'Gd157': 0.01}, None), + ('nuclides_elements', {'I135': 0.01, 'Gd156': 0.1, 'Gd157': 0.01, 'U': 0.01, + 'Xe': 0.1}, None), + ('elements_nuclides', {'U': 0.01, 'Xe': 0.1, 'I135': 0.01, 'Gd156': 0.1, + 'Gd157': 0.01}, None), + ('multiple_transfer', {'U': 0.01, 'Xe': 0.1, 'I135': 0.01, 'Gd156': 0.1, + 'Gd157': 0.01}, None), + ('timesteps', {'U': 0.01, 'Xe': 0.1}, [1]), + ('rates_invalid_1', {'Gd': 0.01, 'Gd157': 0.01, 'Gd156': 0.01}, None), + ('rates_invalid_2', {'Gd156': 0.01, 'Gd157': 0.01, 'Gd': 0.01}, None), + ('rates_invalid_3', {'Gb156': 0.01}, None), + ('rates_invalid_4', {'Gb': 0.01}, None) + ]) +def test_get_set(model, case_name, transfer_rates, timesteps): + """Tests the get/set methods""" + op = CoupledOperator(model, CHAIN_PATH) + number_of_timesteps = 2 + transfer = TransferRates(op, model.materials, number_of_timesteps) + + if timesteps is None: + timesteps = np.arange(number_of_timesteps) + + # Test by Openmc material, material name and material id + material, dest_material, dest_material2 = [m for m in model.materials + if m.depletable] + for material_input in [material, material.name, material.id]: + for dest_material_input in [None, dest_material, dest_material.name, + dest_material.id]: + if case_name == 'rates_invalid_1': + with pytest.raises(ValueError, match='Cannot add transfer ' + 'rate for nuclide Gd157 to material 1 ' + 'where element Gd already has a ' + 'transfer rate.'): + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, + [component], + transfer_rate) + elif case_name == 'rates_invalid_2': + with pytest.raises(ValueError, match='Cannot add transfer ' + f'rate for element Gd to material 1 with ' + r'transfer rate\(s\) for nuclide\(s\) ' + 'Gd156, Gd157.'): + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, + [component], + transfer_rate) + elif case_name == 'rates_invalid_3': + with pytest.raises(ValueError, match='Gb156 is not a valid ' + 'nuclide or element.'): + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, + [component], + transfer_rate) + elif case_name == 'rates_invalid_4': + with pytest.raises(ValueError, match='Gb is not a valid ' + 'nuclide or element.'): + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, + [component], + transfer_rate) + else: + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, [component], + transfer_rate, + timesteps=timesteps, + destination_material=\ + dest_material_input) + assert transfer.get_external_rate( + material_input, component, timesteps, + dest_material_input)[0] == transfer_rate + assert np.all(transfer.external_timesteps == timesteps) + + if timesteps is not None: + for timestep in timesteps: + assert transfer.get_components(material_input, timestep, + dest_material_input) == list(transfer_rates.keys()) + else: + assert transfer.get_components(material_input, timesteps, + dest_material_input) == list(transfer_rates.keys()) + + if case_name == 'multiple_transfer': + for dest2_material_input in [dest_material2, dest_material2.name, + dest_material2.id]: + for component, transfer_rate in transfer_rates.items(): + transfer.set_transfer_rate(material_input, [component], + transfer_rate, + destination_material=\ + dest2_material_input) + for id, dest_mat in zip([0,1],[dest_material,dest_material2]): + assert transfer.get_external_rate( + material_input, component, timesteps)[0] == transfer_rate + +@pytest.mark.parametrize("transfer_rate_units, unit_conv", [ + ('1/s', 1), + ('1/sec', 1), + ('1/min', _SECONDS_PER_MINUTE), + ('1/minute', _SECONDS_PER_MINUTE), + ('1/h', _SECONDS_PER_HOUR), + ('1/hr', _SECONDS_PER_HOUR), + ('1/hour', _SECONDS_PER_HOUR), + ('1/d', _SECONDS_PER_DAY), + ('1/day', _SECONDS_PER_DAY), + ('1/a', _SECONDS_PER_JULIAN_YEAR), + ('1/year', _SECONDS_PER_JULIAN_YEAR), + ]) +def test_units(transfer_rate_units, unit_conv, model): + """ Units testing""" + # create transfer rate Xe + components = ['Xe', 'U235'] + transfer_rate = 1e-5 + number_of_timesteps = 2 + op = CoupledOperator(model, CHAIN_PATH) + transfer = TransferRates(op, model.materials, number_of_timesteps) + + for component in components: + transfer.set_transfer_rate('f', [component], transfer_rate * unit_conv, + transfer_rate_units=transfer_rate_units) + for timestep in range(transfer.number_of_timesteps): + assert transfer.get_external_rate('f', component, timestep)[0] == transfer_rate + + +def test_transfer(run_in_tmpdir, model): + """Tests transfer depletion class without neither reaction rates nor decay + but only transfer rates""" + + # create transfer rate for U + element = ['U'] + transfer_rate = 1e-5 + op = CoupledOperator(model, CHAIN_PATH) + integrator = openmc.deplete.PredictorIntegrator( + op, [1,1], 0.0, timestep_units = 'd') + integrator.add_transfer_rate('f', element, transfer_rate) + integrator.integrate() + + # Get number of U238 atoms from results + results = openmc.deplete.Results('depletion_results.h5') + _, atoms = results.get_atoms(model.materials[0], "U238") + + # Ensure number of atoms equal transfer decay + assert atoms[1] == pytest.approx(atoms[0]*exp(-transfer_rate*3600*24)) + assert atoms[2] == pytest.approx(atoms[1]*exp(-transfer_rate*3600*24)) diff --git a/tests/unit_tests/test_element.py b/tests/unit_tests/test_element.py index bacb988b9a..d91cfaf6e2 100644 --- a/tests/unit_tests/test_element.py +++ b/tests/unit_tests/test_element.py @@ -1,5 +1,5 @@ import openmc -from pytest import approx, raises +from pytest import approx, raises, warns from openmc.data import NATURAL_ABUNDANCE, atomic_mass @@ -37,6 +37,20 @@ def test_expand_enrichment(): assert isotope[1] == approx(ref[isotope[0]]) +def test_expand_no_isotopes(): + """Test that correct warning is raised for elements with no isotopes""" + with warns(UserWarning, match='No naturally occurring'): + element = openmc.Element('Tc') + element.expand(100.0, 'ao') + + +def test_expand_ta(): + ref = {'Ta180': 0.01201, 'Ta181': 99.98799} + element = openmc.Element('Ta') + for isotope in element.expand(100.0, 'ao'): + assert isotope[1] == approx(ref[isotope[0]]) + + def test_expand_exceptions(): """ Test that correct exceptions are raised for invalid input """ diff --git a/tests/unit_tests/test_endf.py b/tests/unit_tests/test_endf.py index 9e69708673..1d4982054c 100644 --- a/tests/unit_tests/test_endf.py +++ b/tests/unit_tests/test_endf.py @@ -23,6 +23,7 @@ def test_float_endf(): assert endf.float_endf('-1.+2') == approx(-100.0) assert endf.float_endf(' ') == 0.0 assert endf.float_endf('9.876540000000000') == approx(9.87654) + assert endf.float_endf('-2.225002+6') == approx(-2.225002e+6) def test_int_endf(): diff --git a/tests/unit_tests/test_energy_cutoff.py b/tests/unit_tests/test_energy_cutoff.py index fb82c579a1..45333f2e1d 100644 --- a/tests/unit_tests/test_energy_cutoff.py +++ b/tests/unit_tests/test_energy_cutoff.py @@ -17,7 +17,7 @@ def inf_medium_model(cutoff_energy, source_energy): model.geometry = openmc.Geometry([cell]) model.settings.run_mode = 'fixed source' - model.settings.source = openmc.Source( + model.settings.source = openmc.IndependentSource( particle='photon', energy=openmc.stats.Discrete([source_energy], [1.0]), ) diff --git a/tests/unit_tests/test_filter_mesh.py b/tests/unit_tests/test_filter_mesh.py new file mode 100644 index 0000000000..a8bd4996dd --- /dev/null +++ b/tests/unit_tests/test_filter_mesh.py @@ -0,0 +1,261 @@ +import math + +import numpy as np +import pytest +from uncertainties import unumpy + +import openmc + + +def test_spherical_mesh_estimators(run_in_tmpdir): + """Test that collision/tracklength estimators agree for SphericalMesh""" + + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.set_density('g/cm3', 10.0) + + sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 1_000 + model.settings.inactive = 10 + model.settings.batches = 20 + + sph_mesh = openmc.SphericalMesh( + r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3) + ) + tally1 = openmc.Tally() + tally1.filters = [openmc.MeshFilter(sph_mesh)] + tally1.scores = ['flux'] + tally1.estimator = 'collision' + + sph_mesh = openmc.SphericalMesh( + r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3) + ) + tally2 = openmc.Tally() + tally2.filters = [openmc.MeshFilter(sph_mesh)] + tally2.scores = ['flux'] + tally2.estimator = 'tracklength' + + model.tallies = openmc.Tallies([tally1, tally2]) + + # Run OpenMC + sp_filename = model.run() + + # Get radial flux distribution + with openmc.StatePoint(sp_filename) as sp: + flux_collision = sp.tallies[tally1.id].mean.ravel() + flux_collision_unc = sp.tallies[tally1.id].std_dev.ravel() + flux_tracklength = sp.tallies[tally2.id].mean.ravel() + flux_tracklength_unc = sp.tallies[tally2.id].std_dev.ravel() + + # Construct arrays with uncertainties + collision = unumpy.uarray(flux_collision, flux_collision_unc) + tracklength = unumpy.uarray(flux_tracklength, flux_tracklength_unc) + delta = collision - tracklength + + # Check that difference is within uncertainty + diff = unumpy.nominal_values(delta) + std_dev = unumpy.std_devs(delta) + assert np.all(diff < 3*std_dev) + + +def test_cylindrical_mesh_estimators(run_in_tmpdir): + """Test that collision/tracklength estimators agree for CylindricalMesh""" + + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.set_density('g/cm3', 10.0) + + cyl = openmc.model.RightCircularCylinder((0., 0., -5.), 10., 10.0, + boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-cyl) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 1_000 + model.settings.inactive = 10 + model.settings.batches = 20 + + cyl_mesh = openmc.CylindricalMesh( + r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), + z_grid=[-5., 5.] + ) + tally1 = openmc.Tally() + tally1.filters = [openmc.MeshFilter(cyl_mesh)] + tally1.scores = ['flux'] + tally1.estimator = 'collision' + + cyl_mesh = openmc.CylindricalMesh( + r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), + z_grid=[-5., 5.] + ) + tally2 = openmc.Tally() + tally2.filters = [openmc.MeshFilter(cyl_mesh)] + tally2.scores = ['flux'] + tally2.estimator = 'tracklength' + + model.tallies = openmc.Tallies([tally1, tally2]) + + # Run OpenMC + sp_filename = model.run() + + # Get radial flux distribution + with openmc.StatePoint(sp_filename) as sp: + flux_collision = sp.tallies[tally1.id].mean.ravel() + flux_collision_unc = sp.tallies[tally1.id].std_dev.ravel() + flux_tracklength = sp.tallies[tally2.id].mean.ravel() + flux_tracklength_unc = sp.tallies[tally2.id].std_dev.ravel() + + # Construct arrays with uncertainties + collision = unumpy.uarray(flux_collision, flux_collision_unc) + tracklength = unumpy.uarray(flux_tracklength, flux_tracklength_unc) + delta = collision - tracklength + + # Check that difference is within uncertainty + diff = unumpy.nominal_values(delta) + std_dev = unumpy.std_devs(delta) + assert np.all(diff < 3*std_dev) + + +@pytest.mark.parametrize("scale", [0.1, 1.0, 1e2, 1e4, 1e5]) +def test_cylindrical_mesh_coincident(scale, run_in_tmpdir): + """Test for cylindrical mesh boundary being coincident with a cell boundary""" + + fuel = openmc.Material() + fuel.add_nuclide('U235', 1.) + fuel.set_density('g/cm3', 4.5) + + zcyl = openmc.ZCylinder(r=1.25*scale) + box = openmc.model.RectangularPrism(4*scale, 4*scale, boundary_type='reflective') + cell1 = openmc.Cell(fill=fuel, region=-zcyl) + cell2 = openmc.Cell(fill=None, region=+zcyl & -box) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 0 + + cyl_mesh = openmc.CylindricalMesh( + r_grid=[0., 1.25*scale], + phi_grid=[0., 2*math.pi], + z_grid=[-1e10, 1e10] + ) + cyl_mesh_filter = openmc.MeshFilter(cyl_mesh) + cell_filter = openmc.CellFilter([cell1]) + + tally1 = openmc.Tally() + tally1.filters = [cyl_mesh_filter] + tally1.scores = ['flux'] + tally2 = openmc.Tally() + tally2.filters = [cell_filter] + tally2.scores = ['flux'] + model.tallies = openmc.Tallies([tally1, tally2]) + + # Run OpenMC + sp_filename = model.run() + + # Get flux for each of the two tallies + with openmc.StatePoint(sp_filename) as sp: + t1 = sp.tallies[tally1.id] + t2 = sp.tallies[tally2.id] + mean1 = t1.mean.ravel()[0] + mean2 = t2.mean.ravel()[0] + + # The two tallies should be exactly the same + assert mean1 == pytest.approx(mean2) + + +@pytest.mark.parametrize("scale", [0.1, 1.0, 1e2, 1e4, 1e5]) +def test_spherical_mesh_coincident(scale, run_in_tmpdir): + """Test for spherical mesh boundary being coincident with a cell boundary""" + + fuel = openmc.Material() + fuel.add_nuclide('U235', 1.) + fuel.set_density('g/cm3', 4.5) + + sph = openmc.Sphere(r=1.25*scale) + rcc = openmc.model.RectangularParallelepiped( + -2*scale, 2*scale, -2*scale, 2*scale, -2*scale, 2*scale, + boundary_type='reflective') + cell1 = openmc.Cell(fill=fuel, region=-sph) + cell2 = openmc.Cell(fill=None, region=+sph & -rcc) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.inactive = 0 + + sph_mesh = openmc.SphericalMesh( + r_grid=[0., 1.25*scale], + phi_grid=[0., 2*math.pi], + theta_grid=[0., math.pi], + ) + + sph_mesh_filter = openmc.MeshFilter(sph_mesh) + cell_filter = openmc.CellFilter([cell1]) + + tally1 = openmc.Tally() + tally1.filters = [sph_mesh_filter] + tally1.scores = ['flux'] + tally2 = openmc.Tally() + tally2.filters = [cell_filter] + tally2.scores = ['flux'] + model.tallies = openmc.Tallies([tally1, tally2]) + + # Run OpenMC + sp_filename = model.run() + + # Get flux for each of the two tallies + with openmc.StatePoint(sp_filename) as sp: + t1 = sp.tallies[tally1.id] + t2 = sp.tallies[tally2.id] + mean1 = t1.mean.ravel()[0] + mean2 = t2.mean.ravel()[0] + + # The two tallies should be exactly the same + assert mean1 == pytest.approx(mean2) + + +def test_get_reshaped_data(run_in_tmpdir): + """Test that expanding MeshFilter dimensions works as expected""" + + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.set_density('g/cm3', 10.0) + + sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 1_000 + model.settings.inactive = 10 + model.settings.batches = 20 + + sph_mesh = openmc.SphericalMesh( + r_grid=np.linspace(0.0, 5.0**3, 20)**(1/3), + theta_grid=np.linspace(0, math.pi, 4), + phi_grid=np.linspace(0, 2*math.pi, 3) + ) + tally1 = openmc.Tally() + efilter = openmc.EnergyFilter([0, 1e5, 1e8]) + meshfilter = openmc.MeshFilter(sph_mesh) + assert meshfilter.shape == (19, 3, 2) + tally1.filters = [efilter, meshfilter] + tally1.scores = ['flux'] + + model.tallies = openmc.Tallies([tally1]) + + # Run OpenMC + sp_filename = model.run() + + # Get flux tally as reshaped data + with openmc.StatePoint(sp_filename) as sp: + t1 = sp.tallies[tally1.id] + data1 = t1.get_reshaped_data() + data2 = t1.get_reshaped_data(expand_dims=True) + + assert data1.shape == (2, 19*3*2, 1, 1) + assert data2.shape == (2, 19, 3, 2, 1, 1) diff --git a/tests/unit_tests/test_filter_meshborn.py b/tests/unit_tests/test_filter_meshborn.py new file mode 100644 index 0000000000..62fa1174e7 --- /dev/null +++ b/tests/unit_tests/test_filter_meshborn.py @@ -0,0 +1,116 @@ +"""Test the meshborn filter using a fixed source calculation on a H1 sphere. + +""" + +import numpy as np +from uncertainties import unumpy +import openmc +import pytest + + +@pytest.fixture +def model(): + """Sphere of H1 with one hemisphere containing the source (x>0) and one + hemisphere with no source (x<0). + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # Materials + h1 = openmc.Material() + h1.add_nuclide("H1", 1.0) + h1.set_density("g/cm3", 1.0) + model.materials = openmc.Materials([h1]) + + # Core geometry + r = 10.0 + sphere = openmc.Sphere(r=r, boundary_type="reflective") + core = openmc.Cell(fill=h1, region=-sphere) + model.geometry = openmc.Geometry([core]) + + # Settings + model.settings.run_mode = 'fixed source' + model.settings.particles = 2000 + model.settings.batches = 8 + + distribution = openmc.stats.Box((0., -r, -r), (r, r, r)) + model.settings.source = openmc.IndependentSource(space=distribution) + + # ============================================================================= + # Tallies + # ============================================================================= + + mesh = openmc.RegularMesh() + mesh.dimension = (2, 2, 1) + mesh.lower_left = (-r, -r, -r) + mesh.upper_right = (r, r, r) + + f = openmc.MeshBornFilter(mesh) + t_1 = openmc.Tally(name="scatter-collision") + t_1.filters = [f] + t_1.scores = ["scatter"] + t_1.estimator = "collision" + + t_2 = openmc.Tally(name="scatter-tracklength") + t_2.filters = [f] + t_2.scores = ["scatter"] + t_2.estimator = "tracklength" + + model.tallies = [t_1, t_2] + + return model + + +def test_estimator_consistency(model, run_in_tmpdir): + """Test that resuts obtained from a tracklength estimator are + consistent with results obtained from a collision estimator. + + """ + # Run OpenMC + sp_filename = model.run() + + # Get radial flux distribution + with openmc.StatePoint(sp_filename) as sp: + scatter_collision = sp.get_tally(name="scatter-collision").mean.ravel() + scatter_collision_std_dev = sp.get_tally(name="scatter-collision").std_dev.ravel() + scatter_tracklength = sp.get_tally(name="scatter-tracklength").mean.ravel() + scatter_tracklength_std_dev = sp.get_tally(name="scatter-tracklength").std_dev.ravel() + + collision = unumpy.uarray(scatter_collision, scatter_collision_std_dev) + tracklength = unumpy.uarray(scatter_tracklength, scatter_tracklength_std_dev) + delta = abs(collision - tracklength) + + diff = unumpy.nominal_values(delta) + std_dev = unumpy.std_devs(delta) + assert np.all(diff <= 3 * std_dev) + + +def test_xml_serialization(): + """Test xml serialization of the meshborn filter.""" + openmc.reset_auto_ids() + + mesh = openmc.RegularMesh() + mesh.dimension = (1, 1, 1) + mesh.lower_left = (0.0, 0.0, 0.0) + mesh.upper_right = (1.0, 1.0, 1.0) + + filter = openmc.MeshBornFilter(mesh) + filter.translation = (2.0, 2.0, 2.0) + assert filter.mesh.id == 1 + assert filter.mesh.dimension == (1, 1, 1) + assert filter.mesh.lower_left == (0.0, 0.0, 0.0) + assert filter.mesh.upper_right == (1.0, 1.0, 1.0) + + repr(filter) + + elem = filter.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'meshborn' + assert elem[0].text == "1" + assert elem.get("translation") == "2.0 2.0 2.0" + + meshes = {1: mesh} + new_filter = openmc.Filter.from_xml_element(elem, meshes=meshes) + assert new_filter.bins == filter.bins + np.testing.assert_equal(new_filter.translation, [2.0, 2.0, 2.0]) diff --git a/tests/unit_tests/test_filter_meshmaterial.py b/tests/unit_tests/test_filter_meshmaterial.py new file mode 100644 index 0000000000..a05ed5a120 --- /dev/null +++ b/tests/unit_tests/test_filter_meshmaterial.py @@ -0,0 +1,66 @@ +import numpy as np +import openmc +from pytest import approx + + +def test_filter_mesh_material(run_in_tmpdir): + # Create four identical materials + openmc.reset_auto_ids() + materials = [] + for i in range(4): + mat = openmc.Material() + mat.id = 10*(i+1) + mat.add_nuclide('Fe56', 1.0) + materials.append(mat) + + # Create a slab model with four cells + z_values = [-10., -5., 0., 5., 10.] + planes = [openmc.ZPlane(z) for z in z_values] + planes[0].boundary_type = 'vacuum' + planes[-1].boundary_type = 'vacuum' + regions = [+left & -right for left, right in zip(planes[:-1], planes[1:])] + cells = [openmc.Cell(fill=m, region=r) for r, m in zip(regions, materials)] + model = openmc.Model() + model.geometry = openmc.Geometry(cells) + model.settings.particles = 1_000 + model.settings.batches = 5 + model.settings.run_mode = 'fixed source' + + # Create a mesh that does not align with all planar surfaces + mesh = openmc.RegularMesh() + mesh.lower_left = (-1., -1., -10.) + mesh.upper_right = (1., 1., 10.) + mesh.dimension = (1, 1, 5) + + # Determine material volumes in each mesh element and use result to create a + # MeshMaterialFilter with corresponding bins + vols = mesh.material_volumes(model) + mmf = openmc.MeshMaterialFilter.from_volumes(mesh, vols) + expected_bins = [(0, 10), (1, 10), (1, 20), (2, 20), (2, 30), (3, 40), (3, 30), (4, 40)] + np.testing.assert_equal(mmf.bins, expected_bins) + + # Create two tallies, one with a mesh filter and one with mesh-material + mesh_tally = openmc.Tally() + mesh_tally.filters = [openmc.MeshFilter(mesh)] + mesh_tally.scores = ['flux'] + mesh_material_tally = openmc.Tally() + mesh_material_tally.filters = [mmf] + mesh_material_tally.scores = ['flux'] + model.tallies = [mesh_tally, mesh_material_tally] + + # Run model to get results on the two tallies + model.run(apply_tally_results=True) + + # The sum of the flux in each mesh-material combination within a single mesh + # element should be equal to the flux in that mesh element + mesh_mean = mesh_tally.mean.ravel() + meshmat_mean = mesh_material_tally.mean.ravel() + assert mesh_mean[0] == approx(meshmat_mean[0]) + assert mesh_mean[1] == approx(meshmat_mean[1] + meshmat_mean[2]) + assert mesh_mean[2] == approx(meshmat_mean[3] + meshmat_mean[4]) + assert mesh_mean[3] == approx(meshmat_mean[5] + meshmat_mean[6]) + assert mesh_mean[4] == approx(meshmat_mean[7]) + assert mesh_tally.mean.sum() == approx(mesh_material_tally.mean.sum()) + + # Make sure get_pandas_dataframe method works + mesh_material_tally.get_pandas_dataframe() diff --git a/tests/unit_tests/test_filter_musurface.py b/tests/unit_tests/test_filter_musurface.py new file mode 100644 index 0000000000..ca0db71f0c --- /dev/null +++ b/tests/unit_tests/test_filter_musurface.py @@ -0,0 +1,38 @@ +import openmc + + +def test_musurface(run_in_tmpdir): + sphere = openmc.Sphere(r=1.0, boundary_type='vacuum') + cell = openmc.Cell(region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 1000 + model.settings.batches = 10 + E = 1.0 + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), + angle=openmc.stats.Isotropic(), + energy=openmc.stats.delta_function(E), + ) + model.settings.run_mode = "fixed source" + + filter1 = openmc.MuSurfaceFilter(200) + filter2 = openmc.SurfaceFilter(sphere) + tally = openmc.Tally() + tally.filters = [filter1, filter2] + tally.scores = ['current'] + model.tallies = openmc.Tallies([tally]) + + # Run OpenMC + sp_filename = model.run() + + # Get current binned by mu + with openmc.StatePoint(sp_filename) as sp: + current_mu = sp.tallies[tally.id].mean.ravel() + + # All contributions should show up in last bin + assert current_mu[-1] == 1.0 + for element in current_mu[:-1]: + assert element == 0.0 + + diff --git a/tests/unit_tests/test_filter_weight.py b/tests/unit_tests/test_filter_weight.py new file mode 100644 index 0000000000..878929ee05 --- /dev/null +++ b/tests/unit_tests/test_filter_weight.py @@ -0,0 +1,44 @@ +import openmc +import numpy as np + + +def test_weightfilter(run_in_tmpdir): + steel = openmc.Material(name='Stainless Steel') + steel.set_density('g/cm3', 8.00) + steel.add_nuclide('Fe56', 1.0) + + sphere = openmc.Sphere(r=50.0, boundary_type='vacuum') + cell = openmc.Cell(region=-sphere, fill=steel) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 100 + model.settings.batches = 10 + + model.settings.source = openmc.IndependentSource( + energy=openmc.stats.delta_function(14e6), + ) + model.settings.run_mode = "fixed source" + + radius = list(range(1, 50)) + sphere_mesh = openmc.SphericalMesh(radius) + mesh_filter = openmc.MeshFilter(sphere_mesh) + weight_filter = openmc.WeightFilter( + [0.999, 0.9999, 0.99999, 0.999999, 1.0, 1.000001 ,1.00001, 1.0001, 1.001] + ) + + tally = openmc.Tally() + tally.filters = [mesh_filter, weight_filter] + tally.estimator = 'analog' + tally.scores = ['flux'] + model.tallies = openmc.Tallies([tally]) + + # Run OpenMC + model.run(apply_tally_results=True) + + # Get current binned by mu + neutron_flux = tally.mean.reshape(48, 8) + + # All contributions should show up in the fourth bin + assert np.all(neutron_flux[:, 3] != 0.0) + neutron_flux[:, 3] = 0.0 + assert np.all(neutron_flux == 0.0) diff --git a/tests/unit_tests/test_filters.py b/tests/unit_tests/test_filters.py index 49526ac673..8c56a310e1 100644 --- a/tests/unit_tests/test_filters.py +++ b/tests/unit_tests/test_filters.py @@ -1,6 +1,6 @@ import numpy as np import openmc -from pytest import fixture, approx +from pytest import fixture, approx, raises @fixture(scope='module') @@ -10,14 +10,14 @@ def box_model(): m.add_nuclide('U235', 1.0) m.set_density('g/cm3', 1.0) - box = openmc.model.rectangular_prism(10., 10., boundary_type='vacuum') - c = openmc.Cell(fill=m, region=box) + box = openmc.model.RectangularPrism(10., 10., boundary_type='vacuum') + c = openmc.Cell(fill=m, region=-box) model.geometry.root_universe = openmc.Universe(cells=[c]) model.settings.particles = 100 model.settings.batches = 10 model.settings.inactive = 0 - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) return model @@ -240,3 +240,129 @@ def test_first_moment(run_in_tmpdir, box_model): assert first_score(sph_scat_tally) == scatter assert first_score(sph_flux_tally) == approx(flux) assert first_score(zernike_tally) == approx(scatter) + + +def test_energy(): + f = openmc.EnergyFilter.from_group_structure('CCFE-709') + assert f.bins.shape == (709, 2) + assert len(f.values) == 710 + + +def test_energyfilter_error_handling(): + with raises(ValueError): + openmc.EnergyFilter([1e6]) + + +def test_lethargy_bin_width(): + f = openmc.EnergyFilter.from_group_structure('VITAMIN-J-175') + assert len(f.lethargy_bin_width) == 175 + energy_bins = openmc.mgxs.GROUP_STRUCTURES['VITAMIN-J-175'] + assert f.lethargy_bin_width[0] == np.log10(energy_bins[1]/energy_bins[0]) + assert f.lethargy_bin_width[-1] == np.log10(energy_bins[-1]/energy_bins[-2]) + + +def test_energyfunc(): + f = openmc.EnergyFunctionFilter( + [0.0, 10.0, 2.0e3, 1.0e6, 20.0e6], + [1.0, 0.9, 0.8, 0.7, 0.6], + 'histogram' + ) + + # Make sure XML roundtrip works + elem = f.to_xml_element() + new_f = openmc.EnergyFunctionFilter.from_xml_element(elem) + np.testing.assert_allclose(f.energy, new_f.energy) + np.testing.assert_allclose(f.y, new_f.y) + assert f.interpolation == new_f.interpolation + + +def test_tabular_from_energyfilter(): + efilter = openmc.EnergyFilter([0.0, 10.0, 20.0, 25.0]) + tab = efilter.get_tabular(values=[5, 10, 10]) + + assert tab.x.tolist() == [0.0, 10.0, 20.0, 25.0] + + # combination of different values passed into get_tabular and different + # width energy bins results in a doubling value for each p value + assert tab.p.tolist() == [0.02, 0.04, 0.08, 0.0] + + # distribution should integrate to unity + assert tab.integral() == approx(1.0) + + # 'histogram' is the default + assert tab.interpolation == 'histogram' + + tab = efilter.get_tabular(values=np.array([10, 10, 5]), interpolation='linear-linear') + assert tab.interpolation == 'linear-linear' + + +def test_energy_filter(): + + # testing that bins descending value raises error + msg = "Values 1.0 and 0.5 appear to be out of order" + with raises(ValueError, match=msg): + openmc.EnergyFilter([0.0, 1.0, 0.5]) + + # testing that bins with same value raises error + msg = "Values 0.25 and 0.25 appear to be out of order" + with raises(ValueError, match=msg): + openmc.EnergyFilter([0.0, 0.25, 0.25]) + + # testing that negative bins values raises error + msg = 'Unable to set "filter value" to "-1.2" since it is less than "0.0"' + with raises(ValueError, match=msg): + openmc.EnergyFilter([-1.2, 0.25, 0.5]) + + +def test_weight(): + f = openmc.WeightFilter([0.01, 0.1, 1.0, 10.0]) + expected_bins = [[0.01, 0.1], [0.1, 1.0], [1.0, 10.0]] + + assert np.allclose(f.bins, expected_bins) + assert len(f.bins) == 3 + + # Make sure __repr__ works + repr(f) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'weight' + + # from_xml_element() + new_f = openmc.Filter.from_xml_element(elem) + assert new_f.id == f.id + assert np.allclose(new_f.bins, f.bins) + + +def test_mesh_material(): + mat1 = openmc.Material() + mat2 = openmc.Material() + + mesh = openmc.RegularMesh() + mesh.lower_left = (-1., -1., -1.) + mesh.upper_right = (1., 1., 1.) + mesh.dimension = (2, 4, 1) + bins = [(0, mat1), (0, mat2), (6, mat1), (7, mat2)] + f = openmc.MeshMaterialFilter(mesh, bins) + + expected_bins = [(0, mat1.id), (0, mat2.id), (6, mat1.id), (7, mat2.id)] + assert np.allclose(f.bins, expected_bins) + assert f.mesh == mesh + assert f.shape == (4,) + + # to_xml_element() + elem = f.to_xml_element() + assert elem.tag == 'filter' + assert elem.attrib['type'] == 'meshmaterial' + + # from_xml_element() + new_f = openmc.Filter.from_xml_element(elem, meshes={mesh.id: mesh}) + assert isinstance(new_f, openmc.MeshMaterialFilter) + assert new_f.id == f.id + assert new_f.mesh == f.mesh + assert np.allclose(new_f.bins, expected_bins) + + # Test hash and str + hash(f) + str(f) diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 64a97e0a16..6cc577c820 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -1,4 +1,5 @@ -import xml.etree.ElementTree as ET +import lxml.etree as ET +from pathlib import Path import numpy as np import openmc @@ -19,7 +20,7 @@ def test_volume(run_in_tmpdir, uo2): model.settings.particles = 100 model.settings.batches = 10 model.settings.run_mode = 'fixed source' - model.settings.source = openmc.Source(space=openmc.stats.Point()) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) ll, ur = model.geometry.bounding_box assert ll == pytest.approx((-outer.r, -outer.r, -outer.r)) @@ -159,12 +160,13 @@ def test_get_by_name(): m2 = openmc.Material(name='Zirconium') m2.add_element('Zr', 1.0) - c1 = openmc.Cell(fill=m1, name='cell1') + s1 = openmc.Sphere(name='surface1') + c1 = openmc.Cell(fill=m1, region=-s1, name='cell1') u1 = openmc.Universe(name='Zircaloy universe', cells=[c1]) - cyl = openmc.ZCylinder() - c2 = openmc.Cell(fill=u1, region=-cyl, name='cell2') - c3 = openmc.Cell(fill=m2, region=+cyl, name='Cell3') + s2 = openmc.ZCylinder(name='surface2') + c2 = openmc.Cell(fill=u1, region=-s2, name='cell2') + c3 = openmc.Cell(fill=m2, region=+s2, name='Cell3') root = openmc.Universe(name='root Universe', cells=[c2, c3]) geom = openmc.Geometry(root) @@ -177,6 +179,13 @@ def test_get_by_name(): mats = geom.get_materials_by_name('zirconium', True, True) assert not mats + surfaces = set(geom.get_surfaces_by_name('surface')) + assert not surfaces ^ {s1, s2} + surfaces = set(geom.get_surfaces_by_name('Surface2', False, True)) + assert not surfaces ^ {s2} + surfaces = geom.get_surfaces_by_name('Surface2', True, True) + assert not surfaces + cells = set(geom.get_cells_by_name('cell')) assert not cells ^ {c1, c2, c3} cells = set(geom.get_cells_by_name('cell', True)) @@ -204,27 +213,27 @@ def test_get_by_name(): def test_hex_prism(): - hex_prism = openmc.model.hexagonal_prism(edge_length=5.0, - origin=(0.0, 0.0), - orientation='y') + hex_prism = openmc.model.HexagonalPrism(edge_length=5.0, + origin=(0.0, 0.0), + orientation='y') # clear checks - assert (0.0, 0.0, 0.0) in hex_prism - assert (10.0, 10.0, 10.0) not in hex_prism + assert (0.0, 0.0, 0.0) in -hex_prism + assert (10.0, 10.0, 10.0) not in -hex_prism # edge checks - assert (0.0, 5.01, 0.0) not in hex_prism - assert (0.0, 4.99, 0.0) in hex_prism + assert (0.0, 5.01, 0.0) not in -hex_prism + assert (0.0, 4.99, 0.0) in -hex_prism - rounded_hex_prism = openmc.model.hexagonal_prism(edge_length=5.0, - origin=(0.0, 0.0), - orientation='y', - corner_radius=1.0) + rounded_hex_prism = openmc.model.HexagonalPrism(edge_length=5.0, + origin=(0.0, 0.0), + orientation='y', + corner_radius=1.0) # clear checks - assert (0.0, 0.0, 0.0) in rounded_hex_prism - assert (10.0, 10.0, 10.0) not in rounded_hex_prism + assert (0.0, 0.0, 0.0) in -rounded_hex_prism + assert (10.0, 10.0, 10.0) not in -rounded_hex_prism # edge checks - assert (0.0, 5.01, 0.0) not in rounded_hex_prism - assert (0.0, 4.99, 0.0) not in rounded_hex_prism + assert (0.0, 5.01, 0.0) not in -rounded_hex_prism + assert (0.0, 4.99, 0.0) not in -rounded_hex_prism def test_get_lattice_by_name(cell_with_lattice): @@ -274,7 +283,22 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): # Export model mixed_lattice_model.export_to_xml() - # Import geometry + mats_from_xml = openmc.Materials.from_xml('materials.xml') + # checking string a Path are both acceptable + for path in ['geometry.xml', Path('geometry.xml')]: + for materials in [mats_from_xml, 'materials.xml']: + # Import geometry from file + geom = openmc.Geometry.from_xml(path=path, materials=materials) + assert isinstance(geom, openmc.Geometry) + ll, ur = geom.bounding_box + assert ll == pytest.approx((-6.0, -6.0, -np.inf)) + assert ur == pytest.approx((6.0, 6.0, np.inf)) + + with pytest.raises(TypeError) as excinfo: + geom = openmc.Geometry.from_xml(path='geometry.xml', materials=None) + assert 'Unable to set "materials" to "None"' in str(excinfo.value) + + # checking that the default args also work geom = openmc.Geometry.from_xml() assert isinstance(geom, openmc.Geometry) ll, ur = geom.bounding_box @@ -338,13 +362,44 @@ def test_remove_redundant_surfaces(): clad = get_cyl_cell(r1, r2, z1, z2, m2) water = get_cyl_cell(r2, r3, z1, z2, m3) root = openmc.Universe(cells=[fuel, clad, water]) - geom = openmc.Geometry(root) - + geom = openmc.Geometry(root=root, merge_surfaces=True, surface_precision=11) + assert geom.merge_surfaces is True + geom.merge_surfaces = False + assert geom.merge_surfaces is False + assert geom.surface_precision == 11 + geom.surface_precision = 10 + assert geom.surface_precision == 10 + model = openmc.model.Model(geometry=geom, + materials=openmc.Materials([m1, m2, m3])) + # There should be 6 redundant surfaces in this geometry - n_redundant_surfs = len(geom.get_redundant_surfaces().keys()) + n_redundant_surfs = len(geom.remove_redundant_surfaces().keys()) assert n_redundant_surfs == 6 - # Remove redundant surfaces - geom.remove_redundant_surfaces() # There should be 0 remaining redundant surfaces - n_redundant_surfs = len(geom.get_redundant_surfaces().keys()) + n_redundant_surfs = len(geom.remove_redundant_surfaces().keys()) assert n_redundant_surfs == 0 + +def test_get_all_nuclides(): + m1 = openmc.Material() + m1.add_nuclide('Fe56', 1) + m1.add_nuclide('Be9', 1) + m2 = openmc.Material() + m2.add_nuclide('Be9', 1) + s = openmc.Sphere() + c1 = openmc.Cell(fill=m1, region=-s) + c2 = openmc.Cell(fill=m2, region=+s) + geom = openmc.Geometry([c1, c2]) + assert geom.get_all_nuclides() == ['Be9', 'Fe56'] + + +def test_redundant_surfaces(): + # Make sure boundary condition is accounted for + s1 = openmc.Sphere(r=5.0) + s2 = openmc.Sphere(r=5.0, boundary_type="vacuum") + c1 = openmc.Cell(region=-s1) + c2 = openmc.Cell(region=+s1) + u_lower = openmc.Universe(cells=[c1, c2]) + c3 = openmc.Cell(fill=u_lower, region=-s2) + geom = openmc.Geometry([c3]) + redundant_surfs = geom.remove_redundant_surfaces() + assert len(redundant_surfs) == 0 diff --git a/tests/unit_tests/test_heating_by_nuclide.py b/tests/unit_tests/test_heating_by_nuclide.py new file mode 100644 index 0000000000..b34d7dd943 --- /dev/null +++ b/tests/unit_tests/test_heating_by_nuclide.py @@ -0,0 +1,57 @@ +import openmc +import pytest + +from tests.testing_harness import config + + +@pytest.fixture +def model(): + # Create simple sphere model + model = openmc.Model() + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.add_nuclide('H1', 1.0) + mat.set_density('g/cm3', 5.0) + sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sph) + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 1000 + model.settings.inactive = 0 + model.settings.batches = 5 + model.settings.photon_transport = True + + # Add two tallies, one with heating by nuclide and one with total heating + particle_filter = openmc.ParticleFilter(['neutron', 'photon']) + heating_by_nuclide = openmc.Tally() + heating_by_nuclide.filters = [particle_filter] + heating_by_nuclide.nuclides = ['U235', 'H1'] + heating_by_nuclide.scores = ['heating'] + + heating_total = openmc.Tally() + heating_total.filters = [particle_filter] + heating_total.scores = ['heating'] + model.tallies.extend([heating_by_nuclide, heating_total]) + + return model + + +def test_heating_by_nuclide(model, run_in_tmpdir): + # If running in MPI mode, setup proper keyword arguments for run() + kwargs = {'openmc_exec': config['exe']} + if config['mpi']: + kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + sp_path = model.run(**kwargs) + + # Get tallies from resulting statepoint + with openmc.StatePoint(sp_path) as sp: + heating_by_nuclide = sp.tallies[model.tallies[0].id] + heating_total = sp.tallies[model.tallies[1].id] + + for particle in heating_by_nuclide.filters[0].bins: + # Get slice of each tally corresponding to a single particle + kwargs = {'filters': [openmc.ParticleFilter], 'filter_bins': [(particle,)]} + particle_slice_by_nuclide = heating_by_nuclide.get_values(**kwargs) + particle_slice_total = heating_total.get_values(**kwargs) + + # Summing over nuclides should equal total + assert particle_slice_by_nuclide.sum() == pytest.approx(particle_slice_total.sum()) diff --git a/tests/unit_tests/test_ifp.py b/tests/unit_tests/test_ifp.py new file mode 100644 index 0000000000..e527f16246 --- /dev/null +++ b/tests/unit_tests/test_ifp.py @@ -0,0 +1,88 @@ +"""Test the Iterated Fission Probability (IFP) method to compute adjoint-weighted +kinetics parameters using dedicated tallies.""" + +import pytest +import openmc + + +def test_xml_serialization(run_in_tmpdir): + """Check that a simple use case can be written and read in XML.""" + parameter = 5 + settings = openmc.Settings() + settings.ifp_n_generation = parameter + settings.export_to_xml() + + read_settings = openmc.Settings.from_xml() + assert read_settings.ifp_n_generation == parameter + + +@pytest.fixture(scope="module") +def geometry(): + openmc.reset_auto_ids() + material = openmc.Material() + material.add_nuclide("U235", 1.0) + sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + return openmc.Geometry([cell]) + + +@pytest.mark.parametrize( + "options, error", + [ + ({"ifp_n_generation": 0}, ValueError), + ({"ifp_n_generation": -1}, ValueError), + ({"run_mode": "fixed source"}, RuntimeError), + ({"inactive": 5, "ifp_n_generation": 6}, RuntimeError), + ({"inactive": 9}, RuntimeError) + ], +) +def test_exceptions(options, error, run_in_tmpdir, geometry): + """Test settings configuration that should return an error.""" + with pytest.raises(error): + settings = openmc.Settings(**options) + settings.particles = 100 + settings.batches = 15 + tally = openmc.Tally(name="ifp-scores") + tally.scores = ["ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"] + tallies = openmc.Tallies([tally]) + model = openmc.Model(geometry=geometry, settings=settings, tallies=tallies) + model.run() + + +@pytest.mark.parametrize( + "num_groups, use_auto_tallies", + [ + (None, True), + (None, False), + (6, True), + (6, False), + ], +) +def test_get_kinetics_parameters(run_in_tmpdir, geometry, num_groups, use_auto_tallies): + # Create basic model + model = openmc.Model(geometry=geometry) + model.settings.particles = 1000 + model.settings.batches = 20 + model.settings.inactive = 5 + model.settings.ifp_n_generation = 5 + + # Add IFP tallies either via the convenience method or manually + if use_auto_tallies: + model.add_kinetics_parameters_tallies(num_groups=num_groups) + else: + for score in ["ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"]: + tally = openmc.Tally() + tally.scores = [score] + if score == "ifp-beta-numerator" and num_groups is not None: + tally.filters = [openmc.DelayedGroupFilter(list(range(1, num_groups + 1)))] + model.tallies.append(tally) + + # Run and get kinetics parameters + sp_file = model.run() + with openmc.StatePoint(sp_file) as sp: + params = sp.get_kinetics_parameters() + assert isinstance(params, openmc.KineticsParameters) + assert params.generation_time is not None + assert params.beta_effective is not None + if num_groups is not None: + assert len(params.beta_effective) == num_groups diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py index 31433af7dc..d72d9c5c3e 100644 --- a/tests/unit_tests/test_lattice.py +++ b/tests/unit_tests/test_lattice.py @@ -1,6 +1,6 @@ from math import sqrt -import xml.etree.ElementTree as ET +import lxml.etree as ET import openmc import pytest @@ -157,11 +157,11 @@ def hlat3(pincell1, pincell2, uo2, water, zr): def test_get_nuclides(rlat2, rlat3, hlat2, hlat3): for lat in (rlat2, hlat2): - nucs = rlat2.get_nuclides() + nucs = lat.get_nuclides() assert sorted(nucs) == ['H1', 'O16', 'U235', 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] for lat in (rlat3, hlat3): - nucs = rlat3.get_nuclides() + nucs = lat.get_nuclides() assert sorted(nucs) == ['H1', 'H2', 'O16', 'U235', 'Zr90', 'Zr91', 'Zr92', 'Zr94', 'Zr96'] @@ -360,4 +360,63 @@ def test_show_indices(): lines = openmc.HexLattice.show_indices(i).split('\n') assert len(lines) == 4*i - 3 lines_x = openmc.HexLattice.show_indices(i, 'x').split('\n') - assert len(lines) == 4*i - 3 + assert len(lines_x) == 4*i - 3 + + +def test_unset_universes(): + elem = ET.Element("dummy") + + lattice = openmc.RectLattice() + lattice.lower_left = (-1., -1.) + lattice.pitch = (1., 1.) + with pytest.raises(ValueError): + lattice.create_xml_subelement(elem) + + hex_lattice = openmc.HexLattice() + hex_lattice.center = (0., 0.) + hex_lattice.pitch = (1.,) + with pytest.raises(ValueError): + hex_lattice.create_xml_subelement(elem) + + +@pytest.mark.parametrize("orientation", ['x', 'y']) +def test_hex_lattice_roundtrip(orientation): + openmc.reset_auto_ids() + + # ensure that the lattice universes are the same on all axial levels + def check_lattice_universes(og_lattice, xml_lattice): + for axial_og, axial_rt in zip(og_lattice.universes, xml_lattice.universes): + for ring_og, ring_rt in zip(axial_og, axial_rt): + assert [u.id for u in ring_og] == [u.id for u in ring_rt] + + latt = openmc.HexLattice() + latt.pitch = (1.0, 1.0) + latt.center = (0.0, 0.0, 0.0) + latt.orientation = orientation + + # fill the lattice with universes in increasing order and repeat for + # the second actial level + lvl_one_univs = [openmc.Universe(cells=[openmc.Cell()]) for _ in range(19)] + lvl_one_univs = [lvl_one_univs[-12:], lvl_one_univs[1:7], lvl_one_univs[:1]] + latt.universes = [lvl_one_univs, lvl_one_univs] + + geom = openmc.Geometry([openmc.Cell(fill=latt)]) + geom.export_to_xml() + + xml_geom = openmc.Geometry.from_xml(materials=openmc.Materials()) + + xml_latt = xml_geom.get_all_lattices()[latt.id] + + check_lattice_universes(latt, xml_latt) + + # same test but with unique universes for each axial level + lvl_two_univs = [openmc.Universe(cells=[openmc.Cell()]) for _ in range(19)] + lvl_two_univs = [lvl_two_univs[-12:], lvl_two_univs[1:7], lvl_two_univs[:1]] + latt.universes = [lvl_one_univs, lvl_two_univs] + + geom.export_to_xml() + + xml_geom = openmc.Geometry.from_xml(materials=openmc.Materials()) + xml_latt = xml_geom.get_all_lattices()[latt.id] + + check_lattice_universes(latt, xml_latt) diff --git a/tests/unit_tests/test_lattice_discretization.py b/tests/unit_tests/test_lattice_discretization.py index b21d7e13e0..c7779ea8bf 100644 --- a/tests/unit_tests/test_lattice_discretization.py +++ b/tests/unit_tests/test_lattice_discretization.py @@ -34,8 +34,8 @@ def test_discretization_clone_only_some_materials(rlat2): fuel1 = next(iter(rlat_clone.get_universe((0, 0)).cells.values())).fill rlat_clone.discretize(materials_to_clone=[fuel1]) - assert next(reversed(rlat_clone.get_universe((0, 0)).cells.values())).fill\ - == next(reversed(rlat_clone.get_universe((1, 0)).cells.values())).fill + assert next(reversed(list(rlat_clone.get_universe((0, 0)).cells.values()))).fill\ + == next(reversed(list(rlat_clone.get_universe((1, 0)).cells.values()))).fill assert next(iter(rlat_clone.get_universe((0, 0)).cells.values())).fill\ != next(iter(rlat_clone.get_universe((1, 0)).cells.values())).fill diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index a0f18f8513..43bc5a8f6f 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -1,4 +1,5 @@ from collections.abc import Mapping +from math import pi import os import numpy as np @@ -66,8 +67,10 @@ def uo2_trigger_model(): model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 100 - model.settings.source = openmc.Source(space=openmc.stats.Box( - [-0.5, -0.5, -1], [0.5, 0.5, 1], only_fissionable=True)) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-0.5, -0.5, -1], [0.5, 0.5, 1]), + constraints={'fissionable': True}, + ) model.settings.verbosity = 1 model.settings.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} model.settings.trigger_active = True @@ -126,7 +129,7 @@ def test_cell(lib_init): cell = openmc.lib.cells[1] assert isinstance(cell.fill, openmc.lib.Material) cell.fill = openmc.lib.materials[1] - assert str(cell) == 'Cell[0]' + assert str(cell) == '' assert cell.name == "Fuel" cell.name = "Not fuel" assert cell.name == "Not fuel" @@ -156,6 +159,34 @@ def test_properties_temperature(lib_init): assert cell.get_temperature() == pytest.approx(200.0) +def test_cell_density(lib_init): + cell = openmc.lib.cells[1] + print('density', cell.get_density()) + orig_density = cell.get_density() + try: + cell.set_density(1.5, 0) + assert cell.get_density(0) == pytest.approx(1.5) + cell.set_density(2.0) + assert cell.get_density() == pytest.approx(2.0) + finally: + cell.set_density(orig_density) + + +def test_properties_cell_density(lib_init): + # Cell density should be 2.0 from above test + cell = openmc.lib.cells[1] + orig_density = cell.get_density() + + # Export properties and change density + openmc.lib.export_properties('properties.h5') + cell.set_density(3.0) + assert cell.get_density() == pytest.approx(3.0) + + # Import properties and check that density is restored + openmc.lib.import_properties('properties.h5') + assert cell.get_density() == pytest.approx(orig_density) + + def test_new_cell(lib_init): with pytest.raises(exc.AllocationError): openmc.lib.Cell(1) @@ -206,6 +237,10 @@ def test_material(lib_init): m.name = "Not hot borated water" assert m.name == "Not hot borated water" + assert m.depletable == False + m.depletable = True + assert m.depletable == True + def test_properties_density(lib_init): m = openmc.lib.materials[1] @@ -283,6 +318,11 @@ def test_energy_function_filter(lib_init): assert len(efunc.y) == 2 assert (efunc.y == [0.0, 2.0]).all() + # Default should be lin-lin + assert efunc.interpolation == 'linear-linear' + efunc.interpolation = 'histogram' + assert efunc.interpolation == 'histogram' + def test_tally(lib_init): t = openmc.lib.tallies[1] @@ -340,6 +380,20 @@ def test_new_tally(lib_init): assert len(openmc.lib.tallies) == 5 +def test_delete_tally(lib_init): + # delete tally 10 which was added in the above test + # check length is one less than before + del openmc.lib.tallies[10] + assert len(openmc.lib.tallies) == 4 + + +def test_invalid_tally_id(lib_init): + # attempt to access a tally that is guaranteed not to have a valid index + max_id = max(openmc.lib.tallies.keys()) + with pytest.raises(KeyError): + openmc.lib.tallies[max_id+1] + + def test_tally_activate(lib_simulation_init): t = openmc.lib.tallies[1] assert not t.active @@ -347,6 +401,19 @@ def test_tally_activate(lib_simulation_init): assert t.active +def test_tally_multiply_density(lib_simulation_init): + # multiply_density is True by default + t = openmc.lib.tallies[1] + assert t.multiply_density + + # Make sure setting multiply_density works + t.multiply_density = False + assert not t.multiply_density + + # Reset to True + t.multiply_density = True + + def test_tally_writable(lib_simulation_init): t = openmc.lib.tallies[1] assert t.writable @@ -529,6 +596,14 @@ def test_regular_mesh(lib_init): assert mesh.upper_right == pytest.approx(ur) assert mesh.width == pytest.approx(width) + np.testing.assert_allclose(mesh.volumes, 1.0) + + # bounding box + mesh.set_parameters(lower_left=ll, upper_right=ur) + bbox = mesh.bounding_box + np.testing.assert_allclose(bbox.lower_left, ll) + np.testing.assert_allclose(bbox.upper_right, ur) + meshes = openmc.lib.meshes assert isinstance(meshes, Mapping) assert len(meshes) == 1 @@ -548,6 +623,50 @@ def test_regular_mesh(lib_init): msf.translation = translation assert msf.translation == translation + # Test material volumes + mesh = openmc.lib.RegularMesh() + mesh.dimension = (2, 2, 1) + mesh.set_parameters(lower_left=(-0.63, -0.63, -0.5), + upper_right=(0.63, 0.63, 0.5)) + vols = mesh.material_volumes() + assert vols.num_elements == 4 + for i in range(vols.num_elements): + elem_vols = vols.by_element(i) + assert sum(f[1] for f in elem_vols) == pytest.approx(1.26 * 1.26 / 4) + + # If the mesh extends beyond the boundaries of the model, we should get a + # GeometryError + mesh.dimension = (1, 1, 1) + mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), + upper_right=(1.0, 1.0, 0.5)) + with pytest.raises(exc.GeometryError, match="not fully contained"): + vols = mesh.material_volumes() + + +def test_regular_mesh_get_plot_bins(lib_init): + mesh: openmc.lib.RegularMesh = openmc.lib.meshes[2] + mesh.dimension = (2, 2, 1) + mesh.set_parameters(lower_left=(-1.0, -1.0, -0.5), + upper_right=(1.0, 1.0, 0.5)) + + # Get bins for a plot view covering only a single mesh bin + mesh_bins = mesh.get_plot_bins((-0.5, -0.5, 0.), (0.1, 0.1), 'xy', (20, 20)) + assert (mesh_bins == 0).all() + mesh_bins = mesh.get_plot_bins((0.5, 0.5, 0.), (0.1, 0.1), 'xy', (20, 20)) + assert (mesh_bins == 3).all() + + # Get bins for a plot view covering all mesh bins. Note that the y direction + # (first dimension) is flipped for plotting purposes + mesh_bins = mesh.get_plot_bins((0., 0., 0.), (2., 2.), 'xy', (20, 20)) + assert (mesh_bins[:10, :10] == 2).all() + assert (mesh_bins[:10, 10:] == 3).all() + assert (mesh_bins[10:, :10] == 0).all() + assert (mesh_bins[10:, 10:] == 1).all() + + # Get bins for a plot view outside of the mesh + mesh_bins = mesh.get_plot_bins((100., 100., 0.), (2., 2.), 'xy', (20, 20)) + assert (mesh_bins == -1).all() + def test_rectilinear_mesh(lib_init): mesh = openmc.lib.RectilinearMesh() @@ -563,12 +682,19 @@ def test_rectilinear_mesh(lib_init): for k, diff_z in enumerate(np.diff(z_grid)): assert np.all(mesh.width[i, j, k, :] == (10, 10, 10)) + np.testing.assert_allclose(mesh.volumes, 1000.0) + + # bounding box + bbox = mesh.bounding_box + np.testing.assert_allclose(bbox.lower_left, (-10., 0., 10.)) + np.testing.assert_allclose(bbox.upper_right, (10., 20., 30.)) + with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.RectilinearMesh(mesh.id) meshes = openmc.lib.meshes assert isinstance(meshes, Mapping) - assert len(meshes) == 2 + assert len(meshes) == 3 mesh = meshes[mesh.id] assert isinstance(mesh, openmc.lib.RectilinearMesh) @@ -579,8 +705,21 @@ def test_rectilinear_mesh(lib_init): msf = openmc.lib.MeshSurfaceFilter(mesh) assert msf.mesh == mesh + # Test material volumes + mesh = openmc.lib.RectilinearMesh() + w = 1.26 + mesh.set_grid([-w/2, -w/4, w/2], [-w/2, -w/4, w/2], [-0.5, 0.5]) + + vols = mesh.material_volumes() + assert vols.num_elements == 4 + assert sum(f[1] for f in vols.by_element(0)) == pytest.approx(w/4 * w/4) + assert sum(f[1] for f in vols.by_element(1)) == pytest.approx(w/4 * 3*w/4) + assert sum(f[1] for f in vols.by_element(2)) == pytest.approx(3*w/4 * w/4) + assert sum(f[1] for f in vols.by_element(3)) == pytest.approx(3*w/4 * 3*w/4) + + def test_cylindrical_mesh(lib_init): - deg2rad = lambda deg: deg*np.pi/180 + deg2rad = lambda deg: deg*pi/180 mesh = openmc.lib.CylindricalMesh() r_grid = [0., 5., 10.] phi_grid = np.radians([0., 10., 20.]) @@ -594,12 +733,20 @@ def test_cylindrical_mesh(lib_init): for k, _ in enumerate(np.diff(z_grid)): assert np.allclose(mesh.width[i, j, k, :], (5, deg2rad(10), 10)) + np.testing.assert_allclose(mesh.volumes[::2], 10/360 * pi * 5**2 * 10) + np.testing.assert_allclose(mesh.volumes[1::2], 10/360 * pi * (10**2 - 5**2) * 10) + + # bounding box + bbox = mesh.bounding_box + np.testing.assert_allclose(bbox.lower_left, (-10., -10., 10.)) + np.testing.assert_allclose(bbox.upper_right, (10., 10., 30.)) + with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.CylindricalMesh(mesh.id) meshes = openmc.lib.meshes assert isinstance(meshes, Mapping) - assert len(meshes) == 3 + assert len(meshes) == 5 mesh = meshes[mesh.id] assert isinstance(mesh, openmc.lib.CylindricalMesh) @@ -610,6 +757,21 @@ def test_cylindrical_mesh(lib_init): msf = openmc.lib.MeshSurfaceFilter(mesh) assert msf.mesh == mesh + # Test material volumes + mesh = openmc.lib.CylindricalMesh() + r_grid = (0., 0.25, 0.5) + phi_grid = np.linspace(0., 2.0*pi, 4) + z_grid = (-0.5, 0.5) + mesh.set_grid(r_grid, phi_grid, z_grid) + + vols = mesh.material_volumes() + assert vols.num_elements == 6 + for i in range(0, 6, 2): + assert sum(f[1] for f in vols.by_element(i)) == pytest.approx(pi * 0.25**2 / 3) + for i in range(1, 6, 2): + assert sum(f[1] for f in vols.by_element(i)) == pytest.approx(pi * (0.5**2 - 0.25**2) / 3) + + def test_spherical_mesh(lib_init): deg2rad = lambda deg: deg*np.pi/180 mesh = openmc.lib.SphericalMesh() @@ -625,12 +787,24 @@ def test_spherical_mesh(lib_init): for k, _ in enumerate(np.diff(phi_grid)): assert np.allclose(mesh.width[i, j, k, :], (5, deg2rad(10), deg2rad(10))) + dtheta = lambda d1, d2: np.cos(deg2rad(d1)) - np.cos(deg2rad(d2)) + f = 1/3 * deg2rad(10.) + np.testing.assert_allclose(mesh.volumes[::4], f * 5**3 * dtheta(0., 10.)) + np.testing.assert_allclose(mesh.volumes[1::4], f * (10**3 - 5**3) * dtheta(0., 10.)) + np.testing.assert_allclose(mesh.volumes[2::4], f * 5**3 * dtheta(10., 20.)) + np.testing.assert_allclose(mesh.volumes[3::4], f * (10**3 - 5**3) * dtheta(10., 20.)) + + # bounding box + bbox = mesh.bounding_box + np.testing.assert_allclose(bbox.lower_left, (-10., -10., -10.)) + np.testing.assert_allclose(bbox.upper_right, (10., 10., 10.)) + with pytest.raises(exc.AllocationError): mesh2 = openmc.lib.SphericalMesh(mesh.id) meshes = openmc.lib.meshes assert isinstance(meshes, Mapping) - assert len(meshes) == 4 + assert len(meshes) == 7 mesh = meshes[mesh.id] assert isinstance(mesh, openmc.lib.SphericalMesh) @@ -641,6 +815,24 @@ def test_spherical_mesh(lib_init): msf = openmc.lib.MeshSurfaceFilter(mesh) assert msf.mesh == mesh + # Test material volumes + mesh = openmc.lib.SphericalMesh() + r_grid = (0., 0.25, 0.5) + theta_grid = np.linspace(0., pi, 3) + phi_grid = np.linspace(0., 2.0*pi, 4) + mesh.set_grid(r_grid, theta_grid, phi_grid) + + vols = mesh.material_volumes() + assert vols.num_elements == 12 + d_theta = theta_grid[1] - theta_grid[0] + d_phi = phi_grid[1] - phi_grid[0] + for i in range(0, 12, 2): + assert sum(f[1] for f in vols.by_element(i)) == pytest.approx( + 0.25**3 / 3 * d_theta * d_phi * 2/pi) + for i in range(1, 12, 2): + assert sum(f[1] for f in vols.by_element(i)) == pytest.approx( + (0.5**3 - 0.25**3) / 3 * d_theta * d_phi * 2/pi) + def test_restart(lib_init, mpi_intracomm): # Finalize and re-init to make internal state consistent with XML. @@ -807,3 +999,52 @@ def test_cell_rotation(pincell_model_w_univ, mpi_intracomm): cell.rotation = (180., 0., 0.) assert cell.rotation == pytest.approx([180., 0., 0.]) openmc.lib.finalize() + + +def test_sample_external_source(run_in_tmpdir, mpi_intracomm): + # Define a simple model and export + mat = openmc.Material() + mat.add_nuclide('U235', 1.0e-2) + sph = openmc.Sphere(r=100.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box([-5., -5., -5.], [5., 5., 5.]), + angle=openmc.stats.Monodirectional((0., 0., 1.)), + energy=openmc.stats.Discrete([1.0e5], [1.0]), + constraints={'fissionable': True} + ) + model.settings.particles = 1000 + model.settings.batches = 10 + model.export_to_xml() + + # Sample some particles and make sure they match specified source + openmc.lib.init() + particles = openmc.lib.sample_external_source(10, prn_seed=3) + assert len(particles) == 10 + for p in particles: + assert -5. < p.r[0] < 5. + assert -5. < p.r[1] < 5. + assert -5. < p.r[2] < 5. + assert p.u[0] == 0.0 + assert p.u[1] == 0.0 + assert p.u[2] == 1.0 + assert p.E == 1.0e5 + + # Using the same seed should produce the same particles + other_particles = openmc.lib.sample_external_source(10, prn_seed=3) + assert len(other_particles) == 10 + for p1, p2 in zip(particles, other_particles): + assert p1.r == p2.r + assert p1.u == p2.u + assert p1.E == p2.E + assert p1.time == p2.time + assert p1.wgt == p2.wgt + + openmc.lib.finalize() + + # Make sure sampling works in volume calculation mode + openmc.lib.init(["-c"]) + openmc.lib.sample_external_source(100) + openmc.lib.finalize() diff --git a/tests/unit_tests/test_lost_particles.py b/tests/unit_tests/test_lost_particles.py new file mode 100644 index 0000000000..478ddbaf0f --- /dev/null +++ b/tests/unit_tests/test_lost_particles.py @@ -0,0 +1,49 @@ +from pathlib import Path + +import openmc +import pytest + +from tests.testing_harness import config + + +@pytest.fixture +def model(): + mat = openmc.Material() + mat.add_nuclide('N14', 1.0) + mat.set_density('g/cm3', 1e-5) + + s1 = openmc.Sphere(r=80.0) + s2 = openmc.Sphere(r=90.0) + s3 = openmc.Sphere(r=100.0, boundary_type='vacuum') + cell1 = openmc.Cell(fill=mat, region=-s1) + cell2 = openmc.Cell(fill=mat, region=+s2 & -s3) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2]) + + model.settings.run_mode = 'fixed source' + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 50 + model.settings.max_lost_particles = 1000 + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point()) + + return model + + +def test_max_write_lost_particles(model: openmc.Model, run_in_tmpdir): + # Set maximum number of lost particle restart files + model.settings.max_write_lost_particles = 5 + + # Run OpenMC to generate lost particle files. Use one thread so that we know + # exactly how much will be produced. If running in MPI mode, setup proper + # keyword arguments for run() + kwargs = {'openmc_exec': config['exe']} + if config['mpi']: + kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + model.run(threads=1, **kwargs) + + # Make sure number of lost particle files is as expected + lost_particle_files = list(Path.cwd().glob('particle*.h5')) + n_procs = int(config['mpi_np']) if config['mpi'] else 1 + assert len(lost_particle_files) == model.settings.max_write_lost_particles * n_procs + diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index 02e53a28db..2e37242720 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -1,8 +1,13 @@ from collections import defaultdict +from pathlib import Path import pytest +import numpy as np + import openmc +from openmc.data import decay_photon_energy +from openmc.deplete import Chain import openmc.examples import openmc.model import openmc.stats @@ -25,6 +30,65 @@ def test_add_nuclide(): with pytest.raises(ValueError): m.add_nuclide('H1', 1.0, 'oa') +def test_add_components(): + """Test adding multipe elements or nuclides at once""" + m = openmc.Material() + components = {'H1': 2.0, + 'O16': 1.0, + 'Zr': 1.0, + 'O': 1.0, + 'Ag110_m1': 1.0, + 'U': {'percent': 1.0, + 'enrichment': 4.5}, + 'Li': {'percent': 1.0, + 'enrichment': 60.0, + 'enrichment_target': 'Li7'}, + 'H': {'percent': 1.0, + 'enrichment': 50.0, + 'enrichment_target': 'H2', + 'enrichment_type': 'wo'}} + m.add_components(components) + with pytest.raises(ValueError): + m.add_components({'U': {'percent': 1.0, + 'enrichment': 100.0}}) + with pytest.raises(ValueError): + m.add_components({'Pu': {'percent': 1.0, + 'enrichment': 3.0}}) + with pytest.raises(ValueError): + m.add_components({'U': {'percent': 1.0, + 'enrichment': 70.0, + 'enrichment_target':'U235'}}) + with pytest.raises(ValueError): + m.add_components({'He': {'percent': 1.0, + 'enrichment': 17.0, + 'enrichment_target': 'He6'}}) + with pytest.raises(ValueError): + m.add_components({'li': 1.0}) # should fail as 1st char is lowercase + with pytest.raises(ValueError): + m.add_components({'LI': 1.0}) # should fail as 2nd char is uppercase + with pytest.raises(ValueError): + m.add_components({'Xx': 1.0}) # should fail as Xx is not an element + with pytest.raises(ValueError): + m.add_components({'n': 1.0}) # check to avoid n for neutron being accepted + with pytest.raises(TypeError): + m.add_components({'H1': '1.0'}) + with pytest.raises(TypeError): + m.add_components({1.0: 'H1'}, percent_type = 'wo') + with pytest.raises(ValueError): + m.add_components({'H1': 1.0}, percent_type = 'oa') + +def test_nuclides_to_ignore(run_in_tmpdir): + """Test nuclides_to_ignore when exporting a material to XML""" + m = openmc.Material() + m.add_nuclide('U235', 1.0) + m.add_nuclide('H1', 1.0) + m.add_nuclide('O16', 1.0) + + mats = openmc.Materials([m]) + mats.export_to_xml(nuclides_to_ignore=['H1']) + + test_mats = openmc.Materials.from_xml() + assert 'H1' not in test_mats[0].get_nuclides() def test_remove_nuclide(): """Test removing nuclides.""" @@ -38,7 +102,18 @@ def test_remove_nuclide(): assert m.nuclides[1].percent == 2.0 -def test_elements(): +def test_remove_elements(): + """Test removing elements.""" + m = openmc.Material() + for elem, percent in [('Li', 1.0), ('Be', 1.0)]: + m.add_element(elem, percent) + m.remove_element('Li') + assert len(m.nuclides) == 1 + assert m.nuclides[0].name == 'Be9' + assert m.nuclides[0].percent == 1.0 + + +def test_add_element(): """Test adding elements.""" m = openmc.Material() m.add_element('Zr', 1.0) @@ -63,7 +138,6 @@ def test_elements(): with pytest.raises(ValueError): m.add_element('n', 1.0) # check to avoid n for neutron being accepted - def test_elements_by_name(): """Test adding elements by name""" m = openmc.Material() @@ -89,7 +163,7 @@ def test_add_elements_by_formula(): m.add_elements_from_formula('Li4SiO4') # checking the ratio of elements is 4:1:4 for Li:Si:O elem = defaultdict(float) - for nuclide, adens in m.get_nuclide_atom_densities().values(): + for nuclide, adens in m.get_nuclide_atom_densities().items(): if nuclide.startswith("Li"): elem["Li"] += adens if nuclide.startswith("Si"): @@ -106,7 +180,7 @@ def test_add_elements_by_formula(): 'O16': 0.443386, 'O17': 0.000168} nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: - assert nuc_dens[nuclide][1] == pytest.approx(ref_dens[nuclide], 1e-2) + assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) # testing the correct nuclides are added to the Material when enriched m = openmc.Material() @@ -118,7 +192,7 @@ def test_add_elements_by_formula(): 'O16': 0.443386, 'O17': 0.000168} nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: - assert nuc_dens[nuclide][1] == pytest.approx(ref_dens[nuclide], 1e-2) + assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) # testing the use of brackets m = openmc.Material() @@ -126,7 +200,7 @@ def test_add_elements_by_formula(): # checking the ratio of elements is 2:2:6 for Mg:N:O elem = defaultdict(float) - for nuclide, adens in m.get_nuclide_atom_densities().values(): + for nuclide, adens in m.get_nuclide_atom_densities().items(): if nuclide.startswith("Mg"): elem["Mg"] += adens if nuclide.startswith("N"): @@ -144,7 +218,7 @@ def test_add_elements_by_formula(): 'O16': 0.599772, 'O17': 0.000227} nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: - assert nuc_dens[nuclide][1] == pytest.approx(ref_dens[nuclide], 1e-2) + assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) # testing non integer multiplier results in a value error m = openmc.Material() @@ -243,6 +317,23 @@ def test_isotropic(): assert m2.isotropic == ['H1'] +def test_get_nuclides(): + mat = openmc.Material() + + mat.add_nuclide('Li6', 1.0) + assert mat.get_nuclides() == ['Li6'] + assert mat.get_nuclides(element='Li') == ['Li6'] + assert mat.get_nuclides(element='Be') == [] + + mat.add_element('Li', 1.0) + assert mat.get_nuclides() == ['Li6', 'Li7'] + assert mat.get_nuclides(element='Be') == [] + + mat.add_element('Be', 1.0) + assert mat.get_nuclides() == ['Li6', 'Li7', 'Be9'] + assert mat.get_nuclides(element='Be') == ['Be9'] + + def test_get_elements(): # test that zero elements exist on creation m = openmc.Material() @@ -278,12 +369,55 @@ def test_get_nuclide_densities(uo2): def test_get_nuclide_atom_densities(uo2): - nucs = uo2.get_nuclide_atom_densities() - for nuc, density in nucs.values(): + for nuc, density in uo2.get_nuclide_atom_densities().items(): assert nuc in ('U235', 'O16') assert density > 0 +def test_get_nuclide_atom_densities_specific(uo2): + one_nuc = uo2.get_nuclide_atom_densities(nuclide='O16') + assert list(one_nuc.keys()) == ['O16'] + assert list(one_nuc.values())[0] > 0 + + all_nuc = uo2.get_nuclide_atom_densities() + assert all_nuc['O16'] == one_nuc['O16'] + + +def test_get_element_atom_densities(uo2): + for element, density in uo2.get_element_atom_densities().items(): + assert element in ('U', 'O') + assert density > 0 + + +def test_get_element_atom_densities_specific(uo2): + one_nuc = uo2.get_element_atom_densities('O') + assert list(one_nuc.keys()) == ['O'] + assert list(one_nuc.values())[0] > 0 + + one_nuc = uo2.get_element_atom_densities('uranium') + assert list(one_nuc.keys()) == ['U'] + assert list(one_nuc.values())[0] > 0 + + with pytest.raises(ValueError, match='not found'): + uo2.get_element_atom_densities('Li') + + with pytest.raises(ValueError, match='not recognized'): + uo2.get_element_atom_densities('proximium') + + +def test_get_nuclide_atoms(): + mat = openmc.Material() + mat.add_nuclide('Li6', 1.0) + mat.set_density('atom/cm3', 3.26e20) + mat.volume = 100.0 + + atoms = mat.get_nuclide_atoms() + assert atoms['Li6'] == pytest.approx(mat.density * mat.volume) + + atoms = mat.get_nuclide_atoms(volume=10.0) + assert atoms['Li6'] == pytest.approx(mat.density * 10.0) + + def test_mass(): m = openmc.Material() m.add_nuclide('Zr90', 1.0, 'wo') @@ -300,6 +434,9 @@ def test_mass(): assert m.get_mass() == pytest.approx(20.0) assert m.fissionable_mass == pytest.approx(10.0) + # Test with volume specified as argument + assert m.get_mass('Zr90', volume=1.0) == pytest.approx(1.0) + def test_materials(run_in_tmpdir): m1 = openmc.Material() @@ -330,7 +467,7 @@ def test_borated_water(): 'O16':2.4672e-02} nuc_dens = m.get_nuclide_atom_densities() for nuclide in ref_dens: - assert nuc_dens[nuclide][1] == pytest.approx(ref_dens[nuclide], 1e-2) + assert nuc_dens[nuclide] == pytest.approx(ref_dens[nuclide], 1e-2) assert m.id == 50 # Test the Celsius conversion. @@ -399,8 +536,234 @@ def test_mix_materials(): dens4 = 1. / (f0 / m1dens + f1 / m2dens) dens5 = f0*m1dens + f1*m2dens m3 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='ao') - m4 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='wo') - m5 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='vo') + m4 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='wo', material_id=999) + m5 = openmc.Material.mix_materials([m1, m2], [f0, f1], percent_type='vo', name='m5') assert m3.density == pytest.approx(dens3) assert m4.density == pytest.approx(dens4) assert m5.density == pytest.approx(dens5) + assert m4.id == 999 + assert m5.name == 'm5' + + +def test_get_activity(): + """Tests the activity of stable, metastable and active materials""" + + # Creates a material with stable isotopes to check the activity is 0 + m1 = openmc.Material() + m1.add_element("Fe", 0.7) + m1.add_element("Li", 0.3) + m1.set_density('g/cm3', 1.5) + # activity in Bq/cc and Bq/g should not require volume setting + assert m1.get_activity(units='Bq/cm3') == 0 + assert m1.get_activity(units='Bq/g') == 0 + m1.volume = 1 + assert m1.get_activity(units='Bq') == 0 + + # Checks that 1g of tritium has the correct activity scaling + m2 = openmc.Material() + m2.add_nuclide("H3", 1) + m2.set_density('g/cm3', 1) + m2.volume = 1 + assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14 + m2.set_density('g/cm3', 2) + assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14*2 + m2.volume = 3 + assert pytest.approx(m2.get_activity(units='Bq')) == 3.559778e14*2*3 + + # Checks that 1 mol of a metastable nuclides has the correct activity + m3 = openmc.Material() + m3.add_nuclide("Tc99_m1", 1) + m3.set_density('g/cm3', 1) + m3.volume = 98.9 + assert pytest.approx(m3.get_activity(units='Bq'), rel=0.001) == 1.93e19 + + # Checks that specific and volumetric activity of tritium are correct + m4 = openmc.Material() + m4.add_nuclide("H3", 1) + m4.set_density('g/cm3', 1.5) + assert pytest.approx(m4.get_activity(units='Bq/g')) == 355978108155965.94 # [Bq/g] + assert pytest.approx(m4.get_activity(units='Bq/kg')) == 355978108155965940 # [Bq/kg] + assert pytest.approx(m4.get_activity(units='Bq/g', by_nuclide=True)["H3"]) == 355978108155965.94 # [Bq/g] + assert pytest.approx(m4.get_activity(units='Bq/cm3')) == 355978108155965.94*3/2 # [Bq/cc] + assert pytest.approx(m4.get_activity(units='Bq/cm3', by_nuclide=True)["H3"]) == 355978108155965.94*3/2 # [Bq/cc] + # volume is required to calculate total activity + m4.volume = 10. + assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq] + + # Test with volume specified as argument + assert pytest.approx(m4.get_activity(units='Bq', volume=1.0)) == 355978108155965.94*3/2 + + # Test units based on Ci + bq = m4.get_activity(units='Bq') + m3 = m4.volume * 1e-6 + assert (ci := m4.get_activity(units='Ci')) == pytest.approx(bq/3.7e10) + assert m4.get_activity(units='Ci/m3') == pytest.approx(ci/m3) + + +def test_get_decay_heat(): + # Set chain file for testing + openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + + """Tests the decay heat of stable, metastable and active materials""" + m1 = openmc.Material() + m1.add_nuclide("U235", 0.2) + m1.add_nuclide("U238", 0.8) + m1.set_density('g/cm3', 10.5) + # decay heat in W/cc and W/g should not require volume setting + assert m1.get_decay_heat(units='W/cm3') == 0 + assert m1.get_decay_heat(units='W/g') == 0 + m1.volume = 1 + assert m1.get_decay_heat(units='W') == 0 + + # Checks that 1g of tritium has the correct decay heat scaling + m2 = openmc.Material() + m2.add_nuclide("I135", 1) + m2.set_density('g/cm3', 1) + m2.volume = 1 + assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193 + m2.set_density('g/cm3', 2) + assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2 + m2.volume = 3 + assert pytest.approx(m2.get_decay_heat(units='W')) == 40175.15720273193*2*3 + + # Checks that 1 mol of a metastable nuclides has the correct decay heat + m3 = openmc.Material() + m3.add_nuclide("Xe135", 1) + m3.set_density('g/cm3', 1) + m3.volume = 98.9 + assert pytest.approx(m3.get_decay_heat(units='W'), rel=0.001) == 846181.2921143445 + + # Checks that specific and volumetric decay heat of tritium are correct + m4 = openmc.Material() + m4.add_nuclide("I135", 1) + m4.set_density('g/cm3', 1.5) + assert pytest.approx(m4.get_decay_heat(units='W/g')) == 40175.15720273193 # [W/g] + assert pytest.approx(m4.get_decay_heat(units='W/kg')) == 40175157.20273193 # [W/kg] + assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g] + assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc] + assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc] + # volume is required to calculate total decay heat + m4.volume = 10. + assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W] + + # Test with volume specified as argument + assert pytest.approx(m4.get_decay_heat(units='W', volume=1.0)) == 40175.15720273193*3/2 + + +def test_decay_photon_energy(): + # Set chain file for testing + openmc.config['chain_file'] = Path(__file__).parents[1] / 'chain_simple.xml' + + # Material representing single atom of I135 and Cs135 + m = openmc.Material() + m.add_nuclide('I135', 1.0e-24) + m.add_nuclide('Cs135', 1.0e-24) + m.volume = 1.0 + + # Get decay photon source and make sure it's the right type + src = m.get_decay_photon_energy() + assert isinstance(src, openmc.stats.Discrete) + + # Make sure units/volume work as expected + src_v2 = m.get_decay_photon_energy(volume=2.0) + assert src.p * 2.0 == pytest.approx(src_v2.p) + src_per_cm3 = m.get_decay_photon_energy(units='Bq/cm3', volume=100.0) + assert (src.p == src_per_cm3.p).all() + src_per_bqg = m.get_decay_photon_energy(units='Bq/g') + src_per_bqkg = m.get_decay_photon_energy(units='Bq/kg') + assert pytest.approx(src_per_bqg.integral()) == src_per_bqkg.integral() / 1000. + + # If we add Xe135 (which has a tabular distribution), the photon source + # should be a mixture distribution + m.add_nuclide('Xe135', 1.0e-24) + src = m.get_decay_photon_energy() + assert isinstance(src, openmc.stats.Mixture) + + # With a single atom of each, the intensity of the photon source should be + # equal to the sum of the intensities for each nuclide + def intensity(src): + return src.integral() if src is not None else 0.0 + + assert src.integral() == pytest.approx(sum( + intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides() + ), rel=1e-3) + + # When the clipping threshold is zero, the intensities should match exactly + src = m.get_decay_photon_energy(0.0) + assert src.integral() == pytest.approx(sum( + intensity(decay_photon_energy(nuc)) for nuc in m.get_nuclides() + )) + + # A material with no unstable nuclides should have no decay photon source + stable = openmc.Material() + stable.add_nuclide('Gd156', 1.0) + stable.volume = 1.0 + assert stable.get_decay_photon_energy() is None + + +def test_avoid_subnormal(run_in_tmpdir): + # Write a materials.xml with a material that has a nuclide density that is + # represented as a subnormal floating point value + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + mat.add_nuclide('H2', 1.0e-315) + mats = openmc.Materials([mat]) + mats.export_to_xml() + + # When read back in, the density should be zero + mats = openmc.Materials.from_xml() + assert mats[0].get_nuclide_atom_densities()['H2'] == 0.0 + + +def test_material_deplete(): + pristine_material = openmc.Material() + pristine_material.add_nuclide("Ni58", 1.0) + pristine_material.set_density("g/cm3", 7.87) + pristine_material.depletable = True + pristine_material.temperature = 293.6 + pristine_material.volume = 1. + + mg_flux = [0.5e11] * 42 + + chain = Chain.from_xml( + Path(__file__).parents[1] / "chain_ni.xml" + ) + + depleted_material = pristine_material.deplete( + multigroup_flux=mg_flux, + energy_group_structure="VITAMIN-J-42", + timesteps=[10, 70.86], + source_rates=[1e19, 0.0], + timestep_units="d", + chain_file=chain, + ) + + for i_step, material in enumerate(depleted_material): + assert isinstance(material, openmc.Material) + if i_step > 0: + assert len(material.get_nuclides()) > len(pristine_material.get_nuclides()) + + Co58_mat_1_step_0 = depleted_material[0].get_nuclide_atom_densities("Co58").get("Co58", 0.0) + Co58_mat_1_step_1 = depleted_material[1].get_nuclide_atom_densities("Co58")["Co58"] + Co58_mat_1_step_2 = depleted_material[2].get_nuclide_atom_densities("Co58")["Co58"] + + assert Co58_mat_1_step_0 == 0.0 + + # Check that Co58 is produced in the first step + assert Co58_mat_1_step_1 > 0.0 + + # Check that Co58 is halved in the second step which is one halflife later + assert np.allclose(Co58_mat_1_step_1 * 0.5, Co58_mat_1_step_2) + + +def test_mean_free_path(): + + mat1 = openmc.Material() + mat1.add_nuclide('Si28', 1.0) + mat1.set_density('g/cm3', 2.32) + assert mat1.mean_free_path(energy=14e6) == pytest.approx(11.41, abs=1e-2) + + mat2 = openmc.Material() + mat2.add_nuclide('Pb208', 1.0) + mat2.set_density('g/cm3', 11.34) + assert mat2.mean_free_path(energy=14e6) == pytest.approx(5.65, abs=1e-2) diff --git a/tests/unit_tests/test_materials.py b/tests/unit_tests/test_materials.py new file mode 100644 index 0000000000..5a382b777d --- /dev/null +++ b/tests/unit_tests/test_materials.py @@ -0,0 +1,80 @@ +from pathlib import Path + +import openmc +from openmc.deplete import Chain + + +def test_materials_deplete(): + pristine_material_1 = openmc.Material() + pristine_material_1.add_nuclide("Ni58", 1.) + pristine_material_1.set_density("g/cm3", 7.87) + pristine_material_1.depletable = True + pristine_material_1.temperature = 293.6 + pristine_material_1.volume = 1. + + pristine_material_2 = openmc.Material() + pristine_material_2.add_nuclide("Ni60", 1.) + pristine_material_2.set_density("g/cm3", 7.87) + pristine_material_2.depletable = True + pristine_material_2.temperature = 293.6 + pristine_material_2.volume = 1. + + pristine_materials = openmc.Materials([pristine_material_1, pristine_material_2]) + + mg_flux = [0.5e11] * 42 + + chain = Chain.from_xml( + Path(__file__).parents[1] / "chain_ni.xml" + ) + + depleted_material = pristine_materials.deplete( + multigroup_fluxes=[mg_flux, mg_flux], + energy_group_structures=["VITAMIN-J-42", "VITAMIN-J-42"], + timesteps=[100, 100], + source_rates=[1e19, 0.0], + timestep_units="d", + chain_file=chain, + ) + + assert list(depleted_material.keys()) == [pristine_material_1.id, pristine_material_2.id] + for mat_id, materials in depleted_material.items(): + for i_step, material in enumerate(materials): + assert isinstance(material, openmc.Material) + if i_step > 0: + assert len(material.get_nuclides()) > 1 + assert mat_id == material.id + + mats = depleted_material[pristine_material_1.id] + Co58_mat_1_step_0 = mats[0].get_nuclide_atom_densities("Co58").get("Co58", 0.0) + Co58_mat_1_step_1 = mats[1].get_nuclide_atom_densities("Co58")["Co58"] + Co58_mat_1_step_2 = mats[2].get_nuclide_atom_densities("Co58")["Co58"] + + assert Co58_mat_1_step_0 == 0.0 + # Co58 is the main activation product of Ni58 in the first irradiation step. + # It then decays in the second cooling step (flux = 0) + assert Co58_mat_1_step_1 > 0.0 and Co58_mat_1_step_1 > Co58_mat_1_step_2 + + Ni59_mat_1_step_0 = mats[0].get_nuclide_atom_densities("Ni59").get("Ni59", 0.0) + Ni59_mat_1_step_1 = mats[1].get_nuclide_atom_densities("Ni59")["Ni59"] + Ni59_mat_1_step_2 = mats[2].get_nuclide_atom_densities("Ni59")["Ni59"] + + assert Ni59_mat_1_step_0 == 0.0 + # Ni59 is one of the main activation product of Ni60 in the first irradiation + # step. It then decays in the second cooling step (flux = 0) + assert Ni59_mat_1_step_1 > 0.0 and Ni59_mat_1_step_1 > Ni59_mat_1_step_2 + + +def test_export_duplicate_materials_to_xml(run_in_tmpdir): + """ + Test exporting Materials to xml with a duplicate and checking that only + unique entities are exported. + """ + my_mat = openmc.Material(name="my_mat") + my_mat2 = openmc.Material(name="my_mat2") + + materials = openmc.Materials([my_mat, my_mat2, my_mat]) + + materials.export_to_xml("materials.xml") + + materials_in = openmc.Materials.from_xml("materials.xml") + assert len(materials_in) == 2 diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py deleted file mode 100644 index c50057fcd6..0000000000 --- a/tests/unit_tests/test_math.py +++ /dev/null @@ -1,247 +0,0 @@ -import numpy as np -import pytest -import scipy as sp -from scipy.stats import shapiro - -import openmc -import openmc.lib - - -def test_t_percentile(): - # Permutations include 1 DoF, 2 DoF, and > 2 DoF - # We will test 5 p-values at 3-DoF values - test_ps = [0.02, 0.4, 0.5, 0.6, 0.98] - test_dfs = [1, 2, 5] - - # The reference solutions come from Scipy - ref_ts = [[sp.stats.t.ppf(p, df) for p in test_ps] for df in test_dfs] - - test_ts = [[openmc.lib.math.t_percentile(p, df) for p in test_ps] - for df in test_dfs] - - # The 5 DoF approximation in openmc.lib.math.t_percentile is off by up to - # 8e-3 from the scipy solution, so test that one separately with looser - # tolerance - assert np.allclose(ref_ts[:-1], test_ts[:-1]) - assert np.allclose(ref_ts[-1], test_ts[-1], atol=1e-2) - - -def test_calc_pn(): - max_order = 10 - test_xs = np.linspace(-1., 1., num=5, endpoint=True) - - # Reference solutions from scipy - ref_vals = np.array([sp.special.eval_legendre(n, test_xs) - for n in range(0, max_order + 1)]) - - test_vals = [] - for x in test_xs: - test_vals.append(openmc.lib.math.calc_pn(max_order, x).tolist()) - - test_vals = np.swapaxes(np.array(test_vals), 0, 1) - - assert np.allclose(ref_vals, test_vals) - - -def test_evaluate_legendre(): - max_order = 10 - # Coefficients are set to 1, but will incorporate the (2l+1)/2 norm factor - # for the reference solution - test_coeffs = [0.5 * (2. * l + 1.) for l in range(max_order + 1)] - test_xs = np.linspace(-1., 1., num=5, endpoint=True) - - ref_vals = np.polynomial.legendre.legval(test_xs, test_coeffs) - - # Set the coefficients back to 1s for the test values since - # evaluate legendre incorporates the (2l+1)/2 term on its own - test_coeffs = [1. for l in range(max_order + 1)] - - test_vals = np.array([openmc.lib.math.evaluate_legendre(test_coeffs, x) - for x in test_xs]) - - assert np.allclose(ref_vals, test_vals) - - -def test_calc_rn(): - max_order = 10 - test_ns = np.array([i for i in range(0, max_order + 1)]) - azi = 0.1 # Longitude - pol = 0.2 # Latitude - test_uvw = np.array([np.sin(pol) * np.cos(azi), - np.sin(pol) * np.sin(azi), - np.cos(pol)]) - - # Reference solutions from the equations - ref_vals = [] - - def coeff(n, m): - return np.sqrt((2. * n + 1) * sp.special.factorial(n - m) / - (sp.special.factorial(n + m))) - - def pnm_bar(n, m, mu): - val = coeff(n, m) - if m != 0: - val *= np.sqrt(2.) - val *= sp.special.lpmv([m], [n], [mu]) - return val[0] - - ref_vals = [] - for n in test_ns: - for m in range(-n, n + 1): - if m < 0: - ylm = pnm_bar(n, np.abs(m), np.cos(pol)) * \ - np.sin(np.abs(m) * azi) - else: - ylm = pnm_bar(n, m, np.cos(pol)) * np.cos(m * azi) - - # Un-normalize for comparison - ylm /= np.sqrt(2. * n + 1.) - ref_vals.append(ylm) - - test_vals = [] - test_vals = openmc.lib.math.calc_rn(max_order, test_uvw) - - assert np.allclose(ref_vals, test_vals) - - -def test_calc_zn(): - n = 10 - rho = 0.5 - phi = 0.5 - - # Reference solution from running the C++ implementation - ref_vals = np.array([ - 1.00000000e+00, 2.39712769e-01, 4.38791281e-01, - 2.10367746e-01, -5.00000000e-01, 1.35075576e-01, - 1.24686873e-01, -2.99640962e-01, -5.48489101e-01, - 8.84215021e-03, 5.68310892e-02, -4.20735492e-01, - -1.25000000e-01, -2.70151153e-01, -2.60091773e-02, - 1.87022545e-02, -3.42888902e-01, 1.49820481e-01, - 2.74244551e-01, -2.43159131e-02, -2.50357380e-02, - 2.20500013e-03, -1.98908812e-01, 4.07587508e-01, - 4.37500000e-01, 2.61708929e-01, 9.10321205e-02, - -1.54686328e-02, -2.74049397e-03, -7.94845816e-02, - 4.75368705e-01, 7.11647284e-02, 1.30266162e-01, - 3.37106977e-02, 1.06401886e-01, -7.31606787e-03, - -2.95625975e-03, -1.10250006e-02, 3.55194307e-01, - -1.44627826e-01, -2.89062500e-01, -9.28644588e-02, - -1.62557358e-01, 7.73431638e-02, -2.55329539e-03, - -1.90923851e-03, 1.57578403e-02, 1.72995854e-01, - -3.66267690e-01, -1.81657333e-01, -3.32521518e-01, - -2.59738162e-02, -2.31580576e-01, 4.20673902e-02, - -4.11710546e-04, -9.36449487e-04, 1.92156884e-02, - 2.82515641e-02, -3.90713738e-01, -1.69280296e-01, - -8.98437500e-02, -1.08693628e-01, 1.78813094e-01, - -1.98191857e-01, 1.65964201e-02, 2.77013853e-04]) - - test_vals = openmc.lib.math.calc_zn(n, rho, phi) - - assert np.allclose(ref_vals, test_vals) - - -def test_calc_zn_rad(): - n = 10 - rho = 0.5 - - # Reference solution from running the C++ implementation - ref_vals = np.array([ - 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, - 4.37500000e-01, -2.89062500e-01,-8.98437500e-02]) - - test_vals = openmc.lib.math.calc_zn_rad(n, rho) - - assert np.allclose(ref_vals, test_vals) - - -def test_rotate_angle(): - uvw0 = np.array([1., 0., 0.]) - phi = 0. - mu = 0. - - # reference: mu of 0 pulls the vector the bottom, so: - ref_uvw = np.array([0., 0., -1.]) - - test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi) - - assert np.array_equal(ref_uvw, test_uvw) - - # Repeat for mu = 1 (no change) - mu = 1. - ref_uvw = np.array([1., 0., 0.]) - - test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi) - - assert np.array_equal(ref_uvw, test_uvw) - - # Now to test phi is None - mu = 0.9 - phi = None - prn_seed = 1 - - # When seed = 1, phi will be sampled as 1.9116495709698769 - # The resultant reference is from hand-calculations given the above - ref_uvw = [0.9, -0.422746750548505, 0.10623175090659095] - test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi, prn_seed) - - assert np.allclose(ref_uvw, test_uvw) - - -def test_maxwell_spectrum(): - prn_seed = 1 - T = 0.5 - ref_val = 0.27767406743161277 - test_val = openmc.lib.math.maxwell_spectrum(T, prn_seed) - - assert ref_val == test_val - - -def test_watt_spectrum(): - prn_seed = 1 - a = 0.5 - b = 0.75 - ref_val = 0.30957476387766697 - test_val = openmc.lib.math.watt_spectrum(a, b, prn_seed) - - assert ref_val == test_val - - -def test_normal_dist(): - # When standard deviation is zero, sampled value should be mean - prn_seed = 1 - mean = 14.08 - stdev = 0.0 - ref_val = 14.08 - test_val = openmc.lib.math.normal_variate(mean, stdev, prn_seed) - assert ref_val == pytest.approx(test_val) - - # Use Shapiro-Wilk test to ensure normality of sampled vairates - stdev = 1.0 - samples = [] - num_samples = 10000 - for _ in range(num_samples): - # sample the normal distribution from openmc - samples.append(openmc.lib.math.normal_variate(mean, stdev, prn_seed)) - prn_seed += 1 - stat, p = shapiro(samples) - assert p > 0.05 - - -def test_broaden_wmp_polynomials(): - # Two branches of the code to worry about, beta > 6 and otherwise - # beta = sqrtE * dopp - # First lets do beta > 6 - test_E = 0.5 - test_dopp = 100. # approximately U235 at room temperature - n = 6 - - ref_val = [2., 1.41421356, 1.0001, 0.70731891, 0.50030001, 0.353907] - test_val = openmc.lib.math.broaden_wmp_polynomials(test_E, test_dopp, n) - - assert np.allclose(ref_val, test_val) - - # now beta < 6 - test_dopp = 5. - ref_val = [1.99999885, 1.41421356, 1.04, 0.79195959, 0.6224, 0.50346003] - test_val = openmc.lib.math.broaden_wmp_polynomials(test_E, test_dopp, n) - - assert np.allclose(ref_val, test_val) diff --git a/tests/unit_tests/test_mcpl_stat_sum.py b/tests/unit_tests/test_mcpl_stat_sum.py new file mode 100644 index 0000000000..b691929560 --- /dev/null +++ b/tests/unit_tests/test_mcpl_stat_sum.py @@ -0,0 +1,69 @@ +"""Test for MCPL stat:sum functionality""" + +from pathlib import Path +import shutil + +import pytest +import openmc + + +@pytest.mark.skipif(shutil.which("mcpl-config") is None, reason="MCPL is not available.") +def test_mcpl_stat_sum_field(run_in_tmpdir): + """Test that MCPL files contain proper stat:sum field with particle count. + + This test verifies that when OpenMC creates MCPL source files, they contain + the stat:sum field. Since MCPL functions are not exposed in the Python API, + this test creates an actual OpenMC simulation to generate MCPL files and + then checks their content. + """ + + mcpl = pytest.importorskip("mcpl") + + # Create a minimal working model that will generate MCPL files + model = openmc.examples.pwr_pin_cell() + model.settings.batches = 5 + model.settings.inactive = 2 + model.settings.particles = 1000 + model.settings.sourcepoint = {'mcpl': True, 'separate': True} + + # Run a short simulation to generate MCPL files + model.run(output=False) + + # Find the generated MCPL file (from the last batch) + mcpl_file = Path('source.5.mcpl') + assert mcpl_file.exists(), "No MCPL files were generated" + + # Open and verify the stat:sum field exists + with mcpl.MCPLFile(mcpl_file) as f: + # Check if stat:sum field exists using convenience property + if hasattr(f, 'stat_sum'): + # Use the convenience .stat_sum property directly + stat_sum_dict = f.stat_sum + assert 'openmc_np1' in stat_sum_dict, "openmc_np1 key not found in stat_sum" + stat_sum_value = int(stat_sum_dict['openmc_np1']) + else: + # Fallback to checking comments for older MCPL versions + comments = f.comments + + # Check for stat:sum in comments (MCPL stores these as comments) + stat_sum_value = None + + for comment in comments: + if 'stat:sum:openmc_np1' in comment: + # Extract the value + parts = comment.split(':') + if len(parts) >= 4: + stat_sum_value = int(parts[3].strip()) + break + else: + pytest.skip("stat:sum field not found - may be running with MCPL < 2.1.0") + + # Verify the stat:sum value is reasonable + assert stat_sum_value != -1, "stat:sum was not updated from initial -1 value" + + # In eigenvalue mode, active batches generate source particles + active_batches = model.settings.batches - model.settings.inactive # 3 active batches + expected_particles = active_batches * model.settings.particles # 3000 total + + assert stat_sum_value == expected_particles, \ + f"stat:sum value {stat_sum_value} doesn't match expected {expected_particles}" diff --git a/tests/unit_tests/test_mesh.py b/tests/unit_tests/test_mesh.py new file mode 100644 index 0000000000..f0f289408a --- /dev/null +++ b/tests/unit_tests/test_mesh.py @@ -0,0 +1,761 @@ +from math import pi +from tempfile import TemporaryDirectory +from pathlib import Path + +import numpy as np +from scipy.stats import chi2 +import pytest +import openmc +import openmc.lib +from openmc.utility_funcs import change_directory +from uncertainties.unumpy import uarray, nominal_values, std_devs + + +@pytest.mark.parametrize("val_left,val_right", [(0, 0), (-1., -1.), (2.0, 2)]) +def test_raises_error_when_flat(val_left, val_right): + """Checks that an error is raised when a mesh is flat""" + mesh = openmc.RegularMesh() + + # Same X + with pytest.raises(ValueError): + mesh.lower_left = [val_left, -25, -25] + mesh.upper_right = [val_right, 25, 25] + + with pytest.raises(ValueError): + mesh.upper_right = [val_right, 25, 25] + mesh.lower_left = [val_left, -25, -25] + + # Same Y + with pytest.raises(ValueError): + mesh.lower_left = [-25, val_left, -25] + mesh.upper_right = [25, val_right, 25] + + with pytest.raises(ValueError): + mesh.upper_right = [25, val_right, 25] + mesh.lower_left = [-25, val_left, -25] + + # Same Z + with pytest.raises(ValueError): + mesh.lower_left = [-25, -25, val_left] + mesh.upper_right = [25, 25, val_right] + + with pytest.raises(ValueError): + mesh.upper_right = [25, 25, val_right] + mesh.lower_left = [-25, -25, val_left] + + +def test_regular_mesh_bounding_box(): + mesh = openmc.RegularMesh() + mesh.lower_left = [-2, -3, -5] + mesh.upper_right = [2, 3, 5] + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (-2, -3 ,-5)) + np.testing.assert_array_equal(bb.upper_right, (2, 3, 5)) + + +def test_rectilinear_mesh_bounding_box(): + mesh = openmc.RectilinearMesh() + mesh.x_grid = [0., 1., 5., 10.] + mesh.y_grid = [-10., -5., 0.] + mesh.z_grid = [-100., 0., 100.] + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (0., -10. ,-100.)) + np.testing.assert_array_equal(bb.upper_right, (10., 0., 100.)) + + +def test_cylindrical_mesh_bounding_box(): + # test with mesh at origin (0, 0, 0) + mesh = openmc.CylindricalMesh( + r_grid=[0.1, 0.2, 0.5, 1.], + z_grid=[0.1, 0.2, 0.4, 0.6, 1.], + origin=(0, 0, 0) + ) + np.testing.assert_array_equal(mesh.upper_right, (1, 1, 1)) + np.testing.assert_array_equal(mesh.lower_left, (-1, -1, 0.1)) + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (-1, -1, 0.1)) + np.testing.assert_array_equal(bb.upper_right, (1, 1, 1)) + + # test with mesh at origin (3, 5, 7) + mesh.origin = (3, 5, 7) + np.testing.assert_array_equal(mesh.upper_right, (4, 6, 8)) + np.testing.assert_array_equal(mesh.lower_left, (2, 4, 7.1)) + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (2, 4, 7.1)) + np.testing.assert_array_equal(bb.upper_right, (4, 6, 8)) + + # changing z grid to contain negative numbers + mesh.z_grid = [-10, 0, 10] + np.testing.assert_array_equal(mesh.lower_left, (2, 4, -3)) + np.testing.assert_array_equal(mesh.upper_right, (4, 6, 17)) + + +def test_spherical_mesh_bounding_box(): + # test with mesh at origin (0, 0, 0) + mesh = openmc.SphericalMesh([0.1, 0.2, 0.5, 1.], origin=(0., 0., 0.)) + np.testing.assert_array_equal(mesh.upper_right, (1, 1, 1)) + np.testing.assert_array_equal(mesh.lower_left, (-1, -1, -1)) + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (-1, -1, -1)) + np.testing.assert_array_equal(bb.upper_right, (1, 1, 1)) + + # test with mesh at origin (3, 5, 7) + mesh.origin = (3, 5, 7) + np.testing.assert_array_equal(mesh.upper_right, (4, 6, 8)) + np.testing.assert_array_equal(mesh.lower_left, (2, 4, 6)) + bb = mesh.bounding_box + assert isinstance(bb, openmc.BoundingBox) + np.testing.assert_array_equal(bb.lower_left, (2, 4, 6)) + np.testing.assert_array_equal(bb.upper_right, (4, 6, 8)) + + +def test_SphericalMesh_initiation(): + # test defaults + mesh = openmc.SphericalMesh(r_grid=(0, 10)) + assert (mesh.origin == np.array([0, 0, 0])).all() + assert (mesh.r_grid == np.array([0, 10])).all() + assert (mesh.theta_grid == np.array([0, pi])).all() + assert (mesh.phi_grid == np.array([0, 2*pi])).all() + + # test setting on creation + mesh = openmc.SphericalMesh( + origin=(1, 2, 3), + r_grid=(0, 2), + theta_grid=(1, 3), + phi_grid=(2, 4) + ) + assert (mesh.origin == np.array([1, 2, 3])).all() + assert (mesh.r_grid == np.array([0., 2.])).all() + assert (mesh.theta_grid == np.array([1, 3])).all() + assert (mesh.phi_grid == np.array([2, 4])).all() + + # test attribute changing + mesh.r_grid = (0, 11) + assert (mesh.r_grid == np.array([0., 11.])).all() + + # test invalid r_grid values + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[1, 1]) + + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[0]) + + # test invalid theta_grid values + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[1, 2], theta_grid=[1, 1]) + + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[1, 2], theta_grid=[0]) + + # test invalid phi_grid values + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[1, 2], phi_grid=[1, 1]) + + with pytest.raises(ValueError): + openmc.SphericalMesh(r_grid=[1, 2], phi_grid=[0]) + + # waffles and pancakes are unfortunately not valid radii + with pytest.raises(TypeError): + openmc.SphericalMesh(('🧇', '🥞')) + + +def test_CylindricalMesh_initiation(): + # test defaults + mesh = openmc.CylindricalMesh(r_grid=(0, 10), z_grid=(0, 10)) + assert (mesh.origin == np.array([0, 0, 0])).all() + assert (mesh.r_grid == np.array([0, 10])).all() + assert (mesh.phi_grid == np.array([0, 2*pi])).all() + assert (mesh.z_grid == np.array([0, 10])).all() + + # test setting on creation + mesh = openmc.CylindricalMesh( + origin=(1, 2, 3), + r_grid=(0, 2), + z_grid=(1, 3), + phi_grid=(2, 4) + ) + assert (mesh.origin == np.array([1, 2, 3])).all() + assert (mesh.r_grid == np.array([0., 2.])).all() + assert (mesh.z_grid == np.array([1, 3])).all() + assert (mesh.phi_grid == np.array([2, 4])).all() + + # test attribute changing + mesh.r_grid = (0., 10.) + assert (mesh.r_grid == np.array([0, 10.])).all() + mesh.z_grid = (0., 4.) + assert (mesh.z_grid == np.array([0, 4.])).all() + + # waffles and pancakes are unfortunately not valid radii + with pytest.raises(TypeError): + openmc.SphericalMesh(('🧇', '🥞')) + + +def test_invalid_cylindrical_mesh_errors(): + # Test invalid r_grid values + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[5, 1], phi_grid=[0, pi], z_grid=[0, 10]) + + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[1, 2, 4, 3], phi_grid=[0, pi], z_grid=[0, 10]) + + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[1], phi_grid=[0, pi], z_grid=[0, 10]) + + # Test invalid phi_grid values + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[0, 1, 2], phi_grid=[-1, 3], z_grid=[0, 10]) + + with pytest.raises(ValueError): + openmc.CylindricalMesh( + r_grid=[0, 1, 2], + phi_grid=[0, 2*pi + 0.1], + z_grid=[0, 10] + ) + + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[0, 1, 2], phi_grid=[pi], z_grid=[0, 10]) + + # Test invalid z_grid values + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[0, 1, 2], phi_grid=[0, pi], z_grid=[5]) + + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[0, 1, 2], phi_grid=[0, pi], z_grid=[5, 1]) + + with pytest.raises(ValueError): + openmc.CylindricalMesh(r_grid=[1, 2, 4, 3], phi_grid=[0, pi], z_grid=[0, 10, 5]) + + +def test_centroids(): + # regular mesh + mesh = openmc.RegularMesh() + mesh.lower_left = (1., 2., 3.) + mesh.upper_right = (11., 12., 13.) + mesh.dimension = (1, 1, 1) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6., 7., 8.]) + + # rectilinear mesh + mesh = openmc.RectilinearMesh() + mesh.x_grid = [1., 11.] + mesh.y_grid = [2., 12.] + mesh.z_grid = [3., 13.] + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [6., 7., 8.]) + + # cylindrical mesh + mesh = openmc.CylindricalMesh(r_grid=(0, 10), z_grid=(0, 10), phi_grid=(0, np.pi)) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [0.0, 5.0, 5.0]) + # ensure that setting an origin is handled correctly + mesh.origin = (5.0, 0, -10) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [5.0, 5.0, -5.0]) + + # spherical mesh, single element xyz-positive octant + mesh = openmc.SphericalMesh(r_grid=[0, 10], theta_grid=[0, 0.5*np.pi], phi_grid=[0, np.pi]) + x = 5.*np.cos(0.5*np.pi)*np.sin(0.25*np.pi) + y = 5.*np.sin(0.5*np.pi)*np.sin(0.25*np.pi) + z = 5.*np.sin(0.25*np.pi) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [x, y, z]) + + mesh.origin = (-5.0, -5.0, 5.0) + np.testing.assert_array_almost_equal(mesh.centroids[0, 0, 0], [x-5.0, y-5.0, z+5.0]) + + +@pytest.mark.parametrize('mesh_type', ('regular', 'rectilinear', 'cylindrical', 'spherical')) +def test_mesh_vertices(mesh_type): + + ijk = (2, 3, 2) + + # create a new mesh object + if mesh_type == 'regular': + mesh = openmc.RegularMesh() + ll = np.asarray([0.]*3) + width = np.asarray([0.5]*3) + mesh.lower_left = ll + mesh.width = width + mesh.dimension = (5, 7, 9) + + # spot check that an element has the correct vertex coordinates asociated with it + # (using zero-indexing here) + exp_i_j_k = ll + np.asarray(ijk, dtype=float) * width + np.testing.assert_equal(mesh.vertices[ijk], exp_i_j_k) + + # shift the mesh using the llc + shift = np.asarray((3.0, 6.0, 10.0)) + mesh.lower_left += shift + np.testing.assert_equal(mesh.vertices[ijk], exp_i_j_k+shift) + elif mesh_type == 'rectilinear': + mesh = openmc.RectilinearMesh() + w = np.asarray([0.5] * 3) + ll = np.asarray([0.]*3) + dims = (5, 7, 9) + mesh.x_grid = np.linspace(ll[0], w[0]*dims[0], dims[0]) + mesh.y_grid = np.linspace(ll[1], w[1]*dims[1], dims[1]) + mesh.z_grid = np.linspace(ll[2], w[2]*dims[2], dims[2]) + exp_vert = np.asarray((mesh.x_grid[2], mesh.y_grid[3], mesh.z_grid[2])) + np.testing.assert_equal(mesh.vertices[ijk], exp_vert) + elif mesh_type == 'cylindrical': + r_grid = np.linspace(0, 5, 10) + z_grid = np.linspace(-10, 10, 20) + phi_grid = np.linspace(0, 2*np.pi, 8) + mesh = openmc.CylindricalMesh(r_grid=r_grid, z_grid=z_grid, phi_grid=phi_grid) + exp_vert = np.asarray((mesh.r_grid[2], mesh.phi_grid[3], mesh.z_grid[2])) + np.testing.assert_equal(mesh.vertices_cylindrical[ijk], exp_vert) + elif mesh_type == 'spherical': + r_grid = np.linspace(0, 13, 14) + theta_grid = np.linspace(0, np.pi, 11) + phi_grid = np.linspace(0, 2*np.pi, 7) + mesh = openmc.SphericalMesh(r_grid=r_grid, theta_grid=theta_grid, phi_grid=phi_grid) + exp_vert = np.asarray((mesh.r_grid[2], mesh.theta_grid[3], mesh.phi_grid[2])) + np.testing.assert_equal(mesh.vertices_spherical[ijk], exp_vert) + + +def test_CylindricalMesh_get_indices_at_coords(): + # default origin (0, 0, 0) and default phi grid (0, 2*pi) + mesh = openmc.CylindricalMesh(r_grid=(0, 5, 10), z_grid=(0, 5, 10)) + assert mesh.get_indices_at_coords([1, 0, 1]) == (0, 0, 0) + assert mesh.get_indices_at_coords([6, 0, 1]) == (1, 0, 0) + assert mesh.get_indices_at_coords([9, 0, 1]) == (1, 0, 0) + assert mesh.get_indices_at_coords([0, 6, 0]) == (1, 0, 0) + assert mesh.get_indices_at_coords([0, 9, 6]) == (1, 0, 1) + assert mesh.get_indices_at_coords([-2, -2, 9]) == (0, 0, 1) + + with pytest.raises(ValueError): + assert mesh.get_indices_at_coords([8, 8, 1]) # resulting r value to large + with pytest.raises(ValueError): + assert mesh.get_indices_at_coords([-8, -8, 1]) # resulting r value to large + with pytest.raises(ValueError): + assert mesh.get_indices_at_coords([1, 0, -1]) # z value below range + with pytest.raises(ValueError): + assert mesh.get_indices_at_coords([1, 0, 11]) # z value above range + + assert mesh.get_indices_at_coords([1, 1, 1]) == (0, 0, 0) + + # negative range on z grid + mesh = openmc.CylindricalMesh( + r_grid=(0, 5, 10), + phi_grid=(0, 0.5 * pi, pi, 1.5 * pi, 1.9 * pi), + z_grid=(-5, 0, 5, 10), + ) + assert mesh.get_indices_at_coords([1, 1, 1]) == (0, 0, 1) # first angle quadrant + assert mesh.get_indices_at_coords([2, 2, 6]) == (0, 0, 2) # first angle quadrant + assert mesh.get_indices_at_coords([-2, 0.1, -1]) == (0, 1, 0) # second angle quadrant + assert mesh.get_indices_at_coords([-2, -0.1, -1]) == (0, 2, 0) # third angle quadrant + assert mesh.get_indices_at_coords([2, -0.9, -1]) == (0, 3, 0) # forth angle quadrant + + with pytest.raises(ValueError): + assert mesh.get_indices_at_coords([2, -0.1, 1]) # outside of phi range + + # origin of mesh not default + mesh = openmc.CylindricalMesh( + r_grid=(0, 5, 10), + phi_grid=(0, 0.5 * pi, pi, 1.5 * pi, 1.9 * pi), + z_grid=(-5, 0, 5, 10), + origin=(100, 200, 300), + ) + assert mesh.get_indices_at_coords([101, 201, 301]) == (0, 0, 1) # first angle quadrant + assert mesh.get_indices_at_coords([102, 202, 306]) == (0, 0, 2) # first angle quadrant + assert mesh.get_indices_at_coords([98, 200.1, 299]) == (0, 1, 0) # second angle quadrant + assert mesh.get_indices_at_coords([98, 199.9, 299]) == (0, 2, 0) # third angle quadrant + assert mesh.get_indices_at_coords([102, 199.1, 299]) == (0, 3, 0) # forth angle quadrant + + +def test_mesh_name_roundtrip(run_in_tmpdir): + + mesh = openmc.RegularMesh() + mesh.name = 'regular-mesh' + mesh.lower_left = (-1, -1, -1) + mesh.width = (1, 1, 1) + mesh.dimension = (1, 1, 1) + + mesh_filter = openmc.MeshFilter(mesh) + tally = openmc.Tally() + tally.filters = [mesh_filter] + tally.scores = ['flux'] + + openmc.Tallies([tally]).export_to_xml() + + xml_tallies = openmc.Tallies.from_xml() + + mesh = xml_tallies[0].find_filter(openmc.MeshFilter).mesh + assert mesh.name == 'regular-mesh' + + +def test_umesh_roundtrip(run_in_tmpdir, request): + umesh = openmc.UnstructuredMesh(request.path.parent / 'test_mesh_tets.e', 'moab') + umesh.output = True + + # create a tally using this mesh + mf = openmc.MeshFilter(umesh) + tally = openmc.Tally() + tally.filters = [mf] + tally.scores = ['flux'] + + tallies = openmc.Tallies([tally]) + tallies.export_to_xml() + + xml_tallies = openmc.Tallies.from_xml() + xml_tally = xml_tallies[0] + xml_mesh = xml_tally.filters[0].mesh + + assert umesh.id == xml_mesh.id + + +@pytest.fixture(scope='module') +def simple_umesh(request): + """Fixture returning UnstructuredMesh with all attributes""" + surf1 = openmc.Sphere(r=20.0, boundary_type="vacuum") + material1 = openmc.Material() + material1.add_element("H", 1.0) + material1.set_density('g/cm3', 1.0) + + materials = openmc.Materials([material1]) + cell1 = openmc.Cell(region=-surf1, fill=material1) + geometry = openmc.Geometry([cell1]) + + umesh = openmc.UnstructuredMesh( + filename=request.path.parent.parent + / "regression_tests/external_moab/test_mesh_tets.h5m", + library="moab", + mesh_id=1 + ) + # setting ID to make it easier to get the mesh from the statepoint later + mesh_filter = openmc.MeshFilter(umesh) + + # Create flux mesh tally to score alpha production + mesh_tally = openmc.Tally(name="test_tally") + mesh_tally.filters = [mesh_filter] + mesh_tally.scores = ["total"] + + tallies = openmc.Tallies([mesh_tally]) + + settings = openmc.Settings() + settings.run_mode = "fixed source" + settings.batches = 2 + settings.particles = 100 + settings.source = openmc.IndependentSource( + space=openmc.stats.Point((0.1, 0.1, 0.1)) + ) + + model = openmc.Model( + materials=materials, geometry=geometry, settings=settings, tallies=tallies + ) + + with change_directory(tmpdir=True): + statepoint_file = model.run() + with openmc.StatePoint(statepoint_file) as sp: + return sp.meshes[1] + + +@pytest.mark.skipif(not openmc.lib._dagmc_enabled(), reason="DAGMC not enabled.") +@pytest.mark.parametrize('export_type', ('.vtk', '.vtu')) +def test_umesh(run_in_tmpdir, simple_umesh, export_type): + """Performs a minimal UnstructuredMesh simulation, reads in the resulting + statepoint file and writes the mesh data to vtk and vtkhdf files. It is + necessary to read in the unstructured mesh from a statepoint file to ensure + it has all the required attributes + """ + # Get VTK modules + vtkIOLegacy = pytest.importorskip("vtkmodules.vtkIOLegacy") + vtkIOXML = pytest.importorskip("vtkmodules.vtkIOXML") + + # Sample some random data and write to VTK + rng = np.random.default_rng() + ref_data = rng.random(simple_umesh.dimension) + filename = f"test_mesh{export_type}" + simple_umesh.write_data_to_vtk(datasets={"mean": ref_data}, filename=filename) + + assert Path(filename).exists() + + if export_type == ".vtk": + reader = vtkIOLegacy.vtkGenericDataObjectReader() + elif export_type == ".vtu": + reader = vtkIOXML.vtkXMLGenericDataObjectReader() + reader.SetFileName(str(filename)) + reader.Update() + + # Get mean from file and make sure it matches original data + arr = reader.GetOutput().GetCellData().GetArray("mean") + mean = np.array([arr.GetTuple1(i) for i in range(ref_data.size)]) + np.testing.assert_almost_equal(mean, ref_data) + + # attempt to apply a dataset with an improper size to a VTK write + with pytest.raises(ValueError, match='Cannot apply dataset "mean"'): + simple_umesh.write_data_to_vtk(datasets={'mean': ref_data[:-2]}, filename=filename) + +def test_mesh_get_homogenized_materials(): + """Test the get_homogenized_materials method""" + # Simple model with 1 cm of Fe56 next to 1 cm of H1 + fe = openmc.Material() + fe.add_nuclide('Fe56', 1.0) + fe.set_density('g/cm3', 5.0) + h = openmc.Material() + h.add_nuclide('H1', 1.0) + h.set_density('g/cm3', 1.0) + + x0 = openmc.XPlane(-1.0, boundary_type='vacuum') + x1 = openmc.XPlane(0.0) + x2 = openmc.XPlane(1.0) + x3 = openmc.XPlane(2.0, boundary_type='vacuum') + cell1 = openmc.Cell(fill=fe, region=+x0 & -x1) + cell2 = openmc.Cell(fill=h, region=+x1 & -x2) + cell_empty = openmc.Cell(region=+x2 & -x3) + model = openmc.Model(geometry=openmc.Geometry([cell1, cell2, cell_empty])) + model.settings.particles = 1000 + model.settings.batches = 10 + + mesh = openmc.RegularMesh() + mesh.lower_left = (-1., -1., -1.) + mesh.upper_right = (1., 1., 1.) + mesh.dimension = (3, 1, 1) + m1, m2, m3 = mesh.get_homogenized_materials(model, n_samples=10_000) + + # Left mesh element should be only Fe56 + assert m1.get_mass_density('Fe56') == pytest.approx(5.0) + + # Middle mesh element should be 50% Fe56 and 50% H1 + assert m2.get_mass_density('Fe56') == pytest.approx(2.5, rel=1e-2) + assert m2.get_mass_density('H1') == pytest.approx(0.5, rel=1e-2) + + # Right mesh element should be only H1 + assert m3.get_mass_density('H1') == pytest.approx(1.0) + + mesh_void = openmc.RegularMesh() + mesh_void.lower_left = (0.5, 0.5, -1.) + mesh_void.upper_right = (1.5, 1.5, 1.) + mesh_void.dimension = (1, 1, 1) + m4, = mesh_void.get_homogenized_materials(model, n_samples=(100, 100, 0)) + + # Mesh element that overlaps void should have half density + assert m4.get_mass_density('H1') == pytest.approx(0.5, rel=1e-2) + + # If not including void, density of homogenized material should be same as + # original material + m5, = mesh_void.get_homogenized_materials( + model, n_samples=1000, include_void=False) + assert m5.get_mass_density('H1') == pytest.approx(1.0) + + +@pytest.fixture +def sphere_model(): + # Model with three materials separated by planes x=0 and z=0 + mats = [] + for i in range(3): + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + mat.set_density('g/cm3', float(i + 1)) + mats.append(mat) + + sph = openmc.Sphere(r=25.0, boundary_type='vacuum') + x0 = openmc.XPlane(0.0) + z0 = openmc.ZPlane(0.0) + cell1 = openmc.Cell(fill=mats[0], region=-sph & +x0 & +z0) + cell2 = openmc.Cell(fill=mats[1], region=-sph & -x0 & +z0) + cell3 = openmc.Cell(fill=mats[2], region=-sph & -z0) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2, cell3]) + model.materials = openmc.Materials(mats) + return model + + +@pytest.mark.parametrize("n_rays", [1000, (10, 10, 0), (10, 0, 10), (0, 10, 10)]) +def test_material_volumes_regular_mesh(sphere_model, n_rays): + """Test the material_volumes method on a regular mesh""" + mesh = openmc.RegularMesh() + mesh.lower_left = (-1., -1., -1.) + mesh.upper_right = (1., 1., 1.) + mesh.dimension = (2, 2, 2) + volumes = mesh.material_volumes(sphere_model, n_rays) + mats = sphere_model.materials + np.testing.assert_almost_equal(volumes[mats[0].id], [0., 0., 0., 0., 0., 1., 0., 1.]) + np.testing.assert_almost_equal(volumes[mats[1].id], [0., 0., 0., 0., 1., 0., 1., 0.]) + np.testing.assert_almost_equal(volumes[mats[2].id], [1., 1., 1., 1., 0., 0., 0., 0.]) + assert volumes.by_element(4) == [(mats[1].id, 1.)] + assert volumes.by_element(0) == [(mats[2].id, 1.)] + + +def test_material_volumes_cylindrical_mesh(sphere_model): + """Test the material_volumes method on a cylindrical mesh""" + cyl_mesh = openmc.CylindricalMesh( + [0., 1.], [-1., 0., 1.,], [0.0, pi/4, 3*pi/4, 5*pi/4, 7*pi/4, 2*pi]) + volumes = cyl_mesh.material_volumes(sphere_model, (0, 100, 100)) + mats = sphere_model.materials + np.testing.assert_almost_equal(volumes[mats[0].id], [ + 0., 0., 0., 0., 0., + pi/8, pi/8, 0., pi/8, pi/8 + ]) + np.testing.assert_almost_equal(volumes[mats[1].id], [ + 0., 0., 0., 0., 0., + 0., pi/8, pi/4, pi/8, 0. + ]) + np.testing.assert_almost_equal(volumes[mats[2].id], [ + pi/8, pi/4, pi/4, pi/4, pi/8, + 0., 0., 0., 0., 0. + ]) + + +def test_mesh_material_volumes_serialize(): + materials = np.array([ + [1, -1, -2], + [-1, -2, -2], + [2, 1, -2], + [2, -2, -2] + ]) + volumes = np.array([ + [0.5, 0.5, 0.0], + [1.0, 0.0, 0.0], + [0.5, 0.5, 0.0], + [1.0, 0.0, 0.0] + ]) + volumes = openmc.MeshMaterialVolumes(materials, volumes) + with TemporaryDirectory() as tmpdir: + path = f'{tmpdir}/volumes.npz' + volumes.save(path) + new_volumes = openmc.MeshMaterialVolumes.from_npz(path) + + assert new_volumes.by_element(0) == [(1, 0.5), (None, 0.5)] + assert new_volumes.by_element(1) == [(None, 1.0)] + assert new_volumes.by_element(2) == [(2, 0.5), (1, 0.5)] + assert new_volumes.by_element(3) == [(2, 1.0)] + + +def test_mesh_material_volumes_boundary_conditions(sphere_model): + """Test the material volumes method using a regular mesh + that overlaps with a vacuum boundary condition.""" + + mesh = openmc.SphericalMesh.from_domain(sphere_model.geometry, dimension=(1, 1, 1)) + # extend mesh beyond the outer sphere surface to test rays crossing the boundary condition + mesh.r_grid[-1] += 5.0 + + # add a new cell to the modelthat occupies the outside of the sphere + sphere_surfaces = list(filter(lambda s: isinstance(s, openmc.Sphere), + sphere_model.geometry.get_all_surfaces().values())) + outer_cell = openmc.Cell(region=+sphere_surfaces[0]) + sphere_model.geometry.root_universe.add_cell(outer_cell) + + volumes = mesh.material_volumes(sphere_model, (0, 100, 100)) + sphere_volume = 4/3*np.pi*25**3 + mats = sphere_model.materials + expected_volumes = [(mats[0].id, 0.25*sphere_volume), + (mats[1].id, 0.25*sphere_volume), + (mats[2].id, 0.5*sphere_volume), + (None, 4/3*np.pi*mesh.r_grid[-1]**3 - sphere_volume)] + + for evaluated, expected in zip(volumes.by_element(0), expected_volumes): + assert evaluated[0] == expected[0] + assert evaluated[1] == pytest.approx(expected[1], rel=1e-2) + + +def test_raytrace_mesh_infinite_loop(run_in_tmpdir): + # Create a model with one large spherical cell + sphere = openmc.Sphere(r=100, boundary_type='vacuum') + cell = openmc.Cell(region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + + # Create a regular mesh and associated tally + mesh_surface = openmc.RegularMesh() + mesh_surface.lower_left = (-30, -30, 30) + mesh_surface.upper_right = (30, 30, 60) + mesh_surface.dimension = (1, 1, 1) + reg_filter = openmc.MeshSurfaceFilter(mesh_surface) + mesh_surface_tally = openmc.Tally() + mesh_surface_tally.filters = [reg_filter] + mesh_surface_tally.scores = ['current'] + model.tallies = [mesh_surface_tally] + + # Define a source such that the z position is on a mesh boundary with a very + # small directional cosine in the z direction + polar = openmc.stats.delta_function(1.75e-7) + azimuthal = openmc.stats.Uniform(0.0, 2.0*pi) + model.settings.source = openmc.IndependentSource( + angle=openmc.stats.PolarAzimuthal(polar, azimuthal) + ) + model.settings.run_mode = 'fixed source' + model.settings.particles = 10 + model.settings.batches = 1 + + # Run the model; this should not cause an infinite loop + model.run() + + +def test_filter_time_mesh(run_in_tmpdir): + """Test combination of TimeFilter and MeshFilter""" + + # Define material + mat = openmc.Material() + mat.add_nuclide('Fe56', 1.0) + mat.set_density('g/cm3', 7.8) + + # Define geometry + surf_Z1 = openmc.XPlane(x0=-1e10, boundary_type="reflective") + surf_Z2 = openmc.XPlane(x0=1e10, boundary_type="reflective") + cell_F = openmc.Cell(fill=mat, region=+surf_Z1 & -surf_Z2) + model = openmc.Model() + model.geometry = openmc.Geometry([cell_F]) + + # Define settings + model.settings.run_mode = "fixed source" + model.settings.particles = 1000 + model.settings.batches = 20 + model.settings.output = {"tallies": False} + model.settings.cutoff = {"time_neutron": 1e-7} + + # Define tallies + + # Create a mesh filter that can be used in a tally + mesh = openmc.RegularMesh() + mesh.dimension = (21, 1, 1) + mesh.lower_left = (-20.5, -1e10, -1e10) + mesh.upper_right = (20.5, 1e10, 1e10) + time_grid = np.linspace(0.0, 1e-7, 21) + + mesh_filter = openmc.MeshFilter(mesh) + time_filter = openmc.TimeFilter(time_grid) + + # Now use the mesh filter in a tally and indicate what scores are desired + tally1 = openmc.Tally(name="collision") + tally1.estimator = "collision" + tally1.filters = [time_filter, mesh_filter] + tally1.scores = ["flux"] + tally2 = openmc.Tally(name="tracklength") + tally2.estimator = "tracklength" + tally2.filters = [time_filter, mesh_filter] + tally2.scores = ["flux"] + model.tallies = openmc.Tallies([tally1, tally2]) + + # Run and post-process + model.run(apply_tally_results=True) + + # Get radial flux distribution + flux_collision = tally1.mean.ravel() + flux_collision_unc = tally1.std_dev.ravel() + flux_tracklength = tally2.mean.ravel() + flux_tracklength_unc = tally2.std_dev.ravel() + + # Construct arrays with uncertainties + collision = uarray(flux_collision, flux_collision_unc) + tracklength = uarray(flux_tracklength, flux_tracklength_unc) + delta = collision - tracklength + + # Compute differences and standard deviations + diff = nominal_values(delta) + std_dev = std_devs(delta) + + # Exclude zero-uncertainty bins + mask = std_dev > 0.0 + dof = int(np.sum(mask)) + + # Global chi-square consistency test between collision and tracklength + # estimators. Target false positive rate ~1e-4 (1 in 10,000) + z = diff[mask] / std_dev[mask] + chi2_stat = np.sum(z * z) + alpha = 1.0e-4 + crit = chi2.ppf(1 - alpha, dof) + assert chi2_stat < crit, ( + f"Collision vs tracklength tallies disagree: chi2={chi2_stat:.2f} " + f">= {crit=:.2f} ({dof=}, {alpha=})" + ) diff --git a/tests/unit_tests/test_mesh_from_domain.py b/tests/unit_tests/test_mesh_from_domain.py new file mode 100644 index 0000000000..5b1173126f --- /dev/null +++ b/tests/unit_tests/test_mesh_from_domain.py @@ -0,0 +1,173 @@ +import numpy as np +import openmc +import pytest + + +def test_reg_mesh_from_cell(): + """Tests a RegularMesh can be made from a Cell and the specified dimensions + are propagated through. Cell is not centralized""" + surface = openmc.Sphere(r=10, x0=2, y0=3, z0=5) + cell = openmc.Cell(region=-surface) + + mesh = openmc.RegularMesh.from_domain(domain=cell, dimension=[7, 11, 13]) + assert isinstance(mesh, openmc.RegularMesh) + assert np.array_equal(mesh.dimension, (7, 11, 13)) + assert np.array_equal(mesh.lower_left, cell.bounding_box[0]) + assert np.array_equal(mesh.upper_right, cell.bounding_box[1]) + + +def test_cylindrical_mesh_from_cell(): + """Tests a CylindricalMesh can be made from a Cell and the specified + dimensions are propagated through.""" + # Cell is not centralized on Z axis + cy_surface = openmc.ZCylinder(r=50) + z_surface_1 = openmc.ZPlane(z0=40) + z_surface_2 = openmc.ZPlane(z0=10) + cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2) + mesh = openmc.CylindricalMesh.from_domain(domain=cell, dimension=[2, 4, 3]) + + assert isinstance(mesh, openmc.CylindricalMesh) + assert np.array_equal(mesh.dimension, (2, 4, 3)) + assert np.array_equal(mesh.r_grid, [0., 25., 50.]) + assert np.array_equal(mesh.phi_grid, [0., 0.5*np.pi, np.pi, 1.5*np.pi, 2.*np.pi]) + assert np.array_equal(mesh.z_grid, [0., 10., 20., 30.]) + assert np.array_equal(mesh.origin, [0., 0., 10.]) + + # Cell is not centralized on Z or X axis + cy_surface = openmc.ZCylinder(r=50, x0=100) + cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2) + mesh = openmc.CylindricalMesh.from_domain(domain=cell, dimension=[1, 1, 1]) + + assert isinstance(mesh, openmc.CylindricalMesh) + assert np.array_equal(mesh.dimension, (1, 1, 1)) + assert np.array_equal(mesh.r_grid, [0., 50.]) + assert np.array_equal(mesh.origin, [100., 0., 10.]) + + # Cell is not centralized on Z, X or Y axis + cy_surface = openmc.ZCylinder(r=50, x0=100, y0=170) + cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2) + mesh = openmc.CylindricalMesh.from_domain(domain=cell, dimension=[1, 1, 1]) + + assert isinstance(mesh, openmc.CylindricalMesh) + assert np.array_equal(mesh.r_grid, [0., 50.]) + assert np.array_equal(mesh.origin, [100., 170., 10.]) + + +def test_reg_mesh_from_region(): + """Tests a RegularMesh can be made from a Region and the default dimensions + are propagated through. Region is not centralized""" + surface = openmc.Sphere(r=1, x0=-5, y0=-3, z0=-2) + region = -surface + + mesh = openmc.RegularMesh.from_domain(domain=region) + assert isinstance(mesh, openmc.RegularMesh) + assert np.array_equal(mesh.dimension, (10, 10, 10)) # default values + assert np.array_equal(mesh.lower_left, region.bounding_box[0]) + assert np.array_equal(mesh.upper_right, region.bounding_box[1]) + + +def test_cylindrical_mesh_from_region(): + """Tests a CylindricalMesh can be made from a Region and the specified + dimensions and phi_grid_bounds are propagated through. Cell is centralized""" + cy_surface = openmc.ZCylinder(r=6) + z_surface_1 = openmc.ZPlane(z0=30) + z_surface_2 = openmc.ZPlane(z0=-30) + cell = openmc.Cell(region=-cy_surface & -z_surface_1 & +z_surface_2) + mesh = openmc.CylindricalMesh.from_domain( + domain=cell, + dimension=(6, 2, 3), + phi_grid_bounds=(0., np.pi) + ) + + assert isinstance(mesh, openmc.CylindricalMesh) + assert np.array_equal(mesh.dimension, (6, 2, 3)) + assert np.array_equal(mesh.r_grid, [0., 1., 2., 3., 4., 5., 6.]) + assert np.array_equal(mesh.phi_grid, [0., 0.5*np.pi, np.pi]) + assert np.array_equal(mesh.z_grid, [0.0, 20., 40., 60]) + assert np.array_equal(mesh.origin, (0.0, 0.0, -30.)) + + +def test_spherical_mesh_from_domain(): + """Tests a SphericalMesh can be made from a Region and the specified + dimensions are propagated through. Cell is not centralized""" + sphere = openmc.Sphere(r=5, x0=2, y0=3, z0=4) + region = -sphere + + geometry = openmc.Geometry(openmc.Universe(cells=[openmc.Cell(region=region)])) + + region_mesh = openmc.SphericalMesh.from_domain( + domain=region, dimension=(4, 3, 4)) + universe_mesh = openmc.SphericalMesh.from_domain( + domain=geometry.root_universe, dimension=(4, 3, 4)) + geometry_mesh = openmc.SphericalMesh.from_domain( + domain=geometry, dimension=(4, 3, 4)) + + + for mesh in (region_mesh, universe_mesh, geometry_mesh): + assert isinstance(mesh, openmc.SphericalMesh) + assert np.array_equal(mesh.dimension, (4, 3, 4)) + assert np.array_equal(mesh.r_grid, [0., 1.25, 2.5, 3.75, 5.0]) + assert np.array_equal(mesh.theta_grid, [0., np.pi/3., 2*np.pi/3., np.pi]) + assert np.array_equal(mesh.phi_grid, [0., np.pi/2., np.pi, 3*np.pi/2., 2*np.pi]) + assert np.array_equal(mesh.origin, (2.0, 3.0, 4.0)) + + for p in mesh.centroids.reshape(-1, 3): + assert p in mesh.bounding_box + + +def test_reg_mesh_from_universe(): + """Tests a RegularMesh can be made from a Universe and the default + dimensions are propagated through. Universe is centralized""" + surface = openmc.Sphere(r=42) + cell = openmc.Cell(region=-surface) + universe = openmc.Universe(cells=[cell]) + + mesh = openmc.RegularMesh.from_domain(universe) + assert isinstance(mesh, openmc.RegularMesh) + assert np.array_equal(mesh.dimension, (10, 10, 10)) # default values + assert np.array_equal(mesh.lower_left, universe.bounding_box[0]) + assert np.array_equal(mesh.upper_right, universe.bounding_box[1]) + + +def test_reg_mesh_from_geometry(): + """Tests a RegularMesh can be made from a Geometry and the default + dimensions are propagated through. Geometry is centralized""" + surface = openmc.Sphere(r=42) + cell = openmc.Cell(region=-surface) + universe = openmc.Universe(cells=[cell]) + geometry = openmc.Geometry(universe) + + mesh = openmc.RegularMesh.from_domain(geometry) + assert isinstance(mesh, openmc.RegularMesh) + assert np.array_equal(mesh.dimension, (10, 10, 10)) # default values + assert np.array_equal(mesh.lower_left, geometry.bounding_box[0]) + assert np.array_equal(mesh.upper_right, geometry.bounding_box[1]) + + +def test_error_from_unsupported_object(): + with pytest.raises(TypeError): + openmc.RegularMesh.from_domain("vacuum energy") + + +def test_regularmesh_from_domain_error_from_small_dimensions(): + surface = openmc.Sphere(r=20) + cell = openmc.Cell(region=-surface) + with pytest.raises( + ValueError, match='Unable to set "dimension" to "-2" since it is less than "1"' + ): + openmc.RegularMesh.from_domain(domain=cell, dimension=-2) + + +def test_dimensions_from_domain_dimensions_from_int(): + region = openmc.model.RectangularParallelepiped( + xmin=-100, + xmax=150, + ymin=-50, + ymax=200, + zmin=300, + zmax=400, + boundary_type="vacuum", + ) + cell = openmc.Cell(region=-region) + mesh = openmc.RegularMesh.from_domain(domain=cell, dimension=1000) + assert mesh.dimension == (14, 14, 5) diff --git a/tests/unit_tests/test_mesh_tets.e b/tests/unit_tests/test_mesh_tets.e new file mode 120000 index 0000000000..8a6287b4de --- /dev/null +++ b/tests/unit_tests/test_mesh_tets.e @@ -0,0 +1 @@ +../regression_tests/unstructured_mesh/test_mesh_tets.e \ No newline at end of file diff --git a/tests/unit_tests/test_model.py b/tests/unit_tests/test_model.py index ff446a83d1..60f8b1a25a 100644 --- a/tests/unit_tests/test_model.py +++ b/tests/unit_tests/test_model.py @@ -1,5 +1,6 @@ from math import pi from pathlib import Path +import os import numpy as np import pytest @@ -34,8 +35,8 @@ def pin_model_attributes(): pitch = 1.25984 fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR') clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR') - box = openmc.model.rectangular_prism(pitch, pitch, - boundary_type='reflective') + box = openmc.model.RectangularPrism(pitch, pitch, + boundary_type='reflective') # Define cells fuel_inf_cell = openmc.Cell(cell_id=1, name='inf fuel', fill=uo2) @@ -43,7 +44,7 @@ def pin_model_attributes(): fuel = openmc.Cell(cell_id=2, name='fuel', fill=fuel_inf_univ, region=-fuel_or) clad = openmc.Cell(cell_id=3, fill=zirc, region=+fuel_or & -clad_or) - water = openmc.Cell(cell_id=4, fill=borated_water, region=+clad_or & box) + water = openmc.Cell(cell_id=4, fill=borated_water, region=+clad_or & -box) # Define overall geometry geom = openmc.Geometry([fuel, clad, water]) @@ -56,9 +57,9 @@ def pin_model_attributes(): # Create a uniform spatial source distribution over fissionable zones bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1] - uniform_dist = openmc.stats.Box( - bounds[:3], bounds[3:], only_fissionable=True) - settings.source = openmc.source.Source(space=uniform_dist) + uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) + settings.source = openmc.IndependentSource( + space=uniform_dist, constraints={'fissionable': True}) entropy_mesh = openmc.RegularMesh() entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50] @@ -250,10 +251,11 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): model = openmc.examples.pwr_pin_cell() model.init_lib(output=False, intracomm=mpi_intracomm) - # Change fuel temperature and density and export properties + # Change cell fuel temperature, density, material density and export properties cell = openmc.lib.cells[1] cell.set_temperature(600.0) cell.fill.set_density(5.0, 'g/cm3') + cell.set_density(10.0) openmc.lib.export_properties(output=False) # Import properties to existing model @@ -263,9 +265,11 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): # First python cell = model.geometry.get_all_cells()[1] assert cell.temperature == [600.0] + assert cell.density == [pytest.approx(10.0, 1e-5)] assert cell.fill.get_mass_density() == pytest.approx(5.0) # Now C assert openmc.lib.cells[1].get_temperature() == 600. + assert openmc.lib.cells[1].get_density() == pytest.approx(10.0, 1e-5) assert openmc.lib.materials[1].get_density('g/cm3') == pytest.approx(5.0) # Clear the C API @@ -282,6 +286,7 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm): ) cell = model_with_properties.geometry.get_all_cells()[1] assert cell.temperature == [600.0] + assert cell.density == [pytest.approx(10.0, 1e-5)] assert cell.fill.get_mass_density() == pytest.approx(5.0) @@ -293,14 +298,14 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # C API execution modes and ensuring they give the same result. sp_path = test_model.run(output=False) with openmc.StatePoint(sp_path) as sp: - cli_keff = sp.k_combined + cli_keff = sp.keff cli_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] cli_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0] test_model.init_lib(output=False, intracomm=mpi_intracomm) sp_path = test_model.run(output=False) with openmc.StatePoint(sp_path) as sp: - lib_keff = sp.k_combined + lib_keff = sp.keff lib_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] lib_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0] @@ -391,16 +396,14 @@ def test_py_lib_attributes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert openmc.lib.materials[1].get_density('atom/b-cm') == \ pytest.approx(0.06891296988603757, abs=1e-13) mat_a_dens = np.sum( - [v[1] for v in test_model.materials[0]. - get_nuclide_atom_densities().values()]) + list(test_model.materials[0].get_nuclide_atom_densities().values())) assert mat_a_dens == pytest.approx(0.06891296988603757, abs=1e-8) # Change the density test_model.update_densities(['UO2'], 2.) assert openmc.lib.materials[1].get_density('atom/b-cm') == \ pytest.approx(2., abs=1e-13) mat_a_dens = np.sum( - [v[1] for v in test_model.materials[0]. - get_nuclide_atom_densities().values()]) + list(test_model.materials[0].get_nuclide_atom_densities().values())) assert mat_a_dens == pytest.approx(2., abs=1e-8) # Now lets do the cell temperature updates. @@ -441,7 +444,7 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): test_model = openmc.Model(geom, mats, settings, tals, plots) initial_mat = mats[0].clone() - initial_u = initial_mat.get_nuclide_atom_densities()['U235'][1] + initial_u = initial_mat.get_nuclide_atom_densities()['U235'] # Note that the chain file includes only U-235 fission to a stable Xe136 w/ # a yield of 100%. Thus all the U235 we lose becomes Xe136 @@ -449,32 +452,46 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): # In this test we first run without pre-initializing the shared library # data and then compare. Then we repeat with the C API already initialized # and make sure we get the same answer - test_model.deplete([1e6], 'predictor', final_step=False, + test_model.deplete(timesteps=[1e6], method='predictor', final_step=False, operator_kwargs=op_kwargs, power=1., output=False) # Get the new Xe136 and U235 atom densities - after_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1] - after_u = mats[0].get_nuclide_atom_densities()['U235'][1] + after_xe = mats[0].get_nuclide_atom_densities()['Xe136'] + after_u = mats[0].get_nuclide_atom_densities()['U235'] assert after_xe + after_u == pytest.approx(initial_u, abs=1e-15) assert test_model.is_initialized is False + # check the tally output + def check_tally_output(): + with openmc.StatePoint('openmc_simulation_n0.h5') as sp: + flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0] + fission = sp.get_tally(id=1).get_values( + scores=['fission'])[0, 0, 0] + + # we're mainly just checking that the result was produced, + # so a rough numerical comparison doesn't hurt to have. + assert flux == pytest.approx(13.1, abs=0.2) + assert fission == pytest.approx(0.47, abs=0.2) + + check_tally_output() + # Reset the initial material densities mats[0].nuclides.clear() densities = initial_mat.get_nuclide_atom_densities() tot_density = 0. - for nuc, density in densities.values(): + for nuc, density in densities.items(): mats[0].add_nuclide(nuc, density) tot_density += density mats[0].set_density('atom/b-cm', tot_density) # Now we can re-run with the pre-initialized API test_model.init_lib(output=False, intracomm=mpi_intracomm) - test_model.deplete([1e6], 'predictor', final_step=False, + test_model.deplete(timesteps=[1e6], method='predictor', final_step=False, operator_kwargs=op_kwargs, power=1., output=False) # Get the new Xe136 and U235 atom densities - after_lib_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1] - after_lib_u = mats[0].get_nuclide_atom_densities()['U235'][1] + after_lib_xe = mats[0].get_nuclide_atom_densities()['Xe136'] + after_lib_u = mats[0].get_nuclide_atom_densities()['U235'] assert after_lib_xe + after_lib_u == pytest.approx(initial_u, abs=1e-15) assert test_model.is_initialized is True @@ -482,6 +499,8 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert after_xe == pytest.approx(after_lib_xe, abs=1e-15) assert after_u == pytest.approx(after_lib_u, abs=1e-15) + check_tally_output() + test_model.finalize_lib() @@ -531,3 +550,366 @@ def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm): assert openmc.lib.materials[3].volume == mats[2].volume test_model.finalize_lib() + + +def test_model_xml(run_in_tmpdir): + + # load a model from examples + pwr_model = openmc.examples.pwr_core() + + # export to separate XMLs manually + pwr_model.settings.export_to_xml('settings_ref.xml') + pwr_model.materials.export_to_xml('materials_ref.xml') + pwr_model.geometry.export_to_xml('geometry_ref.xml') + + # now write and read a model.xml file + pwr_model.export_to_model_xml() + new_model = openmc.Model.from_model_xml() + + # make sure we can also export this again to separate + # XML files + new_model.export_to_xml() + + +def test_single_xml_exec(run_in_tmpdir): + + pincell_model = openmc.examples.pwr_pin_cell() + + pincell_model.export_to_model_xml('pwr_pincell.xml') + + openmc.run(path_input='pwr_pincell.xml') + + with pytest.raises(RuntimeError, match='ex-em-ell.xml'): + openmc.run(path_input='ex-em-ell.xml') + + # test that a file in a different directory can be used + os.mkdir('inputs') + pincell_model.export_to_model_xml('./inputs/pincell.xml') + openmc.run(path_input='./inputs/pincell.xml') + + with pytest.raises(RuntimeError, match='input_dir'): + openmc.run(path_input='input_dir/pincell.xml') + + # Make sure path can be specified with run + pincell_model.run(path='my_model.xml') + + os.mkdir('subdir') + pincell_model.run(path='subdir') + + +def test_nuclides_to_ignore(run_in_tmpdir, pin_model_attributes): + """Test nuclides_to_ignore when exporting a model XML""" + materials, geometry, settings = pin_model_attributes[:3] + model = openmc.Model(geometry=geometry, settings=settings) + + # grab one of the nuclides present in this model as a test + test_nuclide = list(materials[0].get_nuclides())[0] + + # exclude the test nuclide from the XML file during export + model.export_to_model_xml(nuclides_to_ignore=[test_nuclide]) + + # ensure that the nuclide doesn't appear after reading in + # the resulting XML model + xml_model = openmc.Model.from_model_xml() + for material in xml_model.materials: + assert test_nuclide not in material.get_nuclides() + + +def test_model_plot(): + # plots the geometry with source location and checks the resulting + # matplotlib includes the correct coordinates for the scatter plot for all + # basis. + + surface = openmc.Sphere(r=600, boundary_type="vacuum") + cell = openmc.Cell(region=-surface) + geometry = openmc.Geometry([cell]) + source = openmc.IndependentSource(space=openmc.stats.Point((1, 2, 3))) + settings = openmc.Settings(particles=1, batches=1, source=source) + model = openmc.Model(geometry, settings=settings) + + plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="xy") + coords = plot.axes.collections[0].get_offsets().data.flatten() + assert (coords == np.array([1.0, 2.0])).all() + + plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="xz") + coords = plot.axes.collections[0].get_offsets().data.flatten() + assert (coords == np.array([1.0, 3.0])).all() + + plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="yz") + coords = plot.axes.collections[0].get_offsets().data.flatten() + assert (coords == np.array([2.0, 3.0])).all() + + plot = model.plot(n_samples=1, plane_tolerance=0.1, basis="xy") + coords = plot.axes.collections[0].get_offsets().data.flatten() + assert (coords == np.array([])).all() + + # modify model to include another cell that overlaps the original cell entirely + model.geometry.root_universe.add_cell(openmc.Cell(region=-surface)) + axes = model.plot(show_overlaps=True) + white = np.array((1.0, 1.0, 1.0)) + red = np.array((1.0, 0.0, 0.0)) + axes_image = axes.get_images()[0] + image_data = axes_image.get_array() + # ensure that all of the data in the image data is either white or red + test_mask = (image_data == white) | (image_data == red) + assert np.all(test_mask), "Colors other than white or red found in overlap plot image" + + # Close plots to avoid warning + import matplotlib.pyplot as plt + plt.close('all') + + +def test_model_id_map_initialization(run_in_tmpdir): + model = openmc.examples.pwr_assembly() + model.init_lib(output=False) + + id_map = model.id_map( + pixels=(100, 100), + basis='xy', + origin=(0, 0, 0), + width=(10, 10), + ) + + assert id_map.shape == (100, 100, 3) + assert id_map.dtype == np.int32 + + max_cell_id = max(model.geometry.get_all_cells().keys()) + max_material_id = max(model.geometry.get_all_materials().keys()) + + # add some spot checks for the id_map + # Check that the array contains valid cell/material IDs (not all -2) + # The -2 values indicate outside the geometry + assert not np.all(id_map == -2), "All values are -2, indicating no valid geometry found" + + # Check that we have valid cell IDs (first dimension) + valid_cell_ids = id_map[:, :, 0] + assert np.any(valid_cell_ids >= 0), "No valid cell IDs found in the id_map" + + # Check that we have valid material IDs (third dimension) + valid_material_ids = id_map[:, :, 2] + assert np.any(valid_material_ids >= 0), "No valid material IDs found in the id_map" + + # Check that the middle dimension (cell instances) is consistent + # Cell instances should be >= 0 when cell IDs are valid + cell_instances = id_map[:, :, 1] + valid_cells = valid_cell_ids >= 0 + if np.any(valid_cells): + assert np.all(cell_instances[valid_cells] >= 0), "Invalid cell instances found for valid cells" + + # Check that the array contains reasonable ranges of values + # Cell IDs should be within the expected range for the assembly + if np.any(valid_cell_ids >= 0): + max_map_cell_id = np.max(valid_cell_ids) + assert max_map_cell_id <= max_cell_id, \ + f"Cell ID {max_map_cell_id} in the map is greater than the maximum cell ID {max_cell_id}" + + # Material IDs should be within the expected range + if np.any(valid_material_ids >= 0): + max_map_material_id = np.max(valid_material_ids) + assert max_map_material_id <= max_material_id, \ + f"Material ID {max_map_material_id} in the map is greater than the maximum material ID {max_material_id}" + + # Test id_map with pixels outside the model geometry + # Use a plot that's far from the model center to ensure we get -2 values + outside_id_map = model.id_map( + pixels=(50, 50), + basis='xy', + origin=(1000, 1000, 0), # Far from the model center + width=(10, 10), + ) + + assert outside_id_map.shape == (50, 50, 3) + assert outside_id_map.dtype == np.int32 + + # All values should be -2 (outside geometry) for this plot + assert np.all(outside_id_map == -2), "Expected all values to be -2 for plot outside model geometry" + + # Verify that the outside plot has the correct structure + assert np.all(outside_id_map[:, :, 0] == -2), "Cell IDs should all be -2 outside geometry" + assert np.all(outside_id_map[:, :, 1] == -2), "Cell instances should all be -2 outside geometry" + assert np.all(outside_id_map[:, :, 2] == -2), "Material IDs should all be -2 outside geometry" + + # if the model is already initialized, it should not be finalized + # after calling this method + model.id_map( + pixels=(100, 100), + basis='xy', + origin=(0, 0, 0), + width=(10, 10), + ) + assert model.is_initialized + + # if the model is not initialized, it should be finalized + # before exiting this method + model.finalize_lib() + model.id_map( + pixels=(100, 100), + basis='xy', + origin=(0, 0, 0), + width=(10, 10), + ) + assert not model.is_initialized + + +def test_id_map_aligned_model(): + """Test id_map with a 2x2 lattice where pixel boundaries align to cell boundaries""" + # Create materials -- identical compositions, different IDs + mat1 = openmc.Material(material_id=1, name='Material 1') + mat1.set_density('g/cm3', 1.0) + mat1.add_element('H', 1.0) + + mat2 = openmc.Material(material_id=2, name='Material 2') + mat2.set_density('g/cm3', 1.0) + mat2.add_element('H', 1.0) + + mat3 = openmc.Material(material_id=3, name='Material 3') + mat3.set_density('g/cm3', 1.0) + mat3.add_element('H', 1.0) + + mat4 = openmc.Material(material_id=4, name='Material 4') + mat4.set_density('g/cm3', 1.0) + mat4.add_element('H', 1.0) + + outer_mat = openmc.Material(material_id=5, name='Material 5') + outer_mat.set_density('g/cm3', 1.0) + outer_mat.add_element('H', 1.0) + + inner_materials = [mat1, mat2, mat3, mat4] + + # Create square surface that fits inside the lattice cell + # Lattice cell is 1 cm x 1 cm, so square will be 0.6 cm x 0.6 cm centered on the origin + square = openmc.model.RectangularPrism(0.6, 0.6, boundary_type='transmission') + + # Create cells for this universe + inner_cell = openmc.Cell(cell_id=10, region=-square, name='inner_cell') + inner_cell.fill = inner_materials + + outer_cell = openmc.Cell(cell_id=20, region=+square, name='outer_cell') + outer_cell.fill = outer_mat + + # Create universe + universe = openmc.Universe(universe_id=100, cells=[inner_cell, outer_cell]) + + # Create 2x2 lattice + lattice = openmc.RectLattice(lattice_id=1) + lattice.lower_left = [-1.0, -1.0] + lattice.pitch = [1.0, 1.0] + lattice.universes = [[universe, universe], [universe, universe]] + + # Create outer boundary + outer_boundary = openmc.model.RectangularPrism(2.0, 2.0, boundary_type='vacuum') + + # Create root cell + root_cell = openmc.Cell(cell_id=1, name='root', fill=lattice, region=-outer_boundary) + + # Create geometry + geometry = openmc.Geometry([root_cell]) + + # Create settings + settings = openmc.Settings() + settings.particles = 1000 + settings.batches = 10 + + # Create model + model = openmc.Model(settings=settings, geometry=geometry) + + # Generate id_map with pixel boundaries aligned to cell boundaries + # The model is 2 cm x 2 cm, so we'll use 200x200 pixels to get 0.01 cm resolution + # This allows us to align pixels with the squares inside each lattice cell + id_map = model.id_map( + pixels=(200, 200), + basis='xy', + origin=(0.0, 0.0, 0.0), # Align with lattice lower_left + width=(2.0, 2.0), # Align with lattice size + ) + + # Verify id_map properties + assert id_map.shape == (200, 200, 3) + assert id_map.dtype == np.int32 + + cell_id_map = id_map[:, :, 0] + material_ids_map = id_map[:, :, 2] + + # Check that we have valid cell IDs (not all -2) + assert np.any(cell_id_map >= 0), "No valid cell IDs found in the id_map" + + # Check that we have valid material IDs + assert np.any(material_ids_map >= 0), "No valid material IDs found in the id_map" + + # Check that the expected cell IDs are present + expected_cell_ids = [10, 20] # Root cell, inner cell, outer cell + found_cell_ids = np.unique(cell_id_map[cell_id_map >= 0]) + for cell_id in expected_cell_ids: + assert cell_id in found_cell_ids, f"Expected cell ID {cell_id} not found in id_map" + + # Check that the expected material IDs are present + expected_material_ids = [1, 2, 3, 4, 5] # All materials defined above + found_material_ids = np.unique(material_ids_map[material_ids_map >= 0]) + for mat_id in expected_material_ids: + assert mat_id in found_material_ids, f"Expected material ID {mat_id} not found in id_map" + + # Test specific regions to verify lattice structure + # Check center of each lattice cell (should be inner cells) + # Lattice cell centers are at (-0.5, -0.5), (0.5, -0.5), (-0.5, 0.5), (0.5, 0.5) + # With 200x200 pixels over 2x2 units, each pixel is 0.01 units + + # Bottom-left lattice cell center (should be inner cell 10) + bl_cell, bl_instance, bl_material = id_map[-50, 50] + assert bl_cell == 10, f"Expected cell ID 10 at bottom-left center, got {bl_cell}" + assert bl_instance == 0, f"Expected cell instance 0 at bottom-left center, got {bl_instance}" + assert bl_material == 1, f"Expected material ID 1 at bottom-left center, got {bl_material}" + + # Bottom-right lattice cell center (should be inner cell 10) + br_cell, br_instance, br_material = id_map[-50, 150] + assert br_cell == 10, f"Expected cell ID 10 at bottom-right center, got {br_cell}" + assert br_instance == 1, f"Expected cell instance 1 at bottom-right center, got {br_instance}" + assert br_material == 2, f"Expected material ID 2 at bottom-right center, got {br_material}" + + # Top-left lattice cell center (should be inner cell 10) + tl_cell, tl_instance, tl_material = id_map[-150, 50] + assert tl_cell == 10, f"Expected cell ID 10 at top-left center, got {tl_cell}" + assert tl_instance == 2, f"Expected cell instance 2 at top-left center, got {tl_instance}" + assert tl_material == 3, f"Expected material ID 3 at top-left center, got {tl_material}" + + # Top-right lattice cell center (should be inner cell 10) + tr_cell, tr_instance, tr_material = id_map[-150, 150] + assert tr_cell == 10, f"Expected cell ID 10 at top-right center, got {tr_cell}" + assert tr_instance == 3, f"Expected cell instance 3 at top-right center, got {tr_instance}" + assert tr_material == 4, f"Expected material ID 4 at top-right center, got {tr_material}" + + # Check that the model is properly finalized after id_map call + assert not model.is_initialized, "Model should be finalized after id_map call" + + # Check that the values at the corners are correctly set as the outer cell and material + bl_cell, bl_instance, bl_material = id_map[-1, 0] + assert bl_cell == 20, f"Expected cell ID 20 at bottom-left corner, got {bl_cell}" + assert bl_instance == 0, f"Expected cell instance 0 at bottom-left corner, got {bl_instance}" + assert bl_material == 5, f"Expected material ID 5 at bottom-left corner, got {bl_material}" + + br_cell, br_instance, br_material = id_map[-1, -1] + assert br_cell == 20, f"Expected cell ID 20 at bottom-right corner, got {br_cell}" + assert br_instance == 1, f"Expected cell instance 1 at bottom-right corner, got {br_instance}" + assert br_material == 5, f"Expected material ID 5 at bottom-right corner, got {br_material}" + + tl_cell, tl_instance, tl_material = id_map[0, 0] + assert tl_cell == 20, f"Expected cell ID 20 at top-left corner, got {tl_cell}" + assert tl_instance == 2, f"Expected cell instance 2 at top-left corner, got {tl_instance}" + assert tl_material == 5, f"Expected material ID 5 at top-left corner, got {tl_material}" + + tr_cell, tr_instance, tr_material = id_map[0, -1] + assert tr_cell == 20, f"Expected cell ID 20 at top-right corner, got {tr_cell}" + assert tr_instance == 3, f"Expected cell instance 3 at top-right corner, got {tr_instance}" + assert tr_material == 5, f"Expected material ID 5 at top-right corner, got {tr_material}" + +def test_setter_from_list(): + mat = openmc.Material() + model = openmc.Model(materials=[mat]) + assert isinstance(model.materials, openmc.Materials) + + tally = openmc.Tally() + model = openmc.Model(tallies=[tally]) + assert isinstance(model.tallies, openmc.Tallies) + + plot = openmc.Plot() + model = openmc.Model(plots=[plot]) + assert isinstance(model.plots, openmc.Plots) diff --git a/tests/unit_tests/test_no_visible_boundary.py b/tests/unit_tests/test_no_visible_boundary.py new file mode 100644 index 0000000000..7c53e4e3fa --- /dev/null +++ b/tests/unit_tests/test_no_visible_boundary.py @@ -0,0 +1,30 @@ +import openmc + + +def test_no_visible_boundary(run_in_tmpdir): + copper = openmc.Material() + copper.add_nuclide('Cu63', 1.0) + copper.set_density('g/cm3', 0.3) + air = openmc.Material() + air.add_nuclide('N14', 1.0) + air.set_density('g/cm3', 0.0012) + + # Create a simple model of a neutron source directly impinging on a thin + # disc of copper. Neutrons leaving the back of the disc see no surfaces in + # front of them. + disc = openmc.model.RightCircularCylinder((0., 0., 1.), 0.1, 1.2) + box = openmc.model.RectangularPrism(width=10, height=10, boundary_type='vacuum') + c1 = openmc.Cell(fill=copper, region=-disc) + c2 = openmc.Cell(fill=air, region=+disc & -box) + model = openmc.Model() + model.geometry = openmc.Geometry([c1, c2]) + model.settings.run_mode = 'fixed source' + model.settings.particles = 1000 + model.settings.batches = 5 + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), + angle=openmc.stats.Monodirectional((0., 0., 1.)) + ) + + # Run model to ensure it doesn't segfault + model.run() diff --git a/tests/unit_tests/test_nuclide_heating.py b/tests/unit_tests/test_nuclide_heating.py new file mode 100644 index 0000000000..e00d57b4ff --- /dev/null +++ b/tests/unit_tests/test_nuclide_heating.py @@ -0,0 +1,39 @@ +import openmc +from pytest import approx + + +def test_nuclide_heating(run_in_tmpdir): + mat = openmc.Material() + mat.add_nuclide("Li6", 0.5) + mat.add_nuclide("Li7", 0.5) + mat.set_density("g/cm3", 1.0) + + sphere = openmc.Sphere(r=20, boundary_type="reflective") + inside_sphere = openmc.Cell(fill=mat, region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([inside_sphere]) + + model.settings.particles = 1000 + model.settings.batches = 1 + model.settings.photon_transport = True + model.settings.electron_treatment = "ttb" + model.settings.cutoff = {"energy_photon": 1000} + model.settings.run_mode = "fixed source" + model.settings.source = openmc.IndependentSource( + energy=openmc.stats.delta_function(10.0e6), + particle="photon" + ) + + # Create two tallies, one with heating by nuclide and one with total heating + tally1 = openmc.Tally() + tally1.scores = ["heating"] + tally1.nuclides = mat.get_nuclides() + tally2 = openmc.Tally() + tally2.scores = ["heating"] + model.tallies = [tally1, tally2] + + # Run the model + model.run(apply_tally_results=True) + + # Make sure the heating results are consistent + assert tally1.mean.sum() == approx(tally2.mean.sum()) diff --git a/tests/unit_tests/test_photon_heating.py b/tests/unit_tests/test_photon_heating.py new file mode 100644 index 0000000000..05473f5c6f --- /dev/null +++ b/tests/unit_tests/test_photon_heating.py @@ -0,0 +1,30 @@ +import openmc + + +def test_negative_positron_heating(): + m = openmc.Material() + m.add_element('Li', 1.0) + m.set_density('g/cm3', 10.0) + + surf = openmc.Sphere(r=100.0, boundary_type='reflective') + cell = openmc.Cell(fill=m, region=-surf) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Point(), + energy=openmc.stats.Discrete([5.0e6], [1.0]), + particle='photon', + ) + model.settings.particles = 7 + model.settings.batches = 1 + model.settings.electron_treatment = 'led' + model.settings.seed = 513836 + + tally = openmc.Tally() + tally.filters = [openmc.ParticleFilter(['photon', 'electron', 'positron'])] + tally.scores = ['heating'] + model.tallies = openmc.Tallies([tally]) + model.run(apply_tally_results=True) + + assert (tally.mean >= 0.0).all(), "Negative heating detected" diff --git a/tests/unit_tests/test_plots.py b/tests/unit_tests/test_plots.py index 3288617b08..fad574ee69 100644 --- a/tests/unit_tests/test_plots.py +++ b/tests/unit_tests/test_plots.py @@ -1,7 +1,11 @@ +from pathlib import Path + import openmc import openmc.examples import pytest +from openmc.plots import _SVG_COLORS + @pytest.fixture(scope='module') def myplot(): @@ -9,7 +13,7 @@ def myplot(): plot.width = (100., 100.) plot.origin = (2., 3., -10.) plot.pixels = (500, 500) - plot.filename = 'myplot' + plot.filename = './not-a-dir/myplot' plot.type = 'slice' plot.basis = 'yz' plot.background = 'black' @@ -38,15 +42,109 @@ def myplot(): return plot +@pytest.fixture(scope='module') +def myprojectionplot(): + plot = openmc.WireframeRayTracePlot(name='myprojectionplot') + plot.look_at = (0.0, 0.0, 0.0) + plot.camera_position = (4.0, 3.0, 0.0) + plot.pixels = (500, 500) + plot.filename = 'myprojectionplot' + plot.background = (0, 0, 0) + plot.background = 'black' + + plot.color_by = 'material' + m1, m2 = openmc.Material(), openmc.Material() + plot.colors = {m1: (0, 255, 0), m2: (0, 0, 255)} + plot.colors = {m1: 'green', m2: 'blue'} + plot.xs = {m1: 1.0, m2: 0.01} + + plot.mask_components = [openmc.Material()] + plot.mask_background = (255, 255, 255) + plot.mask_background = 'white' + + plot.overlap_color = (255, 211, 0) + plot.overlap_color = 'yellow' + + plot.wireframe_thickness = 2 + + plot.level = 1 + return plot + + +def test_voxel_plot(run_in_tmpdir): + # attempt to preload VTK and skip this test if unavailable + vtk = pytest.importorskip('vtk') + surf1 = openmc.Sphere(r=500, boundary_type='vacuum') + cell1 = openmc.Cell(region=-surf1) + geometry = openmc.Geometry([cell1]) + geometry.export_to_xml() + materials = openmc.Materials() + materials.export_to_xml() + vox_plot = openmc.Plot() + vox_plot.type = 'voxel' + vox_plot.id = 12 + vox_plot.width = (1500., 1500., 1500.) + vox_plot.pixels = (200, 200, 200) + vox_plot.color_by = 'cell' + vox_plot.to_vtk('test_voxel_plot.vti') + + assert Path('plot_12.h5').is_file() + assert Path('test_voxel_plot.vti').is_file() + + vox_plot.filename = 'h5_voxel_plot' + vox_plot.to_vtk(Path('another_test_voxel_plot.vti')) + + assert Path('h5_voxel_plot.h5').is_file() + assert Path('another_test_voxel_plot.vti').is_file() + + slice_plot = openmc.Plot() + with pytest.raises(ValueError): + slice_plot.to_vtk('shimmy.vti') + + def test_attributes(myplot): assert myplot.name == 'myplot' +def test_attributes_proj(myprojectionplot): + assert myprojectionplot.name == 'myprojectionplot' + + def test_repr(myplot): r = repr(myplot) assert isinstance(r, str) +def test_repr_proj(myprojectionplot): + r = repr(myprojectionplot) + assert isinstance(r, str) + + +def test_projection_plot_roundtrip(myprojectionplot): + + elem = myprojectionplot.to_xml_element() + + xml_plot = openmc.WireframeRayTracePlot.from_xml_element(elem) + + svg_colors = _SVG_COLORS + + assert xml_plot.name == myprojectionplot.name + assert xml_plot.look_at == myprojectionplot.look_at + assert xml_plot.camera_position == myprojectionplot.camera_position + assert xml_plot.pixels == myprojectionplot.pixels + assert xml_plot.filename == myprojectionplot.filename + assert xml_plot.background == svg_colors[myprojectionplot.background] + assert xml_plot.color_by == myprojectionplot.color_by + expected_colors = {m.id: svg_colors[c] for m, c in myprojectionplot.colors.items()} + assert xml_plot.colors == expected_colors + # TODO: needs geometry information + # assert xml_plot.mask_components == myprojectionplot.mask_components + assert xml_plot.mask_background == svg_colors[myprojectionplot.mask_background] + # assert xml_plot.overlap_color == svg_colors[myprojectionplot.overlap_color] + assert xml_plot.wireframe_thickness == myprojectionplot.wireframe_thickness + assert xml_plot.level == myprojectionplot.level + + def test_from_geometry(): width = 25. s = openmc.Sphere(r=width/2, boundary_type='vacuum') @@ -58,6 +156,7 @@ def test_from_geometry(): plot = openmc.Plot.from_geometry(geom, basis) assert plot.origin == pytest.approx((0., 0., 0.)) assert plot.width == pytest.approx((width, width)) + assert plot.basis == basis def test_highlight_domains(): @@ -88,22 +187,122 @@ def test_xml_element(myplot): assert getattr(newplot, attr) == getattr(myplot, attr), attr +def test_to_xml_element_proj(myprojectionplot): + elem = myprojectionplot.to_xml_element() + assert 'id' in elem.attrib + assert 'color_by' in elem.attrib + assert 'type' in elem.attrib + assert elem.find('camera_position') is not None + assert elem.find('wireframe_thickness') is not None + assert elem.find('look_at') is not None + assert elem.find('pixels') is not None + assert elem.find('background').text == '0 0 0' + + def test_plots(run_in_tmpdir): p1 = openmc.Plot(name='plot1') p1.origin = (5., 5., 5.) + p1.colors = {10: (255, 100, 0)} + p1.mask_components = [2, 4, 6] p2 = openmc.Plot(name='plot2') p2.origin = (-3., -3., -3.) plots = openmc.Plots([p1, p2]) assert len(plots) == 2 - p3 = openmc.Plot(name='plot3') - plots.append(p3) + p3 = openmc.WireframeRayTracePlot(name='plot3') + plots = openmc.Plots([p1, p2, p3]) assert len(plots) == 3 + p4 = openmc.Plot(name='plot4') + plots.append(p4) + assert len(plots) == 4 + plots.export_to_xml() # from_xml new_plots = openmc.Plots.from_xml() - assert len(plots) - assert plots[0].origin == p1.origin - assert plots[1].origin == p2.origin + assert len(new_plots) + assert new_plots[0].origin == p1.origin + assert new_plots[0].colors == p1.colors + assert new_plots[0].mask_components == p1.mask_components + assert new_plots[1].origin == p2.origin + + +def test_voxel_plot_roundtrip(): + # Define a voxel plot and create XML element + plot = openmc.Plot(name='my voxel plot') + plot.type = 'voxel' + plot.filename = 'voxel1' + plot.pixels = (50, 50, 50) + plot.origin = (0., 0., 0.) + plot.width = (75., 75., 75.) + plot.color_by = 'material' + elem = plot.to_xml_element() + + # Read back from XML and make sure it hasn't changed + new_plot = plot.from_xml_element(elem) + assert new_plot.name == plot.name + assert new_plot.filename == plot.filename + assert new_plot.type == plot.type + assert new_plot.pixels == plot.pixels + assert new_plot.origin == plot.origin + assert new_plot.width == plot.width + assert new_plot.color_by == plot.color_by + + +def test_phong_plot_roundtrip(): + plot = openmc.SolidRayTracePlot(name='my phong plot') + plot.id = 2300 + plot.filename = 'phong1' + plot.pixels = (50, 50) + plot.look_at = (11., 12., 13.) + plot.camera_position = (22., 23., 24.) + plot.diffuse_fraction = 0.5 + plot.horizontal_field_of_view = 90.0 + plot.color_by = 'material' + plot.light_position = (8., 9., 10.) + plot.opaque_domains = [6, 7, 8] + + elem = plot.to_xml_element() + + repr(plot) + + new_plot = openmc.SolidRayTracePlot.from_xml_element(elem) + + assert new_plot.name == plot.name + assert new_plot.id == plot.id + assert new_plot.filename == plot.filename + assert new_plot.pixels == plot.pixels + assert new_plot.look_at == plot.look_at + assert new_plot.camera_position == plot.camera_position + assert new_plot.diffuse_fraction == plot.diffuse_fraction + assert new_plot.horizontal_field_of_view == plot.horizontal_field_of_view + assert new_plot.color_by == plot.color_by + assert new_plot.light_position == plot.light_position + assert new_plot.opaque_domains == plot.opaque_domains + + # ensure the new object is valid to re-write to XML + new_elem = new_plot.to_xml_element() + + +def test_plot_directory(run_in_tmpdir): + pwr_pin = openmc.examples.pwr_pin_cell() + + # create a standard plot, expected to work + plot = openmc.Plot() + plot.filename = 'plot_1' + plot.type = 'slice' + plot.pixels = (10, 10) + plot.color_by = 'material' + plot.width = (100., 100.) + pwr_pin.plots = [plot] + pwr_pin.plot_geometry() + + # use current directory, also expected to work + plot.filename = './plot_1' + pwr_pin.plot_geometry() + + # use a non-existent directory, should raise an error + plot.filename = './not-a-dir/plot_1' + with pytest.raises(RuntimeError, match='does not exist'): + pwr_pin.plot_geometry() diff --git a/tests/unit_tests/test_plotter.py b/tests/unit_tests/test_plotter.py new file mode 100644 index 0000000000..0220cec3e7 --- /dev/null +++ b/tests/unit_tests/test_plotter.py @@ -0,0 +1,183 @@ +import numpy as np +import openmc +import pytest + + +@pytest.fixture(scope='module') +def test_mat(): + mat_1 = openmc.Material() + mat_1.add_element("H", 4.0, "ao") + mat_1.add_element("O", 4.0, "ao") + mat_1.add_element("C", 4.0, "ao") + return mat_1 + + +def test_calculate_cexs_elem_mat_sab(test_mat): + """Checks that sab cross sections are included in the + _calculate_cexs_elem_mat method and have the correct shape""" + + test_mat.add_s_alpha_beta("c_C6H6") + test_mat.set_density("g/cm3", 0.865) + + energy_grid, data = openmc.plotter._calculate_cexs_elem_mat( + test_mat, + ["inelastic"], + sab_name="c_C6H6", + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 1 + assert len(data[0]) == len(energy_grid) + + +@pytest.mark.parametrize("this", ["Li", "Li6"]) +def test_calculate_cexs_with_nuclide_and_element(this): + # single type (reaction) + energy_grid, data = openmc.plotter.calculate_cexs( + this=this, types=[205] + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 1 + assert len(data[0]) == len(energy_grid) + + # two types (reactions) + energy_grid, data = openmc.plotter.calculate_cexs( + this=this, types=[2, "elastic"] + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 2 + assert len(data[0]) == len(energy_grid) + assert len(data[0]) == len(energy_grid) + # reactions are both the same MT number 2 is elastic + assert np.array_equal(data[0], data[1]) + + +def test_calculate_cexs_with_materials(test_mat): + energy_grid, data = openmc.plotter.calculate_cexs( + this=test_mat, types=[205] + ) + + assert isinstance(energy_grid, np.ndarray) + assert isinstance(data, np.ndarray) + assert len(energy_grid) > 1 + assert len(data) == 1 + assert len(data[0]) == len(energy_grid) + + +@pytest.mark.parametrize("this", ["Be", "Be9"]) +def test_plot_xs(this): + from matplotlib.figure import Figure + assert isinstance(openmc.plot_xs({this: ['total', 'elastic', 16, '(n,2n)']}), Figure) + + +def test_plot_xs_mat(test_mat): + from matplotlib.figure import Figure + assert isinstance(openmc.plot_xs({test_mat: ['total']}), Figure) + + +@pytest.mark.parametrize("units", ["eV", "keV", "MeV"]) +def test_plot_xs_energy_axis(units): + plot = openmc.plot_xs({'Be9': ['(n,2n)']}, energy_axis_units=units) + axis_text = plot.get_axes()[0].get_xaxis().get_label().get_text() + assert axis_text == f'Energy [{units}]' + + +def test_plot_axes_labels(): + # just nuclides + axis_label = openmc.plotter._get_yaxis_label( + reactions={ + 'Li6': [205], + 'Li7': [205], + }, divisor_types=False + ) + assert axis_label == 'Microscopic Cross Section [b]' + + # just elements + axis_label = openmc.plotter._get_yaxis_label( + reactions={ + 'Li': [205], + 'Be': [16], + }, divisor_types=False + ) + assert axis_label == 'Microscopic Cross Section [b]' + + # mixed nuclide and element + axis_label = openmc.plotter._get_yaxis_label( + reactions={ + 'Li': [205], + 'Li7': [205], + }, divisor_types=False + ) + assert axis_label == 'Microscopic Cross Section [b]' + + axis_label = openmc.plotter._get_yaxis_label( + reactions={ + "Li": ["heating", "heating-local"], + "Li7": ["heating"], + "Be": ["damage-energy"], + }, + divisor_types=False, + ) + assert axis_label == "Heating Cross Section [eV-barn]" + + with pytest.raises(TypeError): + axis_label = openmc.plotter.plot_xs( + reactions={"Li": ["heating", "heating-local"], "Be9": ["(n,2n)"]} + ) + + # just materials + mat1 = openmc.Material() + mat1.add_nuclide('Fe56', 1) + mat1.set_density('g/cm3', 1) + mat2 = openmc.Material() + mat2.add_element('Fe', 1) + mat2.add_nuclide('Fe55', 1) + mat2.set_density('g/cm3', 1) + axis_label = openmc.plotter._get_yaxis_label( + reactions={ + mat1: [205], + mat2: [16], + }, divisor_types=False + ) + assert axis_label == 'Macroscopic Cross Section [1/cm]' + + # mixed materials and nuclides + with pytest.raises(TypeError): + openmc.plotter._get_yaxis_label( + reactions={'Li6': [205], mat2: [16]}, + divisor_types=False + ) + + # mixed materials and elements + with pytest.raises(TypeError): + openmc.plotter._get_yaxis_label( + reactions={'Li': [205], mat2: [16]}, + divisor_types=False + ) + + +def test_get_title(): + title = openmc.plotter._get_title(reactions={'Li': [205]}) + assert title == 'Cross Section Plot For Li' + title = openmc.plotter._get_title(reactions={'Li6': [205]}) + assert title == 'Cross Section Plot For Li6' + title = openmc.plotter._get_title(reactions={ + 'Li6': [205], + 'Li7': [205] + }) + assert title == 'Cross Section Plot' + + mat1 = openmc.Material() + mat1.add_nuclide('Fe56', 1) + mat1.set_density('g/cm3', 1) + mat1.name = 'my_mat' + title = openmc.plotter._get_title(reactions={mat1: [205]}) + assert title == 'Cross Section Plot For my_mat' diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py index 8b2e06ad1b..cb9fa171bb 100644 --- a/tests/unit_tests/test_region.py +++ b/tests/unit_tests/test_region.py @@ -106,7 +106,7 @@ def test_complement(reset): assert_unbounded(outside_equiv) # string represention - assert str(inside) == '~(1 | -2 | 3)' + assert str(inside) == '(-1 2 -3)' # evaluate method assert (0, 0, 0) in inside @@ -200,3 +200,58 @@ def test_from_expression(reset): assert isinstance(r, openmc.Intersection) assert isinstance(r[1], openmc.Union) assert r[1][:] == [-s2, +s3] + + # Make sure ")(" is handled correctly + r = openmc.Region.from_expression('(-1|2)(2|-3)', surfs) + assert str(r) == '((-1 | 2) (2 | -3))' + + # Opening parenthesis immediately after halfspace + r = openmc.Region.from_expression('1(2|-3)', surfs) + assert str(r) == '(1 (2 | -3))' + r = openmc.Region.from_expression('-1|(1 2(-3))', surfs) + assert str(r) == '(-1 | (1 2 -3))' + +def test_translate_inplace(): + sph = openmc.Sphere() + x = openmc.XPlane() + region = -sph & +x + + # Translating a region should produce new surfaces + region2 = region.translate((0.5, -6.7, 3.9), inplace=False) + assert str(region) != str(region2) + + # Translating a region in-place should *not* produce new surfaces + region3 = region.translate((0.5, -6.7, 3.9), inplace=True) + assert str(region) == str(region3) + + +def test_invalid_operands(): + s = openmc.Sphere() + z = 3 + + # Intersection with invalid operand + with pytest.raises(ValueError, match='must be of type Region'): + -s & +z + + # Union with invalid operand + with pytest.raises(ValueError, match='must be of type Region'): + -s | +z + + # Complement with invalid operand + with pytest.raises(ValueError, match='must be of type Region'): + openmc.Complement(z) + + +def test_plot(): + # Create region and plot + region = -openmc.Sphere() & +openmc.XPlane() + c_before = openmc.Cell() + region.plot() + + # Close plot to avoid warning + import matplotlib.pyplot as plt + plt.close() + + # Ensure that calling plot doesn't affect cell ID space + c_after = openmc.Cell() + assert c_after.id - 1 == c_before.id diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index e2c7259e75..fe618fd2d6 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -3,9 +3,7 @@ import openmc.stats def test_export_to_xml(run_in_tmpdir): - s = openmc.Settings() - s.run_mode = 'fixed source' - s.batches = 1000 + s = openmc.Settings(run_mode='fixed source', batches=1000, seed=17) s.generations_per_batch = 10 s.inactive = 100 s.particles = 1000000 @@ -14,21 +12,25 @@ def test_export_to_xml(run_in_tmpdir): s.keff_trigger = {'type': 'std_dev', 'threshold': 0.001} s.energy_mode = 'continuous-energy' s.max_order = 5 - s.source = openmc.Source(space=openmc.stats.Point()) + s.max_tracks = 1234 + s.source = openmc.IndependentSource(space=openmc.stats.Point()) s.output = {'summary': True, 'tallies': False, 'path': 'here'} s.verbosity = 7 s.sourcepoint = {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True} + 'write': True, 'overwrite': True, 'mcpl': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.surf_source_read = {'path': 'surface_source_1.h5'} s.surf_source_write = {'surface_ids': [2], 'max_particles': 200} s.confidence_intervals = True s.ptables = True - s.seed = 17 + s.plot_seed = 100 s.survival_biasing = True s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy_neutron': 1.0e-5, + 'survival_normalization': True, 'energy_photon': 1000.0, 'energy_electron': 1.0e-5, - 'energy_positron': 1.0e-5} + 'energy_positron': 1.0e-5, 'time_neutron': 1.0e-5, + 'time_photon': 1.0e-5, 'time_electron': 1.0e-5, + 'time_positron': 1.0e-5} mesh = openmc.RegularMesh() mesh.lower_left = (-10., -10., -10.) mesh.upper_right = (10., 10., 10.) @@ -42,7 +44,7 @@ def test_export_to_xml(run_in_tmpdir): s.temperature = {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': (200., 1000.)} s.trace = (10, 1, 20) - s.track = [1, 1, 1, 2, 1, 1] + s.track = [(1, 1, 1), (2, 1, 1)] s.ufs_mesh = mesh s.resonance_scattering = {'enable': True, 'method': 'rvs', 'energy_min': 1.0, 'energy_max': 1000.0, @@ -51,10 +53,34 @@ def test_export_to_xml(run_in_tmpdir): domains=[openmc.Cell()], samples=1000, lower_left=(-10., -10., -10.), upper_right = (10., 10., 10.)) s.create_fission_neutrons = True + s.create_delayed_neutrons = False s.log_grid_bins = 2000 s.photon_transport = False s.electron_treatment = 'led' s.write_initial_source = True + s.weight_window_checkpoints = {'surface': True, 'collision': False} + source_region_mesh = openmc.RegularMesh() + source_region_mesh.dimension = [2, 2, 2] + source_region_mesh.lower_left = [-2, -2, -2] + source_region_mesh.upper_right = [2, 2, 2] + root_universe = openmc.Universe() + s.random_ray = { + 'distance_inactive': 10.0, + 'distance_active': 100.0, + 'ray_source': openmc.IndependentSource( + space=openmc.stats.Box((-1., -1., -1.), (1., 1., 1.)) + ), + 'source_region_meshes': [(source_region_mesh, [root_universe])], + 'volume_estimator': 'hybrid', + 'source_shape': 'linear', + 'volume_normalized_flux_tallies': True, + 'adjoint': False, + 'sample_method': 'halton' + } + s.max_particle_events = 100 + s.max_secondaries = 1_000_000 + s.source_rejection_fraction = 0.01 + s.free_gas_threshold = 800.0 # Make sure exporting XML works s.export_to_xml() @@ -71,26 +97,31 @@ def test_export_to_xml(run_in_tmpdir): assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} assert s.energy_mode == 'continuous-energy' assert s.max_order == 5 - assert isinstance(s.source[0], openmc.Source) + assert s.max_tracks == 1234 + assert isinstance(s.source[0], openmc.IndependentSource) assert isinstance(s.source[0].space, openmc.stats.Point) assert s.output == {'summary': True, 'tallies': False, 'path': 'here'} assert s.verbosity == 7 assert s.sourcepoint == {'batches': [50, 150, 500, 1000], 'separate': True, - 'write': True, 'overwrite': True} + 'write': True, 'overwrite': True, 'mcpl': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} - assert s.surf_source_read == {'path': 'surface_source_1.h5'} + assert s.surf_source_read['path'].name == 'surface_source_1.h5' assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} assert s.confidence_intervals assert s.ptables + assert s.plot_seed == 100 assert s.seed == 17 assert s.survival_biasing assert s.cutoff == {'weight': 0.25, 'weight_avg': 0.5, + 'survival_normalization': True, 'energy_neutron': 1.0e-5, 'energy_photon': 1000.0, - 'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5} + 'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5, + 'time_neutron': 1.0e-5, 'time_photon': 1.0e-5, + 'time_electron': 1.0e-5, 'time_positron': 1.0e-5} assert isinstance(s.entropy_mesh, openmc.RegularMesh) assert s.entropy_mesh.lower_left == [-10., -10., -10.] assert s.entropy_mesh.upper_right == [10., 10., 10.] - assert s.entropy_mesh.dimension == [5, 5, 5] + assert s.entropy_mesh.dimension == (5, 5, 5) assert s.trigger_active assert s.trigger_max_batches == 10000 assert s.trigger_batch_interval == 50 @@ -99,19 +130,20 @@ def test_export_to_xml(run_in_tmpdir): assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} assert s.trace == [10, 1, 20] - assert s.track == [1, 1, 1, 2, 1, 1] + assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) assert s.ufs_mesh.lower_left == [-10., -10., -10.] assert s.ufs_mesh.upper_right == [10., 10., 10.] - assert s.ufs_mesh.dimension == [5, 5, 5] + assert s.ufs_mesh.dimension == (5, 5, 5) assert s.resonance_scattering == {'enable': True, 'method': 'rvs', 'energy_min': 1.0, 'energy_max': 1000.0, 'nuclides': ['U235', 'U238', 'Pu239']} assert s.create_fission_neutrons + assert not s.create_delayed_neutrons assert s.log_grid_bins == 2000 assert not s.photon_transport assert s.electron_treatment == 'led' - assert s.write_initial_source == True + assert s.write_initial_source assert len(s.volume_calculations) == 1 vol = s.volume_calculations[0] assert vol.domain_type == 'cell' @@ -119,3 +151,24 @@ def test_export_to_xml(run_in_tmpdir): assert vol.samples == 1000 assert vol.lower_left == (-10., -10., -10.) assert vol.upper_right == (10., 10., 10.) + assert s.weight_window_checkpoints == {'surface': True, 'collision': False} + assert s.max_particle_events == 100 + assert s.random_ray['distance_inactive'] == 10.0 + assert s.random_ray['distance_active'] == 100.0 + assert s.random_ray['ray_source'].space.lower_left == [-1., -1., -1.] + assert s.random_ray['ray_source'].space.upper_right == [1., 1., 1.] + assert 'source_region_meshes' in s.random_ray + assert len(s.random_ray['source_region_meshes']) == 1 + mesh_and_domains = s.random_ray['source_region_meshes'][0] + recovered_mesh = mesh_and_domains[0] + assert recovered_mesh.dimension == (2, 2, 2) + assert recovered_mesh.lower_left == [-2., -2., -2.] + assert recovered_mesh.upper_right == [2., 2., 2.] + assert s.random_ray['volume_estimator'] == 'hybrid' + assert s.random_ray['source_shape'] == 'linear' + assert s.random_ray['volume_normalized_flux_tallies'] + assert not s.random_ray['adjoint'] + assert s.random_ray['sample_method'] == 'halton' + assert s.max_secondaries == 1_000_000 + assert s.source_rejection_fraction == 0.01 + assert s.free_gas_threshold == 800.0 diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index d4d17a3dab..bb8a1b7852 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -1,5 +1,14 @@ +from collections import Counter +from math import pi + import openmc +import openmc.lib import openmc.stats +import numpy as np +import pytest +from pytest import approx + +from tests.regression_tests import config def test_source(): @@ -7,7 +16,7 @@ def test_source(): energy = openmc.stats.Discrete([1.0e6], [1.0]) angle = openmc.stats.Isotropic() - src = openmc.Source(space=space, angle=angle, energy=energy) + src = openmc.IndependentSource(space=space, angle=angle, energy=energy) assert src.space == space assert src.angle == angle assert src.energy == energy @@ -18,7 +27,7 @@ def test_source(): assert elem.find('angle') is not None assert elem.find('energy') is not None - src = openmc.Source.from_xml_element(elem) + src = openmc.IndependentSource.from_xml_element(elem) assert isinstance(src.angle, openmc.stats.Isotropic) assert src.space.xyz == [0.0, 0.0, 0.0] assert src.energy.x == [1.0e6] @@ -26,19 +35,283 @@ def test_source(): assert src.strength == 1.0 +def test_spherical_uniform(): + r_outer = 2.0 + r_inner = 1.0 + thetas = (0.0, pi/2) + phis = (0.0, pi) + origin = (0.0, 1.0, 2.0) + + sph_indep_function = openmc.stats.spherical_uniform(r_outer, + r_inner, + thetas, + phis, + origin) + + assert isinstance(sph_indep_function, openmc.stats.SphericalIndependent) + +def test_point_cloud(): + positions = [(1, 0, 2), (0, 1, 0), (0, 0, 3), (4, 9, 2)] + strengths = [1, 2, 3, 4] + + space = openmc.stats.PointCloud(positions, strengths) + np.testing.assert_equal(space.positions, positions) + np.testing.assert_equal(space.strengths, strengths) + + src = openmc.IndependentSource(space=space) + assert src.space == space + np.testing.assert_equal(src.space.positions, positions) + np.testing.assert_equal(src.space.strengths, strengths) + + elem = src.to_xml_element() + src = openmc.IndependentSource.from_xml_element(elem) + np.testing.assert_equal(src.space.positions, positions) + np.testing.assert_equal(src.space.strengths, strengths) + + +def test_point_cloud_invalid(): + with pytest.raises(ValueError, match='2D'): + openmc.stats.PointCloud([1, 0, 2, 0, 1, 0]) + + with pytest.raises(ValueError, match='3 values'): + openmc.stats.PointCloud([(1, 0, 2, 3), (4, 5, 2, 3)]) + + with pytest.raises(ValueError, match='1D'): + openmc.stats.PointCloud([(1, 0, 2), (4, 5, 2)], [(1, 2), (3, 4)]) + + with pytest.raises(ValueError, match='same length'): + openmc.stats.PointCloud([(1, 0, 2), (4, 5, 2)], [1, 2, 4]) + + +def test_point_cloud_strengths(run_in_tmpdir, sphere_box_model): + positions = [(1., 0., 2.), (0., 1., 0.), (0., 0., 3.), (-1., -1., 2.)] + strengths = [1, 2, 3, 4] + space = openmc.stats.PointCloud(positions, strengths) + + model = sphere_box_model[0] + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource(space=space) + + try: + model.init_lib() + n_samples = 50_000 + sites = openmc.lib.sample_external_source(n_samples) + finally: + model.finalize_lib() + + count = Counter(s.r for s in sites) + for i, (strength, position) in enumerate(zip(strengths, positions)): + sampled_strength = count[position] / n_samples + expected_strength = pytest.approx(strength/sum(strengths), abs=0.02) + assert sampled_strength == expected_strength, f'Strength incorrect for {positions[i]}' + + def test_source_file(): filename = 'source.h5' - src = openmc.Source(filename=filename) - assert src.file == filename + src = openmc.FileSource(path=filename) + assert src.path.name == filename elem = src.to_xml_element() assert 'strength' in elem.attrib assert 'file' in elem.attrib + def test_source_dlopen(): - library = './libsource.so' - src = openmc.Source(library=library) - assert src.library == library + library = 'libsource.so' + src = openmc.CompiledSource(library) + assert src.library.name == library elem = src.to_xml_element() assert 'library' in elem.attrib + + +def test_source_xml_roundtrip(): + # Create a source and write to an XML element + space = openmc.stats.Box([-5., -5., -5.], [5., 5., 5.]) + energy = openmc.stats.Discrete([1.0e6, 2.0e6, 5.0e6], [0.3, 0.5, 0.2]) + angle = openmc.stats.PolarAzimuthal( + mu=openmc.stats.Uniform(0., 1.), + phi=openmc.stats.Uniform(0., 2*pi), + reference_uvw=(0., 1., 0.) + ) + src = openmc.IndependentSource( + space=space, angle=angle, energy=energy, + particle='photon', strength=100.0 + ) + elem = src.to_xml_element() + + # Read from XML element and make sure data is preserved + new_src = openmc.IndependentSource.from_xml_element(elem) + assert isinstance(new_src.space, openmc.stats.Box) + np.testing.assert_allclose(new_src.space.lower_left, src.space.lower_left) + np.testing.assert_allclose(new_src.space.upper_right, src.space.upper_right) + assert isinstance(new_src.energy, openmc.stats.Discrete) + np.testing.assert_allclose(new_src.energy.x, src.energy.x) + np.testing.assert_allclose(new_src.energy.p, src.energy.p) + assert isinstance(new_src.angle, openmc.stats.PolarAzimuthal) + assert new_src.angle.mu.a == src.angle.mu.a + assert new_src.angle.mu.b == src.angle.mu.b + assert new_src.angle.phi.a == src.angle.phi.a + assert new_src.angle.phi.b == src.angle.phi.b + np.testing.assert_allclose(new_src.angle.reference_uvw, src.angle.reference_uvw) + assert new_src.particle == src.particle + assert new_src.strength == approx(src.strength) + + +@pytest.fixture +def sphere_box_model(): + # Model with two spheres inside a box + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + sph1 = openmc.Sphere(x0=3, r=1.0) + sph2 = openmc.Sphere(x0=-3, r=1.0) + cube = openmc.model.RectangularParallelepiped( + -5., 5., -5., 5., -5., 5., boundary_type='reflective' + ) + cell1 = openmc.Cell(fill=mat, region=-sph1) + cell2 = openmc.Cell(fill=mat, region=-sph2) + non_source_region = +sph1 & +sph2 & -cube + cell3 = openmc.Cell(region=non_source_region) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2, cell3]) + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + + return model, cell1, cell2, cell3 + + +def test_constraints_independent(sphere_box_model, run_in_tmpdir): + model, cell1, cell2, cell3 = sphere_box_model + + # Set up a box source with rejection on the spherical cell + space = openmc.stats.Box((-4., -1., -1.), (4., 1., 1.)) + model.settings.source = openmc.IndependentSource( + space=space, constraints={'domains': [cell1, cell2]} + ) + + # Load up model via openmc.lib and sample source + model.export_to_model_xml() + openmc.lib.init() + particles = openmc.lib.sample_external_source(1000) + + # Make sure that all sampled sources are within one of the spheres + for p in particles: + assert p.r in (cell1.region | cell2.region) + assert p.r not in cell3.region + + openmc.lib.finalize() + + +def test_constraints_mesh(sphere_box_model, run_in_tmpdir): + model, cell1, cell2, cell3 = sphere_box_model + + bbox = cell3.bounding_box + mesh = openmc.RegularMesh() + mesh.lower_left = bbox.lower_left + mesh.upper_right = bbox.upper_right + mesh.dimension = (2, 1, 1) + + left_source = openmc.IndependentSource() + right_source = openmc.IndependentSource() + model.settings.source = openmc.MeshSource( + mesh, [left_source, right_source], constraints={'domains': [cell1, cell2]} + ) + + # Load up model via openmc.lib and sample source + model.export_to_model_xml() + openmc.lib.init() + particles = openmc.lib.sample_external_source(1000) + + # Make sure that all sampled sources are within one of the spheres + for p in particles: + assert p.r in (cell1.region | cell2.region) + assert p.r not in cell3.region + + openmc.lib.finalize() + + +def test_constraints_file(sphere_box_model, run_in_tmpdir): + model = sphere_box_model[0] + + # Create source file with randomly sampled source sites + rng = np.random.default_rng() + energy = rng.uniform(0., 1e6, 10_000) + time = rng.uniform(0., 1., 10_000) + particles = [openmc.SourceParticle(E=e, time=t) for e, t in zip(energy, time)] + openmc.write_source_file(particles, 'uniform_source.h5') + + # Use source file + model.settings.source = openmc.FileSource( + 'uniform_source.h5', + constraints={ + 'time_bounds': [0.25, 0.75], + 'energy_bounds': [500.e3, 1.0e6], + } + ) + + # Load up model via openmc.lib and sample source + model.export_to_model_xml() + openmc.lib.init() + particles = openmc.lib.sample_external_source(1000) + + # Make sure that all sampled sources are within energy/time bounds + for p in particles: + assert 0.25 <= p.time <= 0.75 + assert 500.e3 <= p.E <= 1.0e6 + + openmc.lib.finalize() + + +@pytest.mark.skipif(config['mpi'], reason='Not compatible with MPI') +def test_rejection_fraction(run_in_tmpdir): + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + w = 0.25 + rpp1 = openmc.model.RectangularParallelepiped( + -w/2, w/2, -w/2, w/2, -w/2, w/2) + rpp2 = openmc.model.RectangularParallelepiped( + -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, boundary_type='vacuum') + cell1 = openmc.Cell(fill=mat, region=-rpp1) + cell2 = openmc.Cell(region=+rpp1 & -rpp2) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2]) + + # Create a box source over a 1 cm³ volume that is constrained to the source + # cell of volume (0.25 cm)³ = 0.0125 cm³, which means the default rejection + # fraction of 0.05 won't work + model.settings.particles = 1000 + model.settings.batches = 1 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource( + space=openmc.stats.Box(*(-rpp2).bounding_box), + constraints={'domains': [cell1]} + ) + with pytest.raises(RuntimeError, match='Too few source sites'): + model.run(openmc_exec=config['exe']) + + # With a source rejection fraction below 0.0125, the simulation should run + model.settings.source_rejection_fraction = 0.005 + model.run(openmc_exec=config['exe']) + + +def test_exceptions(): + + with pytest.raises(AttributeError, match=r'Please use the FileSource class'): + s = openmc.IndependentSource() + s.file = 'my_file' + + with pytest.raises(AttributeError, match=r'Please use the CompiledSource class'): + s = openmc.IndependentSource() + s.library = 'my_library' + + with pytest.raises(AttributeError, match=r'Please use the CompiledSource class'): + s = openmc.IndependentSource() + s.parameters = 'my_params' + + with pytest.warns(FutureWarning, match=r'in favor of \'IndependentSource\''): + s = openmc.Source() + + with pytest.raises(AttributeError, match=r'has no attribute \'frisbee\''): + s = openmc.IndependentSource() + s.frisbee diff --git a/tests/unit_tests/test_source_file.py b/tests/unit_tests/test_source_file.py index 1398088e52..41906c80f8 100644 --- a/tests/unit_tests/test_source_file.py +++ b/tests/unit_tests/test_source_file.py @@ -44,6 +44,54 @@ def test_source_file(run_in_tmpdir): assert np.all(arr['delayed_group'] == 0) assert np.all(arr['particle'] == 0) + # Ensure sites read in are consistent + sites = openmc.ParticleList.from_hdf5('test_source.h5') + + xs = np.array([site.r[0] for site in sites]) + ys = np.array([site.r[1] for site in sites]) + zs = np.array([site.r[2] for site in sites]) + assert np.all((xs > 0.0) & (xs < 1.0)) + assert np.all(ys == np.arange(1000)) + assert np.all(zs == 0.0) + u = np.array([s.u for s in sites]) + assert np.all(u[..., 0] == 0.0) + assert np.all(u[..., 1] == 0.0) + assert np.all(u[..., 2] == 1.0) + E = np.array([s.E for s in sites]) + assert np.all(E == n - np.arange(n)) + wgt = np.array([s.wgt for s in sites]) + assert np.all(wgt == 1.0) + dgs = np.array([s.delayed_group for s in sites]) + assert np.all(dgs == 0) + p_types = np.array([s.particle for s in sites]) + assert np.all(p_types == 0) + + # Ensure a ParticleList item is a SourceParticle + site = sites[0] + assert isinstance(site, openmc.SourceParticle) + assert site.E == pytest.approx(n) + + # Ensure site slice read in and exported are consistent + sites_slice = sites[:10] + sites_slice.export_to_hdf5("test_source_slice.h5") + sites_slice = openmc.ParticleList.from_hdf5('test_source_slice.h5') + + assert isinstance(sites_slice, openmc.ParticleList) + assert len(sites_slice) == 10 + E = np.array([s.E for s in sites_slice]) + np.testing.assert_allclose(E, n - np.arange(10)) + + # Ensure site list read in and exported are consistent + df = sites.to_dataframe() + sites_filtered = sites[df[df.E <= 10.0].index.tolist()] + sites_filtered.export_to_hdf5("test_source_filtered.h5") + sites_filtered = openmc.read_source_file('test_source_filtered.h5') + + assert isinstance(sites_filtered, openmc.ParticleList) + assert len(sites_filtered) == 10 + E = np.array([s.E for s in sites_filtered]) + np.testing.assert_allclose(E, np.arange(10, 0, -1)) + def test_wrong_source_attributes(run_in_tmpdir): # Create a source file with animal attributes @@ -54,7 +102,7 @@ def test_wrong_source_attributes(run_in_tmpdir): ]) arr = np.array([(1.0, 2.0, 3), (4.0, 5.0, 6), (7.0, 8.0, 9)], dtype=source_dtype) with h5py.File('animal_source.h5', 'w') as fh: - fh.attrs['filetype'] = np.string_("source") + fh.attrs['filetype'] = np.bytes_("source") fh.create_dataset('source_bank', data=arr) # Create a simple model that uses this lovely animal source @@ -67,7 +115,7 @@ def test_wrong_source_attributes(run_in_tmpdir): settings = openmc.Settings() settings.particles = 100 settings.batches = 10 - settings.source = openmc.Source(filename='animal_source.h5') + settings.source = openmc.FileSource(path='animal_source.h5') settings.export_to_xml() # When we run the model, it should error out with a message that includes @@ -75,3 +123,25 @@ def test_wrong_source_attributes(run_in_tmpdir): with pytest.raises(RuntimeError) as excinfo: openmc.run() assert 'platypus, axolotl, narwhal' in str(excinfo.value) + + +def test_source_file_transport(run_in_tmpdir): + # Create a source file with a single particle + particle = openmc.SourceParticle() + openmc.write_source_file([particle], 'source.h5') + + # Created simple model to use source file + model = openmc.Model() + al = openmc.Material() + al.add_element('Al', 1.0) + al.set_density('g/cm3', 2.7) + sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=al, region=-sph) + model.geometry = openmc.Geometry([cell]) + model.settings.source = openmc.FileSource(path='source.h5') + model.settings.particles = 10 + model.settings.batches = 3 + model.settings.run_mode = 'fixed source' + + # Try running OpenMC + model.run() diff --git a/tests/unit_tests/test_source_mesh.py b/tests/unit_tests/test_source_mesh.py new file mode 100644 index 0000000000..2550cb87eb --- /dev/null +++ b/tests/unit_tests/test_source_mesh.py @@ -0,0 +1,444 @@ +from itertools import product +from pathlib import Path +from math import sqrt +import random + +import pytest +import numpy as np +import openmc +import openmc.lib + +from tests import cdtemp + + +################### +# MeshSpatial Tests +################### +TETS_PER_VOXEL = 12 + +# This test uses a geometry file with cells that match a regular mesh. Each cell +# in the geometry corresponds to 12 tetrahedra in the unstructured mesh file. +@pytest.fixture +def model(): + openmc.reset_auto_ids() + + ### Materials ### + materials = openmc.Materials() + + water_mat = openmc.Material(name="water") + water_mat.add_nuclide("H1", 2.0) + water_mat.add_nuclide("O16", 1.0) + water_mat.set_density("atom/b-cm", 0.07416) + materials.append(water_mat) + + ### Geometry ### + # This test uses a geometry file that resembles a regular mesh. + # 12 tets are used to match each voxel in the geometry. + + # create a regular mesh that matches the superimposed mesh + regular_mesh = openmc.RegularMesh(mesh_id=10) + regular_mesh.lower_left = (-10, -10, -10) + regular_mesh.dimension = (10, 10, 10) + regular_mesh.width = (2, 2, 2) + + root_cell, _ = regular_mesh.build_cells(bc=['vacuum']*6) + + geometry = openmc.Geometry(root=[root_cell]) + + ### Settings ### + settings = openmc.Settings() + settings.run_mode = 'fixed source' + settings.particles = 100 + settings.batches = 2 + + return openmc.Model(geometry=geometry, + materials=materials, + settings=settings) + +### Setup test cases ### +param_values = (['libmesh', 'moab'], # mesh libraries + ['uniform', 'manual']) # Element weighting schemes + +test_cases = [] +for i, (lib, schemes) in enumerate(product(*param_values)): + test_cases.append({'library' : lib, + 'source_strengths' : schemes}) + +def ids(params): + """Test naming function for clarity""" + return f"{params['library']}-{params['source_strengths']}" + +@pytest.mark.parametrize("test_cases", test_cases, ids=ids) +def test_unstructured_mesh_sampling(model, request, test_cases): + # skip the test if the library is not enabled + if test_cases['library'] == 'moab' and not openmc.lib._dagmc_enabled(): + pytest.skip("DAGMC (and MOAB) mesh not enabled in this build.") + + if test_cases['library'] == 'libmesh' and not openmc.lib._libmesh_enabled(): + pytest.skip("LibMesh is not enabled in this build.") + + # setup mesh source ### + mesh_filename = Path(request.fspath).parent / "test_mesh_tets.e" + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, test_cases['library']) + + # subtract one to account for root cell produced by RegularMesh.build_cells + n_cells = len(model.geometry.get_all_cells()) - 1 + + # set source weights according to test case + if test_cases['source_strengths'] == 'uniform': + vol_norm = True + strengths = None + elif test_cases['source_strengths'] == 'manual': + vol_norm = False + # assign random weights + strengths = np.random.rand(n_cells*TETS_PER_VOXEL) + + # create the spatial distribution based on the mesh + space = openmc.stats.MeshSpatial(uscd_mesh, strengths, vol_norm) + + energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) + source = openmc.IndependentSource(space=space, energy=energy) + model.settings.source = source + + with cdtemp([mesh_filename]): + model.export_to_xml() + + n_measurements = 100 + n_samples = 1000 + + cell_counts = np.zeros((n_cells, n_measurements)) + + # This model contains 1000 geometry cells. Each cell is a hex + # corresponding to 12 of the tets. This test runs 1000 samples. This + # results in the following average for each cell + openmc.lib.init([]) + + # perform many sets of samples and track counts for each cell + for m in range(n_measurements): + sites = openmc.lib.sample_external_source(n_samples) + cells = [openmc.lib.find_cell(s.r) for s in sites] + + for c in cells: + # subtract one from index to account for root cell + cell_counts[c[0]._index - 1, m] += 1 + + # make sure particle transport is successful + openmc.lib.run() + openmc.lib.finalize() + + # normalize cell counts to get sampling frequency per particle + cell_counts /= n_samples + + # get the mean and std. dev. of the cell counts + mean = cell_counts.mean(axis=1) + std_dev = cell_counts.std(axis=1) + + if test_cases['source_strengths'] == 'uniform': + exp_vals = np.ones(n_cells) / n_cells + else: + # sum up the source strengths for each tet, these are the expected true mean + # of the sampling frequency for that cell + exp_vals = strengths.reshape(-1, 12).sum(axis=1) / sum(strengths) + + diff = np.abs(mean - exp_vals) + assert((diff < 2*std_dev).sum() / diff.size >= 0.95) + assert((diff < 6*std_dev).sum() / diff.size >= 0.997) + + +def test_strengths_size_failure(request, model): + # setup mesh source ### + mesh_filename = Path(request.fspath).parent / "test_mesh_tets.e" + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, 'libmesh') + + # intentionally incorrectly sized to trigger an error + n_cells = len(model.geometry.get_all_cells()) + strengths = np.random.rand(n_cells*TETS_PER_VOXEL) + + # create the spatial distribution based on the mesh + space = openmc.stats.MeshSpatial(uscd_mesh, strengths) + + energy = openmc.stats.Discrete(x=[15.e+06], p=[1.0]) + source = openmc.IndependentSource(space=space, energy=energy) + model.settings.source = source + + # skip the test if unstructured mesh is not available + if not openmc.lib._libmesh_enabled(): + if openmc.lib._dagmc_enabled(): + source.space.mesh.library = 'moab' + else: + pytest.skip("Unstructured mesh support unavailable.") + + # make sure that an incorrrectly sized strengths array causes a failure + source.space.strengths = source.space.strengths[:-1] + + mesh_filename = Path(request.fspath).parent / source.space.mesh.filename + + with pytest.raises(RuntimeError, match=r'strengths array'), cdtemp([mesh_filename]): + model.export_to_xml() + openmc.run() + + +def test_roundtrip(run_in_tmpdir, model, request): + if not openmc.lib._libmesh_enabled() and not openmc.lib._dagmc_enabled(): + pytest.skip("Unstructured mesh is not enabled in this build.") + + mesh_filename = Path(request.fspath).parent / 'test_mesh_tets.e' + ucd_mesh = openmc.UnstructuredMesh(mesh_filename, library='libmesh') + + if not openmc.lib._libmesh_enabled(): + ucd_mesh.library = 'moab' + + n_cells = len(model.geometry.get_all_cells()) + + space_out = openmc.MeshSpatial(ucd_mesh) + space_out.strengths = np.random.rand(n_cells*TETS_PER_VOXEL) + model.settings.source = openmc.IndependentSource(space=space_out) + + # write out the model + model.export_to_xml() + + model_in = openmc.Model.from_xml() + + space_in = model_in.settings.source[0].space + + np.testing.assert_equal(space_out.strengths, space_in.strengths) + + assert space_in.mesh.id == space_out.mesh.id + assert space_in.volume_normalized == space_out.volume_normalized + + +################### +# MeshSource tests +################### +@pytest.fixture +def void_model(): + """ + A void model containing a single box + """ + model = openmc.Model() + + box = openmc.model.RectangularParallelepiped(*[-10, 10]*3, boundary_type='vacuum') + model.geometry = openmc.Geometry([openmc.Cell(region=-box)]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + + return model + + +@pytest.mark.parametrize('mesh_type', ('rectangular', 'cylindrical')) +def test_mesh_source_independent(run_in_tmpdir, void_model, mesh_type): + """ + A void model containing a single box + """ + model = void_model + + # define a 2 x 2 x 2 mesh + if mesh_type == 'rectangular': + mesh = openmc.RegularMesh.from_domain(model.geometry, (2, 2, 2)) + elif mesh_type == 'cylindrical': + mesh = openmc.CylindricalMesh.from_domain(model.geometry, (1, 4, 2)) + + energy = openmc.stats.Discrete([1.e6], [1.0]) + + # create sources with only one non-zero strength for the source in the mesh + # voxel occupying the lowest octant. Direct source particles straight out of + # the problem from there. This demonstrates that + # 1) particles are only being sourced within the intented mesh voxel based + # on source strength + # 2) particles are respecting the angle distributions assigned to each voxel + sources = np.empty(mesh.dimension, dtype=openmc.SourceBase) + centroids = mesh.centroids + x, y, z = np.swapaxes(mesh.centroids, -1, 0) + for i, j, k in mesh.indices: + # mesh.indices is currently one-indexed, adjust for Python arrays + ijk = (i-1, j-1, k-1) + + # get the centroid of the ijk mesh element and use it to set the + # direction of the source directly out of the problem + centroid = centroids[ijk] + vec = np.sign(centroid, dtype=float) + vec /= np.linalg.norm(vec) + angle = openmc.stats.Monodirectional(vec) + sources[ijk] = openmc.IndependentSource(energy=energy, angle=angle, strength=0.0) + + # create and apply the mesh source + mesh_source = openmc.MeshSource(mesh, sources) + model.settings.source = mesh_source + + # tally the flux on the mesh + mesh_filter = openmc.MeshFilter(mesh) + tally = openmc.Tally() + tally.filters = [mesh_filter] + tally.scores = ['flux'] + + model.tallies = openmc.Tallies([tally]) + + # for each element, set a single-non zero source with particles + # traveling out of the mesh (and geometry) w/o crossing any other + # mesh elements + for flat_index, (i, j, k) in enumerate(mesh.indices): + ijk = (i-1, j-1, k-1) + # zero-out all source strengths and set the strength + # on the element of interest + mesh_source.strength = 0.0 + mesh_source.sources[flat_index].strength = 1.0 + + sp_file = model.run() + + with openmc.StatePoint(sp_file) as sp: + tally_out = sp.get_tally(id=tally.id) + mean = tally_out.get_reshaped_data(expand_dims=True) + + # remove nuclides and scores axes + mean = mean[..., 0, 0] + # the mesh elment with a non-zero source strength should have a value + assert mean[ijk] != 0 + # all other values should be zero + mean[ijk] = 0 + assert np.all(mean == 0), f'Failed on index {ijk} with centroid {mesh.centroids[ijk]}' + + # test roundtrip + xml_model = openmc.Model.from_model_xml() + xml_source = xml_model.settings.source[0] + assert isinstance(xml_source, openmc.MeshSource) + assert xml_source.strength == 1.0 + assert isinstance(xml_source.mesh, type(mesh_source.mesh)) + assert xml_source.mesh.dimension == mesh_source.mesh.dimension + assert xml_source.mesh.id == mesh_source.mesh.id + assert len(xml_source.sources) == len(mesh_source.sources) + + # check strength adjustment methods + assert mesh_source.strength == 1.0 + mesh_source.strength = 100.0 + assert mesh_source.strength == 100.0 + + mesh_source.normalize_source_strengths() + assert mesh_source.strength == 1.0 + + +@pytest.mark.parametrize("library", ('moab', 'libmesh')) +def test_umesh_source_independent(run_in_tmpdir, request, void_model, library): + import openmc.lib + # skip the test if the library is not enabled + if library == 'moab' and not openmc.lib._dagmc_enabled(): + pytest.skip("DAGMC (and MOAB) mesh not enabled in this build.") + + if library == 'libmesh' and not openmc.lib._libmesh_enabled(): + pytest.skip("LibMesh is not enabled in this build.") + + model = void_model + + mesh_filename = Path(request.fspath).parent / "test_mesh_tets.e" + uscd_mesh = openmc.UnstructuredMesh(mesh_filename, library) + ind_source = openmc.IndependentSource() + n_elements = 12_000 + model.settings.source = openmc.MeshSource(uscd_mesh, n_elements*[ind_source]) + model.export_to_model_xml() + with openmc.lib.run_in_memory(): + openmc.lib.simulation_init() + sites = openmc.lib.sample_external_source(10) + openmc.lib.statepoint_write('statepoint.h5') + + with openmc.StatePoint('statepoint.h5') as sp: + uscd_mesh = sp.meshes[uscd_mesh.id] + + # ensure at least that all sites are inside the mesh + bounding_box = uscd_mesh.bounding_box + for site in sites: + assert site.r in bounding_box + + +def test_mesh_source_constraints(run_in_tmpdir): + """Test application of constraints to underlying mesh element sources""" + + # Create simple model with two cells + m1 = openmc.Material() + m1.add_nuclide('H1', 1.0) + m2 = m1.clone() + sph = openmc.Sphere(r=100, boundary_type='vacuum') + box1 = openmc.model.RectangularParallelepiped(-1, 0, -1, 1, -1, 1) + box2 = openmc.model.RectangularParallelepiped(0, 2, -1, 1, -1, 1) + cell1 = openmc.Cell(fill=m1, region=-box1) + cell2 = openmc.Cell(fill=m2, region=-box2) + outer = openmc.Cell(region=-sph & (+box1 | +box2)) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2, outer]) + + # Define a mesh covering the two cells: the first mesh element contains + # cell1 (-1 < x < 0) and the second element contains cells2 (0 < x < 2) + mesh = openmc.RegularMesh() + mesh.lower_left = (-3., -1., -1.) + mesh.upper_right = (3., 1., 1.) + mesh.dimension = (2, 1, 1) + + # Define a mesh source with a randomly chosen probability + p = random.random() + src1 = openmc.IndependentSource(strength=p, constraints={'domains': [cell1]}) + src2 = openmc.IndependentSource(strength=1 - p, constraints={'domains': [cell2]}) + model.settings.source = openmc.MeshSource(mesh, [src1, src2]) + + # Finish settings and export + model.settings.particles = 100 + model.settings.batches = 1 + model.export_to_model_xml() + + with openmc.lib.run_in_memory(): + # Sample sites from the source + sites = openmc.lib.sample_external_source(N := 1000) + + # Check that all sites are either in cell1 or cell2 + xs = np.array([s.r[0] for s in sites]) + assert (xs >= -1.0).all() + assert (xs <= 2.0).all() + + # Check that the correct percentage of the sites are in cell1 + sigma = sqrt(p*(1- p)/N) + frac = xs[(-1.0 <= xs) & (xs <= 0.0)].size / N + assert frac == pytest.approx(p, abs=5*sigma) + + +@pytest.mark.parametrize("mesh_type", ('rectangular', 'cylindrical', 'spherical')) +def test_mesh_spatial(run_in_tmpdir, mesh_type): + """Test that a spherical mesh source works as expected.""" + model = openmc.Model() + + # Set up geometry, a box that is shifted in x, y, and z + box = openmc.model.RectangularParallelepiped(5.0, 25.0, -20.0, 20.0, -30.0, 30.0, boundary_type='vacuum') + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + model.geometry = openmc.Geometry([openmc.Cell(fill=mat, region=-box)]) + + # Create a mesh of each type in turn + if mesh_type == 'rectangular': + mesh = openmc.RegularMesh.from_domain(model.geometry, (10, 2, 2)) + elif mesh_type == 'cylindrical': + mesh = openmc.CylindricalMesh.from_domain(model.geometry, (10, 2, 2)) + assert max(mesh.r_grid) == 10.0, "Cylindrical mesh radius exceeds geometry bounds" + assert mesh.origin[0] == 15.0, "Cylindrical mesh origin x-coordinate is incorrect" + elif mesh_type == 'spherical': + mesh = openmc.SphericalMesh.from_domain(model.geometry, (10, 2, 2)) + assert max(mesh.r_grid) == 10.0, "Spherical mesh radius exceeds geometry bounds" + assert mesh.origin[0] == 15.0, "Spherical mesh origin x-coordinate is incorrect" + + # Create a mesh source with a single particle + ind_source = openmc.IndependentSource(space=openmc.stats.MeshSpatial(mesh, np.prod(mesh.dimension)*[1.0])) + model.settings.source = ind_source + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + + model.export_to_model_xml() + + openmc.lib.init() + openmc.lib.simulation_init() + sites = openmc.lib.sample_external_source(10) + openmc.lib.simulation_finalize() + openmc.lib.finalize() + + # Check that the sites are within the spherical mesh bounds + bbox = mesh.bounding_box + for site in sites: + assert site.r in bbox diff --git a/tests/unit_tests/test_spherical_mesh.py b/tests/unit_tests/test_spherical_mesh.py new file mode 100644 index 0000000000..a95a4151a2 --- /dev/null +++ b/tests/unit_tests/test_spherical_mesh.py @@ -0,0 +1,206 @@ +from itertools import product, permutations + +import openmc +import numpy as np + +import pytest + +geom_size = 5 + +@pytest.fixture() +def model(): + openmc.reset_auto_ids() + + water = openmc.Material(name='water') + water.add_element('H', 2.0) + water.add_element('O', 1.0) + water.set_density('g/cc', 1.0) + + rpp = openmc.model.RectangularParallelepiped(*([-geom_size, geom_size] * 3), + boundary_type='vacuum') + + cell = openmc.Cell(region=-rpp, fill=water) + + geom = openmc.Geometry([cell]) + + source = openmc.IndependentSource() + source.space = openmc.stats.Point() + source.energy = openmc.stats.Discrete([10000], [1.0]) + + settings = openmc.Settings() + settings.particles = 2000 + settings.batches = 10 + settings.run_mode = 'fixed source' + + # build + mesh = openmc.SphericalMesh( + phi_grid=np.linspace(0, 2*np.pi, 13), + theta_grid=np.linspace(0, np.pi, 7), + r_grid=np.linspace(0, geom_size, geom_size), + ) + tally = openmc.Tally() + + mesh_filter = openmc.MeshFilter(mesh) + tally.filters.append(mesh_filter) + + tally.scores.append("flux") + + tallies = openmc.Tallies([tally]) + + return openmc.Model(geometry=geom, settings=settings, tallies=tallies) + +def test_origin_read_write_to_xml(run_in_tmpdir, model): + """Tests that the origin attribute can be written and read back to XML + """ + mesh = model.tallies[0].filters[0].mesh + mesh.origin = [0.1, 0.2, 0.3] + model.tallies.export_to_xml() + + # read back + new_tallies = openmc.Tallies.from_xml() + new_tally = new_tallies[0] + new_mesh = new_tally.filters[0].mesh + np.testing.assert_equal(new_mesh.origin, mesh.origin) + +estimators = ('tracklength', 'collision') +offset = geom_size + 0.001 + +origins = set(permutations((-offset, 0, 0))) +origins |= set(permutations((offset, 0, 0))) + +test_cases = product(estimators, origins) + +def label(p): + if isinstance(p, tuple): + return f'origin:{p}' + if isinstance(p, str): + return f'estimator:{p}' + +@pytest.mark.parametrize('estimator,origin', test_cases, ids=label) +def test_offset_mesh(run_in_tmpdir, model, estimator, origin): + """Tests that the mesh has been moved based on tally results + """ + mesh = model.tallies[0].filters[0].mesh + model.tallies[0].estimator = estimator + # move the center of the spherical mesh + mesh.origin = origin + + sp_filename = model.run() + + with openmc.StatePoint(sp_filename) as sp: + tally = sp.tallies[1] + + # we've translated half of the spherical mesh above the model, + # so ensure that half of the bins are populated + assert np.count_nonzero(tally.mean) == tally.mean.size / 2 + + # check that the half of the mesh that is outside of the geometry + # contains the zero values + mean = tally.get_reshaped_data('mean', expand_dims=True) + centroids = mesh.centroids + for ijk in mesh.indices: + i, j, k = np.array(ijk) - 1 + if model.geometry.find(centroids[i, j, k]): + mean[i, j, k] == 0.0 + else: + mean[i, j, k] != 0.0 + +# Some void geometry tests to check our radial intersection methods on +# spherical and cylindrical meshes + +@pytest.fixture() +def void_coincident_geom_model(): + """A model with many geometric boundaries coincident with mesh boundaries + across many scales + """ + openmc.reset_auto_ids() + + model = openmc.Model() + + model.materials = openmc.Materials() + radii = [0.1, 1, 5, 50, 100, 150, 250] + spheres = [openmc.Sphere(r=ri) for ri in radii] + spheres[-1].boundary_type = 'vacuum' + + regions = openmc.model.subdivide(spheres)[:-1] + cells = [openmc.Cell(region=r, fill=None) for r in regions] + geom = openmc.Geometry(cells) + + model.geometry = geom + + settings = openmc.Settings(run_mode='fixed source') + settings.batches = 2 + settings.particles = 5000 + model.settings = settings + + mesh = openmc.SphericalMesh(r_grid=np.linspace(0, 250, 501)) + mesh_filter = openmc.MeshFilter(mesh) + + tally = openmc.Tally() + tally.scores = ['flux'] + tally.filters = [mesh_filter] + + model.tallies = openmc.Tallies([tally]) + + return model + + +# convenience function for checking tally results +# in the following tests +def _check_void_spherical_tally(statepoint_filename): + with openmc.StatePoint(statepoint_filename) as sp: + flux_tally = sp.tallies[1] + mesh = flux_tally.find_filter(openmc.MeshFilter).mesh + neutron_flux = flux_tally.get_reshaped_data().squeeze() + # the flux values for each bin should equal the width + # width of the mesh bins + d_r = mesh.r_grid[1] - mesh.r_grid[0] + assert neutron_flux == pytest.approx(d_r) + + +def test_void_geom_pnt_src(run_in_tmpdir, void_coincident_geom_model): + # add isotropic point source + src = openmc.IndependentSource() + src.space = openmc.stats.Point() + src.energy = openmc.stats.Discrete([14.06e6], [1]) + void_coincident_geom_model.settings.source = src + + # run model and check tally results + sp_filename = void_coincident_geom_model.run() + _check_void_spherical_tally(sp_filename) + + +def test_void_geom_boundary_src(run_in_tmpdir, void_coincident_geom_model): + # update source to a number of points on the outside of the sphere + # with directions pointing toward the origin + n_sources = 20 + phi_vals = np.linspace(0, np.pi, n_sources) + theta_vals = np.linspace(0, 2.0*np.pi, n_sources) + + bbox = void_coincident_geom_model.geometry.bounding_box + # can't source particles directly on the geometry boundary + outer_r = bbox[1][0] - 1e-08 + + sources = [] + + energy = openmc.stats.Discrete([14.06e6], [1]) + + for phi, theta in zip(phi_vals, theta_vals): + + src = openmc.IndependentSource() + src.energy = energy + + pnt = np.array([np.sin(phi)*np.cos(theta), np.sin(phi)*np.sin(theta), np.cos(phi)]) + u = -pnt + src.space = openmc.stats.Point(outer_r*pnt) + src.angle = openmc.stats.Monodirectional(u) + # set source strengths so that we can still expect + # a tally value of 0.5 + src.strength = 0.5/n_sources + + sources.append(src) + + void_coincident_geom_model.settings.source = sources + + sp_filename = void_coincident_geom_model.run() + _check_void_spherical_tally(sp_filename) diff --git a/tests/unit_tests/test_statepoint.py b/tests/unit_tests/test_statepoint.py new file mode 100644 index 0000000000..7ffaf7ec2c --- /dev/null +++ b/tests/unit_tests/test_statepoint.py @@ -0,0 +1,65 @@ +import openmc + + +def test_get_tally_filter_type(run_in_tmpdir): + """Test various ways of retrieving tallies from a StatePoint object.""" + + mat = openmc.Material() + mat.add_nuclide("H1", 1.0) + mat.set_density("g/cm3", 10.0) + + sphere = openmc.Sphere(r=10.0, boundary_type="vacuum") + cell = openmc.Cell(fill=mat, region=-sphere) + geometry = openmc.Geometry([cell]) + + settings = openmc.Settings() + settings.particles = 10 + settings.batches = 2 + settings.run_mode = "fixed source" + + reg_mesh = openmc.RegularMesh().from_domain(cell) + tally1 = openmc.Tally(tally_id=1) + mesh_filter = openmc.MeshFilter(reg_mesh) + tally1.filters = [mesh_filter] + tally1.scores = ["flux"] + + tally2 = openmc.Tally(tally_id=2, name="heating tally") + cell_filter = openmc.CellFilter(cell) + tally2.filters = [cell_filter] + tally2.scores = ["heating"] + + tallies = openmc.Tallies([tally1, tally2]) + model = openmc.Model( + geometry=geometry, materials=[mat], settings=settings, tallies=tallies + ) + + sp_filename = model.run() + + sp = openmc.StatePoint(sp_filename) + + tally_found = sp.get_tally(filter_type=openmc.MeshFilter) + assert tally_found.id == 1 + + tally_found = sp.get_tally(filter_type=openmc.CellFilter) + assert tally_found.id == 2 + + tally_found = sp.get_tally(filters=[mesh_filter]) + assert tally_found.id == 1 + + tally_found = sp.get_tally(filters=[cell_filter]) + assert tally_found.id == 2 + + tally_found = sp.get_tally(scores=["heating"]) + assert tally_found.id == 2 + + tally_found = sp.get_tally(name="heating tally") + assert tally_found.id == 2 + + tally_found = sp.get_tally(name=None) + assert tally_found.id == 1 + + tally_found = sp.get_tally(id=1) + assert tally_found.id == 1 + + tally_found = sp.get_tally(id=2) + assert tally_found.id == 2 diff --git a/tests/unit_tests/test_statepoint_batches.py b/tests/unit_tests/test_statepoint_batches.py new file mode 100644 index 0000000000..bf54e18786 --- /dev/null +++ b/tests/unit_tests/test_statepoint_batches.py @@ -0,0 +1,26 @@ +from pathlib import Path + +import openmc + + +def test_statepoint_batches(run_in_tmpdir): + # Create a minimal model + mat = openmc.Material() + mat.add_nuclide('U235', 1.0) + mat.set_density('g/cm3', 4.5) + sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sphere) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.batches = 10 + model.settings.inactive = 5 + model.settings.particles = 100 + + # Specify when statepoints should be written + model.settings.statepoint = {'batches': [3, 6, 9]} + + # Run model and ensure that statepoints are created + model.run() + sp_files = ['statepoint.03.h5', 'statepoint.06.h5', 'statepoint.09.h5'] + for f in sp_files: + assert Path(f).is_file() diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py index bbcb12ff11..abf143f12a 100644 --- a/tests/unit_tests/test_stats.py +++ b/tests/unit_tests/test_stats.py @@ -4,8 +4,19 @@ import numpy as np import pytest import openmc import openmc.stats +from scipy.integrate import trapezoid +def assert_sample_mean(samples, expected_mean): + # Calculate sample standard deviation + std_dev = samples.std() / np.sqrt(samples.size - 1) + + # Means should agree within 4 sigma 99.993% of the time. Note that this is + # expected to fail about 1 out of 16,000 times + assert np.abs(expected_mean - samples.mean()) < 4*std_dev + + +@pytest.mark.flaky(reruns=1) def test_discrete(): x = [0.0, 1.0, 10.0] p = [0.3, 0.2, 0.5] @@ -13,8 +24,8 @@ def test_discrete(): elem = d.to_xml_element('distribution') d = openmc.stats.Discrete.from_xml_element(elem) - assert d.x == x - assert d.p == p + np.testing.assert_array_equal(d.x, x) + np.testing.assert_array_equal(d.p, p) assert len(d) == len(x) d = openmc.stats.Univariate.from_xml_element(elem) @@ -26,7 +37,75 @@ def test_discrete(): assert d2.p == [1.0] assert len(d2) == 1 + vals = np.array([1.0, 2.0, 3.0]) + probs = np.array([0.1, 0.7, 0.2]) + exp_mean = (vals * probs).sum() + + d3 = openmc.stats.Discrete(vals, probs) + + # sample discrete distribution and check that the mean of the samples is + # within 4 std. dev. of the expected mean + n_samples = 1_000_000 + samples = d3.sample(n_samples) + assert_sample_mean(samples, exp_mean) + + +def test_delta_function(): + d = openmc.stats.delta_function(14.1e6) + assert isinstance(d, openmc.stats.Discrete) + np.testing.assert_array_equal(d.x, [14.1e6]) + np.testing.assert_array_equal(d.p, [1.0]) + + +def test_merge_discrete(): + x1 = [0.0, 1.0, 10.0] + p1 = [0.3, 0.2, 0.5] + d1 = openmc.stats.Discrete(x1, p1) + + x2 = [0.5, 1.0, 5.0] + p2 = [0.4, 0.5, 0.1] + d2 = openmc.stats.Discrete(x2, p2) + + # Merged distribution should have x values sorted and probabilities + # appropriately combined. Duplicate x values should appear once. + merged = openmc.stats.Discrete.merge([d1, d2], [0.6, 0.4]) + assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0]) + assert merged.p == pytest.approx( + [0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5]) + assert merged.integral() == pytest.approx(1.0) + + # Probabilities add up but are not normalized + d1 = openmc.stats.Discrete([3.0], [1.0]) + triple = openmc.stats.Discrete.merge([d1, d1, d1], [1.0, 2.0, 3.0]) + assert triple.x == pytest.approx([3.0]) + assert triple.p == pytest.approx([6.0]) + assert triple.integral() == pytest.approx(6.0) + + +def test_clip_discrete(): + # Create discrete distribution with two points that are not important, one + # because the x value is very small, and one because the p value is very + # small + d = openmc.stats.Discrete([1e-8, 1.0, 2.0, 1000.0], [3.0, 2.0, 5.0, 1e-12]) + + # Clipping the distribution should result in two points + d_clip = d.clip(1e-6) + assert d_clip.x.size == 2 + assert d_clip.p.size == 2 + + # Make sure inplace returns same object + d_same = d.clip(1e-6, inplace=True) + assert d_same is d + + with pytest.raises(ValueError): + d.clip(-1.) + + with pytest.raises(ValueError): + d.clip(5) + + +@pytest.mark.flaky(reruns=1) def test_uniform(): a, b = 10.0, 20.0 d = openmc.stats.Uniform(a, b) @@ -38,12 +117,21 @@ def test_uniform(): assert len(d) == 2 t = d.to_tabular() - assert t.x == [a, b] - assert t.p == [1/(b-a), 1/(b-a)] + np.testing.assert_array_equal(t.x, [a, b]) + np.testing.assert_array_equal(t.p, [1/(b-a), 1/(b-a)]) assert t.interpolation == 'histogram' + # Sample distribution and check that the mean of the samples is within 4 + # std. dev. of the expected mean + exp_mean = 0.5 * (a + b) + n_samples = 1_000_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, exp_mean) + + +@pytest.mark.flaky(reruns=1) def test_powerlaw(): - a, b, n = 10.0, 20.0, 2.0 + a, b, n = 10.0, 100.0, 2.0 d = openmc.stats.PowerLaw(a, b, n) elem = d.to_xml_element('distribution') @@ -53,6 +141,17 @@ def test_powerlaw(): assert d.n == n assert len(d) == 3 + # Determine mean of distribution + exp_mean = (n+1)*(b**(n+2) - a**(n+2))/((n+2)*(b**(n+1) - a**(n+1))) + + # sample power law distribution and check that the mean of the samples is + # within 4 std. dev. of the expected mean + n_samples = 1_000_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, exp_mean) + + +@pytest.mark.flaky(reruns=1) def test_maxwell(): theta = 1.2895e6 d = openmc.stats.Maxwell(theta) @@ -62,7 +161,21 @@ def test_maxwell(): assert d.theta == theta assert len(d) == 1 + exp_mean = 3/2 * theta + # sample maxwell distribution and check that the mean of the samples is + # within 4 std. dev. of the expected mean + n_samples = 1_000_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, exp_mean) + + # A second sample starting from a different seed + samples_2 = d.sample(n_samples) + assert_sample_mean(samples_2, exp_mean) + assert samples_2.mean() != samples.mean() + + +@pytest.mark.flaky(reruns=1) def test_watt(): a, b = 0.965e6, 2.29e-6 d = openmc.stats.Watt(a, b) @@ -73,19 +186,80 @@ def test_watt(): assert d.b == b assert len(d) == 2 + # mean value form adapted from + # "Prompt-fission-neutron average energy for 238U(n, f ) from + # threshold to 200 MeV" Ethvignot et. al. + # https://doi.org/10.1016/j.physletb.2003.09.048 + exp_mean = 3/2 * a + a**2 * b / 4 + # sample Watt distribution and check that the mean of the samples is within + # 4 std. dev. of the expected mean + n_samples = 1_000_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, exp_mean) + + +@pytest.mark.flaky(reruns=1) def test_tabular(): - x = [0.0, 5.0, 7.0] - p = [0.1, 0.2, 0.05] + # test linear-linear sampling + x = np.array([0.0, 5.0, 7.0, 10.0]) + p = np.array([10.0, 20.0, 5.0, 6.0]) + d = openmc.stats.Tabular(x, p, 'linear-linear') + n_samples = 100_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, d.mean()) + + # test linear-linear normalization + d.normalize() + assert d.integral() == pytest.approx(1.0) + + # test histogram sampling + d = openmc.stats.Tabular(x, p, interpolation='histogram') + samples = d.sample(n_samples) + assert_sample_mean(samples, d.mean()) + + d.normalize() + assert d.integral() == pytest.approx(1.0) + + # ensure that passing a set of probabilities shorter than x works + # for histogram interpolation + d = openmc.stats.Tabular(x, p[:-1], interpolation='histogram') + d.cdf() + d.mean() + assert_sample_mean(d.sample(n_samples), d.mean()) + + # passing a shorter probability set should raise an error for linear-linear + with pytest.raises(ValueError): + d = openmc.stats.Tabular(x, p[:-1], interpolation='linear-linear') + d.cdf() + + # Use probabilities of correct length for linear-linear interpolation and + # call the CDF method + d = openmc.stats.Tabular(x, p, interpolation='linear-linear') + d.cdf() + + +def test_tabular_from_xml(): + x = np.array([0.0, 5.0, 7.0, 10.0]) + p = np.array([10.0, 20.0, 5.0, 6.0]) d = openmc.stats.Tabular(x, p, 'linear-linear') elem = d.to_xml_element('distribution') d = openmc.stats.Tabular.from_xml_element(elem) - assert d.x == x - assert d.p == p + assert all(d.x == x) + assert all(d.p == p) assert d.interpolation == 'linear-linear' assert len(d) == len(x) + # Make sure XML roundtrip works with len(x) == len(p) + 1 + x = np.array([0.0, 5.0, 7.0, 10.0]) + p = np.array([10.0, 20.0, 5.0]) + d = openmc.stats.Tabular(x, p, 'histogram') + elem = d.to_xml_element('distribution') + d = openmc.stats.Tabular.from_xml_element(elem) + assert all(d.x == x) + assert all(d.p == p) + def test_legendre(): # Pu239 elastic scattering at 100 keV @@ -96,29 +270,67 @@ def test_legendre(): # Integrating distribution should yield one mu = np.linspace(-1., 1., 1000) - assert np.trapz(d(mu), mu) == pytest.approx(1.0, rel=1e-4) + assert trapezoid(d(mu), mu) == pytest.approx(1.0, rel=1e-4) with pytest.raises(NotImplementedError): d.to_xml_element('distribution') +@pytest.mark.flaky(reruns=1) def test_mixture(): d1 = openmc.stats.Uniform(0, 5) d2 = openmc.stats.Uniform(3, 7) p = [0.5, 0.5] mix = openmc.stats.Mixture(p, [d1, d2]) - assert mix.probability == p + np.testing.assert_allclose(mix.probability, p) assert mix.distribution == [d1, d2] assert len(mix) == 4 + # Sample and make sure sample mean is close to expected mean + n_samples = 1_000_000 + samples = mix.sample(n_samples) + assert_sample_mean(samples, (2.5 + 5.0)/2) + elem = mix.to_xml_element('distribution') d = openmc.stats.Mixture.from_xml_element(elem) - assert d.probability == p + np.testing.assert_allclose(d.probability, p) assert d.distribution == [d1, d2] assert len(d) == 4 +def test_mixture_clip(): + # Create mixture distribution containing a discrete distribution with two + # points that are not important, one because the x value is very small, and + # one because the p value is very small + d1 = openmc.stats.Discrete([1e-8, 1.0, 2.0, 1000.0], [3.0, 2.0, 5.0, 1e-12]) + d2 = openmc.stats.Uniform(0, 5) + mix = openmc.stats.Mixture([0.5, 0.5], [d1, d2]) + + # Clipping should reduce the contained discrete distribution to 2 points + mix_clip = mix.clip(1e-6) + assert mix_clip.distribution[0].x.size == 2 + assert mix_clip.distribution[0].p.size == 2 + + # Make sure inplace returns same object + mix_same = mix.clip(1e-6, inplace=True) + assert mix_same is mix + + # Make sure clip removes low probability distributions + d_small = openmc.stats.Uniform(0., 1.) + d_large = openmc.stats.Uniform(2., 5.) + mix = openmc.stats.Mixture([1e-10, 1.0], [d_small, d_large]) + mix_clip = mix.clip(1e-3) + assert mix_clip.distribution == [d_large] + + # Make sure warning is raised if tolerance is exceeded + d1 = openmc.stats.Discrete([1.0, 1.001], [1.0, 0.7e-6]) + d2 = openmc.stats.Tabular([0.0, 1.0], [0.7e-6], interpolation='histogram') + mix = openmc.stats.Mixture([1.0, 1.0], [d1, d2]) + with pytest.warns(UserWarning): + mix_clip = mix.clip(1e-6) + + def test_polar_azimuthal(): # default polar-azimuthal should be uniform in mu and phi d = openmc.stats.PolarAzimuthal() @@ -205,15 +417,6 @@ def test_box(): d = openmc.stats.Box.from_xml_element(elem) assert d.lower_left == pytest.approx(lower_left) assert d.upper_right == pytest.approx(upper_right) - assert not d.only_fissionable - - # only fissionable parameter - d2 = openmc.stats.Box(lower_left, upper_right, True) - assert d2.only_fissionable - elem = d2.to_xml_element() - assert elem.attrib['type'] == 'fission' - d = openmc.stats.Spatial.from_xml_element(elem) - assert isinstance(d, openmc.stats.Box) def test_point(): @@ -228,6 +431,8 @@ def test_point(): d = openmc.stats.Point.from_xml_element(elem) assert d.xyz == pytest.approx(p) + +@pytest.mark.flaky(reruns=1) def test_normal(): mean = 10.0 std_dev = 2.0 @@ -241,17 +446,73 @@ def test_normal(): assert d.std_dev == pytest.approx(std_dev) assert len(d) == 2 + # sample normal distribution + n_samples = 100_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, mean) + + +@pytest.mark.flaky(reruns=1) def test_muir(): mean = 10.0 mass = 5.0 temp = 20000. - d = openmc.stats.Muir(mean,mass,temp) + d = openmc.stats.muir(mean, mass, temp) + assert isinstance(d, openmc.stats.Normal) elem = d.to_xml_element('energy') - assert elem.attrib['type'] == 'muir' + assert elem.attrib['type'] == 'normal' - d = openmc.stats.Muir.from_xml_element(elem) - assert d.e0 == pytest.approx(mean) - assert d.m_rat == pytest.approx(mass) - assert d.kt == pytest.approx(temp) - assert len(d) == 3 + d = openmc.stats.Univariate.from_xml_element(elem) + assert isinstance(d, openmc.stats.Normal) + + # sample muir distribution + n_samples = 100_000 + samples = d.sample(n_samples) + assert_sample_mean(samples, mean) + + +@pytest.mark.flaky(reruns=1) +def test_combine_distributions(): + # Combine two discrete (same data as in test_merge_discrete) + x1 = [0.0, 1.0, 10.0] + p1 = [0.3, 0.2, 0.5] + d1 = openmc.stats.Discrete(x1, p1) + x2 = [0.5, 1.0, 5.0] + p2 = [0.4, 0.5, 0.1] + d2 = openmc.stats.Discrete(x2, p2) + + # Merged distribution should have x values sorted and probabilities + # appropriately combined. Duplicate x values should appear once. + merged = openmc.stats.combine_distributions([d1, d2], [0.6, 0.4]) + assert isinstance(merged, openmc.stats.Discrete) + assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0]) + assert merged.p == pytest.approx( + [0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5]) + + # Probabilities add up but are not normalized + d1 = openmc.stats.Discrete([3.0], [1.0]) + triple = openmc.stats.combine_distributions([d1, d1, d1], [1.0, 2.0, 3.0]) + assert triple.x == pytest.approx([3.0]) + assert triple.p == pytest.approx([6.0]) + + # Combine discrete and tabular + t1 = openmc.stats.Tabular(x2, p2) + mixed = openmc.stats.combine_distributions([d1, t1], [0.5, 0.5]) + assert isinstance(mixed, openmc.stats.Mixture) + assert len(mixed.distribution) == 2 + assert len(mixed.probability) == 2 + + # Combine 1 discrete and 2 tabular -- the tabular distributions should + # combine to produce a uniform distribution with mean 0.5. The combined + # distribution should have a mean of 0.25. + t1 = openmc.stats.Tabular([0., 1.], [2.0, 0.0]) + t2 = openmc.stats.Tabular([0., 1.], [0.0, 2.0]) + d1 = openmc.stats.Discrete([0.0], [1.0]) + combined = openmc.stats.combine_distributions([t1, t2, d1], [0.25, 0.25, 0.5]) + assert combined.integral() == pytest.approx(1.0) + + # Sample the combined distribution and make sure the sample mean is within + # uncertainty of the expected value + samples = combined.sample(10_000) + assert_sample_mean(samples, 0.25) diff --git a/tests/unit_tests/test_summary.py b/tests/unit_tests/test_summary.py new file mode 100644 index 0000000000..315ac0ba59 --- /dev/null +++ b/tests/unit_tests/test_summary.py @@ -0,0 +1,41 @@ +import openmc + + +def test_periodic_surface_roundtrip(run_in_tmpdir): + # Create a simple model with periodic surfaces + mat = openmc.Material() + mat.add_nuclide('H1', 1.0) + mat.set_density('g/cm3', 1.0) + cyl = openmc.ZCylinder(r=1.0) + x0 = openmc.XPlane(-5.0, boundary_type='periodic') + y0 = openmc.YPlane(-5.0, boundary_type='periodic') + z0 = openmc.ZPlane(-5.0, boundary_type='periodic') + x1 = openmc.XPlane(5.0, boundary_type='periodic') + y1 = openmc.YPlane(5.0, boundary_type='periodic') + z1 = openmc.ZPlane(5.0, boundary_type='periodic') + x0.periodic_surface = x1 + y0.periodic_surface = y1 + z0.periodic_surface = z1 + cell1 = openmc.Cell(fill=mat, region=-cyl) + cell2 = openmc.Cell(fill=mat, region=+cyl & +x0 & -x1 & +y0 & -y1 & +z0 & -z1) + model = openmc.Model() + model.geometry = openmc.Geometry([cell1, cell2]) + model.settings.particles = 100 + model.settings.batches = 1 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource( + energy=openmc.stats.delta_function(1.0e4) + ) + + # Run model + model.run() + + # Load summary data and check periodic surfaces + summary = openmc.Summary('summary.h5') + surfs = summary.geometry.get_all_surfaces() + for s in [x0, y0, z0, x1, y1, z1]: + assert surfs[s.id].boundary_type == 'periodic' + pairs = [(x0, x1), (y0, y1), (z0, z1)] + for s0, s1 in pairs: + assert surfs[s0.id].periodic_surface == surfs[s1.id] + assert surfs[s1.id].periodic_surface == surfs[s0.id] diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 12cd8c9d20..e9560223d1 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -753,3 +753,16 @@ def test_ztorus(): assert isinstance(sr, openmc.YTorus) sr = s.rotate((0., 90., 0.)) assert isinstance(sr, openmc.XTorus) + + +def test_normalize(): + """Test that equivalent planes give same normalized coefficients""" + p1 = openmc.Plane(a=0.0, b=1.0, c=0.0, d=1.0) + p2 = openmc.Plane(a=0.0, b=2.0, c=0.0, d=2.0) + assert p1.normalize() == p2.normalize() + + p2 = openmc.Plane(a=0.0, b=-1.0, c=0.0, d=-1.0) + assert p1.normalize() == p2.normalize() + + p2 = openmc.YPlane(1.0) + assert p1.normalize() == p2.normalize() diff --git a/tests/unit_tests/test_surface_composite.py b/tests/unit_tests/test_surface_composite.py index a1a218311d..a04fc73c59 100644 --- a/tests/unit_tests/test_surface_composite.py +++ b/tests/unit_tests/test_surface_composite.py @@ -12,7 +12,8 @@ def test_rectangular_parallelepiped(): ymax = ymin + uniform(0., 5.) zmin = uniform(-5., 5.) zmax = zmin + uniform(0., 5.) - s = openmc.model.RectangularParallelepiped(xmin, xmax, ymin, ymax, zmin, zmax) + s = openmc.model.RectangularParallelepiped( + xmin, xmax, ymin, ymax, zmin, zmax) assert isinstance(s.xmin, openmc.XPlane) assert isinstance(s.xmax, openmc.XPlane) assert isinstance(s.ymin, openmc.YPlane) @@ -64,42 +65,46 @@ def test_right_circular_cylinder(axis, indices): x, y, z = 1.0, -2.5, 3.0 h, r = 5.0, 3.0 s = openmc.model.RightCircularCylinder((x, y, z), h, r, axis=axis.lower()) - assert isinstance(s.cyl, getattr(openmc, axis + "Cylinder")) - assert isinstance(s.top, getattr(openmc, axis + "Plane")) - assert isinstance(s.bottom, getattr(openmc, axis + "Plane")) + s_r = openmc.model.RightCircularCylinder((x, y, z), h, r, axis=axis.lower(), + upper_fillet_radius=1.6, + lower_fillet_radius=1.6) + for s in (s, s_r): + assert isinstance(s.cyl, getattr(openmc, axis + "Cylinder")) + assert isinstance(s.top, getattr(openmc, axis + "Plane")) + assert isinstance(s.bottom, getattr(openmc, axis + "Plane")) - # Make sure boundary condition propagates - s.boundary_type = 'reflective' - assert s.boundary_type == 'reflective' - assert s.cyl.boundary_type == 'reflective' - assert s.bottom.boundary_type == 'reflective' - assert s.top.boundary_type == 'reflective' + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.cyl.boundary_type == 'reflective' + assert s.bottom.boundary_type == 'reflective' + assert s.top.boundary_type == 'reflective' - # Check bounding box - ll, ur = (+s).bounding_box - assert np.all(np.isinf(ll)) - assert np.all(np.isinf(ur)) - ll, ur = (-s).bounding_box - assert ll == pytest.approx((x, y, z) + np.roll([0, -r, -r], indices[0])) - assert ur == pytest.approx((x, y, z) + np.roll([h, r, r], indices[0])) + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll == pytest.approx((x, y, z) + np.roll([0, -r, -r], indices[0])) + assert ur == pytest.approx((x, y, z) + np.roll([h, r, r], indices[0])) - # __contains__ on associated half-spaces - point_pos = (x, y, z) + np.roll([h/2, r+1, r+1], indices[0]) - assert point_pos in +s - assert point_pos not in -s - point_neg = (x, y, z) + np.roll([h/2, 0, 0], indices[0]) - assert point_neg in -s - assert point_neg not in +s + # __contains__ on associated half-spaces + point_pos = (x, y, z) + np.roll([h/2, r+1, r+1], indices[0]) + assert point_pos in +s + assert point_pos not in -s + point_neg = (x, y, z) + np.roll([h/2, 0, 0], indices[0]) + assert point_neg in -s + assert point_neg not in +s - # translate method - t = uniform(-5.0, 5.0) - s_t = s.translate((t, t, t)) - ll_t, ur_t = (-s_t).bounding_box - assert ur_t == pytest.approx(ur + t) - assert ll_t == pytest.approx(ll + t) + # translate method + t = uniform(-5.0, 5.0) + s_t = s.translate((t, t, t)) + ll_t, ur_t = (-s_t).bounding_box + assert ur_t == pytest.approx(ur + t) + assert ll_t == pytest.approx(ll + t) - # Make sure repr works - repr(s) + # Make sure repr works + repr(s) @pytest.mark.parametrize( @@ -150,3 +155,509 @@ def test_cone_one_sided(axis, point_pos, point_neg, ll_true): # Make sure repr works repr(s) + + +@pytest.mark.parametrize( + "axis, indices, center", [ + ("X", [2, 0, 1], (0., 0.)), + ("Y", [0, 2, 1], (0., 0.)), + ("Z", [0, 1, 2], (0., 0.)), + ("X", [2, 0, 1], (10., 5.)), + ("Y", [0, 2, 1], (10., 5.)), + ("Z", [0, 1, 2], (10., 5.)), + + ] +) +def test_cylinder_sector(axis, indices, center): + c1, c2 = center + r1, r2 = 0.5, 1.5 + d = (r2 - r1) / 2 + phi1 = -60. + phi2 = 60 + s = openmc.model.CylinderSector(r1, r2, phi1, phi2, center=center, + axis=axis.lower()) + assert isinstance(s.outer_cyl, getattr(openmc, axis + "Cylinder")) + assert isinstance(s.inner_cyl, getattr(openmc, axis + "Cylinder")) + assert isinstance(s.plane1, openmc.Plane) + assert isinstance(s.plane2, openmc.Plane) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.outer_cyl.boundary_type == 'reflective' + assert s.inner_cyl.boundary_type == 'reflective' + assert s.plane1.boundary_type == 'reflective' + assert s.plane2.boundary_type == 'reflective' + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + test_point_ll = np.array([-r2 + c1, -r2 + c2, -np.inf]) + assert ll == pytest.approx(test_point_ll[indices]) + test_point_ur = np.array([r2 + c1, r2 + c2, np.inf]) + assert ur == pytest.approx(test_point_ur[indices]) + + # __contains__ on associated half-spaces + point_pos = np.array([0 + c1, r2 + 1 + c2, 0]) + assert point_pos[indices] in +s + assert point_pos[indices] not in -s + point_neg = np.array([r1 + d + c1, r1 + d + c2, 0]) + assert point_neg[indices] in -s + assert point_neg[indices] not in +s + + # Check __contains__ for sector with reflex angle + s_reflex = openmc.model.CylinderSector( + r1, r2, 0., 270., center=center, axis=axis.lower()) + points = [ + np.array([c1 + r1 + d, c2 + 0.01, 0.]), + np.array([c1, c2 + r1 + d, 0.]), + np.array([c1 - r1 - d, c2, 0.]), + np.array([c1 - 0.01, c2 - r1 - d, 0.]) + ] + for point_neg in points: + assert point_neg[indices] in -s_reflex + assert point_neg[indices] not in +s_reflex + + # translate method + t = uniform(-5.0, 5.0) + s_t = s.translate((t, t, t)) + ll_t, ur_t = (-s_t).bounding_box + assert ur_t == pytest.approx(ur + t) + assert ll_t == pytest.approx(ll + t) + + # Check invalid r1, r2 combinations + with pytest.raises(ValueError): + openmc.model.CylinderSector(r2, r1, phi1, phi2) + + # Check invalid angles + with pytest.raises(ValueError): + openmc.model.CylinderSector(r1, r2, phi2, phi1) + + # Make sure repr works + repr(s) + + +def test_cylinder_sector_from_theta_alpha(): + r1, r2 = 0.5, 1.5 + d = (r2 - r1) / 2 + theta = 120. + alpha = -60. + theta1 = alpha + theta2 = alpha + theta + s = openmc.model.CylinderSector(r1, r2, theta1, theta2) + s_alt = openmc.model.CylinderSector.from_theta_alpha(r1, + r2, + theta, + alpha) + + # Check that the angles are correct + assert s.plane1.coefficients == s_alt.plane1.coefficients + assert s.plane2.coefficients == s_alt.plane2.coefficients + assert s.inner_cyl.coefficients == s_alt.inner_cyl.coefficients + assert s.outer_cyl.coefficients == s_alt.outer_cyl.coefficients + + # Check invalid sector width + with pytest.raises(ValueError): + openmc.model.CylinderSector.from_theta_alpha(r1, r2, 360, alpha) + with pytest.raises(ValueError): + openmc.model.CylinderSector.from_theta_alpha(r1, r2, -1, alpha) + + +@pytest.mark.parametrize( + "axis, plane_tb, plane_lr, axis_idx", [ + ("x", "Z", "Y", 0), + ("y", "Z", "X", 1), + ("z", "Y", "X", 2), + ] +) +def test_isogonal_octagon(axis, plane_tb, plane_lr, axis_idx): + center = np.array([0., 0.]) + point_pos = np.array([0.8, 0.8]) + point_neg = np.array([0.7, 0.7]) + r1 = 1. + r2 = 1. + plane_top_bottom = getattr(openmc, plane_tb + "Plane") + plane_left_right = getattr(openmc, plane_lr + "Plane") + s = openmc.model.IsogonalOctagon(center, r1, r2, axis=axis) + assert isinstance(s.top, plane_top_bottom) + assert isinstance(s.bottom, plane_top_bottom) + assert isinstance(s.right, plane_left_right) + assert isinstance(s.left, plane_left_right) + assert isinstance(s.upper_right, openmc.Plane) + assert isinstance(s.lower_right, openmc.Plane) + assert isinstance(s.upper_left, openmc.Plane) + assert isinstance(s.lower_left, openmc.Plane) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.top.boundary_type == 'reflective' + assert s.bottom.boundary_type == 'reflective' + assert s.right.boundary_type == 'reflective' + assert s.left.boundary_type == 'reflective' + assert s.upper_right.boundary_type == 'reflective' + assert s.lower_right.boundary_type == 'reflective' + assert s.lower_left.boundary_type == 'reflective' + assert s.upper_left.boundary_type == 'reflective' + + # Check bounding box + center = np.insert(center, axis_idx, np.inf) + xmax, ymax, zmax = center + r1 + coord_min = center - r1 + coord_min[axis_idx] *= -1 + xmin, ymin, zmin = coord_min + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ur == pytest.approx((xmax, ymax, zmax)) + assert ll == pytest.approx((xmin, ymin, zmin)) + + # __contains__ on associated half-spaces + point_pos = np.insert(point_pos, axis_idx, 0) + point_neg = np.insert(point_neg, axis_idx, 0) + assert point_pos in +s + assert point_pos not in -s + assert point_neg in -s + assert point_neg not in +s + + # translate method + t = uniform(-5.0, 5.0) + s_t = s.translate((t, t, t)) + ll_t, ur_t = (-s_t).bounding_box + assert ur_t == pytest.approx(ur + t) + assert ll_t == pytest.approx(ll + t) + + # Check invalid r1, r2 combinations + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=1.0, r2=10.) + with pytest.raises(ValueError): + openmc.model.IsogonalOctagon(center, r1=10., r2=1.) + + # Make sure repr works + repr(s) + + +def test_polygon(): + # define a 5 pointed star centered on 1, 1 + star = np.array([[1. , 2. ], + [0.70610737, 1.4045085 ], + [0.04894348, 1.30901699], + [0.52447174, 0.8454915 ], + [0.41221475, 0.19098301], + [1. , 0.5 ], + [1.58778525, 0.19098301], + [1.47552826, 0.8454915 ], + [1.95105652, 1.30901699], + [1.29389263, 1.4045085 ], + [1. , 2. ]]) + points_in = [(1, 1, 0), (0, 1, 1), (1, 0, 1), (.707, .707, 1)] + for i, basis in enumerate(('xy', 'yz', 'xz', 'rz')): + star_poly = openmc.model.Polygon(star, basis=basis) + assert points_in[i] in -star_poly + assert any([points_in[i] in reg for reg in star_poly.regions]) + assert points_in[i] not in +star_poly + assert (0, 0, 0) not in -star_poly + if basis != "rz": + for offsets in [0.6, np.array([0.6] * 10), [0.6] * 10]: + offset_star = star_poly.offset(offsets) + assert (0, 0, 0) in -offset_star + assert any([(0, 0, 0) in reg for reg in offset_star.regions]) + with pytest.raises(ValueError): + star_poly.offset([0.6, 0.6]) + + # check invalid Polygon input points + # duplicate points not just at start and end + rz_points = np.array([[6.88, 3.02], + [6.88, 2.72], + [6.88, 3.02], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [7.63, 0.0], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02]]) + with pytest.raises(ValueError): + openmc.model.Polygon(rz_points) + + # segment traces back on previous segment + rz_points = np.array([[6.88, 3.02], + [6.88, 2.72], + [6.88, 2.32], + [6.88, 2.52], + [7.63, 0.0], + [5.75, 0.0], + [6.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02]]) + with pytest.raises(ValueError): + openmc.model.Polygon(rz_points) + + # segments intersect (line-line) + rz_points = np.array([[6.88, 3.02], + [5.88, 2.32], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02]]) + with pytest.raises(ValueError): + openmc.model.Polygon(rz_points) + + # segments intersect (line-point) + rz_points = np.array([[6.88, 3.02], + [6.3, 2.32], + [7.63, 0.0], + [5.75, 0.0], + [5.75, 1.22], + [6.30, 1.22], + [6.30, 3.02], + [6.88, 3.02]]) + with pytest.raises(ValueError): + openmc.model.Polygon(rz_points) + + # Test "M" shaped polygon + points = np.array([[8.5151581, -17.988337], + [10.381711000000001, -17.988337], + [12.744357, -24.288728000000003], + [15.119406000000001, -17.988337], + [16.985959, -17.988337], + [16.985959, -27.246687], + [15.764328, -27.246687], + [15.764328, -19.116951], + [13.376877, -25.466951], + [12.118039, -25.466951], + [9.7305877, -19.116951], + [9.7305877, -27.246687], + [8.5151581, -27.246687]]) + + # Test points inside and outside by using offset method + m_polygon = openmc.model.Polygon(points, basis='xz') + inner_pts = m_polygon.offset(-0.1).points + assert all([(pt[0], 0, pt[1]) in -m_polygon for pt in inner_pts]) + outer_pts = m_polygon.offset(0.1).points + assert all([(pt[0], 0, pt[1]) in +m_polygon for pt in outer_pts]) + + # Offset of -0.2 will cause self-intersection + with pytest.raises(ValueError): + m_polygon.offset(-0.2) + + +@pytest.mark.parametrize("axis", ["x", "y", "z"]) +def test_cruciform_prism(axis): + center = x0, y0 = (3., 4.) + distances = [2., 3., 5.] + s = openmc.model.CruciformPrism(distances, center, axis=axis) + + if axis == 'x': + i1, i2 = 1, 2 + elif axis == 'y': + i1, i2 = 0, 2 + elif axis == 'z': + i1, i2 = 0, 1 + plane_cls = (openmc.XPlane, openmc.YPlane, openmc.ZPlane) + + # Check type of surfaces + for i in range(3): + assert isinstance(getattr(s, f'hmin{i}'), plane_cls[i1]) + assert isinstance(getattr(s, f'hmax{i}'), plane_cls[i1]) + assert isinstance(getattr(s, f'vmin{i}'), plane_cls[i2]) + assert isinstance(getattr(s, f'vmax{i}'), plane_cls[i2]) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + for i in range(3): + assert getattr(s, f'hmin{i}').boundary_type == 'reflective' + assert getattr(s, f'hmax{i}').boundary_type == 'reflective' + assert getattr(s, f'vmin{i}').boundary_type == 'reflective' + assert getattr(s, f'vmax{i}').boundary_type == 'reflective' + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ur[i1] == pytest.approx(x0 + distances[-1]) + assert ur[i2] == pytest.approx(y0 + distances[-1]) + assert ll[i1] == pytest.approx(x0 - distances[-1]) + assert ll[i2] == pytest.approx(y0 - distances[-1]) + + # __contains__ on associated half-spaces + point_pos, point_neg = np.zeros(3), np.zeros(3) + point_pos[i1] = x0 + 3.1 + point_pos[i2] = y0 + 2.05 + point_neg[i1] = x0 + 3.5 + point_neg[i2] = y0 + 1.99 + assert point_pos in +s + assert point_pos not in -s + assert point_neg in -s + assert point_neg not in +s + + # translate method + t = uniform(-5.0, 5.0) + s_t = s.translate((t, t, t)) + ll_t, ur_t = (-s_t).bounding_box + assert ur_t == pytest.approx(ur + t) + assert ll_t == pytest.approx(ll + t) + + # Make sure repr works + repr(s) + + # Check that non-monotonic distances fail + with pytest.raises(ValueError): + openmc.model.CruciformPrism([1.0, 0.5, 2.0, 3.0]) + with pytest.raises(ValueError): + openmc.model.CruciformPrism([3.0, 2.0, 0.5, 1.0]) + + +def test_box(): + v = (-1.0, -1.0, -2.5) + a1 = (2.0, -1.0, 0.0) + a2 = (1.0, 2.0, 0.0) + a3 = (0.0, 0.0, 5.0) + s = openmc.model.OrthogonalBox(v, a1, a2, a3) + for num in (1, 2, 3): + assert isinstance(getattr(s, f'ax{num}_min'), openmc.Plane) + assert isinstance(getattr(s, f'ax{num}_max'), openmc.Plane) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + for num in (1, 2, 3): + assert getattr(s, f'ax{num}_min').boundary_type == 'reflective' + assert getattr(s, f'ax{num}_max').boundary_type == 'reflective' + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll[2] == pytest.approx(-2.5) + assert ur[2] == pytest.approx(2.5) + + # __contains__ on associated half-spaces + assert (0., 0., 0.) in -s + assert (-2., 0., 0.) not in -s + assert (0., 0.9, 0.) in -s + assert (0., 0., -3.) in +s + assert (0., 0., 3.) in +s + + # translate method + s_t = s.translate((1., 1., 0.)) + assert (-0.01, 0., 0.) in +s_t + assert (0.01, 0., 0.) in -s_t + + # Make sure repr works + repr(s) + + # Version with infinite 3rd dimension + s = openmc.model.OrthogonalBox(v, a1, a2) + assert not hasattr(s, 'ax3_min') + assert not hasattr(s, 'ax3_max') + ll, ur = (-s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + assert (0., 0., 0.) in -s + assert (-2., 0., 0.) not in -s + assert (0., 0.9, 0.) in -s + assert (0., 0., -3.) not in +s + assert (0., 0., 3.) not in +s + + +def test_conical_frustum(): + center_base = (0.0, 0.0, -3) + axis = (0., 0., 3.) + r1 = 2.0 + r2 = 0.5 + s = openmc.model.ConicalFrustum(center_base, axis, r1, r2) + assert isinstance(s.cone, openmc.Cone) + assert isinstance(s.plane_bottom, openmc.Plane) + assert isinstance(s.plane_top, openmc.Plane) + + # Make sure boundary condition propagates + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.cone.boundary_type == 'reflective' + assert s.plane_bottom.boundary_type == 'reflective' + assert s.plane_top.boundary_type == 'reflective' + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert ll[2] == pytest.approx(-3.0) + assert ur[2] == pytest.approx(0.0) + + # __contains__ on associated half-spaces + assert (0., 0., -1.) in -s + assert (0., 0., -4.) not in -s + assert (0., 0., 1.) not in -s + assert (1., 1., -2.99) in -s + assert (1., 1., -0.01) in +s + + # translate method + s_t = s.translate((1., 1., 0.)) + assert (1., 1., -0.01) in -s_t + + # Make sure repr works + repr(s) + + # Denegenerate case with r1 = r2 + s = openmc.model.ConicalFrustum(center_base, axis, r1, r1) + assert (1., 1., -0.01) in -s + + +def test_vessel(): + center = (3.0, 2.0) + r = 1.0 + p1, p2 = -5.0, 5.0 + h1 = h2 = 1.0 + s = openmc.model.Vessel(r, p1, p2, h1, h2, center) + assert isinstance(s.cyl, openmc.Cylinder) + assert isinstance(s.plane_bottom, openmc.Plane) + assert isinstance(s.plane_top, openmc.Plane) + assert isinstance(s.bottom, openmc.Quadric) + assert isinstance(s.top, openmc.Quadric) + + # Make sure boundary condition propagates (but not for planes) + s.boundary_type = 'reflective' + assert s.boundary_type == 'reflective' + assert s.cyl.boundary_type == 'reflective' + assert s.bottom.boundary_type == 'reflective' + assert s.top.boundary_type == 'reflective' + assert s.plane_bottom.boundary_type == 'transmission' + assert s.plane_top.boundary_type == 'transmission' + + # Check bounding box + ll, ur = (+s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + ll, ur = (-s).bounding_box + assert np.all(np.isinf(ll)) + assert np.all(np.isinf(ur)) + + # __contains__ on associated half-spaces + assert (3., 2., 0.) in -s + assert (3., 2., -5.0) in -s + assert (3., 2., 5.0) in -s + assert (3., 2., -5.9) in -s + assert (3., 2., 5.9) in -s + assert (3., 2., -6.1) not in -s + assert (3., 2., 6.1) not in -s + assert (4.5, 2., 0.) in +s + assert (3., 3.2, 0.) in +s + assert (3., 2., 7.) in +s + + # translate method + s_t = s.translate((0., 0., 1.)) + assert (3., 2., 6.1) in -s_t + + # Make sure repr works + repr(s) diff --git a/tests/unit_tests/test_surface_source_write.py b/tests/unit_tests/test_surface_source_write.py new file mode 100644 index 0000000000..6f18d32b71 --- /dev/null +++ b/tests/unit_tests/test_surface_source_write.py @@ -0,0 +1,301 @@ +"""Test the 'surf_source_write' setting used to store particles that cross +surfaces in a file for a given simulation.""" + +from pathlib import Path + +import openmc +import openmc.lib +import pytest +import h5py +import numpy as np + + +@pytest.fixture(scope="module") +def geometry(): + """Simple hydrogen sphere geometry""" + openmc.reset_auto_ids() + material = openmc.Material(name="H1") + material.add_element("H", 1.0) + sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + return openmc.Geometry([cell]) + + +@pytest.mark.parametrize( + "parameter", + [ + {"max_particles": 200}, + {"max_particles": 200, "cell": 1}, + {"max_particles": 200, "cellto": 1}, + {"max_particles": 200, "cellfrom": 1}, + {"max_particles": 200, "surface_ids": [2]}, + {"max_particles": 200, "surface_ids": [2], "cell": 1}, + {"max_particles": 200, "surface_ids": [2], "cellto": 1}, + {"max_particles": 200, "surface_ids": [2], "cellfrom": 1}, + {"max_particles": 200, "surface_ids": [2], "max_source_files": 1}, + ], +) +def test_xml_serialization(parameter, run_in_tmpdir): + """Check that the different use cases can be written and read in XML.""" + settings = openmc.Settings() + settings.surf_source_write = parameter + settings.export_to_xml() + + read_settings = openmc.Settings.from_xml() + assert read_settings.surf_source_write == parameter + + +@pytest.fixture(scope="module") +def model(): + """Simple hydrogen sphere geometry""" + openmc.reset_auto_ids() + model = openmc.Model() + + # Material + h1 = openmc.Material(name="H1") + h1.add_nuclide("H1", 1.0) + h1.set_density('g/cm3', 1e-7) + + # Geometry + radius = 1.0 + sphere = openmc.Sphere(r=radius, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=h1) + model.geometry = openmc.Geometry([cell]) + + # Settings + model.settings = openmc.Settings() + model.settings.run_mode = "fixed source" + model.settings.particles = 100 + model.settings.batches = 3 + model.settings.seed = 1 + + distribution = openmc.stats.Point() + model.settings.source = openmc.IndependentSource(space=distribution) + return model + + +@pytest.mark.parametrize( + "max_particles, max_source_files", + [ + (100, 2), + (100, 3), + (100, 1), + ], +) +def test_number_surface_source_file_created(max_particles, max_source_files, + run_in_tmpdir, model): + """Check the number of surface source files written.""" + model.settings.surf_source_write = { + "max_particles": max_particles, + "max_source_files": max_source_files + } + model.run() + should_be_numbered = max_source_files > 1 + for i in range(1, max_source_files + 1): + if should_be_numbered: + assert Path(f"surface_source.{i}.h5").exists() + if not should_be_numbered: + assert Path("surface_source.h5").exists() + +ERROR_MSG_1 = ( + "A maximum number of particles needs to be specified " + "using the 'max_particles' parameter to store surface " + "source points." +) +ERROR_MSG_2 = "'cell', 'cellfrom' and 'cellto' cannot be used at the same time." + + +@pytest.mark.parametrize( + "parameter, error", + [ + ({"cell": 1}, ERROR_MSG_1), + ({"max_particles": 200, "cell": 1, "cellto": 1}, ERROR_MSG_2), + ({"max_particles": 200, "cell": 1, "cellfrom": 1}, ERROR_MSG_2), + ({"max_particles": 200, "cellto": 1, "cellfrom": 1}, ERROR_MSG_2), + ({"max_particles": 200, "cell": 1, "cellto": 1, "cellfrom": 1}, ERROR_MSG_2), + ], +) +def test_exceptions(parameter, error, run_in_tmpdir, geometry): + """Test parameters configuration that should return an error.""" + settings = openmc.Settings(run_mode="fixed source", batches=5, particles=100) + settings.surf_source_write = parameter + model = openmc.Model(geometry=geometry, settings=settings) + with pytest.raises(RuntimeError, match=error): + model.run() + + +@pytest.fixture(scope="module") +def model(): + """Simple hydrogen sphere divided in two hemispheres + by a z-plane to form 2 cells.""" + openmc.reset_auto_ids() + model = openmc.Model() + + # Material + material = openmc.Material(name="H1") + material.add_element("H", 1.0) + + # Geometry + radius = 1.0 + sphere = openmc.Sphere(r=radius, boundary_type="reflective") + plane = openmc.ZPlane(0.0) + cell_1 = openmc.Cell(region=-sphere & -plane, fill=material) + cell_2 = openmc.Cell(region=-sphere & +plane, fill=material) + root = openmc.Universe(cells=[cell_1, cell_2]) + model.geometry = openmc.Geometry(root) + + # Settings + model.settings = openmc.Settings() + model.settings.run_mode = "fixed source" + model.settings.particles = 100 + model.settings.batches = 3 + model.settings.seed = 1 + + bounds = [-radius, -radius, -radius, radius, radius, radius] + distribution = openmc.stats.Box(bounds[:3], bounds[3:]) + model.settings.source = openmc.IndependentSource(space=distribution) + + return model + + +@pytest.mark.parametrize( + "parameter", + [ + {"max_particles": 200, "cellto": 2, "surface_ids": [2]}, + {"max_particles": 200, "cellfrom": 2, "surface_ids": [2]}, + ], +) +def test_particle_direction(parameter, run_in_tmpdir, model): + """Test the direction of particles with the 'cellfrom' and 'cellto' parameters + on a simple model with only one surface of interest. + + Cell 2 is the upper hemisphere and surface 2 is the plane dividing the sphere + into two hemispheres. + + """ + model.settings.surf_source_write = parameter + model.run() + with h5py.File("surface_source.h5", "r") as f: + source = f["source_bank"] + + assert len(source) == 200 + + # We want to verify that the dot product of the surface's normal vector + # and the direction of the particle is either positive or negative + # depending on cellfrom or cellto. In this case, it is equivalent + # to just compare the z component of the direction of the particle. + for point in source: + if "cellto" in parameter.keys(): + assert point["u"]["z"] > 0.0 + elif "cellfrom" in parameter.keys(): + assert point["u"]["z"] < 0.0 + else: + assert False + + +@pytest.fixture +def model_dagmc(request): + """Model based on the mesh file 'dagmc.h5m' available from + tests/regression_tests/dagmc/legacy. + + """ + openmc.reset_auto_ids() + model = openmc.Model() + + # ============================================================================= + # Materials + # ============================================================================= + + u235 = openmc.Material(name="no-void fuel") + u235.add_nuclide("U235", 1.0, "ao") + u235.set_density("g/cc", 11) + u235.id = 40 + + water = openmc.Material(name="water") + water.add_nuclide("H1", 2.0, "ao") + water.add_nuclide("O16", 1.0, "ao") + water.set_density("g/cc", 1.0) + water.add_s_alpha_beta("c_H_in_H2O") + water.id = 41 + + model.materials = openmc.Materials([u235, water]) + + # ============================================================================= + # Geometry + # ============================================================================= + dagmc_path = Path(request.fspath).parent / "../regression_tests/dagmc/legacy/dagmc.h5m" + dagmc_univ = openmc.DAGMCUniverse(dagmc_path) + model.geometry = openmc.Geometry(dagmc_univ) + + # ============================================================================= + # Settings + # ============================================================================= + + model.settings = openmc.Settings() + model.settings.particles = 300 + model.settings.batches = 5 + model.settings.inactive = 1 + model.settings.seed = 1 + + source_box = openmc.stats.Box([-4, -4, -20], [4, 4, 20]) + model.settings.source = openmc.IndependentSource(space=source_box) + + return model + + +@pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled." +) +@pytest.mark.parametrize( + "parameter", + [ + {"max_particles": 200, "cellto": 1}, + {"max_particles": 200, "cellfrom": 1}, + ], +) +def test_particle_direction_dagmc(parameter, run_in_tmpdir, model_dagmc): + """Test the direction of particles with the 'cellfrom' and 'cellto' parameters + on a DAGMC model.""" + model_dagmc.settings.surf_source_write = parameter + model_dagmc.run() + + r = 7.0 + h = 20.0 + + with h5py.File("surface_source.h5", "r") as f: + source = f["source_bank"] + + assert len(source) == 200 + + for point in source: + + x, y, z = point["r"] + ux, uy, uz = point["u"] + + # If the point is on the upper or lower circle + if np.allclose(abs(z), h): + # If the point is also on the cylindrical surface + if np.allclose(np.sqrt(x**2 + y**2), r): + if "cellfrom" in parameter.keys(): + assert (uz * z > 0) or (ux * x + uy * y > 0) + elif "cellto" in parameter.keys(): + assert (uz * z < 0) or (ux * x + uy * y < 0) + else: + assert False + # If the point is not on the cylindrical surface + else: + if "cellfrom" in parameter.keys(): + assert uz * z > 0 + elif "cellto" in parameter.keys(): + assert uz * z < 0 + else: + assert False + # If the point is not on the upper or lower circle, + # meaning it is on the cylindrical surface + else: + if "cellfrom" in parameter.keys(): + assert ux * x + uy * y > 0 + elif "cellto" in parameter.keys(): + assert ux * x + uy * y < 0 + else: + assert False diff --git a/tests/unit_tests/test_tallies.py b/tests/unit_tests/test_tallies.py index aeeb0612d5..c38f067d58 100644 --- a/tests/unit_tests/test_tallies.py +++ b/tests/unit_tests/test_tallies.py @@ -1,4 +1,5 @@ import numpy as np +import pytest import openmc @@ -9,8 +10,9 @@ def test_xml_roundtrip(run_in_tmpdir): mesh.upper_right = (10., 10., 10.,) mesh.dimension = (5, 5, 5) mesh_filter = openmc.MeshFilter(mesh) + meshborn_filter = openmc.MeshBornFilter(mesh) tally = openmc.Tally() - tally.filters = [mesh_filter] + tally.filters = [mesh_filter, meshborn_filter] tally.nuclides = ['U235', 'I135', 'Li6'] tally.scores = ['total', 'fission', 'heating'] tally.derivative = openmc.TallyDerivative( @@ -26,9 +28,11 @@ def test_xml_roundtrip(run_in_tmpdir): assert len(new_tallies) == 1 new_tally = new_tallies[0] assert new_tally.id == tally.id - assert len(new_tally.filters) == 1 + assert len(new_tally.filters) == 2 assert isinstance(new_tally.filters[0], openmc.MeshFilter) assert np.allclose(new_tally.filters[0].mesh.lower_left, mesh.lower_left) + assert isinstance(new_tally.filters[1], openmc.MeshBornFilter) + assert np.allclose(new_tally.filters[1].mesh.lower_left, mesh.lower_left) assert new_tally.nuclides == tally.nuclides assert new_tally.scores == tally.scores assert new_tally.derivative.variable == tally.derivative.variable @@ -38,3 +42,124 @@ def test_xml_roundtrip(run_in_tmpdir): assert new_tally.triggers[0].trigger_type == tally.triggers[0].trigger_type assert new_tally.triggers[0].threshold == tally.triggers[0].threshold assert new_tally.triggers[0].scores == tally.triggers[0].scores + assert new_tally.multiply_density == tally.multiply_density + + +def test_tally_equivalence(): + tally_a = openmc.Tally() + tally_b = openmc.Tally(tally_id=tally_a.id) + + tally_a.name = 'new name' + assert tally_a != tally_b + tally_b.name = tally_a.name + assert tally_a == tally_b + + assert tally_a == tally_b + ef_a = openmc.EnergyFilter([0.0, 0.1, 1.0, 10.0e6]) + ef_b = openmc.EnergyFilter([0.0, 0.1, 1.0, 10.0e6]) + + tally_a.filters = [ef_a] + assert tally_a != tally_b + tally_b.filters = [ef_b] + assert tally_a == tally_b + + tally_a.scores = ['flux', 'absorption', 'fission', 'scatter'] + assert tally_a != tally_b + tally_b.scores = ['flux', 'absorption', 'fission', 'scatter'] + assert tally_a == tally_b + + tally_a.nuclides = [] + tally_b.nuclides = [] + assert tally_a == tally_b + + tally_a.nuclides = ['total'] + assert tally_a == tally_b + + # a tally with an estimator set to None is equal to + # a tally with an estimator specified + tally_a.estimator = 'collision' + assert tally_a == tally_b + tally_b.estimator = 'collision' + assert tally_a == tally_b + + tally_a.multiply_density = False + assert tally_a != tally_b + tally_b.multiply_density = False + assert tally_a == tally_b + + trigger_a = openmc.Trigger('rel_err', 0.025) + trigger_b = openmc.Trigger('rel_err', 0.025) + + tally_a.triggers = [trigger_a] + assert tally_a != tally_b + tally_b.triggers = [trigger_b] + assert tally_a == tally_b + + +def test_figure_of_merit(sphere_model, run_in_tmpdir): + # Run model with a few simple tally scores + tally = openmc.Tally() + tally.scores = ['total', 'absorption', 'scatter'] + sphere_model.tallies = [tally] + sp_path = sphere_model.run(apply_tally_results=True) + + # Get execution time and relative error + with openmc.StatePoint(sp_path) as sp: + time = sp.runtime['simulation'] + rel_err = tally.std_dev / tally.mean + + # Check that figure of merit is calculated correctly + assert tally.figure_of_merit == pytest.approx(1 / (rel_err**2 * time)) + + +def test_tally_application(sphere_model, run_in_tmpdir): + # Create a tally with most possible gizmos + tally = openmc.Tally(name='test tally') + ef = openmc.EnergyFilter([0.0, 0.1, 1.0, 10.0e6]) + mesh = openmc.RegularMesh.from_domain(sphere_model.geometry, (2, 2, 2)) + mf = openmc.MeshFilter(mesh) + tally.filters = [ef, mf] + tally.scores = ['flux', 'absorption', 'fission', 'scatter'] + sphere_model.tallies = [tally] + + # FIRST RUN + # run the simulation and apply results + sp_file = sphere_model.run(apply_tally_results=True) + # before calling for any property requiring results (including the equivalence check below), + # the following internal attributes of the original should be unset + assert tally._mean is None + assert tally._std_dev is None + assert tally._sum is None + assert tally._sum_sq is None + assert tally._num_realizations == 0 + # the statepoint file property should be set, however + assert tally._sp_filename == sp_file + + with openmc.StatePoint(sp_file) as sp: + assert tally in sp.tallies.values() + sp_tally = sp.tallies[tally.id] + + # at this point the tally information regarding results should be the same + assert (sp_tally.std_dev == tally.std_dev).all() + assert (sp_tally.mean == tally.mean).all() + assert sp_tally.nuclides == tally.nuclides + + # SECOND RUN + # change the number of particles and ensure that the results are different + sphere_model.settings.particles += 1 + sp_file = sphere_model.run(apply_tally_results=True) + + assert (sp_tally.std_dev != tally.std_dev).any() + assert (sp_tally.mean != tally.mean).any() + + # now re-read data from the new stateopint file and + # ensure that the new results match those in + # the latest statepoint + with openmc.StatePoint(sp_file) as sp: + assert tally in sp.tallies.values() + sp_tally = sp.tallies[tally.id] + + # at this point the tally information regarding results should be the same + assert (sp_tally.std_dev == tally.std_dev).all() + assert (sp_tally.mean == tally.mean).all() + assert sp_tally.nuclides == tally.nuclides diff --git a/tests/unit_tests/test_tally_multiply_density.py b/tests/unit_tests/test_tally_multiply_density.py new file mode 100644 index 0000000000..552d76bd52 --- /dev/null +++ b/tests/unit_tests/test_tally_multiply_density.py @@ -0,0 +1,50 @@ +import numpy as np +import openmc +import pytest + + +def test_micro_macro_compare(run_in_tmpdir): + # Create simple sphere model with H1 and H2 + mat = openmc.Material() + mat.add_components({'H1': 1.0, 'H2': 1.0}) + mat.set_density('g/cm3', 1.0) + sph = openmc.Sphere(r=10.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.run_mode = 'fixed source' + model.settings.particles = 1000 + model.settings.batches = 10 + + # Set up two reaction rate tallies, one that multplies by density and the + # other that doesn't + tally_macro = openmc.Tally() + tally_macro.nuclides = ['H1', 'H2', 'H3'] + tally_macro.scores = ['total', 'elastic'] + tally_micro = openmc.Tally() + tally_micro.nuclides = ['H1', 'H2', 'H3'] + tally_micro.scores = ['total', 'elastic'] + tally_micro.multiply_density = False + model.tallies = [tally_macro, tally_micro] + + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + tally_macro = sp.tallies[tally_macro.id] + tally_micro = sp.tallies[tally_micro.id] + + # Make sure multply_density attribute from statepoint is set correctly + assert tally_macro.multiply_density + assert not tally_micro.multiply_density + + # Dividing macro by density should give micro + density = mat.get_nuclide_atom_densities() + for nuc in ('H1', 'H2'): + micro_derived = tally_macro.get_values(nuclides=[nuc]) / density[nuc] + micro = tally_micro.get_values(nuclides=[nuc]) + assert micro_derived == pytest.approx(micro) + + # For macro tally, H3 scores should be zero + assert np.all(tally_macro.get_values(nuclides=['H3']) == 0.0) + + # For micro tally, H3 scores should be positive + assert np.all(tally_micro.get_values(nuclides=['H3']) > 0.0) diff --git a/tests/unit_tests/test_temp_interp.py b/tests/unit_tests/test_temp_interp.py index 6cbe2f63fb..4566070cfd 100644 --- a/tests/unit_tests/test_temp_interp.py +++ b/tests/unit_tests/test_temp_interp.py @@ -10,7 +10,7 @@ import pytest def make_fake_cross_section(): - """Create fake U235 nuclide + """Create fake U235 nuclide with a fake thermal scattering library attached This nuclide is designed to have k_inf=1 at 300 K, k_inf=2 at 600 K, and k_inf=1 at 900 K. The absorption cross section is also constant with @@ -81,8 +81,62 @@ def make_fake_cross_section(): # Export HDF5 file u235_fake.export_to_hdf5('U235_fake.h5', 'w') + # Create a fake thermal scattering library attached to the fake U235 data + c_U_fake = openmc.data.ThermalScattering("c_U_fake", 1.9968, 4.9, [0.0253]) + c_U_fake.nuclides = ['U235'] + + # Create elastic reaction + bragg_edges = [0.00370672, 0.00494229] + factors = [0.00375735, 0.01386287] + coherent_xs = openmc.data.CoherentElastic(bragg_edges, factors) + incoherent_xs_294 = openmc.data.Tabulated1D([0.00370672, 0.00370672], [0.00370672, 0.00370672]) + elastic_xs_base = openmc.data.Sum((coherent_xs, incoherent_xs_294)) + elastic_xs = {'294K': elastic_xs_base, '600K': elastic_xs_base} + coherent_dist = openmc.data.CoherentElasticAE(coherent_xs) + incoherent_dist_294 = openmc.data.IncoherentElasticAEDiscrete([ + [-0.6, -0.18, 0.18, 0.6], [-0.6, -0.18, 0.18, 0.6] + ]) + incoherent_dist_600 = openmc.data.IncoherentElasticAEDiscrete([ + [-0.1, -0.2, 0.2, 0.1], [-0.1, -0.2, 0.2, 0.1] + ]) + elastic_dist = { + '294K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_294), + '600K': openmc.data.MixedElasticAE(coherent_dist, incoherent_dist_600) + } + c_U_fake.elastic = openmc.data.ThermalScatteringReaction(elastic_xs, elastic_dist) + + # Create inelastic reaction + inelastic_xs = { + '294K': openmc.data.Tabulated1D([1.0e-5, 4.9], [13.4, 3.35]), + '600K': openmc.data.Tabulated1D([1.0e-2, 10], [1.4, 5]) + } + breakpoints = [3] + interpolation = [2] + energy = [1.0e-5, 4.3e-2, 4.9] + energy_out = [ + openmc.data.Tabular([0.0002, 0.067, 0.146, 0.366], [0.25, 0.25, 0.25, 0.25]), + openmc.data.Tabular([0.0001, 0.009, 0.137, 0.277], [0.25, 0.25, 0.25, 0.25]), + openmc.data.Tabular([0.0579, 4.555, 4.803, 4.874], [0.25, 0.25, 0.25, 0.25]), + ] + for eout in energy_out: + eout.normalize() + eout.c = eout.cdf() + discrete = openmc.stats.Discrete([-0.9, -0.6, -0.3, -0.1, 0.1, 0.3, 0.6, 0.9], [1/8]*8) + discrete.c = discrete.cdf()[1:] + mu = [[discrete]*4]*3 + dist = openmc.data.IncoherentInelasticAE( + breakpoints, interpolation, energy, energy_out, mu) + inelastic_dist = {'294K': dist, '600K': dist} + inelastic = openmc.data.ThermalScatteringReaction(inelastic_xs, inelastic_dist) + c_U_fake.inelastic = inelastic + + # Export HDF5 file + c_U_fake.export_to_hdf5("c_U_fake.h5") + + # Create a data library of the fake nuclide and its thermal scattering data lib = openmc.data.DataLibrary() lib.register_file('U235_fake.h5') + lib.register_file("c_U_fake.h5") lib.export_to_xml('cross_sections_fake.xml') @@ -98,7 +152,7 @@ def model(tmp_path_factory): mat = openmc.Material() mat.add_nuclide('U235', 1.0) model.materials.append(mat) - model.materials.cross_sections = str(Path('cross_sections_fake.xml').resolve()) + model.materials.cross_sections = 'cross_sections_fake.xml' sph = openmc.Sphere(r=100.0, boundary_type='reflective') cell = openmc.Cell(fill=mat, region=-sph) @@ -119,21 +173,23 @@ def model(tmp_path_factory): @pytest.mark.parametrize( - ["method", "temperature", "fission_expected"], + ["method", "temperature", "fission_expected", "tolerance"], [ - ("nearest", 300.0, 0.5), - ("nearest", 600.0, 1.0), - ("nearest", 900.0, 0.5), - ("interpolation", 360.0, 0.6), - ("interpolation", 450.0, 0.75), - ("interpolation", 540.0, 0.9), - ("interpolation", 660.0, 0.9), - ("interpolation", 750.0, 0.75), - ("interpolation", 840.0, 0.6), + ("nearest", 300.0, 0.5, 10), + ("nearest", 600.0, 1.0, 10), + ("nearest", 900.0, 0.5, 10), + ("interpolation", 360.0, 0.6, 10), + ("interpolation", 450.0, 0.75, 10), + ("interpolation", 540.0, 0.9, 10), + ("interpolation", 660.0, 0.9, 10), + ("interpolation", 750.0, 0.75, 10), + ("interpolation", 840.0, 0.6, 10), + ("interpolation", 295.0, 0.5, 10), + ("interpolation", 990.0, 0.5, 100), ] ) -def test_interpolation(model, method, temperature, fission_expected): - model.settings.temperature = {'method': method, 'default': temperature} +def test_interpolation(model, method, temperature, fission_expected, tolerance): + model.settings.temperature = {'method': method, 'default': temperature, "tolerance": tolerance} sp_filename = model.run() with openmc.StatePoint(sp_filename) as sp: t = sp.tallies[model.tallies[0].id] @@ -147,8 +203,73 @@ def test_interpolation(model, method, temperature, fission_expected): assert abs(nu_fission_mean - nu*fission_expected) < 3*nu_fission_unc # Check that k-effective value matches expected - k = sp.k_combined + k = sp.keff if isnan(k.s): assert k.n == pytest.approx(nu*fission_expected) else: assert abs(k.n - nu*fission_expected) <= 3*k.s + + +def test_temperature_interpolation_tolerance(model): + """Test applying global and cell temperatures with thermal scattering libraries + """ + model.materials[0].add_s_alpha_beta("c_U_fake") + + # Default k-effective, using the thermal scattering data's minimum available temperature + model.settings.temperature = {'method': "nearest", 'default': 294, "tolerance": 50} + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + default_k = sp.keff.n + + # Get k-effective with temperature below the minimum but in interpolation mode + model.settings.temperature = {'method': "interpolation", 'default': 255, "tolerance": 50} + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + interpolated_k = sp.keff.n + + # Get the k-effective with the temperature applied to the cell, instead of globally + model.settings.temperature = {'method': "interpolation", 'default': 500, "tolerance": 50} + for cell in model.geometry.get_all_cells().values(): + cell.temperature = 275 + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + cell_k = sp.keff.n + + # All calculated k-effectives should be equal + assert default_k == pytest.approx(interpolated_k) + assert interpolated_k == pytest.approx(cell_k) + + +def test_temperature_slightly_above(run_in_tmpdir): + """In this test, we have two materials at temperatures close to actual data + temperatures. However, one is slightly above the highest temperature which + invokes separate logic. The k-effective value should be somewhere between + k=2 (if the temperature were only 600 K) and k=1 (if the temperature were + only 900 K).""" + + make_fake_cross_section() + + model = openmc.Model() + mat1 = openmc.Material() + mat1.add_nuclide('U235', 1.0) + mat1.temperature = 900.1 + mat2 = openmc.Material() + mat2.add_nuclide('U235', 1.0) + mat2.temperature = 600.0 + model.materials.extend([mat1, mat2]) + model.materials.cross_sections = 'cross_sections_fake.xml' + + sph1 = openmc.Sphere(r=1.0) + sph2 = openmc.Sphere(r=4.0, boundary_type='reflective') + cell1 = openmc.Cell(fill=mat1, region=-sph1) + cell2 = openmc.Cell(fill=mat2, region=+sph1 & -sph2) + model.geometry = openmc.Geometry([cell1, cell2]) + + model.settings.particles = 1000 + model.settings.inactive = 0 + model.settings.batches = 10 + model.settings.temperature = {'method': 'interpolation'} + + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + assert 1.1 < sp.keff.n < 1.9 diff --git a/tests/unit_tests/test_time_filter.py b/tests/unit_tests/test_time_filter.py index 5b09c6d5f7..b57c80a975 100644 --- a/tests/unit_tests/test_time_filter.py +++ b/tests/unit_tests/test_time_filter.py @@ -59,7 +59,7 @@ def model(request): model.settings.particles = 1000 model.settings.batches = 20 particle = request.param - model.settings.source = openmc.Source( + model.settings.source = openmc.IndependentSource( space=openmc.stats.Point((x, 0., 0.)), angle=openmc.stats.Monodirectional([-1., 0., 0.]), energy=openmc.stats.Discrete([E], [1.0]), @@ -100,7 +100,7 @@ def model_surf(request): model.settings.particles = 1000 model.settings.batches = 20 particle = request.param - model.settings.source = openmc.Source( + model.settings.source = openmc.IndependentSource( space=openmc.stats.Point((0., 0., 0.)), angle=openmc.stats.Monodirectional([1., 0., 0.]), energy=openmc.stats.Discrete([E], [1.0]), @@ -149,3 +149,39 @@ def test_time_filter_surface(model_surf, run_in_tmpdir): # After t0+ε, the current should be zero assert values[2] == 0.0 + + +def test_small_time_interval(run_in_tmpdir): + # Create a model with a photon source at 1.0e8 seconds. Based on the speed + # of the photon, the time intervals are on the order of 1e-9 seconds, which + # are effectively 0 when compared to the starting time of the photon. + mat = openmc.Material() + mat.add_element('N', 1.0) + mat.set_density('g/cm3', 0.001) + sph = openmc.Sphere(r=5.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + model.settings.source = openmc.IndependentSource( + time=openmc.stats.Discrete([1.0e8], [1.0]), + particle='photon' + ) + + # Add tallies with and without a time filter that should match all particles + time_filter = openmc.TimeFilter([0.0, 1.0e100]) + tally_with_filter = openmc.Tally() + tally_with_filter.filters = [time_filter] + tally_with_filter.scores = ['flux'] + tally_without_filter = openmc.Tally() + tally_without_filter.scores = ['flux'] + model.tallies.extend([tally_with_filter, tally_without_filter]) + + # Run the model and make sure the two tallies match + sp_filename = model.run() + with openmc.StatePoint(sp_filename) as sp: + flux_with = sp.tallies[tally_with_filter.id].mean.ravel()[0] + flux_without = sp.tallies[tally_without_filter.id].mean.ravel()[0] + assert flux_with == pytest.approx(flux_without) diff --git a/tests/unit_tests/test_torus.py b/tests/unit_tests/test_torus.py index a5f49faae2..8c413ffe06 100644 --- a/tests/unit_tests/test_torus.py +++ b/tests/unit_tests/test_torus.py @@ -17,14 +17,14 @@ def get_torus_keff(cls, R, r, center=(0, 0, 0)): outer_cell = openmc.Cell(region=+torus & -sphere) model.geometry = openmc.Geometry([torus_cell, outer_cell]) - model.settings.source = openmc.Source(space=openmc.stats.Point(center)) + model.settings.source = openmc.IndependentSource(space=openmc.stats.Point(center)) model.settings.batches = 10 model.settings.inactive = 5 model.settings.particles = 1000 sp_path = model.run() with openmc.StatePoint(sp_path) as sp: - return sp.k_combined + return sp.keff @pytest.mark.parametrize("R,r", [(2.1, 2.0), (3.0, 1.0)]) diff --git a/tests/unit_tests/test_tracks.py b/tests/unit_tests/test_tracks.py new file mode 100644 index 0000000000..fa86641598 --- /dev/null +++ b/tests/unit_tests/test_tracks.py @@ -0,0 +1,192 @@ +from pathlib import Path + +import h5py +import numpy as np +import openmc +import pytest + +from tests.testing_harness import config + + +@pytest.fixture +def sphere_model(): + openmc.reset_auto_ids() + mat = openmc.Material() + mat.add_nuclide('Zr90', 1.0) + mat.set_density('g/cm3', 1.0) + + model = openmc.Model() + sph = openmc.Sphere(r=25.0, boundary_type='vacuum') + cell = openmc.Cell(fill=mat, region=-sph) + model.geometry = openmc.Geometry([cell]) + + model.settings.run_mode = 'fixed source' + model.settings.batches = 2 + model.settings.particles = 50 + + return model + + +def generate_track_file(model, **kwargs): + # If running in MPI mode, setup proper keyword arguments for run() + kwargs.setdefault('openmc_exec', config['exe']) + if config['mpi']: + kwargs['mpi_args'] = [config['mpiexec'], '-n', config['mpi_np']] + model.run(**kwargs) + + if config['mpi'] and int(config['mpi_np']) > 1: + # With MPI, we need to combine track files + track_files = Path.cwd().glob('tracks_p*.h5') + openmc.Tracks.combine(track_files, 'tracks.h5') + else: + track_file = Path('tracks.h5') + assert track_file.is_file() + + +@pytest.mark.parametrize("particle", ["neutron", "photon"]) +def test_tracks(sphere_model, particle, run_in_tmpdir): + # Set track identifiers + sphere_model.settings.track = [(1, 1, 1), (1, 1, 10), (2, 1, 15)] + + # Set source particle + sphere_model.settings.source = openmc.IndependentSource(particle=particle) + + # Run OpenMC to generate tracks.h5 file + generate_track_file(sphere_model) + + # Open track file and make sure we have correct number of tracks + tracks = openmc.Tracks('tracks.h5') + assert len(tracks) == len(sphere_model.settings.track) + + for track, identifier in zip(tracks, sphere_model.settings.track): + # Check attributes on Track object + assert isinstance(track, openmc.Track) + assert track.identifier == identifier + assert isinstance(track.particle_tracks, list) + if particle == 'neutron': + assert len(track.particle_tracks) == 1 + + # Check attributes on ParticleTrack object + particle_track = track.particle_tracks[0] + assert isinstance(particle_track, openmc.ParticleTrack) + + assert particle_track.particle.name.lower() == particle + assert isinstance(particle_track.states, np.ndarray) + + # Sanity checks on actual data + for state in particle_track.states: + assert np.linalg.norm([*state['r']]) <= 25.0001 + assert np.linalg.norm([*state['u']]) == pytest.approx(1.0) + assert 0.0 <= state['E'] <= 20.0e6 + assert state['time'] >= 0.0 + assert 0.0 <= state['wgt'] <= 1.0 + assert state['cell_id'] == 1 + assert state['material_id'] == 1 + + # Checks on 'sources' property + sources = track.sources + assert len(sources) == len(track.particle_tracks) + x = sources[0] + state = particle_track.states[0] + assert x.r == (*state['r'],) + assert x.u == (*state['u'],) + assert x.E == state['E'] + assert x.time == state['time'] + assert x.wgt == state['wgt'] + assert x.particle == particle_track.particle + + +def test_max_tracks(sphere_model, run_in_tmpdir): + # Set maximum number of tracks per process to write + sphere_model.settings.max_tracks = expected_num_tracks = 10 + if config['mpi']: + expected_num_tracks *= int(config['mpi_np']) + + # Run OpenMC to generate tracks.h5 file + generate_track_file(sphere_model, tracks=True) + + # Open track file and make sure we have correct number of tracks + tracks = openmc.Tracks('tracks.h5') + assert len(tracks) == expected_num_tracks + + +def test_filter(sphere_model, run_in_tmpdir): + # Set maximum number of tracks per process to write + sphere_model.settings.max_tracks = 25 + sphere_model.settings.photon_transport = True + + # Run OpenMC to generate tracks.h5 file + generate_track_file(sphere_model, tracks=True) + + tracks = openmc.Tracks('tracks.h5') + for track in tracks: + # Test filtering by particle + matches = track.filter(particle='photon') + for x in matches: + assert x.particle == openmc.ParticleType.PHOTON + + # Test general state filter + matches = track.filter(state_filter=lambda s: s['cell_id'] == 1) + assert isinstance(matches, openmc.Track) + assert matches.particle_tracks == track.particle_tracks + matches = track.filter(state_filter=lambda s: s['cell_id'] == 2) + assert matches.particle_tracks == [] + matches = track.filter(state_filter=lambda s: s['E'] < 0.0) + assert matches.particle_tracks == [] + + # Test filter method on Tracks + matches = tracks.filter(particle='neutron') + assert isinstance(matches, openmc.Tracks) + assert matches == tracks + matches = tracks.filter(state_filter=lambda s: s['E'] > 0.0) + assert matches == tracks + matches = tracks.filter(particle='bunnytron') + assert matches == [] + + +def test_write_to_vtk(sphere_model): + vtk = pytest.importorskip('vtk') + # Set maximum number of tracks per process to write + sphere_model.settings.max_tracks = 25 + sphere_model.settings.photon_transport = True + + # Run OpenMC to generate tracks.h5 file + generate_track_file(sphere_model, tracks=True) + + tracks = openmc.Tracks('tracks.h5') + polydata = tracks.write_to_vtk('tracks.vtp') + + assert isinstance(polydata, vtk.vtkPolyData) + assert Path('tracks.vtp').is_file() + + +def test_restart_track(run_in_tmpdir, sphere_model): + # cut the sphere model in half with an improper boundary condition + plane = openmc.XPlane(x0=-1.0) + for cell in sphere_model.geometry.get_all_cells().values(): + cell.region &= +plane + + # generate lost particle files + with pytest.raises(RuntimeError, match='Maximum number of lost particles has been reached.'): + sphere_model.run(output=False, threads=1) + + lost_particle_files = list(Path.cwd().glob('particle_*.h5')) + assert len(lost_particle_files) > 0 + particle_file = lost_particle_files[0] + # restart the lost particle with tracks enabled + sphere_model.run(tracks=True, restart_file=particle_file) + tracks_file = Path('tracks.h5') + assert tracks_file.is_file() + + # check that the last track of the file matches the lost particle file + tracks = openmc.Tracks(tracks_file) + initial_state = tracks[0].particle_tracks[0].states[0] + restart_r = np.array(initial_state['r']) + restart_u = np.array(initial_state['u']) + + with h5py.File(particle_file, 'r') as lost_particle_file: + lost_r = np.array(lost_particle_file['xyz'][()]) + lost_u = np.array(lost_particle_file['uvw'][()]) + + pytest.approx(restart_r, lost_r) + pytest.approx(restart_u, lost_u) diff --git a/tests/unit_tests/test_transfer_volumes.py b/tests/unit_tests/test_transfer_volumes.py index e1f3c1053f..b768bc88a2 100644 --- a/tests/unit_tests/test_transfer_volumes.py +++ b/tests/unit_tests/test_transfer_volumes.py @@ -2,7 +2,7 @@ from pytest import approx import openmc -from openmc.deplete import PredictorIntegrator, ResultsList +from openmc.deplete import PredictorIntegrator, Results from tests import dummy_operator @@ -19,7 +19,7 @@ def test_transfer_volumes(run_in_tmpdir): PredictorIntegrator(op, dt, power).integrate() # Load the files - res = openmc.deplete.ResultsList.from_hdf5(op.output_dir / "depletion_results.h5") + res = openmc.deplete.Results(op.output_dir / "depletion_results.h5") # Create a dictionary of volumes to transfer res[0].volume['1'] = 1.5 diff --git a/tests/unit_tests/test_triggers.py b/tests/unit_tests/test_triggers.py new file mode 100644 index 0000000000..14bda0cceb --- /dev/null +++ b/tests/unit_tests/test_triggers.py @@ -0,0 +1,146 @@ + +import openmc + +def test_tally_trigger(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # create a tally with triggers applied + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['scatter'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['scatter'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 100 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + with openmc.StatePoint(sp_file) as sp: + expected_realizations = sp.n_realizations + + # adding other scores to the tally should not change the + # number of batches required to satisfy the trigger + tally.scores = ['total', 'absorption', 'scatter'] + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + realizations = sp.n_realizations + + assert realizations == expected_realizations + + +def test_tally_trigger_null_score(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create a tally filter on the materials + mat_filter = openmc.MaterialFilter(pincell.materials) + + # apply a tally with a score that be tallied in this model + tally = openmc.Tally() + tally.filters = [mat_filter] + tally.scores = ['pair-production'] + + trigger = openmc.Trigger('rel_err', 0.05) + trigger.scores = ['pair-production'] + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 50 + pincell.settings.trigger_batch_interval = 5 + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + # verify that the tally mean is zero + tally_out = sp.get_tally(id=tally.id) + assert all(tally_out.mean == 0.0) + + # we expect that this simulation will run + # up to the max allowed batches + total_batches = sp.n_realizations + sp.n_inactive + assert total_batches == pincell.settings.trigger_max_batches + + +def test_tally_trigger_zero_ignored(run_in_tmpdir): + pincell = openmc.examples.pwr_pin_cell() + + # create an energy filter below and around the O-16(n,p) threshold (1.02e7 eV) + e_filter = openmc.EnergyFilter([0.0, 1e7, 2e7]) + + # create a tally with triggers applied + tally = openmc.Tally() + tally.filters = [e_filter] + tally.scores = ['(n,p)'] + tally.nuclides = ["O16"] + + # 100% relative error: should be immediately satisfied in nonzero bin + trigger = openmc.Trigger('rel_err', 1.0) + trigger.scores = ['(n,p)'] + trigger.ignore_zeros = True + + tally.triggers = [trigger] + + pincell.tallies = [tally] + + pincell.settings.particles = 1000 # we need a few more particles for this + pincell.settings.trigger_active = True + pincell.settings.trigger_max_batches = 50 + pincell.settings.trigger_batch_interval = 20 + + sp_file = pincell.run() + + with openmc.StatePoint(sp_file) as sp: + # verify that the first bin is zero and the second is nonzero + tally_out = sp.get_tally(id=tally.id) + below, above = tally_out.mean.squeeze() + assert below == 0.0, "Tally events observed below expected threshold" + assert above > 0, "No tally events observed. Test with more particles." + + # we expect that the trigger fires before max batches are hit + total_batches = sp.n_realizations + sp.n_inactive + assert total_batches < pincell.settings.trigger_max_batches + + + +def test_trigger_he3_production(run_in_tmpdir): + li6 = openmc.Material() + li6.set_density('g/cm3', 1.0) + li6.add_nuclide('Li6', 1.0) + + sph = openmc.Sphere(r=20, boundary_type='vacuum') + outer_cell = openmc.Cell(fill=li6, region=-sph) + model = openmc.Model() + model.geometry = openmc.Geometry([outer_cell]) + model.settings.source = openmc.IndependentSource( + energy=openmc.stats.delta_function(14.1e6) + ) + model.settings.batches = 10 + model.settings.particles = 100 + model.settings.run_mode = 'fixed source' + model.settings.trigger_active = True + model.settings.trigger_batch_interval = 10 + model.settings.trigger_max_batches = 30 + + # Define tally with trigger + trigger = openmc.Trigger(trigger_type='rel_err', threshold=0.0001) + trigger.scores = ['He3-production'] + he3_production_tally = openmc.Tally() + he3_production_tally.scores = ['He3-production'] + he3_production_tally.triggers = [trigger] + model.tallies = openmc.Tallies([he3_production_tally]) + + # Run model to verify that trigger works + model.run() diff --git a/tests/unit_tests/test_uniform_source_sampling.py b/tests/unit_tests/test_uniform_source_sampling.py new file mode 100644 index 0000000000..0d1930328b --- /dev/null +++ b/tests/unit_tests/test_uniform_source_sampling.py @@ -0,0 +1,82 @@ +import openmc +import pytest + + +@pytest.fixture +def sphere_model(): + mat = openmc.Material() + mat.add_nuclide('Li6', 1.0) + mat.set_density('g/cm3', 1.0) + sphere = openmc.Sphere(r=1.0, boundary_type='vacuum') + cell = openmc.Cell(region=-sphere, fill=mat) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + + model.settings.particles = 100 + model.settings.batches = 1 + src1 = openmc.IndependentSource( + energy=openmc.stats.delta_function(1.0e3), + strength=75.0 + ) + src2 = openmc.IndependentSource( + energy=openmc.stats.delta_function(1.0e3), + strength=25.0 + ) + model.settings.source = [src1, src2] + model.settings.run_mode = "fixed source" + model.settings.surf_source_write = { + "max_particles": 100, + } + + tally = openmc.Tally() + tally.scores = ['flux'] + model.tallies = [tally] + return model + + +def test_source_weight(run_in_tmpdir, sphere_model): + # Run OpenMC without uniform source sampling and check that banked particles + # have weight 1 + sphere_model.settings.uniform_source_sampling = False + sphere_model.run() + particles = openmc.ParticleList.from_hdf5('surface_source.h5') + assert set(p.wgt for p in particles) == {1.0} + + # Run with uniform source sampling and check that banked particles have + # weight == strength + sphere_model.settings.uniform_source_sampling = True + sphere_model.run() + particles = openmc.ParticleList.from_hdf5('surface_source.h5') + assert set(p.wgt for p in particles) == {0.5, 1.5} + + +def test_tally_mean(run_in_tmpdir, sphere_model): + # Use only one source + sphere_model.settings.source.pop() + + # Run without uniform source sampling + sphere_model.settings.uniform_source_sampling = False + sp_file = sphere_model.run() + with openmc.StatePoint(sp_file) as sp: + reference_mean = sp.tallies[sphere_model.tallies[0].id].mean + + # Run with uniform source sampling + sphere_model.settings.uniform_source_sampling = True + sp_file = sphere_model.run() + with openmc.StatePoint(sp_file) as sp: + mean = sp.tallies[sphere_model.tallies[0].id].mean + + # Check that tally means match + assert mean == pytest.approx(reference_mean) + + +def test_multiple_sources(sphere_model): + low_strength_src = openmc.IndependentSource( + energy=openmc.stats.delta_function(1.0e6), strength=1e-7) + sphere_model.settings.source.append(low_strength_src) + sphere_model.settings.uniform_source_sampling = True + + # Sample particles from source and make sure 1 MeV shows up despite + # negligible strength + particles = sphere_model.sample_external_source(100) + assert {p.E for p in particles} == {1.0e3, 1.0e6} diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py index ab3fd6e33a..efe8552a64 100644 --- a/tests/unit_tests/test_universe.py +++ b/tests/unit_tests/test_universe.py @@ -1,5 +1,4 @@ -import xml.etree.ElementTree as ET - +import lxml.etree as ET import numpy as np import openmc import pytest @@ -49,17 +48,60 @@ def test_bounding_box(): def test_plot(run_in_tmpdir, sphere_model): + + # model with -inf and inf in the bounding box + pincell = openmc.examples.pwr_pin_cell() + materials = pincell.materials + + mat_colors = { + materials[0]: (200, 1, 1), + materials[1]: "gray", + materials[2]: "limegreen" + } + + for basis in ('xy', 'yz', 'xz'): + plot = pincell.geometry.root_universe.plot( + colors=mat_colors, + color_by="material", + legend=True, + pixels=(10, 10), + basis=basis, + outline=True, + axis_units='m' + ) + assert plot.xaxis.get_label().get_text() == f'{basis[0]} [m]' + assert plot.yaxis.get_label().get_text() == f'{basis[1]} [m]' + + # model with no inf values in bounding box m = sphere_model.materials[0] univ = sphere_model.geometry.root_universe colors = {m: 'limegreen'} + for basis in ('xy', 'yz', 'xz'): - univ.plot( - basis=basis, - pixels=(10, 10), - color_by='material', + plot = univ.plot( colors=colors, + color_by="cell", + legend=False, + pixels=100, + basis=basis, + outline=False ) + assert plot.xaxis.get_label().get_text() == f'{basis[0]} [cm]' + assert plot.yaxis.get_label().get_text() == f'{basis[1]} [cm]' + + msg = "Must pass 'colors' dictionary if you are adding a legend via legend=True." + # This plot call should fail as legend is True but colors is None + with pytest.raises(ValueError, match=msg): + univ.plot( + color_by="cell", + legend=True, + pixels=100, + ) + + # Close plots to avoid warning + import matplotlib.pyplot as plt + plt.close('all') def test_get_nuclides(uo2): @@ -93,10 +135,12 @@ def test_get_all_universes(): u2 = openmc.Universe(cells=[c2]) c3 = openmc.Cell(fill=u1) c4 = openmc.Cell(fill=u2) - u3 = openmc.Universe(cells=[c3, c4]) + u3 = openmc.DAGMCUniverse(filename="") + c5 = openmc.Cell(fill=u3) + u4 = openmc.Universe(cells=[c3, c4, c5]) - univs = set(u3.get_all_universes().values()) - assert not (univs ^ {u1, u2}) + univs = set(u4.get_all_universes().values()) + assert not (univs ^ {u1, u2, u3}) def test_clone(): @@ -107,11 +151,13 @@ def test_clone(): c2.fill = openmc.Material() c3 = openmc.Cell() u1 = openmc.Universe(name='cool', cells=(c1, c2, c3)) + u1.volume = 1. u2 = u1.clone() assert u2.name == u1.name assert u2.cells != u1.cells assert u2.get_all_materials() != u1.get_all_materials() + assert u2.volume == u1.volume u2 = u1.clone(clone_materials=False) assert u2.get_all_materials() == u1.get_all_materials() @@ -120,6 +166,33 @@ def test_clone(): assert next(iter(u3.cells.values())).region ==\ next(iter(u1.cells.values())).region + # Change attributes, make sure clone stays intact + u1.volume = 2. + u1.name = "different name" + assert u3.volume != u1.volume + assert u3.name != u1.name + + # Test cloning a DAGMC universe + dagmc_u = openmc.DAGMCUniverse(filename="", name="DAGMC universe") + dagmc_u.volume = 1. + dagmc_u.auto_geom_ids = True + dagmc_u.auto_mat_ids = True + dagmc_u1 = dagmc_u.clone() + assert dagmc_u1.name == dagmc_u.name + assert dagmc_u1.volume == dagmc_u.volume + assert dagmc_u1.auto_geom_ids == dagmc_u.auto_geom_ids + assert dagmc_u1.auto_mat_ids == dagmc_u.auto_mat_ids + + # Change attributes, check the clone remained intact + dagmc_u.name = "another name" + dagmc_u.auto_geom_ids = False + dagmc_u.auto_mat_ids = False + dagmc_u.volume = 2. + assert dagmc_u1.name != dagmc_u.name + assert dagmc_u1.volume != dagmc_u.volume + assert dagmc_u1.auto_geom_ids != dagmc_u.auto_geom_ids + assert dagmc_u1.auto_mat_ids != dagmc_u.auto_mat_ids + def test_create_xml(cell_with_lattice): cells = [openmc.Cell() for i in range(5)] @@ -132,3 +205,14 @@ def test_create_xml(cell_with_lattice): assert all(c.get('universe') == str(u.id) for c in cell_elems) assert not (set(c.get('id') for c in cell_elems) ^ set(str(c.id) for c in cells)) + + +def test_get_nuclide_densities(): + surf = openmc.Sphere() + material = openmc.Material() + material.add_elements_from_formula("H2O") + material.set_density("g/cm3", 1) + cell = openmc.Cell(region=-surf, fill=material) + universe = openmc.Universe(cells=[cell]) + with pytest.raises(RuntimeError): + universe.get_nuclide_densities() diff --git a/tests/unit_tests/test_urr_capture.py b/tests/unit_tests/test_urr_capture.py index 745d62cef0..34b518e9de 100644 --- a/tests/unit_tests/test_urr_capture.py +++ b/tests/unit_tests/test_urr_capture.py @@ -19,7 +19,7 @@ def th232_model(): model.settings.batches = 10 model.settings.run_mode = 'fixed source' energies = openmc.stats.Uniform(e_min, e_max) - model.settings.source = openmc.Source(energy=energies) + model.settings.source = openmc.IndependentSource(energy=energies) tally = openmc.Tally(name='rates') tally.filters = [openmc.EnergyFilter([e_min, e_max])] diff --git a/tests/unit_tests/test_volume.py b/tests/unit_tests/test_volume.py new file mode 100644 index 0000000000..0993cb66aa --- /dev/null +++ b/tests/unit_tests/test_volume.py @@ -0,0 +1,45 @@ +import numpy as np +import pytest + +import openmc + + +def test_infinity_handling(): + surf1 = openmc.Sphere(boundary_type="vacuum") + cell1 = openmc.Cell(region=-surf1) + + lower_left = (-2, -np.inf, -2) + upper_right = (np.inf, 2, 2) + + with pytest.raises(ValueError, match="must be finite"): + openmc.VolumeCalculation([cell1], 100, lower_left, upper_right) + + +@pytest.mark.parametrize('cls', [openmc.Cell, openmc.Material, openmc.Universe]) +def test_invalid_id(run_in_tmpdir, cls): + m = openmc.Material() + m.add_nuclide('U235', 0.02) + sph = openmc.Sphere(boundary_type='vacuum') + cell = openmc.Cell(fill=m, region=-sph) + model = openmc.Model(geometry=openmc.Geometry([cell])) + + # Apply volume calculation with unused domains + model.settings.volume_calculations = openmc.VolumeCalculation( + [cls()], 10000, *model.geometry.bounding_box) + + with pytest.raises(RuntimeError): + model.calculate_volumes() + + +def test_no_bcs(run_in_tmpdir): + """Ensure that a model without boundary conditions can be used in a volume calculation""" + model = openmc.examples.pwr_pin_cell() + for surface in model.geometry.get_all_surfaces().values(): + surface.boundary_type = 'transmission' + + bbox = openmc.BoundingBox([-1.]*3, [1.]*3) + cells = list(model.geometry.get_all_cells().values()) + vc = openmc.VolumeCalculation(cells, samples=10, lower_left=bbox[0], upper_right=bbox[1]) + + model.settings.volume_calculations = [vc] + model.calculate_volumes() diff --git a/tests/unit_tests/test_waste_classification.py b/tests/unit_tests/test_waste_classification.py new file mode 100644 index 0000000000..072590df96 --- /dev/null +++ b/tests/unit_tests/test_waste_classification.py @@ -0,0 +1,102 @@ +import random + +import openmc +import pytest + + +@pytest.mark.parametrize("metal", [False, True]) +def test_waste_classification_long(metal): + """Test classification when determined by long-lived radionuclides""" + f = 10.0 if metal else 1.0 + limit = 8.0*f + mat = openmc.Material() + mat.add_nuclide('C14', 1e-9*f) + assert mat.get_activity('Ci/m3') < 0.1 * limit + assert mat.waste_classification(metal=metal) == 'Class A' + + mat = openmc.Material() + mat.add_nuclide('C14', 1e-8*f) + assert 0.1 * limit < mat.get_activity('Ci/m3') < limit + assert mat.waste_classification(metal=metal) == 'Class C' + + mat = openmc.Material() + mat.add_nuclide('C14', 1e-7*f) + assert mat.get_activity('Ci/m3') > limit + assert mat.waste_classification(metal=metal) == 'GTCC' + + +@pytest.mark.parametrize("metal", [False, True]) +def test_waste_classification_short(metal): + """Test classification when determined by short-lived radionuclides""" + f = 10.0 if metal else 1.0 + col1, col2, col3 = 3.5*f, 70.0*f, 700.0*f + + mat = openmc.Material() + mat.add_nuclide('Ni63', 1e-10*f) + assert mat.get_activity('Ci/m3') < col1 + assert mat.waste_classification(metal=metal) == 'Class A' + + mat = openmc.Material() + mat.add_nuclide('Ni63', 1e-10*10*f) + assert col1 < mat.get_activity('Ci/m3') < col2 + assert mat.waste_classification(metal=metal) == 'Class B' + + mat = openmc.Material() + mat.add_nuclide('Ni63', 1e-10*200*f) + assert col2 < mat.get_activity('Ci/m3') < col3 + assert mat.waste_classification(metal=metal) == 'Class C' + + mat = openmc.Material() + mat.add_nuclide('Ni63', 1e-10*2000*f) + assert mat.get_activity('Ci/m3') > col3 + assert mat.waste_classification(metal=metal) == 'GTCC' + + +def test_waste_classification_mix(): + """Test classification when determined by a mix of radionuclides""" + # Check example from 10 CFR 61.55 with mix of Sr90 and Cs137 + mat = openmc.Material() + mat.add_nuclide('Sr90', 2.425e-9) + mat.add_nuclide('Cs137', 1.115e-9) + + # In example, activity of Sr90 is 50.0 Ci/m3 and Cs137 is 22.0 Ci/m3 + activity = mat.get_activity(units='Ci/m3', by_nuclide=True) + assert activity['Sr90'] == pytest.approx(50.0, 0.01) + assert activity['Cs137'] == pytest.approx(22.0, 0.01) + + # According to example, the waste should be class B + assert mat.waste_classification() == 'Class B' + + +def test_waste_rating_fetter(): + """Test waste classification using the Fetter limits""" + # For Tc99, Fetter has a more strict limit. Here, we create a material with + # Tc99 at 1 Ci/m3 which exceeds Fetter but not NRC + density = 3.5561e-7 + mat = openmc.Material() + mat.add_nuclide('Tc99', density) + assert mat.get_activity('Ci/m3') == pytest.approx(1.0, 1e-3) + assert mat.waste_disposal_rating(limits='NRC_short_C') < 1.0 + assert mat.waste_disposal_rating(limits='Fetter') > 1.0 + + # With a lower density, it should be Class C under Fetter limits and Class A + # under NRC limits + mat = openmc.Material() + mat.add_nuclide('Tc99', 5.0e-2*density) + assert mat.waste_disposal_rating(limits='NRC_short_A') < 1.0 + assert mat.waste_disposal_rating(limits='Fetter') < 1.0 + + +def test_waste_disposal_rating(): + """Test waste_disposal_rating method""" + mat = openmc.Material() + mat.add_nuclide('K40', random.random()) + + # Check for correct classification based on actual activity + ci_m3 = mat.get_activity('Ci/m3') + assert mat.waste_disposal_rating(limits={'K40': 2*ci_m3}) < 1.0 + assert mat.waste_disposal_rating(limits={'K40': 0.5*ci_m3}) > 1.0 + + wdr = mat.waste_disposal_rating(limits={'K40': 4*ci_m3}, by_nuclide=True) + assert isinstance(wdr, dict) + assert wdr['K40'] == pytest.approx(1/4) diff --git a/tests/unit_tests/weightwindows/test.py b/tests/unit_tests/weightwindows/test.py index aaa0bb523c..d6e509522f 100644 --- a/tests/unit_tests/weightwindows/test.py +++ b/tests/unit_tests/weightwindows/test.py @@ -4,12 +4,54 @@ from pathlib import Path import numpy as np import pytest from uncertainties import ufloat - import openmc +import openmc.lib from openmc.stats import Discrete, Point from tests import cdtemp + +@pytest.fixture +def wws(): + + # weight windows + ww_files = ('ww_n.txt', 'ww_p.txt') + cwd = Path(__file__).parent.absolute() + ww_n_file, ww_p_file = [cwd / Path(f) for f in ww_files] + + # load pre-generated weight windows + # (created using the same tally as above) + ww_n_lower_bnds = np.loadtxt(ww_n_file) + ww_p_lower_bnds = np.loadtxt(ww_p_file) + + # create a mesh matching the one used + # to generate the weight windows + ww_mesh = openmc.RegularMesh() + ww_mesh.lower_left = (-240, -240, -240) + ww_mesh.upper_right = (240, 240, 240) + ww_mesh.dimension = (5, 6, 7) + + # energy bounds matching those of the + # generated weight windows + e_bnds = [0.0, 0.5, 2E7] + + ww_n = openmc.WeightWindows(ww_mesh, + ww_n_lower_bnds, + None, + 10.0, + e_bnds, + survival_ratio=1.01) + + ww_p = openmc.WeightWindows(ww_mesh, + ww_p_lower_bnds, + None, + 10.0, + e_bnds, + survival_ratio=1.01) + + return [ww_n, ww_p] + + @pytest.fixture def model(): openmc.reset_auto_ids() @@ -51,12 +93,12 @@ def model(): settings.run_mode = 'fixed source' settings.particles = 500 settings.batches = 2 - settings.max_splits = 100 + settings.max_history_splits = 100 settings.photon_transport = True space = Point((0.001, 0.001, 0.001)) energy = Discrete([14E6], [1.0]) - settings.source = openmc.Source(space=space, energy=energy) + settings.source = openmc.IndependentSource(space=space, energy=energy) # tally mesh = openmc.RegularMesh() @@ -80,7 +122,7 @@ def model(): return model -def test_weightwindows(model): +def test_weightwindows(model, wws): ww_files = ('ww_n.txt', 'ww_p.txt') cwd = Path(__file__).parent.absolute() @@ -92,40 +134,11 @@ def test_weightwindows(model): analog_sp = model.run() os.rename(analog_sp, 'statepoint.analog.h5') - # weight windows - - # load pre-generated weight windows - # (created using the same tally as above) - ww_n_lower_bnds = np.loadtxt('ww_n.txt') - ww_p_lower_bnds = np.loadtxt('ww_p.txt') - - # create a mesh matching the one used - # to generate the weight windows - ww_mesh = openmc.RegularMesh() - ww_mesh.lower_left = (-240, -240, -240) - ww_mesh.upper_right = (240, 240, 240) - ww_mesh.dimension = (5, 6, 7) - - # energy bounds matching those of the - # generated weight windows - e_bnds = [0.0, 0.5, 2E7] - - ww_n = openmc.WeightWindows(ww_mesh, - ww_n_lower_bnds, - None, - 10.0, - e_bnds, - survival_ratio=1.01) - - ww_p = openmc.WeightWindows(ww_mesh, - ww_p_lower_bnds, - None, - 10.0, - e_bnds, - survival_ratio=1.01) - - model.settings.weight_windows = [ww_n, ww_p] + model.settings.weight_windows = wws + # check that string form of the class can be created + for ww in model.settings.weight_windows: + str(ww) # run again with variance reduction on model.settings.weight_windows_on = True @@ -192,3 +205,171 @@ def test_weightwindows(model): compare_results('neutron', analog_tally, ww_tally) compare_results('photon', analog_tally, ww_tally) + + +def test_lower_ww_bounds_shape(): + """checks that lower_ww_bounds is reshaped to the mesh dimension when set""" + ww_mesh = openmc.RegularMesh() + ww_mesh.lower_left = (-10, -10, -10) + ww_mesh.upper_right = (10, 10, 10) + ww_mesh.dimension = (2, 3, 4) + + ww = openmc.WeightWindows( + mesh=ww_mesh, + lower_ww_bounds=[1]*24, + upper_bound_ratio=5, + energy_bounds=(1, 1e40) + ) + assert ww.lower_ww_bounds.shape == (2, 3, 4, 1) + + +def test_photon_heating(run_in_tmpdir): + water = openmc.Material() + water.add_nuclide('H1', 1.0) + water.add_nuclide('O16', 2.0) + water.set_density('g/cm3', 1.0) + + box = openmc.model.RectangularParallelepiped( + -300, 300, -300, 300, -300, 300, boundary_type='reflective') + cell = openmc.Cell(region=-box, fill=water) + model = openmc.Model() + model.geometry = openmc.Geometry([cell]) + + mesh = openmc.RegularMesh.from_domain(model.geometry, dimension=(5, 5, 5)) + wwg = openmc.WeightWindowGenerator(mesh, particle_type='photon') + model.settings.weight_window_generators = [wwg] + + space = openmc.stats.Point((0, 0, 0)) + energy = openmc.stats.delta_function(5e6) + model.settings.source = openmc.IndependentSource( + space=space, energy=energy, particle='photon') + + model.settings.run_mode = 'fixed source' + model.settings.batches = 5 + model.settings.particles = 100 + + tally = openmc.Tally() + tally.scores = ['heating'] + tally.filters = [ + openmc.ParticleFilter(['photon']), + openmc.MeshFilter(mesh) + ] + model.tallies = [tally] + + sp_file = model.run() + with openmc.StatePoint(sp_file) as sp: + tally_mean = sp.tallies[tally.id].mean + + # these values should be nearly identical + assert np.all(tally_mean >= 0) + + +def test_roundtrip(run_in_tmpdir, model, wws): + model.settings.weight_windows = wws + + # write the model with weight windows to XML + model.export_to_xml() + + # ensure that they can be read successfully from XML and that they match the input values + model_read = openmc.Model.from_xml() + + zipped_wws = zip(model.settings.weight_windows, + model_read.settings.weight_windows) + + # ensure the lower bounds read in from the XML match those of the + for ww_out, ww_in in zipped_wws: + assert(ww_out == ww_in) + + +def test_ww_attrs_python(model): + mesh = openmc.RegularMesh.from_domain(model.geometry) + lower_bounds = np.ones(mesh.dimension) + + # ensure that creation of weight window objects with default arg values + # is successful + wws = openmc.WeightWindows(mesh, lower_bounds, upper_bound_ratio=10.0) + + assert wws.energy_bounds is None + + wwg = openmc.WeightWindowGenerator(mesh) + + assert wwg.energy_bounds is None + +def test_ww_attrs_capi(run_in_tmpdir, model): + model.export_to_xml() + + openmc.lib.init() + + tally = openmc.lib.tallies[model.tallies[0].id] + + wws = openmc.lib.WeightWindows.from_tally(tally) + + # this is the first weight window object created + assert wws.id == 1 + + with pytest.raises(ValueError): + tally.find_filter(openmc.lib.AzimuthalFilter) + + mesh_filter = tally.find_filter(openmc.lib.MeshFilter) + mesh = mesh_filter.mesh + + assert wws.mesh.id == mesh.id + + assert wws.particle == openmc.ParticleType.NEUTRON + + wws.particle = 1 + assert wws.particle == openmc.ParticleType.PHOTON + wws.particle = 'photon' + assert wws.particle == openmc.ParticleType.PHOTON + + with pytest.raises(ValueError): + wws.particle = '🌠' + + energy_filter = tally.find_filter(openmc.lib.EnergyFilter) + np.testing.assert_allclose(np.unique(energy_filter.bins), wws.energy_bounds) + + # at this point the weight window bounds are uninitialized + assert all(wws.bounds[0] == -1) + assert all(wws.bounds[1] == -1) + + wws = openmc.lib.WeightWindows.from_tally(tally, particle='photon') + assert wws.id == 2 + assert wws.particle == openmc.ParticleType.PHOTON + + openmc.lib.finalize() + + +@pytest.mark.parametrize('library', ('libmesh', 'moab')) +def test_unstructured_mesh_applied_wws(request, run_in_tmpdir, library): + """ + Ensure that weight windows on unstructured mesh work when + they aren't part of a tally or weight window generator + """ + + if library == 'libmesh' and not openmc.lib._libmesh_enabled(): + pytest.skip('LibMesh not enabled in this build.') + if library == 'moab' and not openmc.lib._dagmc_enabled(): + pytest.skip('DAGMC (and MOAB) mesh not enabled in this build.') + + water = openmc.Material(name='water') + water.add_nuclide('H1', 2.0) + water.add_nuclide('O16', 1.0) + water.set_density('g/cc', 1.0) + box = openmc.model.RectangularParallelepiped(*(3*[-10, 10]), boundary_type='vacuum') + cell = openmc.Cell(region=-box, fill=water) + + geometry = openmc.Geometry([cell]) + mesh_file = str(request.fspath.dirpath() / 'test_mesh_tets.exo') + mesh = openmc.UnstructuredMesh(mesh_file, library) + + dummy_wws = np.ones((12_000,)) + + wws = openmc.WeightWindows(mesh, dummy_wws, upper_bound_ratio=5.0) + + model = openmc.Model(geometry) + model.settings.weight_windows = wws + model.settings.weight_windows_on = True + model.settings.run_mode = 'fixed source' + model.settings.particles = 100 + model.settings.batches = 2 + model.run() diff --git a/tests/unit_tests/weightwindows/test_mesh_tets.exo b/tests/unit_tests/weightwindows/test_mesh_tets.exo new file mode 120000 index 0000000000..5bf23b369a --- /dev/null +++ b/tests/unit_tests/weightwindows/test_mesh_tets.exo @@ -0,0 +1 @@ +../../regression_tests/unstructured_mesh/test_mesh_tets.e \ No newline at end of file diff --git a/tests/unit_tests/weightwindows/test_ww_gen.py b/tests/unit_tests/weightwindows/test_ww_gen.py new file mode 100644 index 0000000000..a4456e6809 --- /dev/null +++ b/tests/unit_tests/weightwindows/test_ww_gen.py @@ -0,0 +1,404 @@ +import copy +from itertools import permutations +from pathlib import Path + +import numpy as np +import openmc +import openmc.lib +import pytest + + +@pytest.fixture +def model(): + openmc.reset_auto_ids() + + # create a simple spherical shell shielding model + + ### Materials ### + water = openmc.Material() + water.set_density('g/cc', 1.0) + water.add_nuclide("H1", 2) + water.add_nuclide("O16", 1) + + steel = openmc.Material() + steel.set_density('g/cc', 8.0) + steel.add_nuclide("Fe56", 1.0) + + air = openmc.Material() + air.set_density('g/cc', 0.001205) + air.add_nuclide("N14", 0.781557629247) + air.add_nuclide("O16", 0.210668126508) + + boron = openmc.Material() + boron.set_density('g/cc', 2.52) + boron.add_nuclide("B10", 0.15856) + boron.add_nuclide("B11", 0.64144) + boron.add_nuclide("C0", 0.2) + + ### Geometry ### + radii = [5.0, 10.0, 30.0, 31.0, 50.0] + + surfs = [openmc.Sphere(r=r) for r in radii] + + surfs[-1].boundary_type = 'vacuum' + + regions = openmc.model.subdivide(surfs) + + mats = [air, water, steel, boron, air] + + cells = [openmc.Cell(fill=m, region=r) for r, m in zip(regions, mats)] + + geometry = openmc.Geometry(cells) + + ### Settings ### + + settings = openmc.Settings( + run_mode='fixed source', + particles=100, + batches=10, + max_history_splits=10, + survival_biasing=False + ) + + # 10 keV neutron point source at the origin + space = openmc.stats.Point() + energy = openmc.stats.Discrete(x=[1e4], p=[1.0]) + settings.source = openmc.IndependentSource(space=space, energy=energy) + + return openmc.Model(geometry=geometry, settings=settings) + + +# create a tally used for weight window generation +mesh = openmc.RegularMesh() +mesh.lower_left = [-50.0] * 3 +mesh.upper_right = [50.0] * 3 +# use un-equal mesh widths in each dimension to more robustly check +# use of tally data +mesh.dimension = (19, 20, 21) + +mf = openmc.MeshFilter(mesh) + +ef = openmc.EnergyFilter([0.0, 1e7]) + +pf = openmc.ParticleFilter(['neutron', 'photon']) + +filters = [mf, ef, pf] + +test_cases = list(permutations(filters)) +test_cases += list(permutations(filters[:-1])) +test_cases += list(permutations(filters[::2])) + + +def labels(params): + out = [] + for p in params: + if isinstance(p, openmc.ParticleFilter): + out.append('particle') + elif isinstance(p, openmc.MeshFilter): + out.append('mesh') + elif isinstance(p, openmc.EnergyFilter): + out.append('energy') + return "filters:" + '-'.join(out) + + +@pytest.mark.parametrize("filters", test_cases, ids=labels) +def test_ww_gen(filters, run_in_tmpdir, model): + + tally = openmc.Tally() + tally.filters = list(filters) + tally.scores = ['flux'] + model.tallies = openmc.Tallies([tally]) + + model.export_to_model_xml() + + ref_lower = None + ref_upper = None + # test weight window generation capability + with openmc.lib.run_in_memory(): + + # retrieve the tally we created above in memory + lib_tally = openmc.lib.tallies[tally.id] + + # create a new weight windows object + ww = openmc.lib.WeightWindows.from_tally(lib_tally) + + # run particle transport + openmc.lib.run() + + # capture analog data + analog_mean = np.copy(lib_tally.mean) + + # update the weight window values using tally results + ww.update_magic(lib_tally) + + assert any(ww.bounds[0] != -1) + assert any(ww.bounds[1] != -1) + + # make sure that the weight window update doesn't change tally values + np.testing.assert_equal(lib_tally.mean, analog_mean) + + # check against weight windows from the previous iteration + # the order of filters should not change the weight window values + if ref_lower is None: + ref_lower = ww.bounds[0].copy() + else: + np.testing.assert_equal(ref_lower, ww.bounds[0]) + + if ref_upper is None: + ref_upper = ww.bounds[1].copy() + else: + np.testing.assert_equal(ref_upper, ww.bounds[1]) + + # turn on weight windows for the subsequent run + openmc.lib.settings.weight_windows_on = True + + openmc.lib.hard_reset() + + openmc.lib.run() + + ww_mean = np.copy(lib_tally.mean) + + # we expect that the application of weight windows will populate more tally + # bins than the analog run for the meshes in this test model + assert any(ww_mean != analog_mean) + assert np.count_nonzero(ww_mean) > np.count_nonzero(analog_mean) + + +def test_ww_import_export(run_in_tmpdir, model): + # create a tally for weight windows + mesh = openmc.RegularMesh() + mesh.lower_left = [-50.0] * 3 + mesh.upper_right = [50.0] * 3 + # use un-equal mesh widths in each dimension to more robustly check + # use of tally data + mesh.dimension = (3, 4, 5) + + mf = openmc.MeshFilter(mesh) + + e_groups = np.logspace(0, 7, 8) + ef = openmc.EnergyFilter(e_groups) + + pf = openmc.ParticleFilter(['neutron', 'photon']) + + tally = openmc.Tally() + tally.filters = [mf, ef, pf] + tally.scores = ['flux'] + + model.tallies = openmc.Tallies([tally]) + + # first, generate some weight windows + model.export_to_model_xml() + + openmc.lib.init() + + tally = openmc.lib.tallies[tally.id] + + ww = openmc.lib.WeightWindows.from_tally(tally) + + openmc.lib.run() + + mean_before = np.array(tally.mean) + + ww.update_magic(tally) + + mean_after = np.array(tally.mean) + + assert (mean_before == mean_after).all() + + lb_before, up_before = ww.bounds + + # set some additional weight windows properties after transport + ww.survival_ratio = 0.7 + assert ww.survival_ratio == 0.7 + + ww.weight_cutoff = 1e-10 + assert ww.weight_cutoff == 1e-10 + + ww.max_lower_bound_ratio = 200.0 + assert ww.max_lower_bound_ratio == 200.0 + + ww.max_split = 26000 + assert ww.max_split == 26000 + + openmc.lib.export_weight_windows() + + assert Path('weight_windows.h5').exists() + + openmc.lib.import_weight_windows('weight_windows.h5') + + imported_ww = openmc.lib.weight_windows[2] + + lb_after, up_after = imported_ww.bounds + + assert np.allclose(lb_before, lb_after) + assert np.allclose(up_before, up_after) + + assert ww.survival_ratio == imported_ww.survival_ratio + assert ww.max_lower_bound_ratio == imported_ww.max_lower_bound_ratio + assert ww.weight_cutoff == imported_ww.weight_cutoff + assert ww.max_split == imported_ww.max_split + + openmc.lib.finalize() + + +def test_ww_gen_roundtrip(run_in_tmpdir, model): + + mesh = openmc.RegularMesh.from_domain(model.geometry.root_universe) + energy_bounds = np.linspace(0.0, 1e8, 11) + particle_type = 'neutron' + + wwg = openmc.WeightWindowGenerator(mesh, energy_bounds, particle_type) + wwg.update_parameters = {'ratio' : 5.0, + 'threshold': 0.8, + 'value' : 'mean'} + + model.settings.weight_window_generators = wwg + model.export_to_xml() + + model_in = openmc.Model.from_xml() + + assert len(model_in.settings.weight_window_generators) == 1 + + wwg_in = model.settings.weight_window_generators[0] + + # rountrip tests + model_in = openmc.Model.from_xml() + assert len(model_in.settings.weight_window_generators) == 1 + wwg_in = model_in.settings.weight_window_generators[0] + assert wwg_in.max_realizations == 1 + assert wwg_in.on_the_fly == True + assert wwg_in.update_interval == 1 + assert wwg_in.update_parameters == wwg.update_parameters + + with pytest.raises(ValueError): + wwg.method = '🦍🐒' + + with pytest.raises(TypeError): + wwg.update_parameters = {'ratio' : 'one-to-one'} + + with pytest.raises(ValueError): + wwg.max_realizations = -1 + + +def test_python_hdf5_roundtrip(run_in_tmpdir, model): + + # add a tally to the model + mesh = openmc.RegularMesh.from_domain(model.geometry) + + # some arbitrary energy groups + e_groups = np.logspace(0, 6, 4) + energy_filter = openmc.EnergyFilter(e_groups) + + bounds = np.arange(energy_filter.num_bins * np.prod(mesh.dimension)) + + wws = openmc.WeightWindows(mesh, bounds, bounds, energy_bounds=e_groups) + + model.settings.weight_windows = [wws] + + model.export_to_xml() + + # initialize and export wws to HDF5 + openmc.lib.init() + + openmc.lib.export_weight_windows() + + openmc.lib.finalize() + + wws_hdf5 = openmc.WeightWindowsList.from_hdf5()[0] + + # ensure + assert all(wws.energy_bounds == wws_hdf5.energy_bounds) + assert wws.id == wws_hdf5.id + assert wws.mesh.id == wws_hdf5.mesh.id + np.testing.assert_array_equal(wws.lower_ww_bounds, wws_hdf5.lower_ww_bounds) + np.testing.assert_array_equal(wws.upper_ww_bounds, wws_hdf5.upper_ww_bounds) + + +def test_ww_bounds_set_in_memory(run_in_tmpdir, model): + tally = openmc.Tally() + tally.filters = filters + tally.scores = ['flux'] + model.tallies = [tally] + + bounds = np.arange(ef.num_bins * np.prod(mf.mesh.dimension)) + + model.export_to_xml() + + openmc.lib.init() + + lib_tally = openmc.lib.tallies[tally.id] + + wws = openmc.lib.WeightWindows.from_tally(lib_tally) + + wws.bounds = (bounds, bounds) + + openmc.lib.finalize() + + +@pytest.mark.skipif(not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") +def test_ww_generation_with_dagmc(run_in_tmpdir): + mat1 = openmc.Material(name="1") + mat1.add_nuclide("H1", 1, percent_type="ao") + mat1.set_density("g/cm3", 0.001) + + materials = openmc.Materials([mat1]) + dag_univ = openmc.DAGMCUniverse( + Path(__file__).parent.parent / "dagmc" / "dagmc_tetrahedral_no_graveyard.h5m") + bound_dag_univ = dag_univ.bounded_universe(padding_distance=1) + geometry = openmc.Geometry(bound_dag_univ) + + settings = openmc.Settings() + settings.batches = 6 + settings.particles = 30 + settings.run_mode = "fixed source" + + # Create a point source which are supported by random ray mode + my_source = openmc.IndependentSource() + my_source.space = openmc.stats.Point((0.25, 0.25, 0.25)) + my_source.energy = openmc.stats.delta_function(14e6) + settings.source = my_source + + model = openmc.Model(geometry, materials, settings) + + rr_model = copy.deepcopy(model) + rr_model.settings.inactive = 3 + + rr_model.convert_to_multigroup( + method="stochastic_slab", + overwrite_mgxs_library=True, + nparticles=10, + groups="CASMO-2" + ) + + rr_model.convert_to_random_ray() + + mesh = openmc.RegularMesh.from_domain(rr_model, dimension=(4, 4, 4)) + + # avoid writing files we don't make use of + rr_model.settings.output = {"summary": False, "tallies": False} + + # Subdivide random ray source regions + rr_model.settings.random_ray["source_region_meshes"] = [ + (mesh, [rr_model.geometry.root_universe]) + ] + + # less likely to get negative values in the weight window + rr_model.settings.random_ray["volume_estimator"] = "naive" + + # Add a weight window generator to the model + rr_model.settings.weight_window_generators = openmc.WeightWindowGenerator( + method="fw_cadis", + mesh=mesh, + max_realizations=42, + particle_type='neutron', + energy_bounds=[0.0, 100e6] + ) + + rr_model.run() + + model.settings.weight_windows_on = True + model.settings.weight_window_checkpoints = {"collision": True, "surface": True} + model.settings.survival_biasing = False + model.settings.weight_windows = openmc.WeightWindowsList.from_hdf5() + + model.run() diff --git a/tests/unit_tests/weightwindows/test_ww_list.py b/tests/unit_tests/weightwindows/test_ww_list.py new file mode 100644 index 0000000000..d148f382a5 --- /dev/null +++ b/tests/unit_tests/weightwindows/test_ww_list.py @@ -0,0 +1,23 @@ +import openmc + + +def test_ww_roundtrip(request, run_in_tmpdir): + # Load weight windows from a wwinp file + wwinp_file = request.path.with_name('wwinp_n') + wws = openmc.WeightWindowsList.from_wwinp(wwinp_file) + + # Roundtrip them, writing to HDF5 and reading back in + wws.export_to_hdf5('ww.h5') + wws_new = openmc.WeightWindowsList.from_hdf5('ww.h5') + + # Check that the new weight windows are the same as the original + assert len(wws) == len(wws_new) + for ww, ww_new in zip(wws, wws_new): + assert ww.particle_type == ww_new.particle_type + assert (ww.lower_ww_bounds == ww_new.lower_ww_bounds).all() + assert (ww.upper_ww_bounds == ww_new.upper_ww_bounds).all() + assert ww.survival_ratio == ww_new.survival_ratio + assert ww.num_energy_bins == ww_new.num_energy_bins + assert ww.max_split == ww_new.max_split + assert ww.weight_cutoff == ww_new.weight_cutoff + assert ww.mesh.id == ww_new.mesh.id diff --git a/tests/unit_tests/weightwindows/test_ww_mg.py b/tests/unit_tests/weightwindows/test_ww_mg.py new file mode 100644 index 0000000000..23e09ea0c0 --- /dev/null +++ b/tests/unit_tests/weightwindows/test_ww_mg.py @@ -0,0 +1,51 @@ +import numpy as np +import openmc + + +def test_weight_windows_mg(request, run_in_tmpdir): + # import basic random ray model + model = openmc.examples.random_ray_three_region_cube() + + # create a mesh tally + mesh = openmc.RegularMesh.from_domain(model.geometry, (3, 3, 3)) + mesh_tally = openmc.Tally() + mesh_tally.filters = [openmc.MeshFilter(mesh)] + mesh_tally.scores = ['flux'] + model.tallies = [mesh_tally] + + # replace random ray settings with fixed source settings + settings = openmc.Settings() + settings.particles = 5000 + settings.batches = 10 + settings.energy_mode = 'multi-group' + settings.run_mode = 'fixed source' + space = openmc.stats.Point((1, 1, 1)) + energy = openmc.stats.delta_function(1e6) + settings.source = openmc.IndependentSource(space=space, energy=energy) + model.settings = settings + + # perform analog simulation + statepoint = model.run() + + # extract flux from analog simulation + with openmc.StatePoint(statepoint) as sp: + tally_out = sp.get_tally(id=mesh_tally.id) + flux_analog = tally_out.mean + + # load the weight windows for this problem and apply them + ww_lower_bnds = np.loadtxt(request.path.parent / 'ww_mg.txt') + weight_windows = openmc.WeightWindows(mesh, lower_ww_bounds=ww_lower_bnds, upper_bound_ratio=5.0) + model.settings.weight_windows = weight_windows + model.settings.weight_windows_on = True + + # re-run with weight windows + statepoint = model.run() + with openmc.StatePoint(statepoint) as sp: + tally_out = sp.get_tally(id=mesh_tally.id) + flux_ww = tally_out.mean + + # the sum of the fluxes should approach the same value (no bias introduced) + analog_sum = flux_analog.sum() + ww_sum = flux_ww.sum() + assert np.allclose(analog_sum, ww_sum, rtol=1e-2) + diff --git a/tests/unit_tests/weightwindows/test_wwinp_reader.py b/tests/unit_tests/weightwindows/test_wwinp_reader.py new file mode 100644 index 0000000000..637463bb2e --- /dev/null +++ b/tests/unit_tests/weightwindows/test_wwinp_reader.py @@ -0,0 +1,143 @@ +from pathlib import Path + +import numpy as np +import openmc +import pytest + +# check that we can successfully read wwinp files with the following contents: +# +# - neutrons on a rectilinear mesh +# - neutrons and photons on a rectilinear mesh + +# check that the following raises the correct exceptions (for now): +# +# - wwinp file with multiple time steps +# - wwinp file with cylindrical or spherical mesh + + +# expected retults - neutron data only +n_mesh = openmc.RectilinearMesh() +n_mesh.x_grid = np.array([-100.0, + -99.0, + -97.0, + -79.3636, + -61.7273, + -44.0909, + -26.4546, + -8.81818, + 8.81818, + 26.4546, + 44.0909, + 61.7273, + 79.3636, + 97.0, + 99.0, + 100]) +n_mesh.y_grid = np.array([-100.0, + -50.0, + -13.3333, + 23.3333, + 60.0, + 70.0, + 80.0, + 90.0, + 100.0]) +n_mesh.z_grid = np.array([-100.0, + -66.6667, + -33.3333, + 0.0, + 33.3333, + 66.6667, + 100.0]) +n_e_bounds = (np.array([0.0, + 100000.0, + 146780.0]),) +n_particles = ('neutron',) + +# expected results - neutron and photon data +np_mesh = openmc.RectilinearMesh() +np_mesh.x_grid = np.array([-100.0, 100.0]) +# y grid and z grid are the same as the previous mesh +np_mesh.y_grid = n_mesh.y_grid +np_mesh.z_grid = n_mesh.z_grid + +np_e_bounds = (np.array([0.0, 100000.0, 146780.0, 215440.0]), + np.array([0.0, 1.0E8])) +np_particles = ('neutron', 'photon') + +# expected results - photon data only +p_mesh = openmc.RectilinearMesh() +# adopts z grid from previous meshes as its x grid +p_mesh.x_grid = np_mesh.z_grid +# uses the same y grid +p_mesh.y_grid = np_mesh.y_grid +p_mesh.z_grid = np.array([-50.0, 50.0]) + +p_e_bounds = (np.array([0.0, 100000.0, 146780.0, 215440.0, 316230.0]),) +p_particles = ('photon',) + +expected_results = [('wwinp_n', n_mesh, n_particles, n_e_bounds), + ('wwinp_np', np_mesh, np_particles, np_e_bounds), + ('wwinp_p', p_mesh, p_particles, p_e_bounds)] + + +# function for printing readable test labels +def id_fn(params): + suffix = params[0].split('_')[-1] + if suffix == 'n': + return 'neutron-only' + elif suffix == 'np': + return 'neutron-photon' + elif suffix == 'p': + return 'photon-only' + + +@pytest.mark.parametrize('wwinp_data', expected_results, ids=id_fn) +def test_wwinp_reader(wwinp_data, request): + wwinp_file, mesh, particle_types, energy_bounds = wwinp_data + + wws = openmc.WeightWindowsList.from_wwinp(request.node.path.parent / wwinp_file) + + for i, ww in enumerate(wws): + e_bounds = energy_bounds[i] + particle_type = particle_types[i] + + assert ww.particle_type == particle_type + + # check the mesh grid + # there will be some very small changes due to the number of digits + # provided in the wwinp format and the use of np.linspace to compute + # boundaries of the fine mesh intervals + np.testing.assert_allclose(mesh.x_grid, ww.mesh.x_grid, rtol=1e-6) + np.testing.assert_allclose(mesh.y_grid, ww.mesh.y_grid, rtol=1e-6) + np.testing.assert_allclose(mesh.z_grid, ww.mesh.z_grid, rtol=1e-6) + + # check the energy bounds + np.testing.assert_array_equal(e_bounds, ww.energy_bounds) + + # check the expected weight window values mocked in the file -- + # a reversed array of the flat index into the numpy array + n_wws = np.prod((*mesh.dimension, e_bounds.size - 1)) + exp_ww_lb = np.linspace(1, n_wws, n_wws)[::-1] + np.testing.assert_array_equal(exp_ww_lb, ww.lower_ww_bounds.flatten()) + + +# check expected failures +def fail_id_fn(params): + suffix = params[0].split('_')[-1] + if suffix == 't': + return 'time-steps-failure' + elif suffix == 'cyl': + return 'cyl-mesh-failure' + + +expected_failure_data = (('wwinp_t', ValueError), + ('wwinp_cyl', NotImplementedError)) + + +@pytest.mark.parametrize('wwinp_data', expected_failure_data, ids=fail_id_fn) +def test_wwinp_reader_failures(wwinp_data, request): + filename, expected_failure = wwinp_data + + with pytest.raises(expected_failure): + _ = openmc.WeightWindowsList.from_wwinp(request.node.path.parent / filename) diff --git a/tests/unit_tests/weightwindows/ww_mg.txt b/tests/unit_tests/weightwindows/ww_mg.txt new file mode 100644 index 0000000000..7bcc59d97b --- /dev/null +++ b/tests/unit_tests/weightwindows/ww_mg.txt @@ -0,0 +1,27 @@ +5.000000000000000278e-02 +1.023184121346435924e-02 +3.006624096325660397e-03 +1.030178532774538719e-02 +6.058877789444589400e-03 +2.216914234856166583e-03 +3.061186967456217597e-03 +2.148267952185671601e-03 +1.026171712186230980e-03 +1.040022203443005666e-02 +6.040633813799485378e-03 +2.364143118752318716e-03 +6.119726639841410569e-03 +4.329097093078606955e-03 +1.873104469085542763e-03 +2.246957229279350661e-03 +1.851165248260521617e-03 +7.825824911598703530e-04 +3.021300894848398706e-03 +2.286420236345795311e-03 +9.318583473482396160e-04 +2.234702678114806364e-03 +1.813664566119888152e-03 +7.969287848384389462e-04 +1.017895970086981662e-03 +7.707144532950136471e-04 +3.386087166633241791e-04 diff --git a/tests/unit_tests/weightwindows/wwinp_cyl b/tests/unit_tests/weightwindows/wwinp_cyl new file mode 100644 index 0000000000..aac9d46f25 --- /dev/null +++ b/tests/unit_tests/weightwindows/wwinp_cyl @@ -0,0 +1,48 @@ + 1 1 2 16 + 0 4 + 6.0 8.0 1.0 -1.00000E+02 -1.00000E+02 -5.00000E+01 + 6.0 8.0 1.0 -1.00000E+02 -1.00000E+02 5.00000E+01 + -1.00000E+02 1.00000E+02 -5.00000E+01 2.0 + -1.00000E+02 1.00000E+00 -6.66667E+01 1.00000E+00 1.00000E+00 -3.33333E+01 + 1.00000E+00 1.00000E+00 0.00000E+00 1.00000E+00 1.00000E+00 3.33333E+01 + 1.00000E+00 1.00000E+00 6.66667E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -1.00000E+02 1.00000E+00 -5.00000E+01 1.00000E+00 1.00000E+00 -1.33333E+01 + 1.00000E+00 1.00000E+00 2.33333E+01 1.00000E+00 1.00000E+00 6.00000E+01 + 1.00000E+00 1.00000E+00 7.00000E+01 1.00000E+00 1.00000E+00 8.00000E+01 + 1.00000E+00 1.00000E+00 9.00000E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + 0.00000E+01 1.00000E+00 3.14159E+00 1.00000E+00 + 1.00000E-01 1.46780E-01 2.15440E-01 3.16230E-01 + 1.92000E+02 1.60000E+02 1.28000E+02 9.60000E+01 6.40000E+01 3.20000E+01 + 1.88000E+02 1.56000E+02 1.24000E+02 9.20000E+01 6.00000E+01 2.80000E+01 + 1.84000E+02 1.52000E+02 1.20000E+02 8.80000E+01 5.60000E+01 2.40000E+01 + 1.80000E+02 1.48000E+02 1.16000E+02 8.40000E+01 5.20000E+01 2.00000E+01 + 1.76000E+02 1.44000E+02 1.12000E+02 8.00000E+01 4.80000E+01 1.60000E+01 + 1.72000E+02 1.40000E+02 1.08000E+02 7.60000E+01 4.40000E+01 1.20000E+01 + 1.68000E+02 1.36000E+02 1.04000E+02 7.20000E+01 4.00000E+01 8.00000E+00 + 1.64000E+02 1.32000E+02 1.00000E+02 6.80000E+01 3.60000E+01 4.00000E+00 + 1.91000E+02 1.59000E+02 1.27000E+02 9.50000E+01 6.30000E+01 3.10000E+01 + 1.87000E+02 1.55000E+02 1.23000E+02 9.10000E+01 5.90000E+01 2.70000E+01 + 1.83000E+02 1.51000E+02 1.19000E+02 8.70000E+01 5.50000E+01 2.30000E+01 + 1.79000E+02 1.47000E+02 1.15000E+02 8.30000E+01 5.10000E+01 1.90000E+01 + 1.75000E+02 1.43000E+02 1.11000E+02 7.90000E+01 4.70000E+01 1.50000E+01 + 1.71000E+02 1.39000E+02 1.07000E+02 7.50000E+01 4.30000E+01 1.10000E+01 + 1.67000E+02 1.35000E+02 1.03000E+02 7.10000E+01 3.90000E+01 7.00000E+00 + 1.63000E+02 1.31000E+02 9.90000E+01 6.70000E+01 3.50000E+01 3.00000E+00 + 1.90000E+02 1.58000E+02 1.26000E+02 9.40000E+01 6.20000E+01 3.00000E+01 + 1.86000E+02 1.54000E+02 1.22000E+02 9.00000E+01 5.80000E+01 2.60000E+01 + 1.82000E+02 1.50000E+02 1.18000E+02 8.60000E+01 5.40000E+01 2.20000E+01 + 1.78000E+02 1.46000E+02 1.14000E+02 8.20000E+01 5.00000E+01 1.80000E+01 + 1.74000E+02 1.42000E+02 1.10000E+02 7.80000E+01 4.60000E+01 1.40000E+01 + 1.70000E+02 1.38000E+02 1.06000E+02 7.40000E+01 4.20000E+01 1.00000E+01 + 1.66000E+02 1.34000E+02 1.02000E+02 7.00000E+01 3.80000E+01 6.00000E+00 + 1.62000E+02 1.30000E+02 9.80000E+01 6.60000E+01 3.40000E+01 2.00000E+00 + 1.89000E+02 1.57000E+02 1.25000E+02 9.30000E+01 6.10000E+01 2.90000E+01 + 1.85000E+02 1.53000E+02 1.21000E+02 8.90000E+01 5.70000E+01 2.50000E+01 + 1.81000E+02 1.49000E+02 1.17000E+02 8.50000E+01 5.30000E+01 2.10000E+01 + 1.77000E+02 1.45000E+02 1.13000E+02 8.10000E+01 4.90000E+01 1.70000E+01 + 1.73000E+02 1.41000E+02 1.09000E+02 7.70000E+01 4.50000E+01 1.30000E+01 + 1.69000E+02 1.37000E+02 1.05000E+02 7.30000E+01 4.10000E+01 9.00000E+00 + 1.65000E+02 1.33000E+02 1.01000E+02 6.90000E+01 3.70000E+01 5.00000E+00 + 1.61000E+02 1.29000E+02 9.70000E+01 6.50000E+01 3.30000E+01 1.00000E+00 diff --git a/tests/unit_tests/weightwindows/wwinp_n b/tests/unit_tests/weightwindows/wwinp_n new file mode 100644 index 0000000000..73a4fc496b --- /dev/null +++ b/tests/unit_tests/weightwindows/wwinp_n @@ -0,0 +1,263 @@ + 1 1 1 10 + 2 + 15.0 8.0 6.0 -1.00000E+02 -1.00000E+02 -1.00000E+02 + 15.0 8.0 6.0 1.0 + -1.00000E+02 1.00000E+00 -9.90000E+01 1.00000E+00 1.00000E+00 -9.70000E+01 + 1.00000E+00 1.00000E+00 -7.93636E+01 1.00000E+00 1.00000E+00 -6.17273E+01 + 1.00000E+00 1.00000E+00 -4.40909E+01 1.00000E+00 1.00000E+00 -2.64546E+01 + 1.00000E+00 1.00000E+00 -8.81818E+00 1.00000E+00 1.00000E+00 8.81818E+00 + 1.00000E+00 1.00000E+00 2.64546E+01 1.00000E+00 1.00000E+00 4.40909E+01 + 1.00000E+00 1.00000E+00 6.17273E+01 1.00000E+00 1.00000E+00 7.93636E+01 + 1.00000E+00 1.00000E+00 9.70000E+01 1.00000E+00 1.00000E+00 9.90000E+01 + 1.00000E+00 1.00000E+00 1.00000E+02 1.00000E+00 + -1.00000E+02 1.00000E+00 -5.00000E+01 1.00000E+00 1.00000E+00 -1.33333E+01 + 1.00000E+00 1.00000E+00 2.33333E+01 1.00000E+00 1.00000E+00 6.00000E+01 + 1.00000E+00 1.00000E+00 7.00000E+01 1.00000E+00 1.00000E+00 8.00000E+01 + 1.00000E+00 1.00000E+00 9.00000E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -1.00000E+02 1.00000E+00 -6.66667E+01 1.00000E+00 1.00000E+00 -3.33333E+01 + 1.00000E+00 1.00000E+00 0.00000E+00 1.00000E+00 1.00000E+00 3.33333E+01 + 1.00000E+00 1.00000E+00 6.66667E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + 1.00000E-01 1.46780E-01 + 1.44000E+03 1.34400E+03 1.24800E+03 1.15200E+03 1.05600E+03 9.60000E+02 + 8.64000E+02 7.68000E+02 6.72000E+02 5.76000E+02 4.80000E+02 3.84000E+02 + 2.88000E+02 1.92000E+02 9.60000E+01 1.42800E+03 1.33200E+03 1.23600E+03 + 1.14000E+03 1.04400E+03 9.48000E+02 8.52000E+02 7.56000E+02 6.60000E+02 + 5.64000E+02 4.68000E+02 3.72000E+02 2.76000E+02 1.80000E+02 8.40000E+01 + 1.41600E+03 1.32000E+03 1.22400E+03 1.12800E+03 1.03200E+03 9.36000E+02 + 8.40000E+02 7.44000E+02 6.48000E+02 5.52000E+02 4.56000E+02 3.60000E+02 + 2.64000E+02 1.68000E+02 7.20000E+01 1.40400E+03 1.30800E+03 1.21200E+03 + 1.11600E+03 1.02000E+03 9.24000E+02 8.28000E+02 7.32000E+02 6.36000E+02 + 5.40000E+02 4.44000E+02 3.48000E+02 2.52000E+02 1.56000E+02 6.00000E+01 + 1.39200E+03 1.29600E+03 1.20000E+03 1.10400E+03 1.00800E+03 9.12000E+02 + 8.16000E+02 7.20000E+02 6.24000E+02 5.28000E+02 4.32000E+02 3.36000E+02 + 2.40000E+02 1.44000E+02 4.80000E+01 1.38000E+03 1.28400E+03 1.18800E+03 + 1.09200E+03 9.96000E+02 9.00000E+02 8.04000E+02 7.08000E+02 6.12000E+02 + 5.16000E+02 4.20000E+02 3.24000E+02 2.28000E+02 1.32000E+02 3.60000E+01 + 1.36800E+03 1.27200E+03 1.17600E+03 1.08000E+03 9.84000E+02 8.88000E+02 + 7.92000E+02 6.96000E+02 6.00000E+02 5.04000E+02 4.08000E+02 3.12000E+02 + 2.16000E+02 1.20000E+02 2.40000E+01 1.35600E+03 1.26000E+03 1.16400E+03 + 1.06800E+03 9.72000E+02 8.76000E+02 7.80000E+02 6.84000E+02 5.88000E+02 + 4.92000E+02 3.96000E+02 3.00000E+02 2.04000E+02 1.08000E+02 1.20000E+01 + 1.43800E+03 1.34200E+03 1.24600E+03 1.15000E+03 1.05400E+03 9.58000E+02 + 8.62000E+02 7.66000E+02 6.70000E+02 5.74000E+02 4.78000E+02 3.82000E+02 + 2.86000E+02 1.90000E+02 9.40000E+01 1.42600E+03 1.33000E+03 1.23400E+03 + 1.13800E+03 1.04200E+03 9.46000E+02 8.50000E+02 7.54000E+02 6.58000E+02 + 5.62000E+02 4.66000E+02 3.70000E+02 2.74000E+02 1.78000E+02 8.20000E+01 + 1.41400E+03 1.31800E+03 1.22200E+03 1.12600E+03 1.03000E+03 9.34000E+02 + 8.38000E+02 7.42000E+02 6.46000E+02 5.50000E+02 4.54000E+02 3.58000E+02 + 2.62000E+02 1.66000E+02 7.00000E+01 1.40200E+03 1.30600E+03 1.21000E+03 + 1.11400E+03 1.01800E+03 9.22000E+02 8.26000E+02 7.30000E+02 6.34000E+02 + 5.38000E+02 4.42000E+02 3.46000E+02 2.50000E+02 1.54000E+02 5.80000E+01 + 1.39000E+03 1.29400E+03 1.19800E+03 1.10200E+03 1.00600E+03 9.10000E+02 + 8.14000E+02 7.18000E+02 6.22000E+02 5.26000E+02 4.30000E+02 3.34000E+02 + 2.38000E+02 1.42000E+02 4.60000E+01 1.37800E+03 1.28200E+03 1.18600E+03 + 1.09000E+03 9.94000E+02 8.98000E+02 8.02000E+02 7.06000E+02 6.10000E+02 + 5.14000E+02 4.18000E+02 3.22000E+02 2.26000E+02 1.30000E+02 3.40000E+01 + 1.36600E+03 1.27000E+03 1.17400E+03 1.07800E+03 9.82000E+02 8.86000E+02 + 7.90000E+02 6.94000E+02 5.98000E+02 5.02000E+02 4.06000E+02 3.10000E+02 + 2.14000E+02 1.18000E+02 2.20000E+01 1.35400E+03 1.25800E+03 1.16200E+03 + 1.06600E+03 9.70000E+02 8.74000E+02 7.78000E+02 6.82000E+02 5.86000E+02 + 4.90000E+02 3.94000E+02 2.98000E+02 2.02000E+02 1.06000E+02 1.00000E+01 + 1.43600E+03 1.34000E+03 1.24400E+03 1.14800E+03 1.05200E+03 9.56000E+02 + 8.60000E+02 7.64000E+02 6.68000E+02 5.72000E+02 4.76000E+02 3.80000E+02 + 2.84000E+02 1.88000E+02 9.20000E+01 1.42400E+03 1.32800E+03 1.23200E+03 + 1.13600E+03 1.04000E+03 9.44000E+02 8.48000E+02 7.52000E+02 6.56000E+02 + 5.60000E+02 4.64000E+02 3.68000E+02 2.72000E+02 1.76000E+02 8.00000E+01 + 1.41200E+03 1.31600E+03 1.22000E+03 1.12400E+03 1.02800E+03 9.32000E+02 + 8.36000E+02 7.40000E+02 6.44000E+02 5.48000E+02 4.52000E+02 3.56000E+02 + 2.60000E+02 1.64000E+02 6.80000E+01 1.40000E+03 1.30400E+03 1.20800E+03 + 1.11200E+03 1.01600E+03 9.20000E+02 8.24000E+02 7.28000E+02 6.32000E+02 + 5.36000E+02 4.40000E+02 3.44000E+02 2.48000E+02 1.52000E+02 5.60000E+01 + 1.38800E+03 1.29200E+03 1.19600E+03 1.10000E+03 1.00400E+03 9.08000E+02 + 8.12000E+02 7.16000E+02 6.20000E+02 5.24000E+02 4.28000E+02 3.32000E+02 + 2.36000E+02 1.40000E+02 4.40000E+01 1.37600E+03 1.28000E+03 1.18400E+03 + 1.08800E+03 9.92000E+02 8.96000E+02 8.00000E+02 7.04000E+02 6.08000E+02 + 5.12000E+02 4.16000E+02 3.20000E+02 2.24000E+02 1.28000E+02 3.20000E+01 + 1.36400E+03 1.26800E+03 1.17200E+03 1.07600E+03 9.80000E+02 8.84000E+02 + 7.88000E+02 6.92000E+02 5.96000E+02 5.00000E+02 4.04000E+02 3.08000E+02 + 2.12000E+02 1.16000E+02 2.00000E+01 1.35200E+03 1.25600E+03 1.16000E+03 + 1.06400E+03 9.68000E+02 8.72000E+02 7.76000E+02 6.80000E+02 5.84000E+02 + 4.88000E+02 3.92000E+02 2.96000E+02 2.00000E+02 1.04000E+02 8.00000E+00 + 1.43400E+03 1.33800E+03 1.24200E+03 1.14600E+03 1.05000E+03 9.54000E+02 + 8.58000E+02 7.62000E+02 6.66000E+02 5.70000E+02 4.74000E+02 3.78000E+02 + 2.82000E+02 1.86000E+02 9.00000E+01 1.42200E+03 1.32600E+03 1.23000E+03 + 1.13400E+03 1.03800E+03 9.42000E+02 8.46000E+02 7.50000E+02 6.54000E+02 + 5.58000E+02 4.62000E+02 3.66000E+02 2.70000E+02 1.74000E+02 7.80000E+01 + 1.41000E+03 1.31400E+03 1.21800E+03 1.12200E+03 1.02600E+03 9.30000E+02 + 8.34000E+02 7.38000E+02 6.42000E+02 5.46000E+02 4.50000E+02 3.54000E+02 + 2.58000E+02 1.62000E+02 6.60000E+01 1.39800E+03 1.30200E+03 1.20600E+03 + 1.11000E+03 1.01400E+03 9.18000E+02 8.22000E+02 7.26000E+02 6.30000E+02 + 5.34000E+02 4.38000E+02 3.42000E+02 2.46000E+02 1.50000E+02 5.40000E+01 + 1.38600E+03 1.29000E+03 1.19400E+03 1.09800E+03 1.00200E+03 9.06000E+02 + 8.10000E+02 7.14000E+02 6.18000E+02 5.22000E+02 4.26000E+02 3.30000E+02 + 2.34000E+02 1.38000E+02 4.20000E+01 1.37400E+03 1.27800E+03 1.18200E+03 + 1.08600E+03 9.90000E+02 8.94000E+02 7.98000E+02 7.02000E+02 6.06000E+02 + 5.10000E+02 4.14000E+02 3.18000E+02 2.22000E+02 1.26000E+02 3.00000E+01 + 1.36200E+03 1.26600E+03 1.17000E+03 1.07400E+03 9.78000E+02 8.82000E+02 + 7.86000E+02 6.90000E+02 5.94000E+02 4.98000E+02 4.02000E+02 3.06000E+02 + 2.10000E+02 1.14000E+02 1.80000E+01 1.35000E+03 1.25400E+03 1.15800E+03 + 1.06200E+03 9.66000E+02 8.70000E+02 7.74000E+02 6.78000E+02 5.82000E+02 + 4.86000E+02 3.90000E+02 2.94000E+02 1.98000E+02 1.02000E+02 6.00000E+00 + 1.43200E+03 1.33600E+03 1.24000E+03 1.14400E+03 1.04800E+03 9.52000E+02 + 8.56000E+02 7.60000E+02 6.64000E+02 5.68000E+02 4.72000E+02 3.76000E+02 + 2.80000E+02 1.84000E+02 8.80000E+01 1.42000E+03 1.32400E+03 1.22800E+03 + 1.13200E+03 1.03600E+03 9.40000E+02 8.44000E+02 7.48000E+02 6.52000E+02 + 5.56000E+02 4.60000E+02 3.64000E+02 2.68000E+02 1.72000E+02 7.60000E+01 + 1.40800E+03 1.31200E+03 1.21600E+03 1.12000E+03 1.02400E+03 9.28000E+02 + 8.32000E+02 7.36000E+02 6.40000E+02 5.44000E+02 4.48000E+02 3.52000E+02 + 2.56000E+02 1.60000E+02 6.40000E+01 1.39600E+03 1.30000E+03 1.20400E+03 + 1.10800E+03 1.01200E+03 9.16000E+02 8.20000E+02 7.24000E+02 6.28000E+02 + 5.32000E+02 4.36000E+02 3.40000E+02 2.44000E+02 1.48000E+02 5.20000E+01 + 1.38400E+03 1.28800E+03 1.19200E+03 1.09600E+03 1.00000E+03 9.04000E+02 + 8.08000E+02 7.12000E+02 6.16000E+02 5.20000E+02 4.24000E+02 3.28000E+02 + 2.32000E+02 1.36000E+02 4.00000E+01 1.37200E+03 1.27600E+03 1.18000E+03 + 1.08400E+03 9.88000E+02 8.92000E+02 7.96000E+02 7.00000E+02 6.04000E+02 + 5.08000E+02 4.12000E+02 3.16000E+02 2.20000E+02 1.24000E+02 2.80000E+01 + 1.36000E+03 1.26400E+03 1.16800E+03 1.07200E+03 9.76000E+02 8.80000E+02 + 7.84000E+02 6.88000E+02 5.92000E+02 4.96000E+02 4.00000E+02 3.04000E+02 + 2.08000E+02 1.12000E+02 1.60000E+01 1.34800E+03 1.25200E+03 1.15600E+03 + 1.06000E+03 9.64000E+02 8.68000E+02 7.72000E+02 6.76000E+02 5.80000E+02 + 4.84000E+02 3.88000E+02 2.92000E+02 1.96000E+02 1.00000E+02 4.00000E+00 + 1.43000E+03 1.33400E+03 1.23800E+03 1.14200E+03 1.04600E+03 9.50000E+02 + 8.54000E+02 7.58000E+02 6.62000E+02 5.66000E+02 4.70000E+02 3.74000E+02 + 2.78000E+02 1.82000E+02 8.60000E+01 1.41800E+03 1.32200E+03 1.22600E+03 + 1.13000E+03 1.03400E+03 9.38000E+02 8.42000E+02 7.46000E+02 6.50000E+02 + 5.54000E+02 4.58000E+02 3.62000E+02 2.66000E+02 1.70000E+02 7.40000E+01 + 1.40600E+03 1.31000E+03 1.21400E+03 1.11800E+03 1.02200E+03 9.26000E+02 + 8.30000E+02 7.34000E+02 6.38000E+02 5.42000E+02 4.46000E+02 3.50000E+02 + 2.54000E+02 1.58000E+02 6.20000E+01 1.39400E+03 1.29800E+03 1.20200E+03 + 1.10600E+03 1.01000E+03 9.14000E+02 8.18000E+02 7.22000E+02 6.26000E+02 + 5.30000E+02 4.34000E+02 3.38000E+02 2.42000E+02 1.46000E+02 5.00000E+01 + 1.38200E+03 1.28600E+03 1.19000E+03 1.09400E+03 9.98000E+02 9.02000E+02 + 8.06000E+02 7.10000E+02 6.14000E+02 5.18000E+02 4.22000E+02 3.26000E+02 + 2.30000E+02 1.34000E+02 3.80000E+01 1.37000E+03 1.27400E+03 1.17800E+03 + 1.08200E+03 9.86000E+02 8.90000E+02 7.94000E+02 6.98000E+02 6.02000E+02 + 5.06000E+02 4.10000E+02 3.14000E+02 2.18000E+02 1.22000E+02 2.60000E+01 + 1.35800E+03 1.26200E+03 1.16600E+03 1.07000E+03 9.74000E+02 8.78000E+02 + 7.82000E+02 6.86000E+02 5.90000E+02 4.94000E+02 3.98000E+02 3.02000E+02 + 2.06000E+02 1.10000E+02 1.40000E+01 1.34600E+03 1.25000E+03 1.15400E+03 + 1.05800E+03 9.62000E+02 8.66000E+02 7.70000E+02 6.74000E+02 5.78000E+02 + 4.82000E+02 3.86000E+02 2.90000E+02 1.94000E+02 9.80000E+01 2.00000E+00 + 1.43900E+03 1.34300E+03 1.24700E+03 1.15100E+03 1.05500E+03 9.59000E+02 + 8.63000E+02 7.67000E+02 6.71000E+02 5.75000E+02 4.79000E+02 3.83000E+02 + 2.87000E+02 1.91000E+02 9.50000E+01 1.42700E+03 1.33100E+03 1.23500E+03 + 1.13900E+03 1.04300E+03 9.47000E+02 8.51000E+02 7.55000E+02 6.59000E+02 + 5.63000E+02 4.67000E+02 3.71000E+02 2.75000E+02 1.79000E+02 8.30000E+01 + 1.41500E+03 1.31900E+03 1.22300E+03 1.12700E+03 1.03100E+03 9.35000E+02 + 8.39000E+02 7.43000E+02 6.47000E+02 5.51000E+02 4.55000E+02 3.59000E+02 + 2.63000E+02 1.67000E+02 7.10000E+01 1.40300E+03 1.30700E+03 1.21100E+03 + 1.11500E+03 1.01900E+03 9.23000E+02 8.27000E+02 7.31000E+02 6.35000E+02 + 5.39000E+02 4.43000E+02 3.47000E+02 2.51000E+02 1.55000E+02 5.90000E+01 + 1.39100E+03 1.29500E+03 1.19900E+03 1.10300E+03 1.00700E+03 9.11000E+02 + 8.15000E+02 7.19000E+02 6.23000E+02 5.27000E+02 4.31000E+02 3.35000E+02 + 2.39000E+02 1.43000E+02 4.70000E+01 1.37900E+03 1.28300E+03 1.18700E+03 + 1.09100E+03 9.95000E+02 8.99000E+02 8.03000E+02 7.07000E+02 6.11000E+02 + 5.15000E+02 4.19000E+02 3.23000E+02 2.27000E+02 1.31000E+02 3.50000E+01 + 1.36700E+03 1.27100E+03 1.17500E+03 1.07900E+03 9.83000E+02 8.87000E+02 + 7.91000E+02 6.95000E+02 5.99000E+02 5.03000E+02 4.07000E+02 3.11000E+02 + 2.15000E+02 1.19000E+02 2.30000E+01 1.35500E+03 1.25900E+03 1.16300E+03 + 1.06700E+03 9.71000E+02 8.75000E+02 7.79000E+02 6.83000E+02 5.87000E+02 + 4.91000E+02 3.95000E+02 2.99000E+02 2.03000E+02 1.07000E+02 1.10000E+01 + 1.43700E+03 1.34100E+03 1.24500E+03 1.14900E+03 1.05300E+03 9.57000E+02 + 8.61000E+02 7.65000E+02 6.69000E+02 5.73000E+02 4.77000E+02 3.81000E+02 + 2.85000E+02 1.89000E+02 9.30000E+01 1.42500E+03 1.32900E+03 1.23300E+03 + 1.13700E+03 1.04100E+03 9.45000E+02 8.49000E+02 7.53000E+02 6.57000E+02 + 5.61000E+02 4.65000E+02 3.69000E+02 2.73000E+02 1.77000E+02 8.10000E+01 + 1.41300E+03 1.31700E+03 1.22100E+03 1.12500E+03 1.02900E+03 9.33000E+02 + 8.37000E+02 7.41000E+02 6.45000E+02 5.49000E+02 4.53000E+02 3.57000E+02 + 2.61000E+02 1.65000E+02 6.90000E+01 1.40100E+03 1.30500E+03 1.20900E+03 + 1.11300E+03 1.01700E+03 9.21000E+02 8.25000E+02 7.29000E+02 6.33000E+02 + 5.37000E+02 4.41000E+02 3.45000E+02 2.49000E+02 1.53000E+02 5.70000E+01 + 1.38900E+03 1.29300E+03 1.19700E+03 1.10100E+03 1.00500E+03 9.09000E+02 + 8.13000E+02 7.17000E+02 6.21000E+02 5.25000E+02 4.29000E+02 3.33000E+02 + 2.37000E+02 1.41000E+02 4.50000E+01 1.37700E+03 1.28100E+03 1.18500E+03 + 1.08900E+03 9.93000E+02 8.97000E+02 8.01000E+02 7.05000E+02 6.09000E+02 + 5.13000E+02 4.17000E+02 3.21000E+02 2.25000E+02 1.29000E+02 3.30000E+01 + 1.36500E+03 1.26900E+03 1.17300E+03 1.07700E+03 9.81000E+02 8.85000E+02 + 7.89000E+02 6.93000E+02 5.97000E+02 5.01000E+02 4.05000E+02 3.09000E+02 + 2.13000E+02 1.17000E+02 2.10000E+01 1.35300E+03 1.25700E+03 1.16100E+03 + 1.06500E+03 9.69000E+02 8.73000E+02 7.77000E+02 6.81000E+02 5.85000E+02 + 4.89000E+02 3.93000E+02 2.97000E+02 2.01000E+02 1.05000E+02 9.00000E+00 + 1.43500E+03 1.33900E+03 1.24300E+03 1.14700E+03 1.05100E+03 9.55000E+02 + 8.59000E+02 7.63000E+02 6.67000E+02 5.71000E+02 4.75000E+02 3.79000E+02 + 2.83000E+02 1.87000E+02 9.10000E+01 1.42300E+03 1.32700E+03 1.23100E+03 + 1.13500E+03 1.03900E+03 9.43000E+02 8.47000E+02 7.51000E+02 6.55000E+02 + 5.59000E+02 4.63000E+02 3.67000E+02 2.71000E+02 1.75000E+02 7.90000E+01 + 1.41100E+03 1.31500E+03 1.21900E+03 1.12300E+03 1.02700E+03 9.31000E+02 + 8.35000E+02 7.39000E+02 6.43000E+02 5.47000E+02 4.51000E+02 3.55000E+02 + 2.59000E+02 1.63000E+02 6.70000E+01 1.39900E+03 1.30300E+03 1.20700E+03 + 1.11100E+03 1.01500E+03 9.19000E+02 8.23000E+02 7.27000E+02 6.31000E+02 + 5.35000E+02 4.39000E+02 3.43000E+02 2.47000E+02 1.51000E+02 5.50000E+01 + 1.38700E+03 1.29100E+03 1.19500E+03 1.09900E+03 1.00300E+03 9.07000E+02 + 8.11000E+02 7.15000E+02 6.19000E+02 5.23000E+02 4.27000E+02 3.31000E+02 + 2.35000E+02 1.39000E+02 4.30000E+01 1.37500E+03 1.27900E+03 1.18300E+03 + 1.08700E+03 9.91000E+02 8.95000E+02 7.99000E+02 7.03000E+02 6.07000E+02 + 5.11000E+02 4.15000E+02 3.19000E+02 2.23000E+02 1.27000E+02 3.10000E+01 + 1.36300E+03 1.26700E+03 1.17100E+03 1.07500E+03 9.79000E+02 8.83000E+02 + 7.87000E+02 6.91000E+02 5.95000E+02 4.99000E+02 4.03000E+02 3.07000E+02 + 2.11000E+02 1.15000E+02 1.90000E+01 1.35100E+03 1.25500E+03 1.15900E+03 + 1.06300E+03 9.67000E+02 8.71000E+02 7.75000E+02 6.79000E+02 5.83000E+02 + 4.87000E+02 3.91000E+02 2.95000E+02 1.99000E+02 1.03000E+02 7.00000E+00 + 1.43300E+03 1.33700E+03 1.24100E+03 1.14500E+03 1.04900E+03 9.53000E+02 + 8.57000E+02 7.61000E+02 6.65000E+02 5.69000E+02 4.73000E+02 3.77000E+02 + 2.81000E+02 1.85000E+02 8.90000E+01 1.42100E+03 1.32500E+03 1.22900E+03 + 1.13300E+03 1.03700E+03 9.41000E+02 8.45000E+02 7.49000E+02 6.53000E+02 + 5.57000E+02 4.61000E+02 3.65000E+02 2.69000E+02 1.73000E+02 7.70000E+01 + 1.40900E+03 1.31300E+03 1.21700E+03 1.12100E+03 1.02500E+03 9.29000E+02 + 8.33000E+02 7.37000E+02 6.41000E+02 5.45000E+02 4.49000E+02 3.53000E+02 + 2.57000E+02 1.61000E+02 6.50000E+01 1.39700E+03 1.30100E+03 1.20500E+03 + 1.10900E+03 1.01300E+03 9.17000E+02 8.21000E+02 7.25000E+02 6.29000E+02 + 5.33000E+02 4.37000E+02 3.41000E+02 2.45000E+02 1.49000E+02 5.30000E+01 + 1.38500E+03 1.28900E+03 1.19300E+03 1.09700E+03 1.00100E+03 9.05000E+02 + 8.09000E+02 7.13000E+02 6.17000E+02 5.21000E+02 4.25000E+02 3.29000E+02 + 2.33000E+02 1.37000E+02 4.10000E+01 1.37300E+03 1.27700E+03 1.18100E+03 + 1.08500E+03 9.89000E+02 8.93000E+02 7.97000E+02 7.01000E+02 6.05000E+02 + 5.09000E+02 4.13000E+02 3.17000E+02 2.21000E+02 1.25000E+02 2.90000E+01 + 1.36100E+03 1.26500E+03 1.16900E+03 1.07300E+03 9.77000E+02 8.81000E+02 + 7.85000E+02 6.89000E+02 5.93000E+02 4.97000E+02 4.01000E+02 3.05000E+02 + 2.09000E+02 1.13000E+02 1.70000E+01 1.34900E+03 1.25300E+03 1.15700E+03 + 1.06100E+03 9.65000E+02 8.69000E+02 7.73000E+02 6.77000E+02 5.81000E+02 + 4.85000E+02 3.89000E+02 2.93000E+02 1.97000E+02 1.01000E+02 5.00000E+00 + 1.43100E+03 1.33500E+03 1.23900E+03 1.14300E+03 1.04700E+03 9.51000E+02 + 8.55000E+02 7.59000E+02 6.63000E+02 5.67000E+02 4.71000E+02 3.75000E+02 + 2.79000E+02 1.83000E+02 8.70000E+01 1.41900E+03 1.32300E+03 1.22700E+03 + 1.13100E+03 1.03500E+03 9.39000E+02 8.43000E+02 7.47000E+02 6.51000E+02 + 5.55000E+02 4.59000E+02 3.63000E+02 2.67000E+02 1.71000E+02 7.50000E+01 + 1.40700E+03 1.31100E+03 1.21500E+03 1.11900E+03 1.02300E+03 9.27000E+02 + 8.31000E+02 7.35000E+02 6.39000E+02 5.43000E+02 4.47000E+02 3.51000E+02 + 2.55000E+02 1.59000E+02 6.30000E+01 1.39500E+03 1.29900E+03 1.20300E+03 + 1.10700E+03 1.01100E+03 9.15000E+02 8.19000E+02 7.23000E+02 6.27000E+02 + 5.31000E+02 4.35000E+02 3.39000E+02 2.43000E+02 1.47000E+02 5.10000E+01 + 1.38300E+03 1.28700E+03 1.19100E+03 1.09500E+03 9.99000E+02 9.03000E+02 + 8.07000E+02 7.11000E+02 6.15000E+02 5.19000E+02 4.23000E+02 3.27000E+02 + 2.31000E+02 1.35000E+02 3.90000E+01 1.37100E+03 1.27500E+03 1.17900E+03 + 1.08300E+03 9.87000E+02 8.91000E+02 7.95000E+02 6.99000E+02 6.03000E+02 + 5.07000E+02 4.11000E+02 3.15000E+02 2.19000E+02 1.23000E+02 2.70000E+01 + 1.35900E+03 1.26300E+03 1.16700E+03 1.07100E+03 9.75000E+02 8.79000E+02 + 7.83000E+02 6.87000E+02 5.91000E+02 4.95000E+02 3.99000E+02 3.03000E+02 + 2.07000E+02 1.11000E+02 1.50000E+01 1.34700E+03 1.25100E+03 1.15500E+03 + 1.05900E+03 9.63000E+02 8.67000E+02 7.71000E+02 6.75000E+02 5.79000E+02 + 4.83000E+02 3.87000E+02 2.91000E+02 1.95000E+02 9.90000E+01 3.00000E+00 + 1.42900E+03 1.33300E+03 1.23700E+03 1.14100E+03 1.04500E+03 9.49000E+02 + 8.53000E+02 7.57000E+02 6.61000E+02 5.65000E+02 4.69000E+02 3.73000E+02 + 2.77000E+02 1.81000E+02 8.50000E+01 1.41700E+03 1.32100E+03 1.22500E+03 + 1.12900E+03 1.03300E+03 9.37000E+02 8.41000E+02 7.45000E+02 6.49000E+02 + 5.53000E+02 4.57000E+02 3.61000E+02 2.65000E+02 1.69000E+02 7.30000E+01 + 1.40500E+03 1.30900E+03 1.21300E+03 1.11700E+03 1.02100E+03 9.25000E+02 + 8.29000E+02 7.33000E+02 6.37000E+02 5.41000E+02 4.45000E+02 3.49000E+02 + 2.53000E+02 1.57000E+02 6.10000E+01 1.39300E+03 1.29700E+03 1.20100E+03 + 1.10500E+03 1.00900E+03 9.13000E+02 8.17000E+02 7.21000E+02 6.25000E+02 + 5.29000E+02 4.33000E+02 3.37000E+02 2.41000E+02 1.45000E+02 4.90000E+01 + 1.38100E+03 1.28500E+03 1.18900E+03 1.09300E+03 9.97000E+02 9.01000E+02 + 8.05000E+02 7.09000E+02 6.13000E+02 5.17000E+02 4.21000E+02 3.25000E+02 + 2.29000E+02 1.33000E+02 3.70000E+01 1.36900E+03 1.27300E+03 1.17700E+03 + 1.08100E+03 9.85000E+02 8.89000E+02 7.93000E+02 6.97000E+02 6.01000E+02 + 5.05000E+02 4.09000E+02 3.13000E+02 2.17000E+02 1.21000E+02 2.50000E+01 + 1.35700E+03 1.26100E+03 1.16500E+03 1.06900E+03 9.73000E+02 8.77000E+02 + 7.81000E+02 6.85000E+02 5.89000E+02 4.93000E+02 3.97000E+02 3.01000E+02 + 2.05000E+02 1.09000E+02 1.30000E+01 1.34500E+03 1.24900E+03 1.15300E+03 + 1.05700E+03 9.61000E+02 8.65000E+02 7.69000E+02 6.73000E+02 5.77000E+02 + 4.81000E+02 3.85000E+02 2.89000E+02 1.93000E+02 9.70000E+01 1.00000E+00 + diff --git a/tests/unit_tests/weightwindows/wwinp_np b/tests/unit_tests/weightwindows/wwinp_np new file mode 100644 index 0000000000..ed2073cf92 --- /dev/null +++ b/tests/unit_tests/weightwindows/wwinp_np @@ -0,0 +1,49 @@ + 1 1 2 10 + 3 1 + 1.0 8.0 6.0 -1.00000E+02 -1.00000E+02 -1.00000E+02 + 1.0 8.0 6.0 1.0 + -1.00000E+02 1.00000E+00 1.00000E+02 1.00000E+00 + -1.00000E+02 1.00000E+00 -5.00000E+01 1.00000E+00 1.00000E+00 -1.33333E+01 + 1.00000E+00 1.00000E+00 2.33333E+01 1.00000E+00 1.00000E+00 6.00000E+01 + 1.00000E+00 1.00000E+00 7.00000E+01 1.00000E+00 1.00000E+00 8.00000E+01 + 1.00000E+00 1.00000E+00 9.00000E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -1.00000E+02 1.00000E+00 -6.66667E+01 1.00000E+00 1.00000E+00 -3.33333E+01 + 1.00000E+00 1.00000E+00 0.00000E+00 1.00000E+00 1.00000E+00 3.33333E+01 + 1.00000E+00 1.00000E+00 6.66667E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + 1.00000E-01 1.46780E-01 2.15440E-01 + 1.44000E+02 1.26000E+02 1.08000E+02 9.00000E+01 7.20000E+01 5.40000E+01 + 3.60000E+01 1.80000E+01 1.41000E+02 1.23000E+02 1.05000E+02 8.70000E+01 + 6.90000E+01 5.10000E+01 3.30000E+01 1.50000E+01 1.38000E+02 1.20000E+02 + 1.02000E+02 8.40000E+01 6.60000E+01 4.80000E+01 3.00000E+01 1.20000E+01 + 1.35000E+02 1.17000E+02 9.90000E+01 8.10000E+01 6.30000E+01 4.50000E+01 + 2.70000E+01 9.00000E+00 1.32000E+02 1.14000E+02 9.60000E+01 7.80000E+01 + 6.00000E+01 4.20000E+01 2.40000E+01 6.00000E+00 1.29000E+02 1.11000E+02 + 9.30000E+01 7.50000E+01 5.70000E+01 3.90000E+01 2.10000E+01 3.00000E+00 + 1.43000E+02 1.25000E+02 1.07000E+02 8.90000E+01 7.10000E+01 5.30000E+01 + 3.50000E+01 1.70000E+01 1.40000E+02 1.22000E+02 1.04000E+02 8.60000E+01 + 6.80000E+01 5.00000E+01 3.20000E+01 1.40000E+01 1.37000E+02 1.19000E+02 + 1.01000E+02 8.30000E+01 6.50000E+01 4.70000E+01 2.90000E+01 1.10000E+01 + 1.34000E+02 1.16000E+02 9.80000E+01 8.00000E+01 6.20000E+01 4.40000E+01 + 2.60000E+01 8.00000E+00 1.31000E+02 1.13000E+02 9.50000E+01 7.70000E+01 + 5.90000E+01 4.10000E+01 2.30000E+01 5.00000E+00 1.28000E+02 1.10000E+02 + 9.20000E+01 7.40000E+01 5.60000E+01 3.80000E+01 2.00000E+01 2.00000E+00 + 1.42000E+02 1.24000E+02 1.06000E+02 8.80000E+01 7.00000E+01 5.20000E+01 + 3.40000E+01 1.60000E+01 1.39000E+02 1.21000E+02 1.03000E+02 8.50000E+01 + 6.70000E+01 4.90000E+01 3.10000E+01 1.30000E+01 1.36000E+02 1.18000E+02 + 1.00000E+02 8.20000E+01 6.40000E+01 4.60000E+01 2.80000E+01 1.00000E+01 + 1.33000E+02 1.15000E+02 9.70000E+01 7.90000E+01 6.10000E+01 4.30000E+01 + 2.50000E+01 7.00000E+00 1.30000E+02 1.12000E+02 9.40000E+01 7.60000E+01 + 5.80000E+01 4.00000E+01 2.20000E+01 4.00000E+00 1.27000E+02 1.09000E+02 + 9.10000E+01 7.30000E+01 5.50000E+01 3.70000E+01 1.90000E+01 1.00000E+00 + 1.00000E+02 + 4.80000E+01 4.20000E+01 3.60000E+01 3.00000E+01 2.40000E+01 1.80000E+01 + 1.20000E+01 6.00000E+00 4.70000E+01 4.10000E+01 3.50000E+01 2.90000E+01 + 2.30000E+01 1.70000E+01 1.10000E+01 5.00000E+00 4.60000E+01 4.00000E+01 + 3.40000E+01 2.80000E+01 2.20000E+01 1.60000E+01 1.00000E+01 4.00000E+00 + 4.50000E+01 3.90000E+01 3.30000E+01 2.70000E+01 2.10000E+01 1.50000E+01 + 9.00000E+00 3.00000E+00 4.40000E+01 3.80000E+01 3.20000E+01 2.60000E+01 + 2.00000E+01 1.40000E+01 8.00000E+00 2.00000E+00 4.30000E+01 3.70000E+01 + 3.10000E+01 2.50000E+01 1.90000E+01 1.30000E+01 7.00000E+00 1.00000E+00 + diff --git a/tests/unit_tests/weightwindows/wwinp_p b/tests/unit_tests/weightwindows/wwinp_p new file mode 100644 index 0000000000..a9f9f98dff --- /dev/null +++ b/tests/unit_tests/weightwindows/wwinp_p @@ -0,0 +1,48 @@ + 1 1 2 10 + 0 4 + 6.0 8.0 1.0 -1.00000E+02 -1.00000E+02 -5.00000E+01 + 6.0 8.0 1.0 1.0 + -1.00000E+02 1.00000E+00 -6.66667E+01 1.00000E+00 1.00000E+00 -3.33333E+01 + 1.00000E+00 1.00000E+00 0.00000E+00 1.00000E+00 1.00000E+00 3.33333E+01 + 1.00000E+00 1.00000E+00 6.66667E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -1.00000E+02 1.00000E+00 -5.00000E+01 1.00000E+00 1.00000E+00 -1.33333E+01 + 1.00000E+00 1.00000E+00 2.33333E+01 1.00000E+00 1.00000E+00 6.00000E+01 + 1.00000E+00 1.00000E+00 7.00000E+01 1.00000E+00 1.00000E+00 8.00000E+01 + 1.00000E+00 1.00000E+00 9.00000E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -5.00000E+01 1.00000E+00 5.00000E+01 1.00000E+00 + 1.00000E-01 1.46780E-01 2.15440E-01 3.16230E-01 + 1.92000E+02 1.60000E+02 1.28000E+02 9.60000E+01 6.40000E+01 3.20000E+01 + 1.88000E+02 1.56000E+02 1.24000E+02 9.20000E+01 6.00000E+01 2.80000E+01 + 1.84000E+02 1.52000E+02 1.20000E+02 8.80000E+01 5.60000E+01 2.40000E+01 + 1.80000E+02 1.48000E+02 1.16000E+02 8.40000E+01 5.20000E+01 2.00000E+01 + 1.76000E+02 1.44000E+02 1.12000E+02 8.00000E+01 4.80000E+01 1.60000E+01 + 1.72000E+02 1.40000E+02 1.08000E+02 7.60000E+01 4.40000E+01 1.20000E+01 + 1.68000E+02 1.36000E+02 1.04000E+02 7.20000E+01 4.00000E+01 8.00000E+00 + 1.64000E+02 1.32000E+02 1.00000E+02 6.80000E+01 3.60000E+01 4.00000E+00 + 1.91000E+02 1.59000E+02 1.27000E+02 9.50000E+01 6.30000E+01 3.10000E+01 + 1.87000E+02 1.55000E+02 1.23000E+02 9.10000E+01 5.90000E+01 2.70000E+01 + 1.83000E+02 1.51000E+02 1.19000E+02 8.70000E+01 5.50000E+01 2.30000E+01 + 1.79000E+02 1.47000E+02 1.15000E+02 8.30000E+01 5.10000E+01 1.90000E+01 + 1.75000E+02 1.43000E+02 1.11000E+02 7.90000E+01 4.70000E+01 1.50000E+01 + 1.71000E+02 1.39000E+02 1.07000E+02 7.50000E+01 4.30000E+01 1.10000E+01 + 1.67000E+02 1.35000E+02 1.03000E+02 7.10000E+01 3.90000E+01 7.00000E+00 + 1.63000E+02 1.31000E+02 9.90000E+01 6.70000E+01 3.50000E+01 3.00000E+00 + 1.90000E+02 1.58000E+02 1.26000E+02 9.40000E+01 6.20000E+01 3.00000E+01 + 1.86000E+02 1.54000E+02 1.22000E+02 9.00000E+01 5.80000E+01 2.60000E+01 + 1.82000E+02 1.50000E+02 1.18000E+02 8.60000E+01 5.40000E+01 2.20000E+01 + 1.78000E+02 1.46000E+02 1.14000E+02 8.20000E+01 5.00000E+01 1.80000E+01 + 1.74000E+02 1.42000E+02 1.10000E+02 7.80000E+01 4.60000E+01 1.40000E+01 + 1.70000E+02 1.38000E+02 1.06000E+02 7.40000E+01 4.20000E+01 1.00000E+01 + 1.66000E+02 1.34000E+02 1.02000E+02 7.00000E+01 3.80000E+01 6.00000E+00 + 1.62000E+02 1.30000E+02 9.80000E+01 6.60000E+01 3.40000E+01 2.00000E+00 + 1.89000E+02 1.57000E+02 1.25000E+02 9.30000E+01 6.10000E+01 2.90000E+01 + 1.85000E+02 1.53000E+02 1.21000E+02 8.90000E+01 5.70000E+01 2.50000E+01 + 1.81000E+02 1.49000E+02 1.17000E+02 8.50000E+01 5.30000E+01 2.10000E+01 + 1.77000E+02 1.45000E+02 1.13000E+02 8.10000E+01 4.90000E+01 1.70000E+01 + 1.73000E+02 1.41000E+02 1.09000E+02 7.70000E+01 4.50000E+01 1.30000E+01 + 1.69000E+02 1.37000E+02 1.05000E+02 7.30000E+01 4.10000E+01 9.00000E+00 + 1.65000E+02 1.33000E+02 1.01000E+02 6.90000E+01 3.70000E+01 5.00000E+00 + 1.61000E+02 1.29000E+02 9.70000E+01 6.50000E+01 3.30000E+01 1.00000E+00 + diff --git a/tests/unit_tests/weightwindows/wwinp_t b/tests/unit_tests/weightwindows/wwinp_t new file mode 100644 index 0000000000..a12668aa4d --- /dev/null +++ b/tests/unit_tests/weightwindows/wwinp_t @@ -0,0 +1,49 @@ + 1 2 2 10 + 1 1 + 0 4 + 6.0 8.0 1.0 -1.00000E+02 -1.00000E+02 -5.00000E+01 + 6.0 8.0 1.0 1.0 + -1.00000E+02 1.00000E+00 -6.66667E+01 1.00000E+00 1.00000E+00 -3.33333E+01 + 1.00000E+00 1.00000E+00 0.00000E+00 1.00000E+00 1.00000E+00 3.33333E+01 + 1.00000E+00 1.00000E+00 6.66667E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + -1.00000E+02 1.00000E+00 -5.00000E+01 1.00000E+00 1.00000E+00 -1.33333E+01 + 1.00000E+00 1.00000E+00 2.33333E+01 1.00000E+00 1.00000E+00 6.00000E+01 + 1.00000E+00 1.00000E+00 7.00000E+01 1.00000E+00 1.00000E+00 8.00000E+01 + 1.00000E+00 1.00000E+00 9.00000E+01 1.00000E+00 1.00000E+00 1.00000E+02 + 1.00000E+00 + 1.00000E+02 + -5.00000E+01 1.00000E+00 5.00000E+01 1.00000E+00 + 1.00000E-01 1.46780E-01 2.15440E-01 3.16230E-01 + 1.92000E+02 1.60000E+02 1.28000E+02 9.60000E+01 6.40000E+01 3.20000E+01 + 1.88000E+02 1.56000E+02 1.24000E+02 9.20000E+01 6.00000E+01 2.80000E+01 + 1.84000E+02 1.52000E+02 1.20000E+02 8.80000E+01 5.60000E+01 2.40000E+01 + 1.80000E+02 1.48000E+02 1.16000E+02 8.40000E+01 5.20000E+01 2.00000E+01 + 1.76000E+02 1.44000E+02 1.12000E+02 8.00000E+01 4.80000E+01 1.60000E+01 + 1.72000E+02 1.40000E+02 1.08000E+02 7.60000E+01 4.40000E+01 1.20000E+01 + 1.68000E+02 1.36000E+02 1.04000E+02 7.20000E+01 4.00000E+01 8.00000E+00 + 1.64000E+02 1.32000E+02 1.00000E+02 6.80000E+01 3.60000E+01 4.00000E+00 + 1.91000E+02 1.59000E+02 1.27000E+02 9.50000E+01 6.30000E+01 3.10000E+01 + 1.87000E+02 1.55000E+02 1.23000E+02 9.10000E+01 5.90000E+01 2.70000E+01 + 1.83000E+02 1.51000E+02 1.19000E+02 8.70000E+01 5.50000E+01 2.30000E+01 + 1.79000E+02 1.47000E+02 1.15000E+02 8.30000E+01 5.10000E+01 1.90000E+01 + 1.75000E+02 1.43000E+02 1.11000E+02 7.90000E+01 4.70000E+01 1.50000E+01 + 1.71000E+02 1.39000E+02 1.07000E+02 7.50000E+01 4.30000E+01 1.10000E+01 + 1.67000E+02 1.35000E+02 1.03000E+02 7.10000E+01 3.90000E+01 7.00000E+00 + 1.63000E+02 1.31000E+02 9.90000E+01 6.70000E+01 3.50000E+01 3.00000E+00 + 1.90000E+02 1.58000E+02 1.26000E+02 9.40000E+01 6.20000E+01 3.00000E+01 + 1.86000E+02 1.54000E+02 1.22000E+02 9.00000E+01 5.80000E+01 2.60000E+01 + 1.82000E+02 1.50000E+02 1.18000E+02 8.60000E+01 5.40000E+01 2.20000E+01 + 1.78000E+02 1.46000E+02 1.14000E+02 8.20000E+01 5.00000E+01 1.80000E+01 + 1.74000E+02 1.42000E+02 1.10000E+02 7.80000E+01 4.60000E+01 1.40000E+01 + 1.70000E+02 1.38000E+02 1.06000E+02 7.40000E+01 4.20000E+01 1.00000E+01 + 1.66000E+02 1.34000E+02 1.02000E+02 7.00000E+01 3.80000E+01 6.00000E+00 + 1.62000E+02 1.30000E+02 9.80000E+01 6.60000E+01 3.40000E+01 2.00000E+00 + 1.89000E+02 1.57000E+02 1.25000E+02 9.30000E+01 6.10000E+01 2.90000E+01 + 1.85000E+02 1.53000E+02 1.21000E+02 8.90000E+01 5.70000E+01 2.50000E+01 + 1.81000E+02 1.49000E+02 1.17000E+02 8.50000E+01 5.30000E+01 2.10000E+01 + 1.77000E+02 1.45000E+02 1.13000E+02 8.10000E+01 4.90000E+01 1.70000E+01 + 1.73000E+02 1.41000E+02 1.09000E+02 7.70000E+01 4.50000E+01 1.30000E+01 + 1.69000E+02 1.37000E+02 1.05000E+02 7.30000E+01 4.10000E+01 9.00000E+00 + 1.65000E+02 1.33000E+02 1.01000E+02 6.90000E+01 3.70000E+01 5.00000E+00 + 1.61000E+02 1.29000E+02 9.70000E+01 6.50000E+01 3.30000E+01 1.00000E+00 diff --git a/tools/ci/gha-install-dagmc.sh b/tools/ci/gha-install-dagmc.sh index 82759c9bcc..8d0648a504 100755 --- a/tools/ci/gha-install-dagmc.sh +++ b/tools/ci/gha-install-dagmc.sh @@ -3,7 +3,7 @@ set -ex # MOAB Variables -MOAB_BRANCH='Version5.1.0' +MOAB_BRANCH='5.5.1' MOAB_REPO='https://bitbucket.org/fathomteam/moab/' MOAB_INSTALL_DIR=$HOME/MOAB/ @@ -19,7 +19,7 @@ cd $HOME mkdir MOAB && cd MOAB git clone -b $MOAB_BRANCH $MOAB_REPO mkdir build && cd build -cmake ../moab -DENABLE_HDF5=ON -DENABLE_NETCDF=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$MOAB_INSTALL_DIR -DENABLE_BLASLAPACK=OFF +cmake ../moab -DENABLE_HDF5=ON -DENABLE_NETCDF=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$MOAB_INSTALL_DIR make -j && make -j install rm -rf $HOME/MOAB/moab $HOME/MOAB/build diff --git a/tools/ci/gha-install-libmesh.sh b/tools/ci/gha-install-libmesh.sh index 132963820d..d4557d2d3a 100755 --- a/tools/ci/gha-install-libmesh.sh +++ b/tools/ci/gha-install-libmesh.sh @@ -5,7 +5,7 @@ set -ex # libMESH install pushd $HOME mkdir LIBMESH && cd LIBMESH -git clone https://github.com/libmesh/libmesh -b v1.6.0 --recurse-submodules +git clone https://github.com/libmesh/libmesh -b v1.7.1 --recurse-submodules mkdir build && cd build export METHODS="opt" @@ -16,7 +16,6 @@ else ../libmesh/configure --prefix=$HOME/LIBMESH --enable-exodus --disable-netcdf-4 --disable-eigen --disable-lapack --disable-mpi fi make -j4 install -export LIBMESH_PC=$HOME/LIBMESH/lib/pkgconfig/ rm -rf $HOME/LIBMESH/build popd diff --git a/tools/ci/gha-install-vectfit.sh b/tools/ci/gha-install-vectfit.sh index 343942e03d..bd38e1ea8c 100755 --- a/tools/ci/gha-install-vectfit.sh +++ b/tools/ci/gha-install-vectfit.sh @@ -4,10 +4,10 @@ set -ex PYBIND_BRANCH='master' PYBIND_REPO='https://github.com/pybind/pybind11' -XTL_BRANCH='0.6.9' +XTL_BRANCH='0.6.13' XTL_REPO='https://github.com/xtensor-stack/xtl' -XTENSOR_BRANCH='0.21.2' +XTENSOR_BRANCH='0.21.3' XTENSOR_REPO='https://github.com/xtensor-stack/xtensor' XTENSOR_PYTHON_BRANCH='0.24.1' @@ -16,8 +16,6 @@ XTENSOR_PYTHON_REPO='https://github.com/xtensor-stack/xtensor-python' XTENSOR_BLAS_BRANCH='0.17.1' XTENSOR_BLAS_REPO='https://github.com/xtensor-stack/xtensor-blas' -sudo apt-get install -y libblas-dev liblapack-dev - cd $HOME git clone -b $PYBIND_BRANCH $PYBIND_REPO cd pybind11 && mkdir build && cd build && cmake .. && sudo make install @@ -39,7 +37,10 @@ cd $HOME git clone -b $XTENSOR_BLAS_BRANCH $XTENSOR_BLAS_REPO cd xtensor-blas && mkdir build && cd build && cmake .. && sudo make install +# Install wheel (remove when vectfit supports installation with build isolation) +pip install wheel + # Install vectfit cd $HOME git clone https://github.com/liangjg/vectfit.git -pip install ./vectfit +pip install --no-build-isolation ./vectfit diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 9c163c1dcb..1cc792f8d7 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -2,22 +2,6 @@ import os import shutil import subprocess -def which(program): - def is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - return None - def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): # Create build directory and change to it @@ -25,16 +9,16 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): os.mkdir('build') os.chdir('build') - # Build in debug mode by default - cmake_cmd = ['cmake', '-Ddebug=on'] + # Build in debug mode by default with support for MCPL + cmake_cmd = ['cmake', '-DCMAKE_BUILD_TYPE=Debug', '-DOPENMC_USE_MCPL=on'] # Turn off OpenMP if specified if not omp: - cmake_cmd.append('-Dopenmp=off') + cmake_cmd.append('-DOPENMC_USE_OPENMP=off') # Use MPI wrappers when building in parallel if mpi: - os.environ['CXX'] = 'mpicxx' + cmake_cmd.append('-DOPENMC_USE_MPI=on') # Tell CMake to prefer parallel HDF5 if specified if phdf5: @@ -46,16 +30,18 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): cmake_cmd.append('-DHDF5_PREFER_PARALLEL=OFF') if dagmc: - cmake_cmd.append('-Ddagmc=ON') - cmake_cmd.append('-DCMAKE_PREFIX_PATH=~/DAGMC') + cmake_cmd.append('-DOPENMC_USE_DAGMC=ON') + cmake_cmd.append('-DOPENMC_USE_UWUW=ON') + dagmc_path = os.environ.get('HOME') + '/DAGMC' + cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + dagmc_path) if libmesh: - cmake_cmd.append('-Dlibmesh=ON') + cmake_cmd.append('-DOPENMC_USE_LIBMESH=ON') libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) # Build in coverage mode for coverage testing - cmake_cmd.append('-Dcoverage=on') + cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') # Build and install cmake_cmd.append('..') diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index aa40eb90b1..74c3947f18 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -2,12 +2,9 @@ set -ex # Upgrade pip, pytest, numpy before doing anything else. -# TODO: numpy 1.22 results in several failing tests, so we force a lower version -# for now (similar change made in pyproject.toml). When this is removed, those -# tests will need to be updated. pip install --upgrade pip pip install --upgrade pytest -pip install --upgrade "numpy<1.22" +pip install --upgrade numpy # Install NJOY 2016 ./tools/ci/gha-install-njoy.sh @@ -17,6 +14,10 @@ if [[ $DAGMC = 'y' ]]; then ./tools/ci/gha-install-dagmc.sh fi +# Install NCrystal and verify installation +pip install 'ncrystal>=4.1.0' +nctool --test + # Install vectfit for WMP generation if needed if [[ $VECTFIT = 'y' ]]; then ./tools/ci/gha-install-vectfit.sh @@ -27,6 +28,9 @@ if [[ $LIBMESH = 'y' ]]; then ./tools/ci/gha-install-libmesh.sh fi +# Install MCPL +pip install mcpl + # For MPI configurations, make sure mpi4py and h5py are built against the # correct version of MPI if [[ $MPI == 'y' ]]; then @@ -42,10 +46,4 @@ fi python tools/ci/gha-install.py # Install Python API in editable mode -pip install -e .[test,vtk] - -# For coverage testing of the C++ source files -pip install cpp-coveralls - -# For coverage testing of the Python source files -pip install coveralls +pip install -e .[test,vtk,ci] diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index c791167e99..b40238ffb5 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -8,11 +8,14 @@ args=" " if [[ $MPI == 'y' ]]; then args="${args} --mpi " fi - + # Check for event-based if [[ $EVENT == 'y' ]]; then args="${args} --event " fi -# Run regression and unit tests -pytest --cov=openmc -v $args tests +# Run unit tests and then regression tests +pytest -v $args \ + tests/test_matplotlib_import.py \ + tests/unit_tests \ + tests/regression_tests diff --git a/tools/dev/generate_release_notes.py b/tools/dev/generate_release_notes.py new file mode 100644 index 0000000000..dee46ac49e --- /dev/null +++ b/tools/dev/generate_release_notes.py @@ -0,0 +1,17 @@ +import argparse +import re +import subprocess + +parser = argparse.ArgumentParser() +parser.add_argument('tag') +args = parser.parse_args() + +proc = subprocess.run(["git", "log", "--format=%s", f"{args.tag}.."], capture_output=True, text=True) +data = [] +for line in proc.stdout.rstrip().split('\n'): + m = re.match(r'(.*) \(\#(\d+)\)', line) + if m is not None: + data.append(m.groups()) + +for comment, num in sorted(data, key=lambda x: int(x[1])): + print(f'- {comment} (`#{num} `_)') diff --git a/vendor/Catch2 b/vendor/Catch2 new file mode 160000 index 0000000000..5a40b2275c --- /dev/null +++ b/vendor/Catch2 @@ -0,0 +1 @@ +Subproject commit 5a40b2275caa05cf809bf04df848764a9d7df2e2 diff --git a/vendor/fmt b/vendor/fmt index d141cdbeb0..0c9fce2ffe 160000 --- a/vendor/fmt +++ b/vendor/fmt @@ -1 +1 @@ -Subproject commit d141cdbeb0fb422a3fb7173b285fd38e0d1772dc +Subproject commit 0c9fce2ffefecfdce794e1859584e25877b7b592 diff --git a/vendor/gsl-lite b/vendor/gsl-lite deleted file mode 160000 index 913e86d49c..0000000000 --- a/vendor/gsl-lite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 913e86d49c6a1acca980f4e325378f9dc393493a diff --git a/vendor/pugixml b/vendor/pugixml index 41b6ff21c4..ee86beb30e 160000 --- a/vendor/pugixml +++ b/vendor/pugixml @@ -1 +1 @@ -Subproject commit 41b6ff21c455865bb8ef67c5952b7f895b62bacc +Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38 diff --git a/vendor/xtensor b/vendor/xtensor index 31acec1e90..3634f2ded1 160000 --- a/vendor/xtensor +++ b/vendor/xtensor @@ -1 +1 @@ -Subproject commit 31acec1e90bbea6d4bc17af0710a123bd5da6689 +Subproject commit 3634f2ded19e0cf38208c8b86cea9e1d7c8e397d diff --git a/vendor/xtl b/vendor/xtl index c19750fb14..a7c1c5444d 160000 --- a/vendor/xtl +++ b/vendor/xtl @@ -1 +1 @@ -Subproject commit c19750fb1488369dc41f6069bc2b8446fc093e75 +Subproject commit a7c1c5444dfc57f76620391af4c94785ff82c8d6